İnsanların C / C ++ 'da yaptığı yersiz varsayımları gösterecek bir eğitim aracına ne aittir?


121

SO için yeni başlayanlar (ve orta seviye) programcıların C, C ++ ve platformlarındaki gereksiz varsayımlarını tanımalarına ve bunlara meydan okumalarına yardımcı olacak küçük bir eğitim aracı hazırlamak istiyorum.

Örnekler:

  • "tamsayılar etrafına sarılır"
  • "herkesin ASCII'si vardır"
  • "Bir işlev işaretçisini boşlukta saklayabilirim *"

Küçük bir test programının çeşitli platformlarda çalıştırılabileceğini düşündüm, SO'daki deneyimlerimize dayanarak, genellikle birçok deneyimsiz / yarı deneyimsiz ana akım geliştirici tarafından yapılan ve çeşitli makinelerde kırılma yollarını kaydeden "makul" varsayımları çalıştıran.

Bunun amacı, bir şeyi yapmanın "güvenli" olduğunu kanıtlamak değil (ki bunu yapmak imkansızdır, testler sadece kırılırsa her şeyi kanıtlar), bunun yerine en anlaşılmaz bireye bile en göze çarpmayan ifadenin nasıl olduğunu göstermektir. tanımlanmamış veya uygulama tanımlı bir davranışa sahipse, farklı bir makinede kırılma. .

Bunu başarmak için size sormak istiyorum:

  • Bu fikir nasıl geliştirilebilir?
  • Hangi testler iyi olur ve nasıl görünmelidir?
  • Testleri elinize alabileceğiniz ve sonuçları gönderebileceğiniz platformlarda çalıştırır mısınız, böylece bir platform veritabanı elde ederiz, bunlar nasıl farklılaşır ve bu farklılığa neden izin verilir?

İşte test oyuncağının güncel versiyonu:

#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, ve bu topluluk wiki'sini en başından beri yaptım çünkü insanların bunu okuduklarında blabber'ımı düzenlemek istediklerini düşündüm.

GÜNCELLEME Girişiniz için teşekkürler. Cevaplarınızdan birkaç durum ekledim ve Greg'in önerdiği gibi bunun için bir github kurabilir miyim göreceğim.

GÜNCELLEME : Bunun için bir github deposu oluşturdum, dosya "gotcha.c":

Lütfen buraya yamalar veya yeni fikirlerle yanıt verin, böylece burada tartışılabilir veya açıklığa kavuşturulabilir. Onları gotcha.c ile birleştireceğim o zaman.


7
DOS'taki orta modeli düşünün. Fonksiyonlar birden fazla segmentte saklanabilir, bu nedenle bir fonksiyon göstergesi 32 bit uzunluğundadır. Ancak verileriniz yalnızca tek bir segmentte saklanır, bu nedenle veri işaretçileri yalnızca 16 bit uzunluğundadır. Void * bir veri işaretçisi olduğundan, 16 bit genişliğindedir, dolayısıyla bir işlev işaretçisini birine sığdıramazsınız. Bkz. C-jump.com/CIS77/ASM/Directives/D77_0030_models.htm .
David

6
Belki bu kodu github.com veya başka bir yere atarsınız ve sonra insanlar kolayca yamalara katkıda bulunabilir.
Greg Hewgill

1
Buradaki birçok şeyin yardımcı olması gerekir: stackoverflow.com/questions/367633/…
Martin York

4
POSIX, işlev işaretçilerinin void * ile aynı temsile sahip olmasını gerektirir ve bilgi kaybı olmadan dönüştürülebilir (bir atama ile). Bunun nedenlerinden biri, dlsym()void * döndürmesidir, ancak hem veri hem de işlev işaretçileri için tasarlanmıştır. Bu nedenle buna güvenmek o kadar da kötü olmayabilir.
jilles

