Was gehört zu einem Lehrmittel, um die ungerechtfertigten Annahmen zu demonstrieren, die Menschen in C / C ++ treffen?


121

Ich möchte ein kleines Lernwerkzeug für SO vorbereiten, das Anfängern (und fortgeschrittenen Programmierern) helfen soll, ihre ungerechtfertigten Annahmen in C, C ++ und ihren Plattformen zu erkennen und in Frage zu stellen.

Beispiele:

  • "ganze Zahlen wickeln sich um"
  • "Jeder hat ASCII"
  • "Ich kann einen Funktionszeiger in einer Leere speichern *"

Ich dachte mir, dass ein kleines Testprogramm auf verschiedenen Plattformen ausgeführt werden könnte, das die "plausiblen" Annahmen ausführt, die nach unserer Erfahrung in SO normalerweise von vielen unerfahrenen / halb erfahrenen Mainstream-Entwicklern getroffen werden, und die Art und Weise aufzeichnet, wie sie auf verschiedenen Maschinen brechen.

Ziel ist es nicht, zu beweisen, dass es "sicher" ist, etwas zu tun (was unmöglich wäre, die Tests beweisen nur etwas, wenn sie brechen), sondern selbst dem verständnislosesten Individuum zu demonstrieren, wie unauffällig der Ausdruck ist brechen Sie auf einem anderen Computer ab, wenn dieser ein undefiniertes oder implementierungsdefiniertes Verhalten aufweist. .

Um dies zu erreichen, möchte ich Sie fragen:

  • Wie kann diese Idee verbessert werden?
  • Welche Tests wären gut und wie sollten sie aussehen?
  • Würden Sie die Tests auf den Plattformen ausführen, die Sie in die Hände bekommen und die Ergebnisse veröffentlichen können, damit wir eine Datenbank mit Plattformen erhalten, wie sie sich unterscheiden und warum dieser Unterschied zulässig ist?

Hier ist die aktuelle Version für das Testspielzeug:

#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, und ich habe dieses Community-Wiki von Anfang an erstellt, weil ich dachte, dass die Leute mein Geschwätz bearbeiten wollen, wenn sie dies lesen.

UPDATE Vielen Dank für Ihre Eingabe. Ich habe einige Fälle aus Ihren Antworten hinzugefügt und werde sehen, ob ich einen Github dafür einrichten kann, wie Greg vorgeschlagen hat.

UPDATE : Ich habe dafür ein Github-Repo erstellt, die Datei lautet "gotcha.c":

Bitte antworten Sie hier mit Patches oder neuen Ideen, damit diese hier diskutiert oder geklärt werden können. Ich werde sie dann in gotcha.c zusammenführen.


7
Betrachten Sie das mittlere Modell unter DOS. Funktionen können in mehreren Segmenten gespeichert werden, sodass ein Funktionszeiger 32 Bit lang ist. Ihre Daten werden jedoch nur in einem einzelnen Segment gespeichert, daher sind Datenzeiger nur 16 Bit lang. Da void * ein Datenzeiger ist, ist er 16 Bit breit, sodass Sie keinen Funktionszeiger in einen einfügen können. Siehe c-jump.com/CIS77/ASM/Directives/D77_0030_models.htm .
David gegeben

6
Vielleicht könnten Sie diesen Code auf github.com oder so hochwerfen und dann könnten die Leute leicht Patches beisteuern.
Greg Hewgill

1
Viele Dinge hier sollten helfen: stackoverflow.com/questions/367633/…
Martin York

4
POSIX erfordert, dass Funktionszeiger dieselbe Darstellung wie void * haben und ohne Informationsverlust konvertiert werden können (mit einer Umwandlung). Einer der Gründe dafür ist, dass dlsym()ein void * zurückgegeben wird, aber sowohl für Daten- als auch für Funktionszeiger vorgesehen ist. Daher kann es nicht so schlimm sein, sich darauf zu verlassen.
Jilles

