web-dev-qa-db-fra.com

Qu'est-ce qui appartient à un outil éducatif pour démontrer les hypothèses injustifiées que les gens font en C / C ++?

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

Exemples:

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

Je me suis dit qu'un petit programme de test pouvait être exécuté sur différentes plates-formes, qui exécute les hypothèses "plausibles" qui, d'après notre expérience en SO, sont généralement faites par de nombreux développeurs grand public inexpérimentés/semi-expérimentés et enregistrent la façon dont elles se brisent 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 n'importe quoi s'ils se cassent), mais plutôt de démontrer même à l'individu le moins compréhensif comment l'expression la plus discrète se casse sur une machine différente, si elle a un comportement indé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, afin que nous nous retrouvions avec une base de données des plates-formes, comment 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 blabber quand ils liraient cela.

[~ # ~] mise à jour [~ # ~] Merci pour votre contribution. J'ai ajouté quelques cas à partir de vos réponses et je verrai 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'ils puissent être discutés ou clarifiés ici. Je les fusionnerai alors dans gotcha.c.

121
Nordic Mainframe

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

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

est non 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;
  } 
91
Prasoon Saurav

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

Mémoire Turbo C++/DOS/Compact

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 (vice-émulateur)

alt text


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é-effacement 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: int et long sont définis comme 32 bits, ce qui signifie qu'aucun des deux n'est assez long pour un pointeur.


avr-gcc 4.3.2/ATmega168 (Arduino Diecimila)

Les hypothèses qui ont échoué sont les suivantes:

..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 possède un PC 16 bits, mais le code et les données sont dans des espaces d'adressage séparés. Les Atmegas plus grands 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

38
Nordic Mainframe

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. Il a échoué pour un élève, car sizeof renvoie des valeurs de type size_t, pas int, int sur cette implémentation était de 16 bits et size_t avait 32 ans, et c'était du big-endian. (La plate-forme était Lightspeed C sur Macintoshes à base de 680x0. J'ai dit que c'était il y a longtemps.)

26
David Thornley

Vous devez inclure le ++ et -- hypothèses que les gens font.

a[i++]= i;

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

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

15
S.Lott