3
@tristopia: Nokta 15 burada, çünkü yeni başlayanların çoğu, verilerin sürekli olarak paketlenmediğini, bunun yerine belirli sınırlara göre hizalandığını öğrenince şaşırır. Üye sırasını değiştirdiklerinde ve farklı nesne boyutları aldıklarında şaşırırlar. Ayrıca, paketleme, birçok çağdaş mikro denetleyici veya gömülü cihaz için varsayılan moddur. AVR Atmega ve TurboC / MSDOS çıktım da paketlenmiş durumda. MSDOS hala endüstriyel uygulamalarda kullanılmaktadır.
Nordic Mainframe

Yanıtlar:


91

Aşağıdakileri içeren alt ifadelerin değerlendirme sırası

  • bir işlev çağrısının argümanları ve
  • Operatörlerin işlenenler (örneğin +, -, =, *, /), hariç:
    • ikili mantıksal operatörler ( &&ve ||),
    • üçlü koşullu operatör ( ?:) ve
    • virgül operatörü ( ,)

olduğu Tanımlanmamış

Örneğin

  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
Bunu her zaman fonksiyon parametreleri hakkında biliyordum, ama bunu operatörler açısından hiç düşünmemiştim ... ... ve eğer seni bir üretim ortamında böyle bir kod yazdığını görürsem, seni ıslak bir erişte ile tokatlarım.
riwalk

3
@Billy: Ama yalnızca operatörlerin ilkel sürümleri için.
Dennis Zickefoose