3
@tristopia: Punkt 15 ist da, weil viele Anfänger oft überrascht sind zu erfahren, dass Daten nicht kontinuierlich gepackt werden, sondern an bestimmten Grenzen ausgerichtet sind. Sie sind verwirrt, wenn sie die Reihenfolge der Mitglieder ändern und unterschiedliche Objektgrößen erhalten. Außerdem ist das Packen der Standardmodus bei vielen modernen Mikrocontrollern oder eingebetteten Geräten. Mein AVR Atmega und TurboC / MSDOS Ausgang ist auch gepackt. MSDOS wird immer noch in industriellen Anwendungen verwendet.
Nordic Mainframe

Antworten:


91

Die Reihenfolge der Auswertung von Unterausdrücken, einschließlich

  • die Argumente eines Funktionsaufrufs und
  • Operanden von Operatoren (zB +, -, =, *, /), mit Ausnahme von:
    • die binären logischen Operatoren ( &&und ||),
    • der ternäre bedingte Operator ( ?:) und
    • der Kommaoperator ( ,)

ist nicht spezifiziert

Beispielsweise

  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
Ich hatte das immer über Funktionsparameter gewusst, aber ich habe nie an Operatoren gedacht ... ... und wenn ich jemals sehe, dass Sie in einer Produktionsumgebung solchen Code schreiben, werde ich Sie mit einer feuchten Nudel schlagen.
Riwalk

3
@ Billy: Aber nur für die primitiven Versionen der Operatoren.
Dennis Zickefoose

