122 votes

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

J'aimerais préparer un petit outil éducatif, DONC, ce qui devrait aider les débutants (et intermédiaires) les programmeurs à reconnaître et à remettre en question leurs hypothèses injustifiées en C, C++ et leurs plates-formes.

Exemples:

  • "entiers enrouler autour de la"
  • "tout le monde a ASCII"
  • "Je peux stocker un pointeur de fonction dans un void*"

J'ai pensé qu'un petit programme de test peut être exécuté sur différentes plates-formes, qui dirige le "plausible" les hypothèses qui sont, de notre expérience, DONC, habituellement faite par de nombreux inexpérimentés/semiexperienced intégrer les développeurs et les moyens avec lesquels ils cassent sur diverses machines.

Le but n'est pas de prouver qu'il est "sûr" pour faire quelque chose (ce qui serait impossible à faire, les tests de prouver que quelque chose s'ils se cassent), mais plutôt de démontrer à même la plupart d'incompréhension individuels de la façon la plus discrète expression pause sur une machine différente, si elle a un indéfini ou de mise en œuvre de comportement défini..

Pour atteindre ce que je voudrais vous demander:

  • Comment cette idée peut-elle être améliorée?
  • Ce qui serait bon et comment doivent-ils ressembler?
  • Souhaitez-vous exécuter les tests sur les plates-formes que vous pouvez obtenir vos mains sur et publier les résultats, de sorte que nous nous retrouvons avec une base de données de plates-formes, en quoi ils diffèrent, et pourquoi cette différence est-elle autorisée?

Voici la version actuelle pour le test de jouet:

#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 fait ce wiki de la communauté dès le début parce que je pensais que les gens veulent modifier mon blabla quand ils ont lu.

Mise à JOUR Merci pour vos commentaires. J'ai ajouté un peu de cas de vos réponses et de voir si je peux mettre en place 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":

Merci de répondre ici, avec des correctifs ou de nouvelles idées, de sorte qu'ils peuvent être discutés ou précisé ici. Je vais les fusionner en gotcha.c ensuite.

91voto

Prasoon Saurav Points 47488

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

  • les arguments d'un appel de fonction et
  • les opérandes des opérateurs (par exemple, +, -, =, * , /), à l'exception de:
    • le binaire opérateurs logiques (&& 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;
  } 

38voto

Nordic Mainframe Points 13717

26voto

David Thornley Points 39051

Il y a longtemps, j'enseignais 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 produit des valeurs de type size_t , non int , int sur cette implémentation: 16 bits et size_t avait 32 ans et c'était big-endian. (La plate-forme était Lightspeed C sur Macintoshes 680x0. J'ai dit que c'était il y a longtemps.)

15voto

S.Lott Points 207588

Vous devez inclure les hypothèses ++ et -- .

 a[i++]= i;
 

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

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

8voto

David Given Points 4935

Très intéressant!!!

D'autres choses je pense qu'il pourrait être utile de vérifier:

  • faire des pointeurs de fonction et les pointeurs de données existent dans le même espace d'adressage? (Sauts de Harvard l'architecture des machines, comme des DOS petite mode. Ne sais pas comment vous pouvez tester pour elle, si.)

  • si vous prenez un NULL pointeur de données, et de le jeter à la appropriée de type entier, a-t-elle la valeur numérique 0? (Ruptures sur certaines très anciennes machines à --- voir http://c-faq.com/null/machexamp.html.) Idem avec le pointeur de la fonction. Aussi, ils peuvent être des valeurs différentes.

  • ne incrémenter un pointeur passé la fin de sa correspondante de l'objet de stockage, et puis de nouveau, provoquer des résultats sensées? (Je ne sais pas de toutes les machines de ce fait des pauses sur, mais je crois que la C spec ne vous autorise pas à même de réfléchir sur les pointeurs qui ne pointent pas sur (a) le contenu d'un tableau ou (b) l'élément immédiatement après le tableau ou (c) la valeur NULL. Voir http://c-faq.com/aryptr/non0based.html.)

  • ne la comparaison de deux pointeurs vers les différents objets de stockage avec < et > pour produire des résultats cohérents? (Je peux imaginer ce qui se brisent sur exotiques segment des machines; la spécification interdit de telles comparaisons, de sorte que le compilateur aurait le droit de comparer la partie du décalage du pointeur de la souris uniquement, et non pas le segment de la partie.)

Hmm. Je vais essayer de penser un peu plus.

Edit: Ajout de quelques clarifier les liens au super C de la FAQ.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X