Spørgsmål:
Hvordan håndteres fejl (ikke relateret til syntaks) i arduino og i AVR-arkitekturen generelt?
Coder_fox
2018-11-29 01:39:23 UTC
view on stackexchange narkive permalink

Jeg var bare nysgerrig efter, hvordan AVR-arkitekturen administrerer fejl, der ville få et almindeligt skrivebordsprogram til at gå ned. Jeg taler om logiske fejl, for eksempel matematiske problemer, der er udefinerede. Såsom at dele med 0 og få en kvadratrode af et negativt tal. Først forventede jeg, at ved at give en fejl til AVR-chippen, ville den bare returnere 0. Men jeg kørte dette program:

  void setup () {Serial.begin (9600);} void loop () {int x = 0; int p = 50; int resultat; resultat = p / x; Serial.println (resultat); resultat = sqrt (-10); forsinkelse (1000); Serial.println (resultat); forsinkelse (10000);}  

og fik denne output:

  42949672950  

Efter dette blev jeg virkelig forvirret . Hvorfor tager resultat variablen den maksimale værdi af en usigneret lang ? Da jeg erklærede en int , skal mikrocontrolleren have dedikeret 2 bytes dynamisk hukommelse til denne variabel, men tilsyneladende lykkedes det på en eller anden måde at få yderligere 2 byte til at gemme dataene. Kan dette betyde, at AVR-chippen kan ødelægge data på andre hukommelsesplaceringer, mens de allokerer nye data til den variabel, der netop blev tilført udefineret til den? Okay måske sætter AVR-chippen bare matematisk vrøvl til 4294967295. Men nej i eksemplet med at få kvadratroden på -10 ser vi, at værdien blev 0. Selvom dette sandsynligvis er en funktion, der behandles af et bibliotek, så måske er der en slags beskyttelse mod disse fejl. Jeg forsøgte også at køre programmet ovenfor, men med en byte -variabel til -resultatet i stedet for int.

  ugyldig opsætning () {Seriel.begynder (9600);} ugyldig sløjfe () {int x = 0; int p = 50; int resultat; resultat = p / x; Serial.println (resultat); resultat = sqrt ( -10); forsinkelse (1000); Serial.println (resultat); forsinkelse (10000);}  

Og resultatet blev det samme. Så er der en slags dokumentation om fejlhåndtering i AVR, da dette er vigtigt at vide under udvikling.

P.S. Alt blev kørt på en ægte Atmega 328p chip på en arduino nano.

Jeg tror, ​​at standarden er, at den er udefineret til heltal matematik. Flydepunktet er i henhold til IEEE-standarden og returnerer en nan, men ingen undtagelse. At fange en undtagelse i Arduino er ikke mulig (tror jeg): https://stackoverflow.com/questions/10095591/enable-exceptions-in-the-arduino-environment Når du finder mærkelige ting med en byte, bedes du give en fuld tegne, at vi kan prøve at fortælle os, hvilket arduino-kort du bruger.
@Jot Dette er et interessant punkt, men hvorfor skulle en nan erstattes med 4294967295. De har tilsyneladende ikke den samme binære værdi. Hvorfor inkluderede Atmel ikke en nan-værdi (som måske kunne deaktiveres gennem sikringer) i deres processorarkitektur. Dette er en nyttig fejlfindingsfunktion, der bruges meget i alle større programmeringssprog.
-1
Et heltal kan ikke være et 'nan', der er angivet for flydende punkt. Opdater dit spørgsmål med kort og mikrocontroller og måske en komplet skitse.
Jeg forsøgte selv at lave en skitse, og jeg har forskellige resultater. Vis din skitse, og fortæl os resultaterne af den skitse, du viser. Kører du dette i en simulator?
@Jot Nej, alt køres på en rigtig arduino.
Tak for den fulde skitse. Jeg har tilføjet et svar. Du er stødt på en meget underlig compiler-fejl, der forårsager 4294967295. Nu forstår jeg, hvorfor min kode ikke viste dette nummer, og din kode gør det.
To svar:
Edgar Bonet
2018-11-29 02:36:52 UTC
view on stackexchange narkive permalink

Det enkle svar er: de håndteres slet ikke.

I henhold til C- og C ++ -standarderne kaldes det, du påberåber sig, udefineret adfærd , hvilket betyder, at alt kan ske. I praktiske termer betyder det, at kompilatoren kan udføre sine optimeringer forudsat at du aldrig vil påberåbe sig udefineret adfærd og ignorere fuldstændigt, hvad der kunne ske, hvis antagelsen ikke holder. Den genererede kode kunne være helt brudt, det vil ikke være kompilatorens skyld.

AVR-arkitekturen kan ikke foretage divisioner eller flydende matematik. Således sker alle de fejl, du har her, på softwareniveau. Bemærk, at sqrt (-10) er ikke en fejl (det er NaN, som håndteres korrekt af avr-libc), men konverterer det til en int (som du gør når du tildeler det til resultat ) er en fejl.

