Just realised while looking at the actual value of some ARM register that my code cannot work properly if i try to add 0xff to a 32-bit value to decrement it. I swear i've been using a char though. And that was precisely my mistake. I overlooked that, unless ints, chars signedness isn't defined by the C (or C++) standard, but rather by the platform's implementation. And x86 and ARM implementations differ, for that matter. So the code above might display "-1 is -1" or "-1 is 255" depending on the target you compile it for, and even worse, it will compile without complaints (even with -Wall) unless you explictly request -Wconversion on the command line. Read this "scatter-gather thoughts" to learn more about it.#include <stdio.h> char n=-1; int function() { return n; } int main() { printf("char -1 is %i\n",function()); return 0; }
Dans ma tête, "un char = un byte signé = -128..127" et bien sûr, comme je l'ai appris il y a longtemps à mes dépens. Et bien sûr les "int", dont on ne sait jamais trop bien la taille, sont signés aussi. Eh ben il faudra que je relise plus attentivement le Kernighan&Ritchie, parce que je me suis retrouvé avec 255 sortant de function() ! En ajoutant (à la recommandation de Cyril) -Wconversion à mon code, j'obtiens enfin un avertissement du compilateur :
test.c:2: warning: negative integer implicitly converted to unsigned typeEn fait, à relire ce très bon article sur "scatter-gather thoughts", je prends enfin pleinement conscience de mon erreur: un char peut être signé ou non-signé par défaut, en fonction de ce qui est préférable (le plus optimal) sur une architecture donnée. Sur x86, c'était donc char = int8_t et sous ARM (au moins avec le devkitpro) char = uint8_t.
see-also : signs from hell by Conarac
Plus précisément, voici mes flags gcc (4.3.2) :
ReplyDeleteexport CFLAGS='-O3 -pedantic -Wall -Wextra -Wformat=2 -Winit-self
-Wmissing-include-dirs -Wfloat-equal -Wundef -Wshadow
-Wunsafe-loop-optimizations -Wpointer-arith -Wbad-function-cast
-Wcast-qual -Wwrite-strings -Wconversion -Waggregate-return
-Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations
-Wmissing-noreturn -Wmissing-format-attribute -Wpacked -Wpadded
-Wredundant-decls -Wnested-externs -Wswitch-enum -Wswitch-default -Wc
++-compat -Wcast-align -Wlogical-op -Winline -Wvolatile-register-var
-Wdisabled-optimization'
Pour mon propre code, j'y ajoute systématiquement '-std=c99'.
Mes flags g++ (4.3.2) :
export CXXFLAGS='-O3 -ansi -pedantic -Wall -Wabi -Wctor-dtor-privacy
-Weffc++ -Wstrict-null-sentinel -Wold-style-cast -Woverloaded-virtual
-Wsign-promo -Wformat=2 -Winit-self -Wmissing-include-dirs
-Wswitch-default -Wswitch-enum -Wextra -Wfloat-equal -Wundef -Wshadow
-Wpointer-arith -Wcast-qual -Wcast-align -Wconversion -Wlogical-op
-Wmissing-declarations -Wmissing-noreturn -Wmissing-format-attribute
-Wpacked -Wredundant-decls -Wvolatile-register-var'
Flags que j'utilise à l'occasion mais qui peuvent poser des problèmes
avec du code parfaitement valide :
# -Winline -Wdisabled-optimization -Wunsafe-loop-optimizations
# -Waggregate-return -Wpadded
Bonnes compilations.
Et pour pouvoir appliquer de tels changements avec devkitpro, j'ajoute ces nouvelles "règles" à mon makefile:
ReplyDeleteGENFLAGS := -g \
-march=armv5te -mtune=arm946e-s -fomit-frame-pointer\
-ffast-math \
$(ARCH)
GENFLAGS += $(INCLUDE) -DARM9
CFLAGS := $(GENFLAGS) $(EXTRAFLAGS)
CXXFLAGS := $(GENFLAGS) -fno-rtti $(EXTRAFLAGS)