Apa yang dimiliki alat pendidikan untuk mendemonstrasikan asumsi tidak beralasan yang dibuat orang dalam C / C ++?


121

Saya ingin menyiapkan sedikit alat pendidikan untuk SO yang akan membantu programmer pemula (dan menengah) untuk mengenali dan menantang asumsi mereka yang tidak beralasan di C, C ++ dan platform mereka.

Contoh:

  • "bilangan bulat membungkus"
  • "setiap orang memiliki ASCII"
  • "Saya dapat menyimpan penunjuk fungsi dalam kekosongan *"

Saya membayangkan bahwa program pengujian kecil dapat dijalankan pada berbagai platform, yang menjalankan asumsi "masuk akal" yang, dari pengalaman kami di SO, biasanya dibuat oleh banyak pengembang arus utama yang tidak berpengalaman / setengah berpengalaman dan mencatat cara mereka merusak mesin yang beragam.

Tujuannya bukan untuk membuktikan bahwa melakukan sesuatu "aman" (yang tidak mungkin dilakukan, tes hanya membuktikan apa saja jika rusak), tetapi untuk menunjukkan kepada individu yang paling tidak mengerti bagaimana ekspresi yang paling tidak mencolok merusak mesin lain, jika memiliki perilaku yang tidak ditentukan atau implementasi yang ditentukan. .

Untuk mencapai ini saya ingin bertanya kepada Anda:

  • Bagaimana ide ini dapat ditingkatkan?
  • Tes mana yang akan baik dan bagaimana seharusnya?
  • Apakah Anda akan menjalankan tes pada platform yang bisa Anda dapatkan dan memposting hasilnya, sehingga kami mendapatkan database platform, bagaimana perbedaannya dan mengapa perbedaan ini diperbolehkan?

Inilah versi saat ini untuk mainan uji:

#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, dan saya membuat wiki komunitas ini sejak awal karena saya pikir orang-orang ingin mengedit pengobrol saya ketika mereka membaca ini.

UPDATE Terima kasih atas masukan Anda. Saya telah menambahkan beberapa kasus dari jawaban Anda dan akan melihat apakah saya dapat menyiapkan github untuk ini seperti yang disarankan Greg.

UPDATE : Saya telah membuat repo github untuk ini, file tersebut adalah "gotcha.c":

Harap jawab di sini dengan tambalan atau ide baru, sehingga dapat didiskusikan atau diklarifikasi di sini. Saya akan menggabungkannya menjadi gotcha.c.


7
Pertimbangkan model medium di DOS. Fungsi dapat disimpan dalam beberapa segmen, jadi penunjuk fungsi memiliki panjang 32 bit. Tetapi data Anda hanya disimpan dalam satu segmen, oleh karena itu panjang pointer data hanya 16 bit. Karena void * adalah penunjuk data, lebarnya 16 bit, jadi Anda tidak dapat memasukkan penunjuk fungsi ke dalamnya. Lihat c-jump.com/CIS77/ASM/Directives/D77_0030_models.htm .
David Diberikan

6
Mungkin Anda bisa membuang kode ini di github.com atau sesuatu dan kemudian orang dapat dengan mudah berkontribusi tambalan.
Greg Hewgill

1
Banyak hal di sini yang akan membantu: stackoverflow.com/questions/367633/…
Martin York

4
POSIX mensyaratkan bahwa pointer fungsi memiliki representasi yang sama dengan void * dan dapat dikonversi (dengan cast) tanpa kehilangan informasi. Salah satu alasannya adalah karena dlsym()mengembalikan void * tetapi ditujukan untuk penunjuk data dan fungsi. Oleh karena itu mungkin tidak terlalu buruk untuk bergantung pada ini.
jilles

3
@tristopia: Poin 15 ada di sini, karena banyak pemula yang sering terkejut mengetahui bahwa data tidak dikemas secara terus-menerus tetapi selaras dengan batas-batas tertentu. Mereka bingung saat mengubah urutan anggota dan mendapatkan ukuran objek yang berbeda. Juga, pengemasan adalah mode default dengan banyak pengontrol mikro kontemporer atau perangkat yang disematkan. Output AVR Atmega dan TurboC / MSDOS saya juga dikemas. MSDOS masih digunakan dalam aplikasi industri.
Nordic Mainframe

Jawaban:


91