Hvis du virkelig vil vide detaljerne om, hvad der sker med hver af disse fejl, er der kun en mulighed: du skal adskille den eksekverbare programmere og læse forsamlingslisten.

_ "... udefineret adfærd, hvilket betyder, at alt kan ske." _ - Det er værd at understrege, at dette helt er en C / C ++-ting, der ikke er relateret til AVR-chips. Den nøjagtige samme advarsel gælder for kode, der kører på f.eks. En Intel i5.
Det er også værd at bemærke, at mens C ++ - standarden siger, at compileren kan gøre * hvad som helst *, definerer C ++ - compilere typisk yderligere adfærd ud over C ++ - specifikationen. For eksempel definerer Visual Studios kompilator adfærden for 'x / 0', som ikke var defineret i C ++ spec, baseret på hvad den ved om arkitekturen. Dette er virkelig nyttigt, når du vil gøre noget, som specifikationen forbyder, men din særlige kompilator tillader det, f.eks. Gcc-udvidelser.
Jot
2018-11-29 12:40:48 UTC
view on stackexchange narkive permalink

Om nummeret 4294967295

Til min overraskelse producerer skitsen faktisk nummeret 4294967295. Det er ikke okay.
Det viser sig, at kompilatoren kender tallene og forsøger at lave matematikken selv (i stedet for runtime).

Ved runtime resulterer 50/0 i et underskrevet heltal på -1.
Når en byte er brugt, resulterer 50/0 i 255.
Resultatet af denne beregning er en variabel med kun 1'er. Det er 0xFFFF for en 16-bit variabel. Det resulterer i 65535 for et usigneret heltal og -1 for et underskrevet heltal og 255 for et usigneret byte.

Indtil videre er det okay. Det giver mening.

Men når compileren forsøger at lave matematikken selv med 50/0 , forvandler den resultatet til en 32-bit variabel på 0xFFFFFFFF. Compileren lægger ikke matematikken ind i det binære resultat, men kalder Serial.println () direkte med en 32-bit usigneret længde på 0xFFFFFFFF, hvilket er 4294967295.

Årsagen er ikke den variable type variablen ' resultat ', men delingen med ' p / x '. Compileren mener, at det er bedre at lave et 32-bit heltal, når matematikken er færdig med to 16-bit heltal.

Jeg synes, at når kompilatoren optimeres, skal resultatet være det samme som en kørselstid.
Der er en interessant diskussion under dette svar. @EdgarBonet kalder kompilatorens adfærd underlig, men ikke en fejl.

Nedenfor er en testskitse, der producerer 4294967295 med Arduino 1.8.7 til en Arduino Uno:

  ugyldig opsætning ( ) {Serial.begin (9600);} ugyldig loop () {int x = 0; int p = 50; int-resultat; resultat = p / x; Serial.println (resultat); forsinkelse (5000);}  

Koden udsender normale resultater, når 'x' eller 'p' beregnes, for eksempel resultatet fra en funktion, eller når en af ​​de tre variabler er lavet flygtige.

Compilerens håndtering af udefineret adfærd er _ aldrig_ en fejl, fordi det, per definition, uanset hvad compileren gør er acceptabelt i henhold til sprogstandarden.
@EdgarBonet Jeg synes, det skal gøre hvad som helst på samme måde. Efter din mening kan kompilatoren indsætte kode for at fløjte en melodi, når den divideres med nul? Jeg tror ikke det. At ændre en int til usigneret lang er efter min mening for underlig.
Selvom compileren måske ikke _bevidst_ gør dumme ting, sker der mærkelige ting, når du påberåber udefineret adfærd. Se for eksempel begyndelsen på [dette blogindlæg] (http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html) (hele indlægsserien er en must-read til enhver C- eller C ++ -programmerer). Mærkelig? Ja. Insekt? Ingen.
@EdgarBonet, tak, interessant at læse det. De ting, de nævner, er ligetil. Hvad der sker her er, at runtime kaldes en bestemt funktion, og når compileren optimerer den, kaldes en anden (overbelastet) funktion. Det er et skridt videre ind i underlighed.
_ "Efter din mening kan kompilatoren indsætte kode for at fløjte en melodi, når den divideres med nul? Jeg tror ikke det." _ - Når du påberåber udefineret adfærd, går alt, inklusive uventet store variabler, melodiflytning eller formatering af din hårde køre. Velkommen til den vidunderlige verden af ​​C og C ++. Det ville dog være interessant at se, om den samme "variable promotion" forekommer uden UB.
Det sker kun, når x er nul, og p er ikke-nul. Når 4294967295 udskrives, er størrelsen på (resultat) stadig 2.


Denne spørgsmål og svar blev automatisk oversat fra det engelske sprog.Det originale indhold er tilgængeligt på stackexchange, som vi takker for den cc by-sa 4.0-licens, den distribueres under.
Loading...