1
@ Tennis: Das stimmt. (Aus diesem Grund ist es ein Element in Effective / MoreEffective C ++, diese niemals zu überladen (es sei denn, Sie schreiben boost::spirit)
Billy ONeal

1
@ Daniel: Ich bin nicht sicher, was du sagen willst. Es hört sich so an, als würden Sie vorschlagen, dass es in Ordnung ist, die Operatoren zu überladen, da nur die Benutzer Ihrer Klasse dies falsch verstehen könnten. Wenn Sie nicht in reinem C ++ schreiben, spielt dies keine Rolle. Beides macht überhaupt keinen Sinn.
Dennis Zickefoose

2
@ user420536: Das Verhalten ist nur nicht angegeben, aber nicht undefiniert. Ja, das Beispiel kann entweder Hello World! oder Welt! Hallo, aber das ist nur nicht angegeben, da die Reihenfolge der Auswertung der Operanden des +Operators nicht angegeben ist (Compiler-Autoren müssen das Verhalten nicht dokumentieren). Es verstößt nicht gegen eine Sequenzpunktregel als solche.
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 stürzt ab. "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 / Kleiner Speicher

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 / Mittlerer Speicher

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 / Kompaktspeicher

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 (Vizemulator)

Alt-Text


Ich werde diese später aktualisieren:


Borland C ++ Builder 6.0 unter 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-Bit

(muss als C ++ kompiliert werden, da der CLR-Compiler kein reines C unterstützt)

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 (gcc-4.5.2-Vorlase)

- 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

64-Bit-Windows verwendet das LLP64-Modell: Beide intund longsind als 32-Bit definiert, was bedeutet, dass keines für einen Zeiger lang genug ist.


avr-gcc 4.3.2 / ATmega168 (Arduino Diecimila)

Die fehlgeschlagenen Annahmen sind:

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

Der Atmega168 verfügt über einen 16-Bit-PC, Code und Daten befinden sich jedoch in separaten Adressräumen. Größere Atmegas haben einen 22-Bit-PC!.


gcc 4.2.1 unter MacOSX 10.6, kompiliert mit -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
Und Sie haben eine andere Annahme identifiziert: Sie können 80 Zeichen in eine Terminalzeile einfügen.
Mike Seymour

3
sizeof(void*)>=sizeof(void(*)())wäre relevanter als ==. Alles , was wir über Pflege ist „können wir einen Funktionszeiger in einem void - Zeiger speichern“, so , ob die Annahme Sie testen müssen , ist ein void*ist mindestens so groß wie ein Funktionszeiger.
Jalf

1
Wenn Ihre Umgebung POSIX-kompatibel ist, sollten Sie damit einverstanden seinsizeof(void*)>=sizeof(void(*)())
Daniel Earwicker

26

Vor langer Zeit unterrichtete ich C aus einem Lehrbuch, das hatte

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

als Beispielfrage. Er scheiterte für einen Studenten, weil die sizeofRenditen Werte des Typs size_t, nicht int, intauf dieser Implementierung 16 Bit war und size_twar 32, und es war Big-Endian. (Die Plattform war Lightspeed C auf 680x0-basierten Macintosh-Computern. Ich sagte, es ist lange her.)


7
+1 für den Hinweis auf einen der häufigsten und am häufigsten übersehenen Fehler dieser Art.
R .. GitHub STOP HELPING ICE

4
Dies geschieht auch auf 64-Bit-Systemen, bei denen size_t 64 Bit beträgt und Ints fast immer kürzer sind. Win64 ist immer noch seltsamer, weil size_t da ist unsigned long long. Als Test 17 hinzugefügt.
Nordic Mainframe

Leider unterstützt die C-Laufzeit von Microsoft den zModifikator für size_tgroße Ganzzahlen long longnicht und wird auf einigen Plattformen auch nicht unterstützt. Es gibt also keine sichere tragbare Möglichkeit, die gedruckte Größe eines Objekts zu formatieren oder umzuwandeln.
Phil Miller

15

Sie müssen die ++und --Annahmen berücksichtigen, die die Leute machen.

a[i++]= i;

Zum Beispiel ist es syntaktisch legal, führt aber zu unterschiedlichen Ergebnissen, abhängig von zu vielen Dingen, um sie zu begründen.

Jede Anweisung mit ++(oder --) und einer Variablen, die mehrmals vorkommt, ist ein Problem.


Und das ist auch eine so häufige Frage!
Matthieu M.

8

Sehr interessant!

Andere Dinge, die mir einfallen, könnten nützlich sein, um zu überprüfen:

  • Gibt es Funktionszeiger und Datenzeiger im selben Adressraum? (Bricht in Maschinen mit Harvard-Architektur wie dem kleinen DOS-Modus ein. Ich weiß jedoch nicht, wie Sie darauf testen würden.)

  • Wenn Sie einen NULL-Datenzeiger nehmen und ihn in den entsprechenden Integer-Typ umwandeln, hat er den numerischen Wert 0? (Pausen auf einigen wirklich alten Maschinen --- siehe http://c-faq.com/null/machexamp.html .) Das Gleiche gilt für den Funktionszeiger. Sie können auch unterschiedliche Werte sein.

  • Verursacht das Inkrementieren eines Zeigers über das Ende seines entsprechenden Speicherobjekts hinaus und dann wieder zurück sinnvolle Ergebnisse? (Ich kenne keine Maschinen, auf denen dies tatsächlich funktioniert, aber ich glaube, die C-Spezifikation erlaubt es Ihnen nicht einmal , an Zeiger zu denken , die weder auf (a) den Inhalt eines Arrays noch auf (b) das Element verweisen unmittelbar nach dem Array oder (c) NULL. Siehe http://c-faq.com/aryptr/non0based.html .)

  • führt der Vergleich zweier Zeiger mit unterschiedlichen Speicherobjekten mit <und> zu konsistenten Ergebnissen? (Ich kann mir vorstellen, dass dies auf exotischen segmentbasierten Maschinen nicht funktioniert. Die Spezifikation verbietet solche Vergleiche, sodass der Compiler berechtigt wäre, nur den versetzten Teil des Zeigers und nicht den Segmentteil zu vergleichen.)

Hmm. Ich werde versuchen, an etwas mehr zu denken.

Bearbeiten: Es wurden einige klarstellende Links zu den hervorragenden C-FAQ hinzugefügt.


2
Vor einiger Zeit habe ich übrigens ein experimentelles Projekt namens Clue ( cluecc.sourceforge.net ) durchgeführt, mit dem Sie C in Lua, Javascript, Perl, LISP usw. kompilieren konnten. Es hat das undefinierte Verhalten im C-Standard rücksichtslos ausgenutzt, damit Zeiger funktionieren . Es kann interessant sein, diesen Test daran zu versuchen.
David gegeben

1
Mit IIRC C können Sie einen Zeiger über das Ende eines Objekts hinaus um 1 erhöhen , jedoch nicht weiter. Das Dekrementieren auf eine Position vor dem Beginn eines Objekts ist jedoch nicht zulässig.
R .. GitHub STOP HELPING ICE

@R. Gleiches gilt für C ++. Ein weiteres Inkrementieren kann unterbrochen werden, wenn das Inkrementieren des Zeigers einen Überlauf auf CPUs verursacht, die Zeiger nicht nur als Ganzzahlen behandeln.
Jalf

5

Ich denke, Sie sollten sich bemühen, zwischen zwei sehr unterschiedlichen Klassen von "falschen" Annahmen zu unterscheiden. Eine gute Hälfte (Rechtsverschiebung und Vorzeichenerweiterung, ASCII-kompatible Codierung, Speicher ist linear, Daten- und Funktionszeiger sind kompatibel usw.) sind für die meisten C-Codierer ziemlich vernünftige Annahmen und können sogar als Teil des Standards enthalten sein Wenn C heute entworfen würde und wenn wir keinen alten IBM-Junk-Großvater hätten. Die andere Hälfte (Dinge im Zusammenhang mit Speicher-Aliasing, Verhalten von Bibliotheksfunktionen bei Überlappung von Eingabe- und Ausgabespeicher, 32-Bit-Annahmen wie Zeiger, die passen intoder die Sie verwenden könnenmalloc Ohne Prototyp ist diese Aufrufkonvention für variadische und nicht-variadische Funktionen identisch. ...) Entweder im Widerspruch zu Optimierungen, die moderne Compiler durchführen möchten, oder zur Migration auf 64-Bit-Maschinen oder andere neue Technologien.


Es ist nicht nur "IBM Junk" (obwohl ich der Meinung bin, dass das IBM-Zeug Junk ist). Viele eingebettete Systeme haben heutzutage ähnliche Probleme.
Rmeador

Zur Verdeutlichung bedeutet die Verwendung mallocohne Prototyp <stdlib.h>, dass ein Nein-Nein nicht berücksichtigt wird , was mallocstandardmäßig dazu führt int malloc(int), dass 64-Bit unterstützt werden soll.
Joey Adams

Technisch gesehen können Sie nicht einschließen <stdlib.h>, solange Sie einen anderen Header einfügen, der definiert, size_tund dann selbst mallocmit einem korrekten Prototyp deklarieren .
R .. GitHub STOP HELPING ICE

5

Hier ist eine lustige: Was ist mit dieser Funktion falsch?

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;
}

[Antwort (rot13): Inevnqvp nethzragf borl gur byq X & E cebzbgvba ehyrf, juvpu zrnaf lbh pnaabg hfr 'sybng' (sei 'pune' sei '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, das ist gut. clang 2.7 isst dies und erzeugt ohne Vorwarnung völligen Unsinn.
Nordic Mainframe

va_arg wird erweitert, wenn es sich um ein Makro handelt und die while-Schleife nur die erste Anweisung ausführt, von vielleicht vielen?
Maister

Nein (wenn das passiert wäre, wäre es ein Fehler in der Implementierung).
zwol

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

Ein anderer befasst sich mit dem Textmodus in fopen. Die meisten Programmierer gehen davon aus, dass entweder Text und Binär identisch sind (Unix) oder dass der Textmodus \rZeichen hinzufügt (Windows). C wurde jedoch auf Systeme portiert, die Datensätze mit fester Breite verwenden, bei denen fputc('\n', file)in einer Textdatei Leerzeichen oder ähnliches hinzugefügt werden müssen, bis die Dateigröße ein Vielfaches der Datensatzlänge beträgt.

Und hier sind meine Ergebnisse:

gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3 auf 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

Ich habe tatsächlich Code gesehen, der pow(2, n)mit Bitoperationen kombiniert wurde .
Dan04

4

Einige von ihnen können nicht einfach in C getestet werden, da das Programm wahrscheinlich bei Implementierungen abstürzt, bei denen die Annahme nicht zutrifft.


"Es ist in Ordnung, mit einer Variablen mit Zeigerwert etwas zu tun. Sie muss nur dann einen gültigen Zeigerwert enthalten, wenn Sie ihn dereferenzieren."

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 */
}

Gleiches gilt für Integral- und Gleitkommatypen (außer unsigned char), die Trap-Darstellungen haben dürfen.


"Ganzzahlige Berechnungen werden umgangen. Dieses Programm gibt also eine große negative Ganzzahl aus."

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

(Nur C89.) "Es ist in Ordnung, am Ende von zu fallen main."

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

2
Als konkretes Beispiel: Beim Kompilieren folgt auf gcc -ftrapv -Odie AusgabeWe like to think that:Aborted
caf

@caf: "Diese Option generiert Traps für signierten Überlauf bei Additions-, Subtraktions- und Multiplikationsoperationen." Schön zu wissen, danke.
Gilles 'SO - hör auf böse zu sein'

1
Der letzte ist auch in C ++ (98, 03 und 0x) in
Ordnung

Das ist böse, weil Pre-ANSI C dies erlaubt hat und C99 auch.
Joshua

@Joshua: AFAIK Es gibt keinen Unterschied zwischen Pre-ANSI C und C89 bei der Rückkehr von mainohne Wert: Das Programm ist korrekt, gibt jedoch einen undefinierten Beendigungsstatus zurück (C89 §2.1.2.2). Bei vielen Implementierungen (wie gcc und älteren Unix-Compilern) erhalten Sie alles, was sich zu diesem Zeitpunkt in einem bestimmten Register befand. Das Programm funktioniert normalerweise so lange, bis es in einem Makefile oder einer anderen Umgebung verwendet wird, die den Beendigungsstatus überprüft.
Gilles 'SO - hör auf böse zu sein'

4

Nun, die klassischen Portabilitätsannahmen, die noch nicht gemeint sind, sind

  • Annahmen über die Größe integraler Typen
  • Endianness

4
"Endianness", einschließlich "There is a endianness": Es gibt Middle-Endian-Maschinen, und der Standard erlaubt seltsame Dinge wie das Speichern eines shortWertes fedcab9876543210 (das sind 16 Binärziffern) als die beiden Bytes 0248ace und fdb97531.
Gilles 'SO - hör auf böse zu sein'

Ja, Endianess umfasst mit Sicherheit gemischte / mittlere Endian sowie große und kleine. Wenn Sie sich für benutzerdefinierte Hardware entscheiden, können Sie in jedem Bus eine beliebige Endianess haben.
jk.

Middle Endian ist als PDP-Endian bekannt. Gilles beschreibt etwas noch Seltsameres, das Kopfschmerzen bei der Implementierung von TCP / IP verursachen würde.
Joshua

@ Gilles: Middle-Endian ... Ich bin sehr froh, dass ich mich nicht weiterentwickle. (aber jetzt werde ich gebeten, ein Middle-Endian-Netzwerkprojekt durchzuführen, da bin ich mir sicher) ...
Paul Nathan

ARM FPE verwendete Middle-Endian-Doubles, bei denen sie als <high ​​quad> <low quad> -Paar gespeichert wurden, aber die Reihenfolge der Bits in jedem Quad war falsch herum. (Zum Glück macht ARM VFP dies nicht mehr.)
David Given

4
  • Diskretisierungsfehler aufgrund von Gleitkommadarstellung. Wenn Sie beispielsweise die Standardformel verwenden, um quadratische Gleichungen zu lösen, oder endliche Differenzen, um Ableitungen zu approximieren, oder die Standardformel, um Varianzen zu berechnen, geht die Genauigkeit aufgrund der Berechnung von Differenzen zwischen ähnlichen Zahlen verloren. Der Gauß-Algorithmus zum Lösen linearer Systeme ist schlecht, da sich Rundungsfehler ansammeln. Daher werden QR- oder LU-Zerlegung, Cholesky-Zerlegung, SVD usw. verwendet. Das Hinzufügen von Gleitkommazahlen ist nicht assoziativ. Es gibt denormale, unendliche und NaN-Werte. a + b - ab .

  • Zeichenfolgen: Unterschied zwischen Zeichen, Codepunkten und Codeeinheiten. Wie Unicode auf den verschiedenen Betriebssystemen implementiert ist; Unicode-Codierungen. Das Öffnen einer Datei mit einem beliebigen Unicode-Dateinamen ist mit C ++ nicht portabel möglich.

  • Rennbedingungen, auch ohne Threading: Wenn Sie testen, ob eine Datei vorhanden ist, kann das Ergebnis jederzeit ungültig werden.

  • ERROR_SUCCESS = 0


4

Fügen Sie eine Prüfung für ganzzahlige Größen hinzu. Die meisten Leute gehen davon aus, dass ein int größer als ein Short größer als ein Char ist. Diese können jedoch alle falsch sein:sizeof(char) < sizeof(int); sizeof(short) < sizeof(int); sizeof(char) < sizeof(short)

Dieser Code schlägt möglicherweise fehl (stürzt bei nicht ausgerichtetem Zugriff ab)

unsigned char buf[64];

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

Würde dieser Code in C ++ fehlschlagen? IIRC, es ist illegal, Zeiger zwischen nicht verwandten Typen umzuwandeln, AUSSER für char *, die in einen beliebigen Typ umgewandelt werden können (oder ist es umgekehrt?).
Rmeador

1
Sie könnten es einfach int *p = (int*)&buf[1];in C ++ tun , die Leute erwarten, dass das auch funktioniert.
Nr.

@nos, ja, das kann fehlschlagen, aber der Fehler ist ein Absturz, sodass sein Programm nicht darauf testen kann. :(
Joshua

1
sizeof(char) < sizeof(int)Wird benötigt. Beispielsweise gibt fgetc () den Wert des Zeichens als vorzeichenloses Zeichen zurück, das in int konvertiert wurde oder EOFein negativer Wert ist. unsigned charMöglicherweise sind keine Füllbits vorhanden. Dies kann also nur erreicht werden, indem int größer als char gemacht wird. Außerdem erfordern (die meisten Versionen von) der C-Spezifikation, dass jeder Wert aus dem Bereich -32767..32767 in einem int gespeichert werden kann.
Jilles

@illes noch, es gibt DSPs mit 32-Bit-Zeichen und 32-Bit-Ints.
Nr.

3

Einige Dinge zu integrierten Datentypen:

  • charund signed charsind tatsächlich zwei verschiedene Typen (im Gegensatz zu intund signed intdie sich auf denselben vorzeichenbehafteten Ganzzahltyp beziehen).
  • Vorzeichenbehaftete Ganzzahlen sind nicht erforderlich, um das Zweierkomplement zu verwenden. Das Komplement und das Vorzeichen + die Größe eines Menschen sind ebenfalls gültige Darstellungen negativer Zahlen. Dies macht Bitoperationen mit negativen Zahlen implementierungsdefiniert .
  • Wenn Sie einer vorzeichenbehafteten Ganzzahlvariablen eine Ganzzahl außerhalb des Bereichs zuweisen, ist das Verhalten implementierungsdefiniert .
  • In C90 -3/5könnte zurückkehren 0oder -1. Eine Rundung gegen Null für den Fall, dass ein Operand negativ war, ist nur in C99 nach oben und C ++ 0x nach oben garantiert.
  • Es gibt keine genauen Größengarantien für die eingebauten Typen. Der Standard deckt nur minimale Anforderungen ab, z. B. a inthat mindestens 16 Bit, a longhat mindestens 32 Bit, a long longhat mindestens 64 Bit. A floatkann mindestens 6 höchstwertige Dezimalstellen korrekt darstellen. A doublekann mindestens 10 höchstwertige Dezimalstellen korrekt darstellen.
  • IEEE 754 ist für die Darstellung von Gleitkommazahlen nicht obligatorisch.

Zugegeben, auf den meisten Maschinen haben wir zwei Komplemente und IEEE 754-Floats.


Ich frage mich, welchen Wert es hat, wenn Ganzzahlzuweisungen außerhalb des Bereichs implementierungsdefiniert werden und nicht undefiniertes Verhalten. Auf einigen Plattformen würde eine solche Anforderung den Compiler zwingen, zusätzlichen Code für int mult(int a,int b) { return (long)a*b;}[z. B. wenn int32 Bit, aber Register und long64] generiert werden. Ohne eine solche Anforderung, die „natürliche“ Verhalten der schnellsten Implementierung long l=mult(1000000,1000000);würde eingestellt lgleich 1000000000000, auch wenn das eine „unmögliche“ Wert für eine ist int.
Supercat

3

Wie wäre es mit diesem:

Kein Datenzeiger kann jemals mit einem gültigen Funktionszeiger identisch sein.

Dies ist WAHR für alle flachen Modelle, MS-DOS TINY-, LARGE- und HUGE-Modelle, falsch für MS-DOS SMALL-Modelle und fast immer falsch für MEDIUM- und COMPACT-Modelle (abhängig von der Ladeadresse benötigen Sie ein wirklich altes DOS mach es wahr).

Ich kann dafür keinen Test schreiben

Und noch schlimmer: Zeiger, die in ptrdiff_t umgewandelt wurden, können verglichen werden. Dies gilt nicht für das MS-DOS LARGE-Modell (der einzige Unterschied zwischen LARGE und HUGE besteht darin, dass HUGE Compiler-Code zum Normalisieren von Zeigern hinzufügt).

Ich kann keinen Test schreiben, da die Umgebung, in der diese Bomben hart sind, keinen Puffer größer als 64 KB zuweist, sodass der Code, der dies demonstriert, auf anderen Plattformen abstürzt.

Dieser spezielle Test würde ein inzwischen nicht mehr funktionierendes System weitergeben (beachten Sie, dass dies von den Interna von malloc abhängt):

  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

BEARBEITEN: Auf die letzte Version des Programms aktualisiert

Solaris-SPARC

gcc 3.4.6 in 32 bit

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 in 64 bit

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

und mit SUNStudio 11 32 Bit

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

und mit SUNStudio 11 64 Bit

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

Sie können text-mode ( fopen("filename", "r")) verwenden, um jede Art von Textdatei zu lesen.

Während dies theoretisch gut funktionieren sollteftell() , werden in einigen Versionen der Windows-Standardbibliothek ftell()häufig ungültige Werte zurückgegeben , wenn Sie sie auch in Ihrem Code verwenden und Ihre Textdatei UNIX-artige Zeilenenden aufweist . Die Lösung besteht darin, stattdessen den Binärmodus zu verwenden ( fopen("filename", "rb")).


1

gcc 3.3.2 unter AIX 5.3 (ja, wir müssen gcc aktualisieren)

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

Eine Annahme, die einige in C ++ machen können, ist, dass a auf structdas beschränkt ist, was es in C kann. Tatsache ist, dass a in C ++ structwie a ist, classaußer dass es standardmäßig alles öffentlich hat.

C ++ Struktur:

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


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

protected:
  void Testing2();
};

1

Standard-Mathematikfunktionen auf verschiedenen Systemen liefern keine identischen Ergebnisse.


1

Visual Studio Express 2010 auf 32-Bit x86.

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

Über 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).

Beachten Sie, dass Codepad nicht hatte stddef.h. Ich habe Test 9 aufgrund eines Codepads entfernt und Warnungen als Fehler verwendet. Ich habe die countVariable auch umbenannt , da sie aus irgendeinem Grund bereits definiert wurde.

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

Wie wäre es mit einer Verschiebung nach rechts um übermäßige Beträge - ist dies nach dem Standard zulässig oder einen Test wert?

Gibt Standard C das Verhalten des folgenden Programms an:

void print_string (char * st)
{
  char ch;
  while ((ch = * st ++)! = 0)
    putch (ch); / * Angenommen, dies ist definiert * /
}}
int main (nichtig)
{
  print_string ("Hallo");
  return 0;
}}