Urutan evaluasi subekspresi, termasuk

  • argumen dari pemanggilan fungsi dan
  • operan dari operator (misalnya, +, -, =, *, /), dengan pengecualian:
    • operator logika biner ( &&dan ||),
    • operator kondisional terner ( ?:), dan
    • operator koma ( ,)

tidak ditentukan

Sebagai contoh

  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
Saya selalu tahu tentang parameter fungsi, tapi saya tidak pernah memikirkannya dalam istilah operator ... ... dan jika saya pernah melihat Anda menulis kode seperti itu di lingkungan produksi, saya akan menamparmu dengan mie basah.
riwalk

3
@ Billy: Tapi hanya untuk versi primitif dari operator.
Dennis Zickefoose

1
@ Dennis: Itu benar. (Itulah sebabnya mengapa item di Effective / MoreEffective C ++ untuk tidak pernah membebani mereka (Kecuali Anda sedang menulis boost::spirit)
Billy ONeal

1
@ Daniel: Saya tidak yakin apa yang Anda coba katakan. Sepertinya Anda menyarankan tidak apa-apa untuk membebani operator karena hanya pengguna kelas Anda yang mungkin salah, dan jika Anda tidak menulis C ++ langsung, tidak masalah. Tidak ada satupun yang masuk akal sama sekali.
Dennis Zickefoose

2
@ user420536: Perilakunya hanya tidak ditentukan tetapi tidak ditentukan. Ya, contohnya dapat mencetak Hello World! atau Dunia! Halo, tetapi itu tidak ditentukan karena urutan evaluasi operan +operator tidak ditentukan (Penulis kompiler tidak perlu mendokumentasikan perilaku tersebut). Itu tidak melanggar aturan titik urutan seperti itu.
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 macet. "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

dentang 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 / Memori Kecil

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 / Memori Sedang

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 / Memori Kompak

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 (wakil emulator)

alt teks


Saya akan memperbaruinya nanti:


Borland C ++ Builder 6.0 pada 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 64bit

(harus dikompilasi sebagai C ++ karena compiler CLR tidak mendukung C murni)

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 prapelase)

- 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 menggunakan model LLP64: Keduanya intdan longdidefinisikan sebagai 32-bit, yang berarti keduanya tidak cukup panjang untuk sebuah pointer.


avr-gcc 4.3.2 / ATmega168 (Arduino Diecimila)

Asumsi yang gagal adalah:

..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 memiliki PC 16 bit, tetapi kode dan data berada di ruang alamat terpisah. Atmegas yang lebih besar memiliki PC 22 bit !.


gcc 4.2.1 di MacOSX 10.6, dikompilasi dengan -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
Dan Anda telah mengidentifikasi asumsi lain: bahwa Anda dapat memasukkan 80 karakter pada jalur terminal.
Mike Seymour

3
sizeof(void*)>=sizeof(void(*)())akan lebih relevan daripada ==. Semua yang kita pedulikan adalah "dapat kita menyimpan fungsi pointer dalam pointer kekosongan", sehingga asumsi yang Anda butuhkan untuk menguji adalah apakah void*ini setidaknya sebagai besar sebagai pointer fungsi.
jalf

1
Jika lingkungan Anda sesuai dengan POSIX, Anda akan baik-baik saja sizeof(void*)>=sizeof(void(*)())- lihat opengroup.org/onlinepubs/009695399/functions/dlsym.html
Daniel Earwicker

26

Dahulu kala, saya mengajar C dari buku teks yang dimiliki

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

sebagai contoh pertanyaan. Gagal untuk siswa, karena sizeofmenghasilkan nilai tipe size_t, bukan int, intpada implementasi ini adalah 16 bit dan size_t32, dan itu adalah big-endian. (Platformnya adalah Lightspeed C pada Macintosh berbasis 680x0. Saya bilang sudah lama sekali.)


7
+1 untuk menunjukkan salah satu kesalahan yang paling umum, dan sering diabaikan, semacam ini.
R .. GitHub STOP HELPING ICE

4
Ini juga terjadi pada sistem 64-bit, di mana size_t adalah 64 bit dan int hampir selalu lebih pendek. Win64 masih lebih aneh, karena size_t unsigned long longada di sana. Ditambahkan sebagai Test 17.
Nordic Mainframe

Sayangnya, runtime C Microsoft tidak mendukung zpengubah untuk size_tbilangan bulat berukuran, dan long longjuga tidak didukung pada beberapa platform. Jadi tidak ada cara portabel yang aman untuk memformat atau mentransmisikan ukuran cetakan suatu objek.
Phil Miller

