Wednesday, December 09, 2009

-Wconversion

Ca doit faire bien 10 ans maintenant que je pratique quotidiennement le C (ou un de ses "dérivés"), et il y a des jours où je crois honnètement avoir fait le tour du langage. Pourtant, les pièges restent bien présent, en particulier compte tenu du fait que je cesse de le pratiquer dans un seul environnement. Usermode, kernel mode, programmation embarquée sous ARM, etc. A force d'avoir utilisé uniquement gcc-linux-ia32 pendant un paquet d'années, il y a des particularités que j'ai fini par prendre pour des vérités. Un exemple: ce code m'a valu bien des misères avec la gestion des pentes :
#include <stdio.h>
char n=-1;

int function() {
return n;
}

int main() {
printf("char -1 is %i\n",function());
return 0;
}
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.

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 type
En 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

2 comments:

  1. Plus précisément, voici mes flags gcc (4.3.2) :

    export 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.

    ReplyDelete
  2. Et pour pouvoir appliquer de tels changements avec devkitpro, j'ajoute ces nouvelles "règles" à mon makefile:

    GENFLAGS := -g \
    -march=armv5te -mtune=arm946e-s -fomit-frame-pointer\
    -ffast-math \
    $(ARCH)

    GENFLAGS += $(INCLUDE) -DARM9

    CFLAGS := $(GENFLAGS) $(EXTRAFLAGS)
    CXXFLAGS := $(GENFLAGS) -fno-rtti $(EXTRAFLAGS)

    ReplyDelete

this is the right place for quickstuff