Qu'est-ce qui appartient à un outil pédagogique pour démontrer les suppositions injustifiées que les gens font en C / C ++?


121

Je voudrais préparer un petit outil éducatif pour SO qui devrait aider les programmeurs débutants (et intermédiaires) à reconnaître et à contester leurs hypothèses injustifiées en C, C ++ et leurs plates-formes.

Exemples:

  • "les entiers s'enroulent"
  • "tout le monde a ASCII"
  • "Je peux stocker un pointeur de fonction dans un vide *"

J'ai pensé qu'un petit programme de test pourrait être exécuté sur différentes plates-formes, qui exécute les hypothèses «plausibles» qui sont, d'après notre expérience en SO, généralement faites par de nombreux développeurs grand public inexpérimentés / semi-expérimentés et enregistrent les façons dont ils se cassent sur diverses machines.

Le but de ceci n'est pas de prouver qu'il est «sûr» de faire quelque chose (ce qui serait impossible à faire, les tests ne prouvent que quoi que ce soit s'ils cassent), mais plutôt de démontrer à l'individu le plus incompréhensible comment l'expression la plus discrète break sur une machine différente, si elle a un comportement non défini ou défini par l'implémentation. .

Pour y parvenir, je voudrais vous demander:

  • Comment cette idée peut-elle être améliorée?
  • Quels tests seraient bons et à quoi devraient-ils ressembler?
  • Souhaitez-vous exécuter les tests sur les plates-formes sur lesquelles vous pouvez mettre la main et publier les résultats, de sorte que nous nous retrouvions avec une base de données de plates-formes, en quoi elles diffèrent et pourquoi cette différence est autorisée?

Voici la version actuelle du jouet de test:

#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <stddef.h>
int count=0;
int total=0;
void expect(const char *info, const char *expr)
{
    printf("..%s\n   but '%s' is false.\n",info,expr);
    fflush(stdout);
    count++;
}
#define EXPECT(INFO,EXPR) if (total++,!(EXPR)) expect(INFO,#EXPR)

/* stack check..How can I do this better? */
ptrdiff_t check_grow(int k, int *p)
{
    if (p==0) p=&k;
    if (k==0) return &k-p;
    else return check_grow(k-1,p);
}
#define BITS_PER_INT (sizeof(int)*CHAR_BIT)

int bits_per_int=BITS_PER_INT;
int int_max=INT_MAX;
int int_min=INT_MIN;

/* for 21 - left to right */
int ltr_result=0;
unsigned ltr_fun(int k)
{
    ltr_result=ltr_result*10+k;
    return 1;
}

int main()
{
    printf("We like to think that:\n");
    /* characters */
    EXPECT("00 we have ASCII",('A'==65));
    EXPECT("01 A-Z is in a block",('Z'-'A')+1==26);
    EXPECT("02 big letters come before small letters",('A'<'a'));
    EXPECT("03 a char is 8 bits",CHAR_BIT==8);
    EXPECT("04 a char is signed",CHAR_MIN==SCHAR_MIN);

    /* integers */
    EXPECT("05 int has the size of pointers",sizeof(int)==sizeof(void*));
    /* not true for Windows-64 */
    EXPECT("05a long has at least the size of pointers",sizeof(long)>=sizeof(void*));

    EXPECT("06 integers are 2-complement and wrap around",(int_max+1)==(int_min));
    EXPECT("07 integers are 2-complement and *always* wrap around",(INT_MAX+1)==(INT_MIN));
    EXPECT("08 overshifting is okay",(1<<bits_per_int)==0);
    EXPECT("09 overshifting is *always* okay",(1<<BITS_PER_INT)==0);
    {
        int t;
        EXPECT("09a minus shifts backwards",(t=-1,(15<<t)==7));
    }
    /* pointers */
    /* Suggested by jalf */
    EXPECT("10 void* can store function pointers",sizeof(void*)>=sizeof(void(*)()));
    /* execution */
    EXPECT("11 Detecting how the stack grows is easy",check_grow(5,0)!=0);
    EXPECT("12 the stack grows downwards",check_grow(5,0)<0);

    {
        int t;
        /* suggested by jk */
        EXPECT("13 The smallest bits always come first",(t=0x1234,0x34==*(char*)&t));
    }
    {
        /* Suggested by S.Lott */
        int a[2]={0,0};
        int i=0;
        EXPECT("14 i++ is strictly left to right",(i=0,a[i++]=i,a[0]==1));
    }
    {
        struct {
            char c;
            int i;
        } char_int;
        EXPECT("15 structs are packed",sizeof(char_int)==(sizeof(char)+sizeof(int)));
    }
    {
        EXPECT("16 malloc()=NULL means out of memory",(malloc(0)!=NULL));
    }

    /* suggested by David Thornley */
    EXPECT("17 size_t is unsigned int",sizeof(size_t)==sizeof(unsigned int));
    /* this is true for C99, but not for C90. */
    EXPECT("18 a%b has the same sign as a",((-10%3)==-1) && ((10%-3)==1));

    /* suggested by nos */
    EXPECT("19-1 char<short",sizeof(char)<sizeof(short));
    EXPECT("19-2 short<int",sizeof(short)<sizeof(int));
    EXPECT("19-3 int<long",sizeof(int)<sizeof(long));
    EXPECT("20 ptrdiff_t and size_t have the same size",(sizeof(ptrdiff_t)==sizeof(size_t)));
#if 0
    {
        /* suggested by R. */
        /* this crashed on TC 3.0++, compact. */
        char buf[10];
        EXPECT("21 You can use snprintf to append a string",
               (snprintf(buf,10,"OK"),snprintf(buf,10,"%s!!",buf),strcmp(buf,"OK!!")==0));
    }
#endif

    EXPECT("21 Evaluation is left to right",
           (ltr_fun(1)*ltr_fun(2)*ltr_fun(3)*ltr_fun(4),ltr_result==1234));

    {
    #ifdef __STDC_IEC_559__
    int STDC_IEC_559_is_defined=1;
    #else 
    /* This either means, there is no FP support
     *or* the compiler is not C99 enough to define  __STDC_IEC_559__
     *or* the FP support is not IEEE compliant. */
    int STDC_IEC_559_is_defined=0;
    #endif
    EXPECT("22 floating point is always IEEE",STDC_IEC_559_is_defined);
    }

    printf("From what I can say with my puny test cases, you are %d%% mainstream\n",100-(100*count)/total);
    return 0;
}

Oh, et j'ai créé ce wiki communautaire dès le début parce que je pensais que les gens voulaient éditer mon discours quand ils liraient ceci.

MISE À JOUR Merci pour votre contribution. J'ai ajouté quelques cas à partir de vos réponses et je vais voir si je peux configurer un github pour cela, comme Greg l'a suggéré.

MISE À JOUR : J'ai créé un dépôt github pour cela, le fichier est "gotcha.c":

Veuillez répondre ici avec des correctifs ou de nouvelles idées, afin qu'elles puissent être discutées ou clarifiées ici. Je vais alors les fusionner dans gotcha.c.


7
Considérez le modèle moyen sous DOS. Les fonctions peuvent être stockées dans plusieurs segments, donc un pointeur de fonction a une longueur de 32 bits. Mais vos données ne sont stockées que dans un seul segment, donc les pointeurs de données ne font que 16 bits de long. Puisque void * est un pointeur de données, sa largeur est de 16 bits, vous ne pouvez donc pas insérer un pointeur de fonction dans un. Voir c-jump.com/CIS77/ASM/Directives/D77_0030_models.htm .
David donné

6
Peut-être pourriez-vous lancer ce code sur github.com ou quelque chose du genre , puis les gens pourraient facilement apporter des correctifs.
Greg Hewgill

1
Beaucoup de choses ici devraient aider: stackoverflow.com/questions/367633/…
Martin York

4
POSIX exige que les pointeurs de fonction aient la même représentation que void * et puissent être convertis (avec un cast) sans perte d'informations. L'une des raisons à cela est que dlsym()renvoie un void * mais est destiné à la fois aux pointeurs de données et de fonction. Par conséquent, il n'est peut-être pas si grave de dépendre de cela.
jilles

3
@tristopia: Le point 15 est ici, car de nombreux débutants sont souvent surpris d'apprendre que les données ne sont pas emballées en continu mais alignées sur certaines limites. Ils sont perplexes lorsqu'ils modifient l'ordre des membres et obtiennent des tailles d'objet différentes. En outre, l'emballage est le mode par défaut avec de nombreux microcontrôleurs contemporains ou appareils intégrés. Ma sortie AVR Atmega et TurboC / MSDOS est également emballée. MSDOS est toujours utilisé dans les applications industrielles.
Nordic Mainframe

Réponses:


91

L'ordre d'évaluation des sous-expressions, y compris

  • les arguments d'un appel de fonction et
  • opérandes des opérateurs (par exemple +, -, =, *, /), à l'exception de:
    • les opérateurs logiques binaires ( &&et ||),
    • l'opérateur conditionnel ternaire ( ?:), et
    • l'opérateur virgule ( ,)

n'est pas spécifié

Par exemple

  int Hello()
  {
       return printf("Hello"); /* printf() returns the number of 
                                  characters successfully printed by it
                               */
  }

  int World()
  {
       return printf("World !");
  }

  int main()
  {

      int a = Hello() + World(); //might print Hello World! or World! Hello
      /**             ^
                      | 
                Functions can be called in either order
      **/
      return 0;
  } 

1
J'avais toujours su cela à propos des paramètres de fonction, mais je n'y ai jamais pensé en termes d'opérateurs ... ... et si jamais je vous vois écrire du code comme ça dans un environnement de production, je vous giflerai avec une nouille humide.
riwalk

3
@Billy: Mais uniquement pour les versions primitives des opérateurs.
Dennis Zickefoose

1
@Dennis: C'est vrai. (C'est pourquoi c'est un élément de Effective / MoreEffective C ++ pour ne jamais les surcharger (Sauf si vous écrivez boost::spirit)
Billy ONeal

1
@Daniel: Je ne suis pas sûr de ce que vous essayez de dire. Il semble que vous suggériez de surcharger les opérateurs car ce ne sont que les utilisateurs de votre classe qui pourraient se tromper, et si vous n'écrivez pas en C ++ pur, cela n'a pas d'importance. Aucun de ces éléments n'a de sens.
Dennis Zickefoose

2
@ user420536: Le comportement est simplement non spécifié mais pas indéfini. Oui, l'exemple peut imprimer soit Hello World! ou Monde! Bonjour mais c'est juste non spécifié car l'ordre d'évaluation des opérandes de l' +opérateur n'est pas spécifié (les rédacteurs du compilateur n'ont pas besoin de documenter le comportement). Il ne viole aucune règle de point de séquence en tant que telle.
Prasoon Saurav

38

sdcc 29.7 / ucSim / Z80

We like to think that:
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..19-2 short<int
   but 'sizeof(short)<sizeof(int)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
   but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
From what I can say with my puny test cases, you are Stop at 0x0013f3: (106) Invalid instruction 0x00dd

printf plante. "O_O"


gcc 4.4@x86_64-suse-linux

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 79% mainstream

gcc 4.4@x86_64-suse-linux (-O2)

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 82% mainstream

clang 2.7@x86_64-suse-linux

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..21a Function Arguments are evaluated right to left
but '(gobble_args(0,ltr_fun(1),ltr_fun(2),ltr_fun(3),ltr_fun(4)),ltr_result==4321)' is false.
ltr_result is 1234 in this case
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 72% mainstream

open64 4.2.3@x86_64-suse-linux

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..21a Function Arguments are evaluated right to left
but '(gobble_args(0,ltr_fun(1),ltr_fun(2),ltr_fun(3),ltr_fun(4)),ltr_result==4321)' is false.
ltr_result is 1234 in this case
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 75% mainstream

intel 11.1@x86_64-suse-linux

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..21a Function Arguments are evaluated right to left
but '(gobble_args(0,ltr_fun(1),ltr_fun(2),ltr_fun(3),ltr_fun(4)),ltr_result==4321)' is false.
ltr_result is 1234 in this case
..26 sizeof() does not evaluate its arguments
but '(i=10,sizeof(char[((i=20),10)]),i==10)' is false.
From what I can say with my puny test cases, you are 75% mainstream

Turbo C ++ / DOS / Petite mémoire

We like to think that:
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..16 malloc()=NULL means out of memory
but '(malloc(0)!=NULL)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
From what I can say with my puny test cases, you are 81% mainstream

Turbo C ++ / DOS / Mémoire moyenne

We like to think that:
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..10 void* can store function pointers
but 'sizeof(void*)>=sizeof(void(*)())' is false.
..16 malloc()=NULL means out of memory
but '(malloc(0)!=NULL)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
From what I can say with my puny test cases, you are 78% mainstream

Turbo C ++ / DOS / Mémoire compacte

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..16 malloc()=NULL means out of memory
but '(malloc(0)!=NULL)' is false.
..19-2 short<int
but 'sizeof(short)<sizeof(int)' is false.
..20 ptrdiff_t and size_t have the same size
but '(sizeof(ptrdiff_t)==sizeof(size_t))' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
..25 pointer arithmetic works outside arrays
but '(diff=&var.int2-&var.int1, &var.int1+diff==&var.int2)' is false.
..25a pointer arithmetic works outside arrays
but '(diff=&p1-&p2, &p2+diff==&p1)' is false.
From what I can say with my puny test cases, you are 75% mainstream

cl65 @ Commodore PET (émulateur vice)

texte alternatif


Je les mettrai à jour plus tard:


Borland C ++ Builder 6.0 sur Windows XP

..04 a char is signed
   but 'CHAR_MIN==SCHAR_MIN' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09 overshifting is *always* okay
   but '(1<<BITS_PER_INT)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..16 malloc()=NULL means out of memory
   but '(malloc(0)!=NULL)' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 71% mainstream

Visual Studio Express 2010 C ++ CLR, Windows 7 64 bits

(doit être compilé en C ++ car le compilateur CLR ne prend pas en charge le C pur)

We like to think that:
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 78% mainstream

MINGW64 (pré-version gcc-4.5.2)

- http://mingw-w64.sourceforge.net/

We like to think that:
..05 int has the size of pointers
   but 'sizeof(int)==sizeof(void*)' is false.
..05a long has at least the size of pointers
   but 'sizeof(long)>=sizeof(void*)' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
   but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 67% mainstream

Windows 64 bits utilise le modèle LLP64: les deux intet longsont définis comme 32 bits, ce qui signifie qu'aucun n'est assez long pour un pointeur.


avr-gcc 4.3.2 / ATmega168 (Arduino Diecimila)

Les hypothèses qui ont échoué sont:

..14 i++ is structly left to right
..16 malloc()=NULL means out of memory
..19-2 short<int
..21 Evaluation is left to right
..22 floating point is always IEEE

L'Atmega168 a un PC 16 bits, mais le code et les données sont dans des espaces d'adressage séparés. Les plus grands Atmegas ont un PC 22 bits !.


gcc 4.2.1 sur MacOSX 10.6, compilé avec -arch ppc

We like to think that:
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits come always first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 78% mainstream


32
Et vous avez identifié une autre hypothèse: que vous pouvez insérer 80 caractères sur une ligne terminale.
Mike Seymour du

3
sizeof(void*)>=sizeof(void(*)())serait plus pertinent que ==. Tout ce qui nous importe, c'est "pouvons-nous stocker un pointeur de fonction dans un pointeur vide", donc l'hypothèse que vous devez tester est de savoir si a void*est au moins aussi grand qu'un pointeur de fonction.
jalf

1
Si votre environnement est compatible POSIX, vous devriez être d'accord avec sizeof(void*)>=sizeof(void(*)())- voir opengroup.org/onlinepubs/009695399/functions/dlsym.html
Daniel Earwicker

26

Il y a longtemps, j'enseignais le C à partir d'un manuel qui avait

printf("sizeof(int)=%d\n", sizeof(int));

comme exemple de question. Cela a échoué pour un étudiant, parce que les sizeofvaleurs de type size_t, non int, intsur cette implémentation étaient 16 bits et size_t32, et c'était big-endian. (La plate-forme était Lightspeed C sur Macintosh 680x0. J'ai dit que c'était il y a longtemps.)


7
+1 pour avoir signalé l'une des erreurs de ce type les plus courantes et les plus souvent négligées.
R .. GitHub STOP HELPING ICE

4
Cela se produit également sur les systèmes 64 bits, où size_t est 64 bits et les ints sont presque toujours plus courts. Win64 est encore plus étrange, car size_t est unsigned long longlà. Ajouté en tant que test 17.
Nordic Mainframe

Malheureusement, l'environnement d'exécution C de Microsoft ne prend pas en charge le zmodificateur pour size_tles entiers dimensionnés et long longn'est pas pris en charge sur certaines plates-formes également. Il n'y a donc pas de moyen portable sûr de formater ou de mouler la taille imprimée d'un objet.
Phil Miller

15

Vous devez inclure les hypothèses ++et les --hypothèses émises par les gens.

a[i++]= i;

Par exemple, est syntaxiquement légale, mais produit des résultats variables en fonction de trop de choses à raisonner.

Toute instruction qui a ++(ou --) et une variable qui se produit plus d'une fois est un problème.


Et c'est juste une question tellement courante aussi!
Matthieu M.

8

Très intéressant!

D'autres choses auxquelles je peux penser pourraient être utiles à vérifier:

  • les pointeurs de fonction et les pointeurs de données existent-ils dans le même espace d'adressage? (Ruptures dans les machines d'architecture de Harvard comme le petit mode DOS. Je ne sais pas comment vous le testeriez, cependant.)

  • si vous prenez un pointeur de données NULL et le transtypez dans le type entier approprié, a-t-il la valeur numérique 0? (Rupture sur certaines machines vraiment anciennes --- voir http://c-faq.com/null/machexamp.html .) Idem avec le pointeur de fonction. En outre, il peut s'agir de valeurs différentes.

  • est-ce que l'incrémentation d'un pointeur au-delà de la fin de son objet de stockage correspondant, puis en arrière, produit des résultats raisonnables? (Je ne connais pas de machines sur lesquelles cela fonctionne réellement, mais je pense que la spécification C ne vous permet même pas de penser à des pointeurs qui ne pointent ni vers (a) le contenu d'un tableau ni (b) l'élément immédiatement après le tableau ou (c) NULL. Voir http://c-faq.com/aryptr/non0based.html .)

  • la comparaison de deux pointeurs vers différents objets de stockage avec <et> produit-elle des résultats cohérents? (Je peux imaginer cette rupture sur des machines exotiques basées sur des segments; la spécification interdit de telles comparaisons, de sorte que le compilateur aurait le droit de comparer uniquement la partie décalée du pointeur, et non la partie segment.)

Hmm. Je vais essayer de penser à un peu plus.

Edit: Ajout de quelques liens de clarification vers l'excellente FAQ C.


2
Incidemment, il y a quelque temps, j'ai fait un projet expérimental appelé Clue ( cluecc.sourceforge.net ) qui vous permettait de compiler C en Lua, Javascript, Perl, LISP, etc. Il exploitait impitoyablement le comportement indéfini du standard C pour faire fonctionner les pointeurs . Il peut être intéressant d'essayer ce test dessus.
David donné

1
IIRC C vous permet d'incrémenter un pointeur de 1 au-delà de la fin d'un objet, mais pas plus. La décrémentation à une position antérieure au début d'un objet n'est cependant pas autorisée.
R .. GitHub STOP HELPING ICE

@R. Idem en C ++. Et une incrémentation supplémentaire peut être interrompue si l'incrémentation du pointeur provoque un débordement, sur les processeurs qui ne traitent pas seulement les pointeurs comme des entiers.
jalf

5

Je pense que vous devriez faire un effort pour faire la distinction entre deux classes très différentes d'hypothèses «incorrectes». Une bonne moitié (décalage à droite et extension de signe, encodage compatible ASCII, la mémoire est linéaire, les pointeurs de données et de fonction sont compatibles, etc.) sont des hypothèses assez raisonnables à faire pour la plupart des codeurs C, et pourraient même être incluses dans le standard si C était en cours de conception aujourd'hui et si nous n'avions pas de droits acquis sur les déchets IBM hérités. L'autre moitié (choses liées à l'alias de la mémoire, comportement des fonctions de la bibliothèque lorsque la mémoire d'entrée et de sortie se chevauchent, hypothèses 32 bits comme celle des pointeursint ou que vous pouvez utilisermalloc sans prototype, cette convention d'appel est identique pour les fonctions variadiques et non variadiques, ...) soit en conflit avec les optimisations que les compilateurs modernes veulent effectuer, soit avec la migration vers des machines 64 bits ou d'autres nouvelles technologies.


il ne s'agit pas simplement de "déchets IBM" (même si je suis d'accord que les trucs IBM sont des déchets). De nombreux systèmes embarqués rencontrent aujourd'hui des problèmes similaires.
rmeador

Pour clarifier, utiliser mallocsans prototype signifie ne pas inclure <stdlib.h>, ce qui entraîne la mallocvaleur par défaut int malloc(int), un non-non si vous souhaitez prendre en charge le 64 bits.
Joey Adams du

Techniquement, vous êtes libre de ne pas inclure <stdlib.h>tant que vous incluez un autre en-tête qui définit size_tet que vous déclarez ensuite mallocavec un prototype correct vous-même.
R .. GitHub STOP AIDING ICE

5

En voici une amusante: quel est le problème avec cette fonction?

float sum(unsigned int n, ...)
{
    float v = 0;
    va_list ap;
    va_start(ap, n);
    while (n--)
        v += va_arg(ap, float);
    va_end(ap);
    return v;
}

[Réponse (rot13): Inevnqvp nethzragf borl gur byq X&E cebzbgvba ehyrf, juvpu zrnaf lbh pnaabg hfr 'sybng' (be 'pune' be 'fubeg') va in_net! Naq gur pbzcvyre vf erdhverq abg gb gerng guvf nf n pbzcvyr-gvzr reebe. (TPP qbrf rzvg n jneavat, gubhtu.)]


Oh, c'est bon. clang 2.7 mange cela et produit un non-sens complet sans avertissement.
Nordic Mainframe

va_arg se développe s'il s'agit d'une macro et que la boucle while n'exécute que la première instruction, peut-être plusieurs?
Maister

Non (si cela arrivait, ce serait un bogue dans l'implémentation).
zwol

5
EXPECT("## pow() gives exact results for integer arguments", pow(2, 4) == 16);

Un autre concerne le mode texte dans fopen. La plupart des programmeurs supposent que texte et binaire sont identiques (Unix) ou que le mode texte ajoute des \rcaractères (Windows). Mais C a été porté sur des systèmes qui utilisent des enregistrements à largeur fixe, sur lesquels fputc('\n', file)sur un fichier texte signifie ajouter des espaces ou quelque chose jusqu'à ce que la taille du fichier soit un multiple de la longueur de l'enregistrement.

Et voici mes résultats:

gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3 sur x86-64

We like to think that:
..05 int has the size of pointers
   but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..14 i++ is strictly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
   but 'sizeof(size_t)==sizeof(unsigned int)' is false.
From what I can say with my puny test cases, you are 78% mainstream

J'ai en fait vu du code combiné pow(2, n)avec des opérations sur les bits.
dan04

4

Certains d'entre eux ne peuvent pas être facilement testés depuis l'intérieur de C car le programme est susceptible de planter sur les implémentations où l'hypothèse ne tient pas.


"Il est normal de faire quoi que ce soit avec une variable à valeur de pointeur. Elle n'a besoin de contenir une valeur de pointeur valide que si vous la déréférencer."

void noop(void *p); /* A no-op function that the compiler doesn't know to optimize away */
int main () {
    char *p = malloc(1);
    free(p);
    noop(p); /* may crash in implementations that verify pointer accesses */
    noop(p - 42000); /* and if not the previous instruction, maybe this one */
}

Idem pour les types à virgule flottante et intégrale (autres que unsigned char), qui sont autorisés à avoir des représentations d'interruption.


"Les calculs entiers se terminent. Donc, ce programme imprime un grand entier négatif."

#include <stdio.h>
int main () {
    printf("%d\n", INT_MAX+1); /* may crash due to signed integer overflow */
    return 0;
}

(C89 uniquement.) "Il est normal de tomber à la fin de main."

#include <stdio.h>
int main () {
    puts("Hello.");
} /* The status code is 7 on many implementations. */

2
À titre d'exemple concret: une fois compilé avec gcc -ftrapv -O, la sortie est We like to think that:suivie deAborted
caf

@caf: "Cette option génère des interruptions pour le débordement signé sur les opérations d'addition, de soustraction, de multiplication." Bon à savoir, merci.
Gilles 'SO- arrête d'être diabolique'

1
Le dernier est également correct en C ++ (98, 03 et 0x) et renvoie implicitement 0.
jalf

Ce qui est désagréable car pré-ANSI C le permettait et C99 le faisait aussi.
Joshua

@Joshua: AFAIK il n'y a pas de différence entre pré-ANSI C et C89 au retour de mainsans valeur: le programme est correct mais renvoie un état de terminaison indéfini (C89 §2.1.2.2). Avec de nombreuses implémentations (telles que gcc et les anciens compilateurs Unix), vous obtenez tout ce qui se trouvait dans un certain registre à ce stade. Le programme fonctionne généralement jusqu'à ce qu'il soit utilisé dans un makefile ou un autre environnement qui vérifie l'état de fin.
Gilles 'SO- arrête d'être diabolique'

4

Eh bien, les hypothèses classiques de portabilité qui ne sont pas encore signifiées sont

  • hypothèses sur la taille des types intégraux
  • endianité

4
"Endianness", y compris "There is an endianness": il y a des machines middle-endian, et le standard permet des choses bizarres comme stocker une shortvaleur fedcab9876543210 (soit 16 chiffres binaires) comme les deux octets 0248ace et fdb97531.
Gilles 'SO- arrête d'être diabolique'

yes endianess inclut à coup sûr le mix / middle endian ainsi que big et little. si vous utilisez du matériel personnalisé, vous pourriez avoir n'importe quelle extrémité que vous aimez sur n'importe quel bus.
jk.

Le milieu endian est connu comme PDP endian. Gilles décrit quelque chose d'encore plus étrange qui causerait des maux de tête pour l'implémentation de TCP / IP.
Joshua

@Gilles: middle-endian ... Je suis très content de ne pas développer sur celui-là. (mais maintenant on me demandera de faire un projet de réseautage middle-endian, j'en suis sûr) ...
Paul Nathan

ARM FPE utilisait des doubles du milieu de gamme, où ils étaient stockés sous forme de paire <high ​​quad> <low quad> mais l'ordre des bits à l'intérieur de chaque quad était dans le mauvais sens. (Heureusement, ARM VFP ne fait plus cela.)
David Donné

4
  • Erreurs de discrétisation dues à la représentation en virgule flottante. Par exemple, si vous utilisez la formule standard pour résoudre des équations quadratiques, ou des différences finies pour approcher les dérivées, ou la formule standard pour calculer les variances, la précision sera perdue en raison du calcul des différences entre des nombres similaires. L'algorithme de Gauß pour résoudre des systèmes linéaires est mauvais car les erreurs d'arrondi s'accumulent, donc on utilise la décomposition QR ou LU, la décomposition de Cholesky, SVD, etc. L'ajout de nombres à virgule flottante n'est pas associatif. Il existe des valeurs dénormales, infinies et NaN. a + b - ab .

  • Chaînes: différence entre les caractères, les points de code et les unités de code. Comment Unicode est implémenté sur les différents systèmes d'exploitation; Encodages Unicode. L'ouverture d'un fichier avec un nom de fichier Unicode arbitraire n'est pas possible avec C ++ de manière portable.

  • Conditions de course, même sans thread: si vous testez si un fichier existe, le résultat peut devenir invalide à tout moment.

  • ERROR_SUCCESS = 0


4

Incluez une vérification des tailles entières. La plupart des gens supposent qu'un int est plus grand qu'un court est plus grand qu'un caractère. Cependant, tout cela peut être faux:sizeof(char) < sizeof(int); sizeof(short) < sizeof(int); sizeof(char) < sizeof(short)

Ce code peut échouer (se bloque lors d'un accès non aligné)

unsigned char buf[64];

int i = 234;
int *p = &buf[1];
*p = i;
i = *p;

ce code échouerait-il en C ++? IIRC, il est illégal de lancer des pointeurs entre des types non liés, SAUF pour char *, qui peuvent être convertis en n'importe quel type (ou est-ce l'inverse?).
rmeador

1
Vous pouvez simplement le faire int *p = (int*)&buf[1];en C ++, les gens s'attendent à ce que cela fonctionne aussi.
nos

@nos, oui ça peut échouer mais l'échec est un crash donc son programme ne peut pas tester celui-là. :(
Joshua

1
sizeof(char) < sizeof(int)est requis. Par exemple, fgetc () renvoie la valeur du caractère sous la forme d'un caractère non signé converti en int, ou EOFqui est une valeur négative. unsigned charpeut ne pas avoir de bits de remplissage, donc la seule façon de le faire est de rendre int plus grand que char. En outre, (la plupart des versions de) la spécification C exigent que toute valeur comprise entre -32767..32767 puisse être stockée dans un int.
jilles

@illes toujours, il y a des DSP avec des caractères 32 bits et des entiers 32 bits.
nos

3

Quelques informations sur les types de données intégrés:

  • charet signed charsont en fait deux types distincts (contrairement intet signed intqui font référence au même type entier signé).
  • les entiers signés ne sont pas obligés d'utiliser le complément à deux. Le complément et le signe + la grandeur de l'un sont également des représentations valides de nombres négatifs. Cela rend les opérations binaires impliquant des nombres négatifs définis par l'implémentation .
  • Si vous affectez un entier hors plage à une variable d'entier signé, le comportement est défini par l'implémentation .
  • Dans C90, -3/5pourrait retourner 0ou -1. Arrondir vers zéro au cas où un opérande serait négatif n'est garanti que dans C99 vers le haut et C ++ 0x vers le haut.
  • Il n'y a pas de garantie de taille exacte pour les types intégrés. La norme ne couvre que les exigences minimales telles que an inta au moins 16 bits, a longa au moins 32 bits, a long longa au moins 64 bits. A floatpeut au moins représenter correctement les 6 chiffres décimaux les plus significatifs. A doublepeut au moins représenter correctement les 10 chiffres décimaux les plus significatifs.
  • IEEE 754 n'est pas obligatoire pour représenter des nombres à virgule flottante.

Certes, sur la plupart des machines, nous aurons un complément à deux et des flotteurs IEEE 754.


Je me demande quelle valeur il y a à avoir des affectations d'entiers hors plage définies par l'implémentation plutôt que par un comportement indéfini? Sur certaines plates-formes, une telle exigence obligerait le compilateur à générer du code supplémentaire pour int mult(int a,int b) { return (long)a*b;}[par exemple, si intest 32 bits, mais registres et longsont 64]. Sans une telle exigence, le comportement "naturel" de l'implémentation la plus rapide de long l=mult(1000000,1000000);serait légal à 1000000000000, même si c'est une valeur "impossible" pour un int.
supercat

3

Celui-ci, ça va:

Aucun pointeur de données ne peut jamais être identique à un pointeur de fonction valide.

Ceci est VRAI pour tous les modèles plats, MS-DOS TINY, LARGE et HUGE, faux pour le modèle MS-DOS SMALL, et presque toujours faux pour les modèles MEDIUM et COMPACT (dépend de l'adresse de chargement, vous aurez besoin d'un très ancien DOS pour le rendre vrai).

Je ne peux pas écrire de test pour ça

Et pire: les pointeurs lancés vers ptrdiff_t peuvent être comparés. Ce n'est pas vrai pour le modèle MS-DOS LARGE (la seule différence entre LARGE et HUGE est HUGE ajoute le code du compilateur pour normaliser les pointeurs).

Je ne peux pas écrire de test car l'environnement dans lequel ces bombes durent n'allouera pas un tampon supérieur à 64K, donc le code qui le démontre planterait sur d'autres plates-formes.

Ce test particulier passerait sur un système maintenant disparu (notez que cela dépend des composants internes de malloc):

  char *ptr1 = malloc(16);
  char *ptr2 = malloc(16);
  if ((ptrdiff_t)ptr2 - 0x20000 == (ptrdiff_t)ptr1)
      printf("We like to think that unrelated pointers are equality comparable when cast to the appropriate integer, but they're not.");

3

EDIT: mis à jour vers la dernière version du programme

Solaris-SPARC

gcc 3.4.6 en 32 bits

We like to think that:
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09 overshifting is *always* okay
   but '(1<<BITS_PER_INT)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits always come first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is strictly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 72% mainstream

gcc 3.4.6 en 64 bits

We like to think that:
..05 int has the size of pointers
   but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09 overshifting is *always* okay
   but '(1<<BITS_PER_INT)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits always come first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is strictly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
   but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 68% mainstream

et avec SUNStudio 11 32 bits

We like to think that:
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits always come first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is strictly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
From what I can say with my puny test cases, you are 79% mainstream

et avec SUNStudio 11 64 bits

We like to think that:
..05 int has the size of pointers
   but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits always come first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is strictly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
   but 'sizeof(size_t)==sizeof(unsigned int)' is false.
From what I can say with my puny test cases, you are 75% mainstream

2

Vous pouvez utiliser text-mode ( fopen("filename", "r")) pour lire n'importe quel type de fichier texte.

Bien que cela devrait en théorie fonctionner très bien, si vous utilisez également ftell()dans votre code et que votre fichier texte a des fins de ligne de style UNIX, dans certaines versions de la bibliothèque standard Windows, ftell()des valeurs non valides seront souvent renvoyées. La solution consiste à utiliser le mode binaire à la place ( fopen("filename", "rb")).


1

gcc 3.3.2 sur AIX 5.3 (oui, nous devons mettre à jour gcc)

We like to think that:
..04 a char is signed
   but 'CHAR_MIN==SCHAR_MIN' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits come always first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..16 malloc()=NULL means out of memory
   but '(malloc(0)!=NULL)' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 71% mainstream

1

Une hypothèse que certains peuvent faire en C ++ est que a structest limité à ce qu'il peut faire en C. Le fait est que, en C ++, a structest comme a classsauf qu'il a tout public par défaut.

Structure C ++:

struct Foo
{
  int number1_;  //this is public by default


//this is valid in C++:    
private: 
  void Testing1();
  int number2_;

protected:
  void Testing2();
};

1

Les fonctions mathématiques standard sur différents systèmes ne donnent pas des résultats identiques.


1

Visual Studio Express 2010 sur x86 32 bits.

Z:\sandbox>cl testtoy.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

testtoy.c
testtoy.c(54) : warning C4293: '<<' : shift count negative or too big, undefined
 behavior
Microsoft (R) Incremental Linker Version 10.00.30319.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:testtoy.exe
testtoy.obj

Z:\sandbox>testtoy.exe
We like to think that:
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 78% mainstream

1

Via Codepad.org ( C++: g++ 4.1.2 flags: -O -std=c++98 -pedantic-errors -Wfatal-errors -Werror -Wall -Wextra -Wno-missing-field-initializers -Wwrite-strings -Wno-deprecated -Wno-unused -Wno-non-virtual-dtor -Wno-variadic-macros -fmessage-length=0 -ftemplate-depth-128 -fno-merge-constants -fno-nonansi-builtins -fno-gnu-keywords -fno-elide-constructors -fstrict-aliasing -fstack-protector-all -Winvalid-pch).

Notez que Codepad n'avait pas stddef.h. J'ai supprimé le test 9 en raison du codepad utilisant des avertissements comme erreurs. J'ai également renommé la countvariable car elle était déjà définie pour une raison quelconque.

We like to think that:
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
From what I can say with my puny test cases, you are 84% mainstream

1

Qu'en est-il du décalage vers la droite par des quantités excessives - est-ce que cela est autorisé par la norme ou vaut-il la peine d'être testé?

La norme C spécifie-t-elle le comportement du programme suivant:

void print_string (char * st)
{
  char ch;
  tandis que ((ch = * st ++)! = 0)
    putch (ch); / * Supposons que cela soit défini * /
}
int main (void)
{
  print_string ("Bonjour");
  return 0;
}

Sur au moins un compilateur que j'utilise, ce code échouera à moins que l'argument de print_string ne soit un "char const *". La norme permet-elle une telle restriction?

Certains systèmes permettent de produire des pointeurs vers des 'int' non alignés et d'autres non. Cela pourrait valoir la peine d'être testé.


C89 §3.3.7: «Si la valeur de l'opérande droit est négative ou est supérieure ou égale à la largeur en bits de l'opérande gauche promu, le comportement n'est pas défini.» (s'applique à la fois <<et >>). C99 a un langage identique au §6.5.7-3.
Gilles 'SO- arrête d'être mauvais'

A part putch(pourquoi n'avez-vous pas utilisé le standard putchar?), Je ne vois aucun comportement indéfini dans votre programme. C89 §3.1.4 spécifie qu '«un littéral de chaîne de caractères a […] type' tableau de char '» (note: non const), et que «si le programme tente de modifier un littéral de chaîne […], le comportement est indéfini» . De quel compilateur s'agit-il et comment traduit-il ce programme?
Gilles 'SO- arrêtez d'être diabolique'

2
En C ++, les constantes de caractères ne sont pas char [], elles sont const char []. Cependant ... il y avait un trou spécifique dans le système de type pour vous permettre d'utiliser une constante de chaîne dans un contexte où un caractère * était attendu et de ne pas obtenir d'erreur de type. Cela a conduit à des situations où print_string ("foo") fonctionnerait mais pas print_string ("foo" +0). C'était profondément déroutant, en particulier dans les environnements où les fichiers C sont compilés à l'aide d'un compilateur C ++ par défaut. Le trou a été supprimé dans les nouveaux compilateurs mais il en reste encore beaucoup d'anciens. AFAIK C99 définit toujours les constantes de chaîne comme étant char [].
David donné

1
Sur les compilateurs HiTech pour la série de contrôleurs Microchip PIC, un pointeur sans qualificateur de stockage ne peut pointer que vers la RAM. Un pointeur qualifié const peut pointer vers la RAM ou la ROM. Les pointeurs non qualifiés const sont déréférencés directement dans le code; Les pointeurs qualifiés const sont déréférencés via la routine de bibliothèque. Selon le type particulier de PIC, les pointeurs non qualifiés const sont de 1 ou 2 octets; ceux qualifiés par const sont 2 ou 3. Puisque la ROM est beaucoup plus abondante que la RAM, avoir des constantes dans la ROM est généralement une bonne chose.
supercat

@David Given: Notez également mon commentaire précédent. Je préfère les compilateurs qui utilisent des qualificatifs autres que "const" pour désigner la classe de stockage matériel; le compilateur HiTech a quelques bizarreries plutôt ennuyeuses avec son allocation de classe de stockage (par exemple, les éléments de données dont la "taille de composant" est un octet, ou les éléments de données qui font plus de 256 octets, vont dans un "gros" segment. D'autres éléments de données vont dans le " bss "pour le module qu'ils ont défini; tous les éléments" bss "d'un module doivent tenir dans les 256 octets. Les tableaux légèrement courts de 256 octets peuvent être une véritable nuisance.
supercat

0

Pour info, pour ceux qui doivent traduire leurs compétences C en Java, voici quelques pièges.

EXPECT("03 a char is 8 bits",CHAR_BIT==8);
EXPECT("04 a char is signed",CHAR_MIN==SCHAR_MIN);

En Java, char est 16 bits et signé. l'octet est de 8 bits et signé.

/* not true for Windows-64 */
EXPECT("05a long has at least the size of pointers",sizeof(long)>=sizeof(void*));

long est toujours 64 bits, les références peuvent être 32 bits ou 64 bits (si vous avez plus d'une application avec plus de 32 Go) Les JVM 64 bits utilisent généralement des références 32 bits.

EXPECT("08 overshifting is okay",(1<<bits_per_int)==0);
EXPECT("09 overshifting is *always* okay",(1<<BITS_PER_INT)==0);

Le décalage est masqué de sorte que i << 64 == i == i << -64, i << 63 == i << -1

EXPECT("13 The smallest bits always come first",(t=0x1234,0x34==*(char*)&t));

ByteOrder.nativeOrder () peut être BIG_ENDIAN ou LITTLE_ENDIAN

EXPECT("14 i++ is strictly left to right",(i=0,a[i++]=i,a[0]==1));

i = i++ ne change jamais i

/* suggested by David Thornley */
EXPECT("17 size_t is unsigned int",sizeof(size_t)==sizeof(unsigned int));

La taille des collections et des tableaux est toujours de 32 bits, que la JVM soit 32 bits ou 64 bits.

EXPECT("19-1 char<short",sizeof(char)<sizeof(short));
EXPECT("19-2 short<int",sizeof(short)<sizeof(int));
EXPECT("19-3 int<long",sizeof(int)<sizeof(long));

char est 16 bits, court est 16 bits, int est 32 bits et long est 64 bits.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.