Auf mindestens einem von mir verwendeten Compiler schlägt dieser Code fehl, es sei denn, das Argument für print_string ist "char const *". Erlaubt der Standard eine solche Einschränkung?

Einige Systeme erlauben es einem, Zeiger auf nicht ausgerichtete Ints zu erzeugen, andere nicht. Könnte einen Test wert sein.


C89 §3.3.7: „Wenn der Wert des rechten Operanden negativ ist oder größer oder gleich der Breite in Bits des heraufgestuften linken Operanden ist, ist das Verhalten undefiniert.“ (gilt für beide <<und >>). C99 hat in §6.5.7-3 die gleiche Sprache.
Gilles 'SO - hör auf böse zu sein'

Abgesehen von putch(warum haben Sie den Standard nicht verwendet putchar?) Kann ich in Ihrem Programm kein undefiniertes Verhalten feststellen. C89 §3.1.4 legt fest, dass „ein Zeichenfolgenliteral vom Typ […] 'Array of char' hat“ (Anmerkung: nein const) und dass „wenn das Programm versucht, ein Zeichenfolgenliteral zu ändern […], das Verhalten undefiniert ist“. . Welcher Compiler ist das und wie übersetzt er dieses Programm?
Gilles 'SO - hör auf böse zu sein'

2
In C ++ sind Zeichenkonstanten nicht char [], sondern const char []. Allerdings ... gab es früher eine bestimmte Lücke im Typsystem, damit Sie eine Zeichenfolgenkonstante in einem Kontext verwenden können, in dem ein Zeichen * erwartet wurde und kein Typfehler auftritt. Dies führte zu Situationen, in denen print_string ("foo") funktionieren würde, print_string ("foo" +0) jedoch nicht. Dies war zutiefst verwirrend, insbesondere in Umgebungen, in denen C-Dateien standardmäßig mit einem C ++ - Compiler kompiliert werden. Das Loch wurde in neuen Compilern entfernt, aber es gibt immer noch viele alte. AFAIK C99 definiert weiterhin String-Konstanten als char [].
David gegeben