15

Anda perlu memasukkan asumsi ++dan --asumsi yang dibuat orang.

a[i++]= i;

Misalnya, secara sintaksis legal, tetapi menghasilkan hasil yang bervariasi bergantung pada terlalu banyak hal untuk dipikirkan.

Pernyataan apa pun yang memiliki ++ (atau --) dan variabel yang muncul lebih dari sekali merupakan masalah.


Dan itu juga pertanyaan umum!
Matthieu M.

8

Sangat menarik!

Hal-hal lain yang menurut saya mungkin berguna untuk diperiksa:

  • apakah penunjuk fungsi dan penunjuk data ada di ruang alamat yang sama? (Kerusakan pada mesin arsitektur Harvard seperti mode kecil DOS. Namun, tidak tahu bagaimana Anda akan mengujinya.)

  • jika Anda mengambil penunjuk data NULL dan melemparkannya ke tipe integer yang sesuai, apakah ia memiliki nilai numerik 0? (Kerusakan pada beberapa mesin yang sangat kuno --- lihat http://c-faq.com/null/machexamp.html .) Ditto dengan function pointer. Juga, mereka mungkin memiliki nilai yang berbeda.

  • apakah menaikkan pointer melewati akhir objek penyimpanan yang sesuai, dan kemudian kembali lagi, menyebabkan hasil yang masuk akal? (Saya tidak tahu mesin apa pun yang benar-benar rusak, tetapi saya yakin spesifikasi C tidak memungkinkan Anda untuk berpikir tentang pointer yang tidak menunjuk ke (a) isi array atau (b) elemen segera setelah array atau (c) NULL Lihat http://c-faq.com/aryptr/non0based.html .)

  • apakah membandingkan dua pointer ke objek penyimpanan yang berbeda dengan <dan> menghasilkan hasil yang konsisten? (Saya dapat membayangkan ini melanggar pada mesin berbasis segmen eksotis; spek melarang perbandingan seperti itu, sehingga kompiler berhak untuk membandingkan bagian offset dari pointer saja, dan bukan bagian segmen.)

Hmm. Saya akan mencoba dan memikirkan lebih banyak lagi.

Sunting: Menambahkan beberapa tautan klarifikasi ke C FAQ yang sangat baik.


2
Kebetulan, beberapa waktu yang lalu saya melakukan proyek eksperimental bernama Clue ( cluecc.sourceforge.net ) yang memungkinkan Anda untuk mengkompilasi C ke dalam Lua, Javascript, Perl, LISP, dll. Itu dengan kejam mengeksploitasi perilaku tidak terdefinisi dalam standar C untuk membuat pointer berfungsi . Mungkin menarik untuk mencoba tes ini di atasnya.
David Diberikan

1
IIRC C memungkinkan Anda untuk menambah penunjuk sebesar 1 di luar akhir objek, tetapi tidak lebih jauh. Namun, mengurangi ke posisi sebelum permulaan objek tidak diperbolehkan.
R .. GitHub STOP HELPING ICE

@R. Sama di C ++. Dan menambah lebih jauh mungkin rusak jika menaikkan penunjuk menyebabkan overflow, pada CPU yang tidak hanya memperlakukan pointer sebagai bilangan bulat.
jalf

5

Saya pikir Anda harus berusaha untuk membedakan antara dua kelas asumsi "salah" yang sangat berbeda. Setengah yang baik (pergeseran kanan dan ekstensi tanda, pengkodean yang kompatibel dengan ASCII, memori linier, penunjuk data dan fungsi kompatibel, dll.) Adalah asumsi yang cukup masuk akal untuk dibuat oleh sebagian besar pembuat kode C, dan bahkan mungkin dimasukkan sebagai bagian dari standar jika C sedang dirancang hari ini dan jika kami tidak memiliki warisan IBM junk kakek-in. Separuh lainnya (hal-hal yang terkait dengan aliasing memori, perilaku fungsi pustaka ketika memori input dan output tumpang tindih, asumsi 32-bit seperti pointer itu cocok intatau yang dapat Anda gunakanmalloc tanpa prototipe, konvensi pemanggilan itu identik untuk fungsi variadic dan non-variadic, ...) baik konflik dengan pengoptimalan yang ingin dilakukan kompiler modern atau dengan migrasi ke mesin 64-bit atau teknologi baru lainnya.


ini bukan hanya "sampah IBM" (meskipun saya setuju barang-barang IBM adalah sampah). Banyak sistem tertanam saat ini memiliki masalah serupa.
rmeador

Untuk memperjelas, menggunakan malloctanpa prototipe berarti tidak termasuk <stdlib.h>, yang menyebabkan mallocdefault ke int malloc(int), tidak-tidak jika Anda ingin mendukung 64-bit.
Joey Adams

Secara teknis Anda bebas untuk tidak memasukkannya <stdlib.h>selama Anda menyertakan tajuk lain yang menentukan size_tdan Anda kemudian menyatakan sendiri mallocdengan prototipe yang benar.
R .. GitHub STOP HELPING ICE

5

Ini yang menyenangkan: Apa yang salah dengan fungsi ini?

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

[Answer (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, itu bagus. dentang 2.7 memakan ini dan menghasilkan omong kosong tanpa peringatan.
Nordic Mainframe

va_arg meluas jika itu makro dan loop sementara hanya mengeksekusi pernyataan pertama, mungkin banyak?
Maister

Tidak (jika itu terjadi akan menjadi bug dalam implementasi).
zwol

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

Satu lagi tentang mode teks dalam fopen. Kebanyakan pemrogram berasumsi bahwa teks dan biner adalah sama (Unix) atau mode teks menambahkan \rkarakter (Windows). Tetapi C telah di-porting ke sistem yang menggunakan record dengan lebar tetap, yang fputc('\n', file)pada file teks berarti menambahkan spasi atau sesuatu sampai ukuran file adalah kelipatan dari panjang record.

Dan inilah hasil saya:

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

Saya sebenarnya telah melihat kode yang digabungkan pow(2, n)dengan operasi bit.
dan04

4

Beberapa di antaranya tidak dapat dengan mudah diuji dari dalam C karena program tersebut kemungkinan besar akan mogok pada implementasi yang asumsi tidak berlaku.


"Tidak apa-apa melakukan apa pun dengan variabel bernilai pointer. Ini hanya perlu berisi nilai pointer yang valid jika Anda membedakannya."

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

Sama dengan tipe integral dan floating point (selain unsigned char), yang diperbolehkan memiliki representasi trap.


"Perhitungan bilangan bulat membungkus. Jadi program ini mencetak bilangan bulat negatif yang besar."

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

(C89 saja.) "Tidak apa-apa untuk jatuh dari akhir main."

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

2
Sebagai contoh konkret: Ketika dikompilasi dengan gcc -ftrapv -O, hasilnya We like to think that:diikuti olehAborted
caf

@caf: "Opsi ini menghasilkan perangkap untuk limpahan bertanda pada operasi penjumlahan, pengurangan, perkalian." Senang mengetahuinya, terima kasih.
Gilles 'SO- berhenti menjadi jahat'

1
Yang terakhir baik-baik saja di C ++ (98, 03 dan 0x) juga, dan secara implisit mengembalikan 0.
jalf

Yang menjijikkan karena pra-ANSI C mengizinkan ini dan C99 juga melakukannya.
Joshua

@Joshua: AFAIK tidak ada perbedaan antara pra-ANSI C dan C89 saat kembali dari maintanpa nilai: program benar tetapi mengembalikan status penghentian yang tidak ditentukan (C89 §2.1.2.2). Dengan banyak implementasi (seperti gcc, dan kompiler unix yang lebih lama) Anda mendapatkan apa pun yang ada di register tertentu pada saat itu. Program ini biasanya bekerja sampai digunakan di makefile atau lingkungan lain yang memeriksa status penghentian.
Gilles 'SO- berhenti bersikap jahat'

4

Nah, asumsi portabilitas klasik yang belum disebutkan adalah

  • asumsi tentang ukuran tipe integral
  • ketekunan

4
"Endianness", termasuk "There is an endianness": ada mesin middle-endian, dan standar memungkinkan hal-hal aneh seperti menyimpan shortnilai fedcab9876543210 (yaitu 16 digit biner) sebagai dua byte 0248ace dan fdb97531.
Gilles 'SO- berhenti menjadi jahat'

ya endianess pasti termasuk mixed / middle endian serta big and little. jika Anda pergi ke perangkat keras khusus, Anda dapat memiliki kesempurnaan yang Anda suka di bus mana pun.
jk.

Endian tengah dikenal sebagai PDP endian. Gilles mendeskripsikan sesuatu yang bahkan lebih aneh yang akan menyebabkan sakit kepala saat mengimplementasikan TCP / IP.
Joshua

@ Gilles: middle-endian ... Saya sangat senang saya tidak mengembangkan yang satu itu. (tapi sekarang saya akan diminta untuk melakukan proyek jaringan middle-endian, saya yakin) ...
Paul Nathan

ARM FPE menggunakan double middle-endian, di mana mereka disimpan sebagai pasangan <high ​​quad> <low quad> tetapi urutan bit di dalam setiap quad adalah dengan cara yang salah. (Untungnya, ARM VFP tidak melakukan ini lagi.)
David Diberikan

4
  • Kesalahan diskritisasi karena representasi floating point. Misalnya, jika Anda menggunakan rumus standar untuk menyelesaikan persamaan kuadrat, atau perbedaan hingga untuk memperkirakan turunan, atau rumus standar untuk menghitung varian, presisi akan hilang karena penghitungan perbedaan antara bilangan serupa. Algoritma Gauß untuk menyelesaikan sistem linier adalah buruk karena kesalahan pembulatan menumpuk, sehingga seseorang menggunakan dekomposisi QR atau LU, dekomposisi Cholesky, SVD, dll. Penambahan bilangan floating point tidak bersifat asosiatif. Ada nilai denormal, tak terbatas dan NaN. a + b - ab .

  • String: Perbedaan antara karakter, poin kode, dan unit kode. Bagaimana Unicode diimplementasikan pada berbagai sistem operasi; Pengkodean unicode. Membuka file dengan nama file Unicode yang berubah-ubah tidak dimungkinkan dengan C ++ secara portabel.

  • Kondisi balapan, bahkan tanpa threading: jika Anda menguji apakah sebuah file ada, hasilnya bisa menjadi tidak valid kapan saja.

  • ERROR_SUCCESS = 0


4

Sertakan centang untuk ukuran integer. Kebanyakan orang berasumsi bahwa int lebih besar dari short lebih besar dari char. Namun, ini semua mungkin salah:sizeof(char) < sizeof(int); sizeof(short) < sizeof(int); sizeof(char) < sizeof(short)

Kode ini mungkin gagal (macet ke akses yang tidak selaras)

unsigned char buf[64];

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

apakah kode ini akan gagal di C ++? IIRC, adalah ilegal untuk melemparkan pointer di antara tipe yang tidak terkait, KECUALI untuk char *, yang dapat dilemparkan ke semua tipe (atau sebaliknya?).
rmeador

1
Anda bisa melakukannya int *p = (int*)&buf[1];di c ++, orang-orang berharap itu berhasil juga.
no

@nos, ya itu bisa gagal tetapi gagal adalah crash sehingga programnya tidak dapat menguji yang satu itu. :(
Joshua

1
sizeof(char) < sizeof(int)diperlukan. Misalnya, fgetc () mengembalikan nilai karakter sebagai karakter unsigned yang dikonversi menjadi int, atau EOFyang merupakan nilai negatif. unsigned charmungkin tidak memiliki bit padding, jadi satu-satunya cara untuk melakukannya adalah dengan membuat int lebih besar dari char. Selain itu, (sebagian besar versi) spesifikasi C mengharuskan nilai apa pun dari kisaran -32767..32767 dapat disimpan di int.
jilles

@illes masih, ada DSP dengan 32 bit chars dan 32 bit int.
no

3

Beberapa hal tentang tipe data bawaan:

  • chardan signed charsebenarnya adalah dua jenis yang berbeda (tidak seperti intdansigned int yang mengacu pada jenis bilangan bulat bertanda yang sama).
  • bilangan bulat bertanda tangan tidak diperlukan untuk menggunakan komplemen dua. Komplemen dan tanda + besaran seseorang juga merupakan representasi valid dari bilangan negatif. Ini membuat operasi bit yang melibatkan implementasi bilangan negatif ditentukan .
  • Jika Anda menetapkan bilangan bulat di luar rentang ke variabel bilangan bulat bertanda, perilakunya ditentukan oleh penerapan .
  • Di C90, -3/5bisa kembali 0atau -1. Pembulatan ke arah nol jika satu operan negatif hanya dijamin di C99 ke atas dan C ++ 0x ke atas.
  • Tidak ada jaminan ukuran pasti untuk tipe built-in. Standar hanya mencakup persyaratan minimal seperti intmemiliki setidaknya 16 bit, a longmemiliki setidaknya 32 bit, a long longmemiliki setidaknya 64 bit. A floatsetidaknya dapat mewakili 6 digit desimal paling signifikan dengan benar. SEBUAHdouble setidaknya dapat mewakili 10 digit desimal paling signifikan dengan benar.
  • IEEE 754 tidak wajib untuk mewakili angka floating point.

Memang, pada kebanyakan mesin kita akan memiliki dua komplemen dan IEEE 754 float.


Saya bertanya-tanya apa nilai yang ada dalam memiliki tugas integer di luar jangkauan menjadi implementasi-didefinisikan daripada Perilaku yang Tidak Ditetapkan? Pada beberapa platform, persyaratan seperti itu akan memaksa kompilator untuk menghasilkan kode tambahan untuk int mult(int a,int b) { return (long)a*b;}[misalnya jika int32 bit, tetapi register dan long64]. Tanpa persyaratan seperti itu, perilaku "alami" dari implementasi tercepat long l=mult(1000000,1000000);akan ditetapkan lsama dengan 1000000000000, meskipun itu adalah nilai yang "tidak mungkin" untuk sebuah int.
supercat

3

Bagaimana dengan yang ini:

Tidak ada penunjuk data yang bisa sama dengan penunjuk fungsi yang valid.

Ini BENAR untuk semua model datar, MS-DOS TINY, LARGE, dan model BESAR, salah untuk model MS-DOS KECIL, dan hampir selalu salah untuk model MEDIUM dan COMPACT (tergantung pada alamat pemuatan, Anda memerlukan DOS yang sangat lama untuk buat itu benar).

Saya tidak bisa menulis tes untuk ini

Dan lebih buruk: petunjuk yang dicor ke ptrdiff_t dapat dibandingkan. Ini tidak benar untuk model MS-DOS BESAR (satu-satunya perbedaan antara LARGE dan HUGE adalah BESAR menambahkan kode kompiler untuk menormalkan petunjuk).

Saya tidak dapat menulis tes karena lingkungan di mana bom ini keras tidak akan mengalokasikan buffer yang lebih besar dari 64K sehingga kode yang menunjukkannya akan macet di platform lain.

Tes khusus ini akan lulus pada satu sistem yang sekarang tidak berfungsi (perhatikan itu tergantung pada internal malloc):

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

3

EDIT: Diperbarui ke versi terakhir program

Solaris-SPARC

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

dan dengan 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

dan dengan 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

Anda dapat menggunakan mode teks ( fopen("filename", "r")) untuk membaca segala jenis file teks.

Sementara ini harus dalam pekerjaan teori baik-baik saja, jika Anda juga menggunakan ftell()dalam kode Anda, dan file teks Anda memiliki UNIX-style line-ujung, di beberapa versi Windows standar perpustakaan, ftell()sering akan kembali nilai-nilai yang tidak valid. Solusinya adalah dengan menggunakan mode biner sebagai gantinya ( fopen("filename", "rb")).


1

gcc 3.3.2 di AIX 5.3 (ya, kita perlu memperbarui gcc)

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

1

Asumsi yang mungkin dilakukan beberapa orang di C ++ adalah bahwa a structterbatas pada apa yang dapat dilakukannya di C. Faktanya adalah, di C ++, a structseperti a classkecuali ia memiliki semua yang bersifat publik secara default.

Struktur C ++:

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


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

protected:
  void Testing2();
};

1

Fungsi matematika standar pada sistem yang berbeda tidak memberikan hasil yang identik.


1

Visual Studio Express 2010 pada 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

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

Perhatikan bahwa Codepad tidak memiliki stddef.h. Saya menghapus tes 9 karena codepad menggunakan peringatan sebagai kesalahan. Saya juga mengganti nama countvariabel karena sudah ditentukan karena beberapa alasan.

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

Bagaimana dengan pengalihan kanan dengan jumlah yang berlebihan - apakah itu diperbolehkan oleh standar, atau layak untuk diuji?

Apakah Standar C menentukan perilaku program berikut:

batal print_string (char * st)
{
  char ch;
  sementara ((ch = * st ++)! = 0)
    putch (ch); / * Asumsikan ini didefinisikan * /
}
int utama (kosong)
{
  print_string ("Halo");
  kembali 0;
}

Pada setidaknya satu kompiler yang saya gunakan, kode itu akan gagal kecuali argumen ke print_string adalah "char const *". Apakah standar mengizinkan pembatasan seperti itu?

Beberapa sistem memungkinkan seseorang untuk menghasilkan pointer ke 'int' yang tidak selaras dan yang lainnya tidak. Mungkin layak untuk diuji.


C89 §3.3.7: "Jika nilai operan kanan negatif atau lebih besar dari atau sama dengan lebar dalam bit operan kiri yang dipromosikan, perilakunya tidak ditentukan." (berlaku untuk <<dan >>). C99 memiliki bahasa yang identik di §6.5.7-3.
Gilles 'SO- berhenti bersikap jahat'

Selain putch(mengapa Anda tidak menggunakan standar putchar?), Saya tidak dapat melihat perilaku tidak terdefinisi dalam program Anda. C89 §3.1.4 menetapkan bahwa "literal string karakter memiliki […] tipe 'array of char'" (catatan: tidak const), dan bahwa "jika program mencoba mengubah literal string […], perilaku tidak ditentukan" . Kompiler apa itu, dan bagaimana cara menerjemahkan program ini?
Gilles 'SO- berhenti bersikap jahat'

2
Dalam C ++, konstanta karakter bukan char [], melainkan const char []. Namun ... dulu ada lubang khusus dalam sistem tipe untuk memungkinkan Anda menggunakan konstanta string dalam konteks di mana karakter * diharapkan dan tidak mendapatkan kesalahan tipe. Hal ini menyebabkan situasi di mana print_string ("foo") akan berfungsi tetapi print_string ("foo" +0) tidak. Ini sangat membingungkan, khususnya di lingkungan di mana file C dikompilasi menggunakan kompiler C ++ secara default. Lubang telah dihapus di kompiler baru tetapi masih banyak yang lama di sekitar. AFAIK C99 masih mendefinisikan konstanta string menjadi char [].
David Diberikan

1
Pada kompiler HiTech untuk rangkaian pengontrol Microchip PIC, penunjuk tanpa kualifikasi penyimpanan hanya dapat mengarah ke RAM. Pointer yang memenuhi syarat const dapat mengarah ke RAM atau ROM. Pointer yang tidak memenuhi syarat const dirujuk langsung dalam kode; pointer yang memenuhi syarat const didereferensi melalui rutinitas perpustakaan. Bergantung pada jenis PIC tertentu, pointer yang tidak memenuhi syarat const adalah 1 atau 2 byte; yang memenuhi syarat const adalah 2 atau 3. Karena ROM jauh lebih banyak daripada RAM, memiliki konstanta di ROM umumnya merupakan hal yang baik.
supercat

@David Diberikan: Perhatikan komentar saya sebelumnya juga. Saya lebih suka kompiler yang menggunakan qualifier selain "const" untuk menunjukkan kelas penyimpanan perangkat keras; kompilator HiTech memiliki beberapa kebiasaan yang agak mengganggu dengan alokasi kelas penyimpanannya (misalnya item data yang "ukuran komponennya" adalah satu byte, atau item data yang berukuran lebih dari 256 byte, masuk dalam segmen "besar". Item data lainnya masuk ke dalam " bss "untuk modul yang mereka definisikan; semua item" bss "dalam modul harus muat dalam 256 byte. Array yang sedikit pendek dari 256 byte dapat menjadi gangguan nyata.
supercat

0

FYI, Bagi mereka yang harus menerjemahkan keterampilan C mereka ke Java, berikut adalah beberapa gotcha.

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

Di Java, char 16-bit dan ditandatangani. byte adalah 8-bit dan ditandatangani.

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

long selalu 64-bit, referensi bisa 32-bit atau 64-bit (jika Anda memiliki lebih dari aplikasi dengan lebih dari 32 GB) 64-bit JVM biasanya menggunakan referensi 32-bit.

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

Pergeseran ditutup sehingga i << 64 == i == i << -64, i << 63 == i << -1

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

ByteOrder.nativeOrder () bisa BIG_ENDIAN atau LITTLE_ENDIAN

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

i = i++ tidak pernah berubah i

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

Ukuran koleksi dan array selalu 32-bit terlepas dari apakah JVM itu 32-bit atau 64-bit.

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, pendek 16-bit, int 32-bit dan panjang 64-bit.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.