Close

Linux x Windows rozdíly v kompilaci?

Úvodní stránka Fórum Hardware Arduino Linux x Windows rozdíly v kompilaci?

Aktuálně je na stránce zobrazeno 6 příspěvků - 1. až 6. (celkem z 6)
  • Autor
    Příspěvky
  • #10466
    schamann
    Účastník

    Zdravim objevil jsem ve svem kodu chybu a zajimao by me cim to?

    Setup {
     if (TestFunction()) {
       Serial.print("OK");
     } else {
       Serial.print("FALSE");
     }
    }
    
    boolean TestFunction() {
      if (1 == 1) {
         //return true; // ano, zaremovane
     } else {
        return false;
     } 
    }

    Pouzivam Arduino MEGA.
    Na WIN kompilaci dostanu odpoved OK
    na LIN kompilaci dostanu odpoved FALSE

    Predpokladam, ze spravna odpoved je FALSE
    Tak me jen prekvapilo, jak je mozne, ze stejny kod zkompilovany na WIN reaguje jinak mez program zkompilovany na Linuxu.

    boolean je typ int, a vyhodnocovana podminka se pta jestli (TestFunction() == true)
    pokud vim, tak false = 0 a cokoilv jineho nez 0 je vzdy true.
    Takze mam 2 vysvetleni:
    1,bud deklarovanim boolean funkce vubec promenna boolean nevznikne a hodnota se priradi az prikazem return pak je podminka if ( == true)… a to pak kazda kompilace vyhodnoti jinak

    2, LIN pri kompilaci definuje boolen jako false, a WIN jako true.

    Tak nevim co s tim,

    zacal jsem se v tom trosku vrtat a mimojine me prekvapilo, ze kompilace na linuxu je mensi nez stehny kod z win.
    Takze vysledek asi nebude uplne identicky.

    programuju doma na win, ale v terenu upgraduju z lin notebooku. Nerad bych se dockal dalsich prekvapeni…

    Je takovych znamych rozdilu v LIN x WIN více?

    #10471
    Vojtěch Vosáhlo
    Účastník

    Jak můžete říct že správná odpověď je false? Ten kód koukne na to jestli se 1==1, řekne ano ale pak nic nevrací, tzn na return nikdy nenarazí. Může si teda vrátit co chce.
    Řešení je v celku jednoduché. Vždy používat return ve všech částech kde by jste měl. Mnoho IDE by vás to ani nenechalo zkompilovat, vyjela by hláška jako „Not all parts of the code return a value.“ Pokud nic nenastavíte, program si asi dělá trochu co chce.

    Jinak tu funkce upravená tak aby vždy něco vrátila by vapadala takto ale to asi víte.

    boolean TestFunction() {
      if (true) {
         return true;
         break;
          
     }
        return false;
    }

    Pokud to je zakomentované tak samozřejmě nevrátí nic…

    #10473
    schamann
    Účastník

    To ja vim, ze nic nevrati… Otazka byla proc to kazdy kompilator zpracuje jinak, ve win dostanu hlasku OK, a v Lin dostanu Hlasku False. Tedy, ja testuju vysledek Testfunction(), ktery je prazdny. a zajima me proc je vyhodnoceni v ruznych kompilacich ruzne. nikoli to, ze je to samo o sobe blbost.

    Mimochodem nevrati „co chce“, vrati neco co win vyhodnoti jako true tedy jakoukoliv hodnotu mimo 0 a pod linuxovou kompilaci vrati 0 protoze vysledek je FALSE. A nebo vrati NULL a pak to kazdy kompilator vyhodnoti jinak a to je to co me zajima.

    Muj asi mylny predpoklad na FALSE pochazi z Visual Studia kde programuju uz 20 let. Pokud v Basicu deklaruji funkci Boolean, je vzdy vysledek false, pokud mu neridam hodnotu true.

    #10475
    schamann
    Účastník

    Mozna mela puvodni otazka byt jinak postavena:
    Je konstrukce if (Testfunction()) { sama o sobe spravna? neni bezpecnejsi pouzit treba jiny operator?

    #10476
    Zbyšek Voda
    Správce

    Dobrý den, mě ta otázka přijde položená docela jasně 🙂

    Pouštím se teď na trochu tenký led, tak snad neplácnu nějakou blbost.

    Podle mě jde o to, že norma C++ nevynucuje návrat hodnoty z funkce pomocí return (i když tato funkce není void).Potom tedy závisí na kompilátoru, jak si s tím poradí. Použitý kompilátor pro Arduino je avr-gcc, který se ale asi na obou platformách chová trochu odlišně (právě v takovýchto normou nedefinovaných případech).

    Podle mě může dojít ke dvěma možnostem:
    1) Na linuxu je defaultní hodnota z funkcí bez return nulová, na Windows nenulová.
    Tato možnost ale asi není moc pravděpodobná.

    2) V https://gcc.gnu.org/wiki/avr-gcc píší, že jednobytové hodnoty se vrací pomocí registrů (ATmega v Arduinech je většinou 8-bitová, takže má i 8-bit registry). Boolean je jenom jiné pojmenování pro bezznaménkové osmibitové číslo, takže se asi vrací v registrech. A právě registry v tom podle mě dělají ten rozdíl – na linuxu se kompilátor chová tak, že registr použitý k návratu vynuluje, kdežto na Windows ne. Díky tomu v registru zůstane hodnota, která už tam byla před voláním funkce (a s největší pravděpodobností je nenulová), takže pak dostanete odpověď OK.

    Aspoň takto si to vysvětluji.

    Použití if (Testfunction()) je v pořádku. Můžete si představit, že kompilátor toto vidí jako if (Testfunction() != 0).

    #10480
    Zbyšek Voda
    Správce

    Začal jsem to zkoumat a je to celkem zajímavý problém.

    Zkusil jsem udělat úplně jednoduchou non-void funkci, která ale, stejně jako u vás, nic nevrací. Ta funkce vypadá takto:

    #include <stdint.h>
    
    uint8_t fce(){
        
    }
    
    int main(){
        uint8_t a = fce();
        
    	return 0;
    }

    Použil jsem uint8_t, protože boolean je vlastně „převlečený“ typ uint_8 – viz:
    typedef uint8_t boolean;

    Když tento miniprogram proženu přes kompilátor avr-gcc a zadám přepínače pro výstup v assembleru, získám kód níže. Příkaz pro spuštění kompilace je takovýto (jsem na MacOS):
    /Applications/Arduino.app/Contents/Java/hardware/tools/avr/bin/avr-gcc -S main.c

    	.file	"main.c"
    __SP_H__ = 0x3e
    __SP_L__ = 0x3d
    __SREG__ = 0x3f
    __tmp_reg__ = 0
    __zero_reg__ = 1
    	.text
    .global	fce
    	.type	fce, @function
    fce:
    	push r28
    	push r29
    	in r28,__SP_L__
    	in r29,__SP_H__
    /* prologue: function */
    /* frame size = 0 */
    /* stack size = 2 */
    .L__stack_usage = 2
    /* epilogue start */
    	pop r29
    	pop r28
    	ret
    	.size	fce, .-fce
    .global	main
    	.type	main, @function
    main:
    	push r28
    	push r29
    	push __zero_reg__
    	in r28,__SP_L__
    	in r29,__SP_H__
    /* prologue: function */
    /* frame size = 1 */
    /* stack size = 3 */
    .L__stack_usage = 3
    	rcall fce
    	std Y+1,r24
    	ldi r24,0
    	ldi r25,0
    /* epilogue start */
    	pop __tmp_reg__
    	pop r29
    	pop r28
    	ret
    	.size	main, .-main
    	.ident	"GCC: (GNU) 4.9.2"
    

    K pochopení toho, co se děje, stačí najít řádek std Y+1,r24. Toto je instrukce v assembleru (viz . Slouží k tomu, že se někam do paměti na adresu danou Y+1 (proměnná a v programu) uloží hodnota z registru r24. Když se ale na program podíváte, do tohoto registru se předtím nic neukládá, ani se nenuluje, takže je hodnota nedefinovaná.

    Tento příklad je zjednodušený, v případě při kompilaci pro Arduino dojde k připojení několika dalších funkcí. Pro ilustraci je to ale myslím dobré 🙂

    Podle mě tedy dojde k tomu, že při kompilaci na Linuxu se ještě přidá instrukce pro vynulování registru r24. Je to ale jenom teorie. Můžete vyzkoušet, docela by mě zajímalo, jestli ten assemblerový kód vypadá stejně 🙂

Aktuálně je na stránce zobrazeno 6 příspěvků - 1. až 6. (celkem z 6)
  • Pro reakci na toto téma se musíte přihlásit.