Très intéressant!

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

  • les pointeurs de fonction et les pointeurs de données existent-ils dans le même espace d'adressage? (Des pauses dans les machines d'architecture 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 convertissez en le type entier approprié, a-t-il la valeur numérique 0? (Interrompt certaines machines très anciennes --- voir http://c-faq.com/null/machexamp.html .) Idem avec pointeur de fonction. En outre, il peut s'agir de valeurs différentes.

  • l'incrémentation d'un pointeur au-delà de la fin de son objet de stockage correspondant, puis à nouveau, provoque-t-elle des résultats raisonnables? (Je ne connais aucune machine sur laquelle cela se casse, mais je pense que la spécification C ne vous permet même pas de penser aux pointeurs qui ne t pointe vers (a) le contenu d'un tableau ou (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 des objets de stockage différents avec <et> produit-elle des résultats cohérents? (Je peux imaginer cette rupture sur des machines basées sur des segments exotiques; la spécification interdit de telles comparaisons, donc le compilateur aurait le droit de comparer la partie décalée du pointeur uniquement, et non la partie segment.)

Hmm. Je vais essayer de penser à plus.

Éditer: Ajout de quelques liens de clarification à l'excellente FAQ C.

8
David Given

Je pense que vous devriez faire un effort pour distinguer deux classes très différentes d'hypothèses "incorrectes". Une bonne moitié (décalage vers la droite et extension de signe, encodage compatible ASCII, la mémoire est linéaire, les pointeurs de données et de fonctions sont compatibles, etc.) sont des hypothèses assez raisonnables pour la plupart des codeurs C à faire, et pourraient même être inclus dans la norme si C était conçu aujourd'hui et si nous n'avions pas de droits acquis d'IBM indésirables. L'autre moitié (choses liées à l'alias de mémoire, comportement des fonctions de bibliothèque lorsque la mémoire d'entrée et de sortie se chevauchent, hypothèses 32 bits telles que les pointeurs tiennent dans int ou que vous pouvez utiliser malloc 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 ou avec la migration vers des machines 64 bits ou d'autres nouvelles technologies.

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 le texte et le binaire sont identiques (Unix) ou que le mode texte ajoute des caractères \r (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
5
dan04

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.)]

5
zwol
  • 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 approximer des dérivées, ou la formule standard pour calculer des variances, la précision sera perdue en raison du calcul des différences entre des nombres similaires. L'algorithme de Gauß pour résoudre les systèmes linéaires est mauvais car les erreurs d'arrondi s'accumulent, donc on utilise QR ou LU, décomposition de Cholesky, SVD, etc.) L'ajout de nombres à virgule flottante n'est pas associatif. , valeurs 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 concurrence, même sans thread: si vous testez l'existence d'un fichier, le résultat peut devenir invalide à tout moment.

  • ERROR_SUCCESS = 0

4
Philipp

Inclure une vérification pour les tailles entières. La plupart des gens supposent qu'un int est plus grand qu'un court est plus grand qu'un char. Cependant, ceux-ci peuvent tous être faux: sizeof(char) < sizeof(int); sizeof(short) < sizeof(int); sizeof(char) < sizeof(short)

Ce code peut échouer (se bloque sur un accès non aligné)

unsigned char buf[64];

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

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 correct de faire quoi que ce soit avec une variable à valeur de pointeur. Elle ne doit contenir une valeur de pointeur valide que si vous la déréférencez."

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 avec les types à virgule intégrale et à virgule flottante (autres que unsigned char), qui peuvent avoir des représentations d'interruption.


"Les calculs entiers s'enroulent. 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. */

Eh bien, les hypothèses de portabilité classiques non encore mentionnées sont

  • endianisme
4
jk.

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

  • char et signed char sont en fait deux types distincts (contrairement à int et signed int qui font référence au même type d'entier signé).
  • les entiers signés ne sont pas tenus d'utiliser le complément à deux. Le complément et le signe + la grandeur de ceux-ci 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 entière signée, le comportement est défini par l'implémentation.
  • En C90, -3/5 pourrait retourner 0 ou -1. L'arrondi vers zéro dans le cas où un opérande était négatif n'est garanti qu'en C99 vers le haut et C++ 0x vers le haut.
  • Il n'y a aucune garantie de taille exacte pour les types intégrés. La norme ne couvre que des exigences minimales telles qu'un int a au moins 16 bits, un long a au moins 32 bits, un long long a au moins 64 bits. Un float peut représenter au moins 6 chiffres décimaux les plus significatifs correctement. Un double peut représenter au moins 10 chiffres décimaux les plus significatifs correctement.
  • IEEE 754 n'est pas obligatoire pour représenter les nombres à virgule flottante.

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

3
sellibitze

EDIT: mise à 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
3
Patrick Schlüter

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, les modèles 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 (cela 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 castés sur 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 parce que l'environnement dans lequel ces bombes durs n'alloueront pas de tampon supérieur à 64 Ko, de sorte que le code qui le démontre planterait sur d'autres plates-formes.

Ce test particulier passerait sur un système désormais disparu (notez que cela dépend des 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
Joshua

Vous pouvez utiliser le mode texte (fopen("filename", "r")) pour lire n'importe quelle sorte 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 le style UNIX les fins de ligne, dans certaines versions de la bibliothèque standard de Windows, ftell() renverra souvent des valeurs non valides. La solution consiste à utiliser le mode binaire à la place (fopen("filename", "rb")).

2
Chinmay Kanchi

Qu'en est-il du décalage vers la droite par des quantités excessives - est-ce autorisé par la norme ou mérite-t-il d'être testé?

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

 void print_string (char * st) 
 {
 char ch; 
 while ((ch = * st ++)! = 0) 
 putch ( ch);/* Supposons que cela soit défini */
} 
 Int main (void) 
 {
 Print_string ("Hello"); 
 Return 0; 
} 

Sur au moins un compilateur que j'utilise, ce code échouera sauf si l'argument de print_string est 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é.

1
supercat

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
chauncey

Une hypothèse que certains peuvent faire en C++ est qu'un struct est limité à ce qu'il peut faire en C. Le fait est qu'en C++, un struct est comme un class sauf 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
Alerty

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

1
arsenm

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
Paul Nathan

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 des erreurs. J'ai également renommé la variable count 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
Brian

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 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 machines virtuelles Java 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 de 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, short est 16 bits, int est 32 bits et long est 64 bits.

0
Peter Lawrey