1
Bei den HiTech-Compilern für die Controller der Microchip PIC-Serie kann ein Zeiger ohne Speicherqualifizierer nur auf RAM zeigen. Ein const-qualifizierter Zeiger kann entweder auf RAM oder ROM zeigen. Nicht const-qualifizierte Zeiger werden direkt im Code dereferenziert. const-qualifizierte Zeiger werden über die Bibliotheksroutine dereferenziert. Abhängig vom jeweiligen PIC-Typ sind nicht const-qualifizierte Zeiger 1 oder 2 Byte. const-qualifizierte sind 2 oder 3. Da ROM viel umfangreicher als RAM ist, ist es im Allgemeinen eine gute Sache, Konstanten im ROM zu haben.
Supercat

@ David Given: Beachten Sie auch meinen vorherigen Kommentar. Ich bevorzuge Compiler, die andere Qualifikationsmerkmale als "const" verwenden, um die Hardwarespeicherklasse zu bezeichnen. Der HiTech-Compiler hat einige ziemlich nervige Macken bei der Zuordnung der Speicherklassen (z. B. Datenelemente, deren "Komponentengröße" ein Byte ist, oder Datenelemente, die mehr als 256 Byte groß sind, befinden sich in einem "großen" Segment. Andere Datenelemente befinden sich im " bss "-Segment für das Modul, das sie definiert haben; alle" bss "-Elemente in einem Modul müssen in 256 Bytes passen. Arrays, die etwas
weniger als

0

Zu Ihrer Information: Für diejenigen, die ihre C-Kenntnisse in Java übersetzen müssen, sind hier einige Fallstricke.

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

In Java ist char 16-Bit und signiert. Byte ist 8-Bit und signiert.

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

long ist immer 64-Bit, Referenzen können 32-Bit- oder 64-Bit-Referenzen sein (wenn Sie mehr als eine App mit mehr als 32 GB haben). 64-Bit-JVMs verwenden normalerweise 32-Bit-Referenzen.

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

Die Verschiebung wird maskiert, so dass i << 64 == i == i << -64, i << 63 == i << -1

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

ByteOrder.nativeOrder () kann BIG_ENDIAN oder LITTLE_ENDIAN sein

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

i = i++ Ändert sich nie i

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

Die Größe von Sammlungen und Arrays beträgt immer 32-Bit, unabhängig davon, ob die JVM 32-Bit oder 64-Bit ist.

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 ist 16-Bit, short ist 16-Bit, int ist 32-Bit und long ist 64-Bit.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.