1
@Dennis: Bu doğru. (Bu nedenle, Etkili / Daha Etkili C ++ 'da bunlara asla aşırı yüklenmeyecek bir öğedir (yazmıyorsanız boost::spirit)
Billy ONeal

1
@Daniel: Ne söylemeye çalıştığından emin değilim. Görünüşe göre operatörleri aşırı yüklemenin sorun olmadığını düşünüyorsunuz, çünkü bu sadece sınıfınızın kullanıcıları yanlış anlayabilir ve düz C ++ ile yazmıyorsanız önemli değil. Hiçbiri mantıklı değil.
Dennis Zickefoose

2
@ user420536: Davranış sadece belirtilmemiş ancak tanımlanmamış değil. Evet, örnek ya Hello World! veya Dünya! Merhaba, ancak bu yalnızca belirtilmemiş çünkü +operatörün işlenenlerinin değerlendirme sırası belirtilmemiş (Derleyici yazarlarının davranışı belgelemesine gerek yoktur). Herhangi bir sıralama noktası kuralını bu şekilde ihlal etmez .
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 çöküyor. "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 / Küçük Bellek

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 / Orta Bellek

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 / Kompakt Bellek

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 (emülatör yardımcısı)

alternatif metin


Bunları daha sonra güncelleyeceğim:


Borland C ++ Builder 6.0, Windows XP'de

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

(CLR derleyicisi saf C'yi desteklemediği için C ++ olarak derlenmelidir)

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 ön sürüm)

- 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, LLP64 modelini kullanır: Her ikisi de intve long32 bit olarak tanımlanır, bu da hiçbirinin bir işaretçi için yeterince uzun olmadığı anlamına gelir.


avr-gcc 4.3.2 / ATmega168 (Arduino Diecimila)

Başarısız olan varsayımlar şunlardır:

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

Atmega168'de 16 bitlik bir PC bulunur, ancak kod ve veriler ayrı adres alanlarında bulunur. Daha büyük Atmegas'ın 22 bit PC'si var !.


MacOSX 10.6 üzerinde gcc 4.2.1, -arch ppc ile derlenmiştir

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
Ve başka bir varsayım belirlediniz: bir terminal satırına 80 karakter sığdırabileceğiniz.
Mike Seymour

3
sizeof(void*)>=sizeof(void(*)())== seçeneğinden daha alakalı olur. Eğer teste gerek varsayım bir olup olmadığıdır yüzden umurumda, "Biz bir boşluk pointer bir işlev işaretçisi saklayabilir" dir void*olan en az bir işlev işaretçisi büyüklüğünde.
jalf

1
Ortamınız POSIX uyumluysa, sorun yaşamazsınızsizeof(void*)>=sizeof(void(*)())
Daniel Earwicker

26

Uzun zaman önce, C'yi bir ders kitabından öğretiyordum.

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

örnek bir soru olarak. Bir öğrenci için başarısız oldu, çünkü bu uygulamada 16 bit ve 32 bitlik değil , sizeoftürden değerler veriyordu ve bu çok büyüktü. (Platform, 680x0 tabanlı Macintosh'larda Lightspeed C idi. Uzun zaman önce dedim.)size_tintintsize_t


7
+1, bu türden en yaygın ve genellikle gözden kaçan hatalardan birini işaret etmek için.
R .. GitHub BUZA YARDIM ETMEYİ DURDUR

4
Bu aynı zamanda size_t'nin 64 bit olduğu ve inçlerin neredeyse her zaman daha kısa olduğu 64 bit sistemlerde de olur. Win64 hala daha garip çünkü size_t bir unsigned long longvar. Test 17 olarak eklendi.
Nordic Mainframe

Ne yazık ki, Microsoft'un C çalışma zamanı, boyutlandırılmış tamsayılar için zdeğiştiriciyi desteklemiyor ve bazı platformlarda da desteklenmiyor. Dolayısıyla, bir nesnenin basılı boyutunu biçimlendirmenin veya yayınlamanın güvenli bir yolu yoktur. size_tlong long
Phil Miller

15

İnsanların yaptığı ++ve --varsayımlarını eklemeniz gerekir .

a[i++]= i;

Örneğin, sözdizimsel olarak yasaldır, ancak mantık yürütmek için çok fazla şeye bağlı olarak değişen sonuçlar üretir.

++(Veya --) ve birden çok kez ortaya çıkan bir değişkene sahip herhangi bir ifade bir sorundur.


Ve bu da çok yaygın bir soru!
Matthieu M.

8

Çok ilginç!

Aklıma gelen diğer şeyler kontrol etmek yararlı olabilir:

  • işlev işaretçileri ve veri işaretçileri aynı adres alanında var mı? (Harvard mimari makinelerinde DOS küçük modu gibi kırılıyor. Yine de bunu nasıl test edeceğinizi bilmiyorum.)

  • Eğer bir NULL veri işaretçisi alıp uygun tamsayı tipine çevirirseniz, 0 sayısal değerine sahip mi? (Bazı gerçekten eski makinelerde kırılır - bkz. Http://c-faq.com/null/machexamp.html .) İşlev göstericili aynen. Ayrıca farklı değerler olabilirler.

  • bir işaretçiyi, karşılık gelen depolama nesnesinin sonunu geçtikten sonra artırmak ve sonra tekrar geri getirmek mantıklı sonuçlara neden olur mu? (Bunun gerçekten bozulduğu herhangi bir makine bilmiyorum, ancak C spesifikasyonunun, (a) bir dizinin içeriğini veya (b) öğeyi işaret etmeyen işaretçileri düşünmenize bile izin vermediğine inanıyorum. Diziden hemen sonra veya (c) NULL. Bkz. http://c-faq.com/aryptr/non0based.html .)

  • <ve> ile iki işaretçiyi farklı depolama nesneleriyle karşılaştırmak tutarlı sonuçlar üretiyor mu? (Egzotik segment tabanlı makinelerde bu kırılmayı hayal edebiliyorum; Spesifikasyon bu tür karşılaştırmaları yasaklıyor, böylece derleyici segment kısmını değil, yalnızca işaretçinin ofset kısmını karşılaştırma hakkına sahip olacak.)

Hmm. Biraz daha deneyip düşüneceğim.

Düzenleme: Mükemmel C SSS'ye bazı açıklayıcı bağlantılar eklendi.


2
Bu arada, bir süre önce Clue ( cluecc.sourceforge.net ) adlı deneysel bir proje yaptım ve C'yi Lua, Javascript, Perl, LISP, vb. Şeklinde derlemenize izin verdi. İşaretçilerin çalışması için C standardındaki tanımsız davranışı acımasızca kullandı. . Üzerinde bu testi denemek ilginç olabilir.
David

1
IIRC C, işaretçiyi bir nesnenin sonunun ötesinde 1 artırmanıza izin verir , ancak daha fazlasını yapmaz. Bununla birlikte, bir nesnenin başlangıcından önce bir konuma düşürmeye izin verilmez.
R .. GitHub BUZA YARDIM ETMEYİ DUR

@R. C ++ ile aynı. İşaretçiyi yalnızca tamsayı olarak değerlendirmeyen CPU'larda, işaretçiyi artırmak bir taşmaya neden oluyorsa, daha fazla artırmak bozulabilir.
jalf

5

Bence çok farklı iki "yanlış" varsayım sınıfını birbirinden ayırmak için çaba göstermelisiniz. İyi bir yarı (sağ kaydırma ve işaret uzantısı, ASCII uyumlu kodlama, bellek doğrusaldır, veri ve işlev işaretçileri uyumludur, vb.) Çoğu C kodlayıcısının yapması için oldukça makul varsayımlardır ve hatta standardın bir parçası olarak dahil edilebilir C bugün tasarlanıyor olsaydı ve eski IBM önemsiz büyükbabamız olmasaydı. Diğer yarısı (bellek örtüşme ile ilgili şeyler, giriş ve çıkış belleği örtüştüğünde kitaplık işlevlerinin davranışı, bu işaretçilerin sığdığı intveya kullanabileceğiniz gibi 32 bitlik varsayımlarmalloc bir prototip olmadan, bu çağrı kuralı değişken ve değişken olmayan işlevler için aynıdır, ...) ya modern derleyicilerin gerçekleştirmek istediği optimizasyonlarla ya da 64 bit makinelere veya diğer yeni teknolojilere geçişle çelişir.


bu sadece "IBM çöpü" değil (yine de IBM işlerinin önemsiz olduğunu kabul ediyorum). Günümüzde birçok gömülü sistemin benzer sorunları vardır.
rmeador

Açıklamak gerekirse, mallocprototip olmadan kullanmak <stdlib.h>, 64-bit'i desteklemek istiyorsanız, mallocvarsayılan olarak int malloc(int)hayır- hayır'a neden olan dahil etmemek anlamına gelir .
Joey Adams

Teknik <stdlib.h>olarak, tanımlayan başka bir başlık eklediğiniz size_tve daha sonra mallockendiniz doğru bir prototip ile beyan ettiğiniz sürece dahil etmekte özgürsünüz .
R .. GitHub BUZA YARDIM ETMEYİ DURDUR

5

İşte eğlenceli bir tane: Bu fonksiyonun nesi var?

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

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


Oh, bu iyiydi. clang 2.7 bunu yiyor ve hiçbir uyarı vermeden tamamen saçmalık üretiyor.
Nordic Mainframe

va_arg bir makro ise ve while döngüsü yalnızca ilk ifadeyi yürütürse genişler, belki de birçok?
Maister

Hayır (bu gerçekleşirse, uygulamada bir hata olur).
zwol

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

Bir diğeri, içindeki metin modu ile ilgilidir fopen. Çoğu programcı, metin ve ikilinin aynı olduğunu (Unix) veya metin modunun \rkarakter eklediğini (Windows) varsayar . Ancak C, fputc('\n', file)dosya boyutu kayıt uzunluğunun bir katı olana kadar bir metin dosyasında boşluk veya başka bir şey eklemek anlamına gelen sabit genişlikli kayıtları kullanan sistemlere taşındı .

Ve işte sonuçlarım:

x86-64 üzerinde gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3

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

Aslında pow(2, n)bit işlemleriyle birleştirilen kod gördüm .
dan04

4

Bazıları C içinden kolayca test edilemez çünkü program muhtemelen varsayımın geçerli olmadığı uygulamalarda çökebilir.


"İşaretçi değerli bir değişkenle herhangi bir şey yapmakta sorun yok. Sadece referansını kaldırırsanız geçerli bir işaretçi değeri içermesi gerekir."

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

unsigned charTuzak temsillerine sahip olmasına izin verilen integral ve kayan nokta türleri (dışında ) ile aynıdır .


"Tamsayı hesaplamaları etrafı sarar. Yani bu program büyük bir negatif tamsayı yazdırır."

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

(Yalnızca C89.) "Sonundan düşmek sorun değil main."

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

2
Somut bir örnek olarak: ile derlendiğinde gcc -ftrapv -Oçıktının We like to think that:ardındanAborted
caf

@caf: "Bu seçenek, toplama, çıkarma, çarpma işlemlerinde işaretli taşma için tuzaklar oluşturur." Bilmek güzel, teşekkürler.
Gilles 'SO- kötü olmayı bırak'

1
Sonuncusu da C ++ (98, 03 ve 0x) ile tamamdır ve örtük olarak 0 döndürür.
jalf

Bu iğrenç çünkü pre-ANSI C buna izin veriyor ve C99 da bunu yapıyor.
Joshua

@Joshua: AFAIK, ANSI öncesi C ile C89 arasında değer içermeyen dönüşte fark mainyoktur: program doğrudur ancak tanımlanmamış bir sonlandırma durumu döndürür (C89 §2.1.2.2). Pek çok uygulamayla (gcc ve daha eski unix derleyicileri gibi) o noktada belirli bir kayıt defterinde ne varsa elde edersiniz. Program tipik olarak bir makefile veya sonlandırma durumunu kontrol eden başka bir ortamda kullanılıncaya kadar çalışır.
Gilles 'SO- kötü olmayı bırak'

4

Henüz kastedilmeyen klasik taşınabilirlik varsayımları

  • integral türlerinin boyutu hakkında varsayımlar
  • endian

4
"Endianness", "Bir endianness vardır" dahil: orta-endian makineler vardır ve standart short, iki bayt 0248ace ve fdb97531 olarak bir fedcab9876543210 (16 ikili hane) değerinin saklanması gibi garip şeylere izin verir .
Gilles 'SO- kötü olmayı bırak'

evet endianess kesin olarak karışık / orta endian'ı ve büyük ve küçük'ü içerir. Özel donanıma giderseniz, herhangi bir otobüste istediğiniz herhangi bir sonsuzluğa sahip olabilirsiniz.
jk.

Orta endian, PDP endian olarak bilinir. Gilles, TCP / IP'nin uygulanmasında baş ağrısına neden olsa da daha da tuhaf bir şeyi açıklıyor.
Joshua

@Gilles: middle-endian ... Bu konuda geliştirmediğim için çok mutluyum. (ama şimdi bir orta-endian ağ projesi yapmam istenecek, eminim) ...
Paul Nathan

ARM FPE, <yüksek dörtlü> <düşük dörtlü> bir çift olarak depolandıkları orta uç çiftleri kullandı, ancak her dörtlü içindeki bitlerin sıralaması yanlış yoldaydı. (Neyse ki, ARM VFP artık bunu yapmıyor.)
David

4
  • Kayan nokta gösterimi nedeniyle ayrıklaştırma hataları. Örneğin, ikinci dereceden denklemleri çözmek için standart formülü veya yaklaşık türevler için sonlu farkları veya varyansları hesaplamak için standart formülü kullanırsanız, benzer sayılar arasındaki farkların hesaplanması nedeniyle hassasiyet kaybolacaktır. Doğrusal sistemleri çözmek için Gauß algoritması kötüdür çünkü yuvarlama hataları birikir, bu nedenle QR veya LU ayrıştırma, Cholesky ayrıştırma, SVD vb. Kullanılır. Kayan nokta sayılarının eklenmesi ilişkili değildir. Normal, sonsuz ve NaN değerleri vardır. a + b - birb .

  • Dizeler: Karakterler, kod noktaları ve kod birimleri arasındaki fark. Unicode'un çeşitli işletim sistemlerinde nasıl uygulandığı; Unicode kodlamaları. Rasgele bir Unicode dosya adına sahip bir dosyayı C ++ ile taşınabilir bir şekilde açmak mümkün değildir.

  • Diş açmasız bile yarış koşulları: Bir dosyanın var olup olmadığını test ederseniz, sonuç her an geçersiz hale gelebilir.

  • ERROR_SUCCESS = 0


4

Tamsayı boyutları için bir kontrol ekleyin. Çoğu insan bir int'in kısa bir karakterden daha büyük olduğunu varsayar. Ancak bunların hepsi yanlış olabilir:sizeof(char) < sizeof(int); sizeof(short) < sizeof(int); sizeof(char) < sizeof(short)

Bu kod başarısız olabilir (hizalanmamış erişimle çöker)

unsigned char buf[64];

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

bu kod C ++ 'da başarısız olur mu? IIRC, herhangi bir türe dönüştürülebilen (veya tam tersi mi?) İlgisiz türler arasında işaretçi kullanmak yasa dışıdır, char * HARİÇ.
rmeador

1
Sadece int *p = (int*)&buf[1];c ++ 'da yapabilirsiniz, insanlar bunun da çalışmasını bekler.
hayır

@nos, evet bu başarısız olabilir, ancak başarısızlık çöktü, bu yüzden programı bunu test edemez. :(
Joshua

1
sizeof(char) < sizeof(int)gerekli. Örneğin, fgetc (), karakterin değerini int'e dönüştürülmüş işaretsiz bir karakter olarak veya EOFnegatif bir değer olarak döndürür . unsigned charpadding bitleri olmayabilir, bu yüzden bunu yapmanın tek yolu int'i char'dan daha büyük yapmaktır. Ayrıca, (çoğu sürüm) C spesifikasyonu, -32767..32767 aralığındaki herhangi bir değerin bir int içinde depolanabilmesini gerektirir.
jilles

@illes hala, 32 bit karakterli ve 32 bit girişli DSP'ler var.
hayır

3

Yerleşik veri türleri hakkında birkaç şey:

  • charve signed charaslında iki farklı türdür (aksine intve signed intaynı işaretli tam sayı türüne atıfta bulunur).
  • ikinin tümlemesini kullanmak için işaretli tamsayılar gerekli değildir. Bir'in tümleyeni ve işareti + büyüklüğü de negatif sayıların geçerli temsilleridir. Bu, uygulama tanımlı negatif sayıları içeren bit işlemlerini yapar .
  • İşaretli bir tamsayı değişkenine aralık dışı bir tamsayı atarsanız, davranış uygulama tanımlıdır .
  • C90'da -3/5geri dönebilir 0veya -1. Bir işlenenin negatif olması durumunda sıfıra yuvarlama, yalnızca yukarı doğru C99 ve C ++ 0x için garanti edilir.
  • Yerleşik tipler için kesin boyut garantisi yoktur. Bir gibi standart sadece asgari şartları kapsar intsahip en az 16 bit bir, longsahip en az bir, 32 bit long longolan , en az 64 bit. A float, en az 6 en önemli ondalık basamağı doğru şekilde temsil edebilir. A double, en az 10 en önemli ondalık basamağı doğru şekilde temsil edebilir.
  • IEEE 754, kayan noktalı sayıları temsil etmek için zorunlu değildir.

Kuşkusuz, çoğu makinede iki tamamlayıcıya ve IEEE 754 şamandıralara sahip olacağız.


Aralık dışı tamsayı atamalarının Tanımsız Davranış yerine uygulama tanımlı olmasının ne değeri olduğunu merak ediyorum. Bazı platformlarda, böyle bir gereksinim derleyiciyi int mult(int a,int b) { return (long)a*b;}[örneğin int32 bit, ancak yazmaçlar ve long64 ise] için fazladan kod üretmeye zorlar . Böyle bir gereklilik olmadan, en hızlı uygulanmasının "doğal" davranışı, bir için "imkansız" bir değer olsa bile, eşit long l=mult(1000000,1000000);olacaktır . l1000000000000int
supercat

3

Buna ne dersin:

Hiçbir veri işaretçisi hiçbir zaman geçerli bir işlev işaretçisi ile aynı olamaz.

Bu, tüm düz modeller, MS-DOS TINY, LARGE ve HUGE modelleri için DOĞRU, MS-DOS SMALL modeli için yanlış ve ORTA ve KOMPAKT modeller için hemen hemen her zaman yanlıştır (yük adresine bağlı olarak, gerçekten eski bir DOS'a ihtiyacınız olacaktır. doğru yapın).

Bunun için bir test yazamam

Daha da kötüsü: ptrdiff_t'ye atılan işaretçiler karşılaştırılabilir. Bu MS-DOS LARGE modeli için geçerli değildir (LARGE ve HUGE arasındaki tek fark, HUGE'un işaretçileri normalleştirmek için derleyici kodu eklemesidir).

Bir test yazamıyorum çünkü bu bombaların sert olduğu ortam 64K'dan daha büyük bir tampon tahsis etmeyecek, bu yüzden bunu gösteren kod diğer platformlarda çökecektir.

Bu özel test, artık feshedilmiş bir sistemi geçecektir (bunun malloc'un dahili özelliklerine bağlı olduğuna dikkat edin):

  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

DÜZENLEME: Programın son sürümüne güncellendi

Solaris-SPARC

gcc 3.4.6 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

64 bit gcc 3.4.6

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

ve SUNStudio 11 32 bit ile

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

ve SUNStudio 11 64 bit ile

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

fopen("filename", "r")Her türden metin dosyasını okumak için metin modunu ( ) kullanabilirsiniz.

Bu ederken gerektiğini gayet teori çalışmalarında, ayrıca kullanırsanız ftell()kodunuzda ve metin dosyası UNIX tarzı çizgi sonlar vardır, Windows standart kütüphanede bazı sürümlerinde, ftell()genellikle geçersiz değerler dönecektir. Çözüm, bunun yerine ikili modu kullanmaktır ( fopen("filename", "rb")).


1

AIX 5.3 üzerinde gcc 3.3.2 (evet, gcc'yi güncellememiz gerekiyor)

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

Bazılarının C ++ 'da yapabileceği bir varsayım, a'nın C'de yapabilecekleriyle structsınırlı olduğudur. Gerçek şu ki, C ++' da a struct, classvarsayılan olarak herkese açık her şeye sahip olması dışında a gibidir .

C ++ yapısı:

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


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

protected:
  void Testing2();
};

1

Farklı sistemlerdeki standart matematik fonksiyonları aynı sonuçları vermez.


1

32 bit x86 üzerinde Visual Studio Express 2010.

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

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

/out:testtoy.exe
testtoy.obj

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

1

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

Codepad'in sahip olmadığını unutmayın stddef.h. Hata olarak uyarıları kullanan kodlayıcı nedeniyle test 9'u kaldırdım. Bir countnedenden dolayı zaten tanımlanmış olduğu için değişkeni de yeniden adlandırdım .

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

Aşırı miktarlarda sağa kaydırmaya ne dersiniz - buna standart izin veriyor mu yoksa test etmeye değer mi?

Standart C aşağıdaki programın davranışını belirtir mi:

void print_string (char * st)
{
  char ch;
  while ((ch = * st ++)! = 0)
    Putch (CH); / * Bunun tanımlı olduğunu varsayalım * /
}
int main (void)
{
  ( "Merhaba") print_string;
  dönüş 0;
}

Kullandığım en az bir derleyicide, print_string argümanı bir "char const *" olmadığı sürece bu kod başarısız olacak . Standart böyle bir kısıtlamaya izin veriyor mu?

Bazı sistemler, birinin hizalanmamış 'int'ler için işaretçi üretmesine izin verirken diğerleri yapmaz. Test etmeye değer olabilir.


C89 §3.3.7: "Sağ işlenenin değeri negatifse veya yükseltilen sol işlenenin bit cinsinden genişliğine eşit veya bundan büyükse, davranış tanımsızdır." (her ikisi için de geçerlidir <<ve >>). C99, §6.5.7-3'te aynı dile sahiptir.
Gilles 'SO- kötü olmayı bırak'

putch(Neden standardı kullanmadınız putchar?) Dışında, programınızda tanımsız herhangi bir davranış göremiyorum. C89 §3.1.4, "bir karakter dizisi değişmezinin […] 'char dizisi' türüne sahip olduğunu '' (not: hayır const) ve" program bir dizgeyi […] değiştirmeye çalışırsa, davranışın tanımsız olduğunu "belirtir. . Bu hangi derleyici ve bu programı nasıl tercüme ediyor?
Gilles 'SO- kötü olmayı bırak'

2
C ++ 'da karakter sabitleri char [] değildir , bunlar const char []' dır. Ancak ... orada kullanılan bir karakter * beklenen ve bir tür hatası aldığınız olmayan bir bağlamda bir dize sabiti kullanmasına izin vermek tipi sisteminde belirli bir delik olması. Bu, print_string'in ("foo") çalışacağı ancak print_string'in ("foo" +0) çalışmayacağı durumlara yol açtı. Bu, özellikle C dosyalarının varsayılan olarak bir C ++ derleyicisi kullanılarak derlendiği ortamlarda çok kafa karıştırıcıydı. Yeni derleyicilerde delik kaldırıldı, ancak etrafta hala çok sayıda eski var. AFAIK C99 hala karakter dizisi sabitlerini char [] olarak tanımlar.
David

1
Microchip PIC serisi denetleyiciler için HiTech derleyicilerinde, depolama niteleyicisi olmayan bir işaretçi yalnızca RAM'i gösterebilir. Sabit nitelikli bir işaretçi, RAM veya ROM'u gösterebilir. Const niteliği taşımayan işaretçiler doğrudan kodda başvuruda bulunulur; const özellikli işaretçiler, kitaplık yordamı aracılığıyla başvurudan kaldırılır. Belirli PIC türüne bağlı olarak, const olmayan işaretçiler 1 veya 2 bayttır; const nitelikli olanlar 2 veya 3'tür. ROM, RAM'den çok daha bol olduğu için, ROM'da sabitlere sahip olmak genellikle iyi bir şeydir.
supercat

@David Given: Önceki yorumuma da dikkat edin. Donanım depolama sınıfını belirtmek için "const" dışında niteleyiciler kullanan derleyicileri tercih ederim; HiTech derleyicisinin depolama sınıfı tahsisinde oldukça rahatsız edici bazı tuhaflıklar vardır (örn. "bileşen boyutu" bir bayt olan veri öğeleri veya 256 bayttan fazla veri öğeleri "büyük" bir segmente gider. Diğer veri öğeleri " Tanımlandıkları modül için bss "segmenti; bir modüldeki tüm" bss "öğeleri 256 bayta sığmalıdır. 256 bayttan biraz daha kısa diziler gerçek bir
baş

0

Bilginize, C becerilerini Java'ya çevirmek zorunda olanlar için, işte birkaç sorun var.

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

Java'da, char 16 bittir ve imzalıdır. bayt 8 bittir ve imzalanmıştır.

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

long her zaman 64 bittir, başvurular 32 bit veya 64 bit olabilir (32 GB'den fazla bir uygulamanız varsa) 64 bit JVM'ler genellikle 32 bit başvurular kullanır.

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

Kaydırma, i << 64 == i == i << -64, i << 63 == i << -1 olacak şekilde maskelenmiştir.

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

ByteOrder.nativeOrder () BIG_ENDIAN veya LITTLE_ENDIAN olabilir

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

i = i++ asla değişmez i

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

Koleksiyonların ve dizilerin boyutu, JVM'nin 32 bit veya 64 bit olmasına bakılmaksızın her zaman 32 bittir.

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 16 bit, kısa 16 bit, int 32 bit ve uzun 64 bittir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.