Điều gì thuộc về một công cụ giáo dục để chứng minh những giả định không chính đáng mà mọi người đưa ra trong C / C ++?


121

Tôi muốn chuẩn bị một công cụ giáo dục nhỏ cho SO sẽ giúp các lập trình viên mới bắt đầu (và trung cấp) nhận ra và thách thức các giả định không chính đáng của họ trong C, C ++ và các nền tảng của họ.

Ví dụ:

  • "số nguyên quấn quanh"
  • "mọi người đều có ASCII"
  • "Tôi có thể lưu trữ một con trỏ hàm trong một khoảng trống *"

Tôi nhận thấy rằng một chương trình thử nghiệm nhỏ có thể được chạy trên nhiều nền tảng khác nhau, chạy các giả định "hợp lý", theo kinh nghiệm của chúng tôi trong SO, thường được thực hiện bởi nhiều nhà phát triển chính thống thiếu kinh nghiệm / bán kinh nghiệm và ghi lại cách chúng phá vỡ trên các máy khác nhau.

Mục tiêu của việc này không phải để chứng minh rằng làm điều gì đó là "an toàn" (điều mà không thể làm được, các bài kiểm tra chỉ chứng minh bất cứ điều gì nếu chúng bị phá vỡ), mà thay vào đó là để chứng minh cho cả những người không hiểu nhất cách diễn đạt kín đáo nhất. phá vỡ trên một máy khác, nếu nó có một hành vi chưa được xác định hoặc triển khai được xác định. .

Để đạt được điều này, tôi muốn hỏi bạn:

  • Làm thế nào để ý tưởng này có thể được cải thiện?
  • Những bài kiểm tra nào sẽ tốt và chúng trông như thế nào?
  • Bạn có chạy thử nghiệm trên các nền tảng mà bạn có thể thực hiện và đăng kết quả, để chúng tôi đưa ra cơ sở dữ liệu về các nền tảng, chúng khác nhau như thế nào và tại sao lại cho phép sự khác biệt này?

Đây là phiên bản hiện tại cho đồ chơi thử nghiệm:

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

Ồ, và tôi đã lập wiki cộng đồng này ngay từ đầu vì tôi nhận ra rằng mọi người muốn chỉnh sửa lời nói dối của tôi khi họ đọc nó.

CẬP NHẬT Cảm ơn bạn đã đóng góp ý kiến. Tôi đã thêm một số trường hợp từ câu trả lời của bạn và sẽ xem liệu tôi có thể thiết lập github cho việc này như Greg đã đề xuất hay không.

CẬP NHẬT : Tôi đã tạo một repo github cho việc này, tệp là "gotcha.c":

Vui lòng trả lời ở đây với các bản vá lỗi hoặc ý tưởng mới, vì vậy chúng có thể được thảo luận hoặc làm rõ ở đây. Sau đó tôi sẽ hợp nhất chúng thành gotcha.c.


7
Hãy xem xét mô hình phương tiện trong DOS. Các hàm có thể được lưu trữ trong nhiều phân đoạn, do đó, một con trỏ hàm dài 32 bit. Nhưng dữ liệu của bạn chỉ được lưu trữ trong một phân đoạn duy nhất, do đó, con trỏ dữ liệu chỉ dài 16 bit. Vì void * là một con trỏ dữ liệu, nó rộng 16 bit, vì vậy bạn không thể lắp một con trỏ hàm vào một. Xem c-jump.com/CIS77/ASM/Directives/D77_0030_models.htm .
David Given

6
Có lẽ bạn có thể tung mã này lên github.com hoặc một cái gì đó và sau đó mọi người có thể dễ dàng đóng góp các bản vá.
Greg Hewgill

1
Rất nhiều thứ ở đây sẽ hữu ích: stackoverflow.com/questions/367633/…
Martin York

4
POSIX yêu cầu rằng các con trỏ hàm phải có cùng cách biểu diễn như void * và có thể được chuyển đổi (với kiểu ép kiểu) mà không làm mất thông tin. Một trong những lý do cho điều này là dlsym()trả về giá trị void * nhưng dành cho cả con trỏ hàm và dữ liệu. Do đó, có thể không quá tệ khi phụ thuộc vào điều này.
jilles 11/10/10

3
@tristopia: Điểm 15 là ở đây, bởi vì nhiều người mới bắt đầu thường ngạc nhiên khi biết rằng dữ liệu không được đóng gói liên tục mà thay vào đó được căn chỉnh theo các ranh giới nhất định. Họ bối rối khi họ thay đổi thứ tự thành viên và nhận được các kích thước đối tượng khác nhau. Ngoài ra, đóng gói là chế độ mặc định với nhiều bộ điều khiển vi mô hoặc thiết bị nhúng hiện đại. Đầu ra AVR Atmega và TurboC / MSDOS của tôi cũng được đóng gói. MSDOS vẫn được sử dụng trong các ứng dụng công nghiệp.
Nordic Mainframe

Câu trả lời:


91

Thứ tự đánh giá các biểu hiện phụ, bao gồm

  • các đối số của một lệnh gọi hàm và
  • toán hạng của các nhà khai thác (ví dụ +, -, =, *, /), với ngoại lệ của:
    • toán tử logic nhị phân ( &&||),
    • toán tử điều kiện bậc ba ( ?:) và
    • toán tử dấu phẩy ( ,)

Không xác định

Ví dụ

  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
Tôi đã luôn biết điều đó về các tham số chức năng, nhưng tôi chưa bao giờ nghĩ đến nó về mặt toán tử ... ... và nếu tôi thấy bạn viết mã như vậy trong môi trường sản xuất, tôi sẽ tát bạn một cái tát.
riwalk

3
@Billy: Nhưng chỉ dành cho các phiên bản nguyên thủy của các toán tử.
Dennis Zickefoose

1
@Dennis: Đúng là như vậy. (Đó là lý do tại sao nó là một mục trong hiệu quả / MoreEffective C ++ để không bao giờ quá tải những (Trừ khi bạn viết đang boost::spirit)
Billy Oneal

1
@Daniel: Tôi không chắc bạn đang muốn nói gì. Có vẻ như bạn đang gợi ý rằng việc quá tải các toán tử được chấp nhận bởi vì chỉ những người dùng trong lớp của bạn mới có thể mắc lỗi và nếu bạn không viết bằng C ++ thẳng thì điều đó không thành vấn đề. Không có bất kỳ ý nghĩa nào cả.
Dennis Zickefoose

2
@ user420536: Hành vi chỉ là không xác định nhưng không phải là không xác định. Có, ví dụ có thể in Hello World! hoặc Thế giới! Xin chào nhưng đó chỉ là chưa xác định vì thứ tự đánh giá các toán hạng của +toán tử là không xác định (Người viết trình biên dịch không cần ghi lại hành vi). Nó không vi phạm bất kỳ quy tắc điểm trình tự nào như vậy.
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 bị treo. "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 / Bộ nhớ nhỏ

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 / Bộ nhớ trung bình

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 / Bộ nhớ nhỏ gọn

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 (ứng dụng giả lập)

văn bản thay thế


Tôi sẽ cập nhật những điều này sau:


Borland C ++ Builder 6.0 trên 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

(phải được biên dịch dưới dạng C ++ vì trình biên dịch CLR không hỗ trợ C thuần túy)

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

- http://mingw-w64.sourceforge.net/

We like to think that:
..05 int has the size of pointers
   but 'sizeof(int)==sizeof(void*)' is false.
..05a long has at least the size of pointers
   but 'sizeof(long)>=sizeof(void*)' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..14 i++ is structly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
   but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 67% mainstream

Windows 64 bit sử dụng mô hình LLP64: Cả hai intlongđược định nghĩa là 32 bit, có nghĩa là không đủ dài cho một con trỏ.


avr-gcc 4.3.2 / ATmega168 (Arduino Diecimila)

Các giả định không thành công là:

..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 có PC 16 bit, nhưng mã và dữ liệu nằm trong không gian địa chỉ riêng biệt. Atmegas lớn hơn có PC 22 bit !.


gcc 4.2.1 trên MacOSX 10.6, được biên dịch với -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
Và bạn đã xác định một giả định khác: rằng bạn có thể vừa với 80 ký tự trên một dòng đầu cuối.
Mike Seymour

3
sizeof(void*)>=sizeof(void(*)())sẽ có liên quan hơn ==. Tất cả những gì chúng ta quan tâm là "chúng ta có thể lưu trữ một con trỏ hàm trong một con trỏ void không", do đó, giả thiết bạn cần kiểm tra là liệu a void*ít nhất bằng một con trỏ hàm hay không.
jalf

1
Nếu môi trường của bạn là POSIX-compliant, bạn sẽ không sao với sizeof(void*)>=sizeof(void(*)())- xem opengroup.org/onlinepubs/009695399/functions/dlsym.html
Daniel Earwicker

26

Cách đây rất lâu, tôi đã dạy C từ một cuốn sách giáo khoa có

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

như một câu hỏi mẫu. Nó không thành công đối với một sinh viên, bởi vì sizeofcác giá trị của kiểu size_t, không phải int, inttrên việc triển khai này là 16 bit và size_tlà 32, và nó là big-endian. (Nền tảng là Lightspeed C trên Macintoshes dựa trên 680x0. Tôi đã nói rằng nó đã có từ lâu.)


7
+1 để chỉ ra một trong những lỗi phổ biến nhất và thường bị bỏ qua của loại này.
R .. GitHub DỪNG TRỢ GIÚP LÚC NỮA,

4
Điều này cũng xảy ra trên các hệ thống 64 bit, trong đó size_t là 64 bit và int hầu như luôn ngắn hơn. Win64 vẫn khác thường hơn, bởi vì size_t unsigned long longở đó. Được thêm vào dưới dạng Thử nghiệm 17.
Nordic Mainframe,

Thật không may, thời gian chạy C của Microsoft không hỗ trợ công cụ zsửa đổi cho size_tcác số nguyên có kích thước và long longcũng không được hỗ trợ trên một số nền tảng. Vì vậy, không có cách di động an toàn nào để định dạng hoặc truyền kích thước in của một đối tượng.
Phil Miller

15

Bạn cần phải bao gồm ++--giả định người thực hiện.

a[i++]= i;

Ví dụ, về mặt cú pháp là hợp pháp, nhưng tạo ra các kết quả khác nhau tùy thuộc vào quá nhiều thứ để suy luận.

Bất kỳ câu lệnh nào có ++(hoặc --) và một biến xảy ra nhiều lần là một vấn đề.


Và nó cũng chỉ là một câu hỏi phổ biến!
Matthieu M.

8

Rất thú vị!

Những thứ khác mà tôi có thể nghĩ về nó có thể hữu ích để kiểm tra:

  • con trỏ hàm và con trỏ dữ liệu có tồn tại trong cùng một không gian địa chỉ không? (Các lỗi trong máy kiến ​​trúc Harvard như chế độ nhỏ DOS. Tuy nhiên, không biết bạn sẽ kiểm tra nó như thế nào.)

  • Nếu bạn lấy một con trỏ dữ liệu NULL và ép nó sang kiểu số nguyên thích hợp, nó có giá trị số 0 không? (Vỡ trên một số máy thực sự cổ --- xem http://c-faq.com/null/machexamp.html .) Ditto với con trỏ hàm. Ngoài ra, chúng có thể là các giá trị khác nhau.

  • Việc tăng một con trỏ qua phần cuối của đối tượng lưu trữ tương ứng và sau đó quay lại, có gây ra kết quả hợp lý không? (Tôi không biết về bất kỳ máy nào mà điều này thực sự xảy ra, nhưng tôi tin rằng thông số C không cho phép bạn thậm chí nghĩ về các con trỏ không trỏ đến (a) nội dung của một mảng hoặc (b) phần tử ngay sau mảng hoặc (c) NULL. Xem http://c-faq.com/aryptr/non0based.html .)

  • Việc so sánh hai con trỏ tới các đối tượng lưu trữ khác nhau với <và> có tạo ra kết quả nhất quán không? (Tôi có thể tưởng tượng sự phá vỡ này trên các máy dựa trên phân đoạn kỳ lạ; đặc tả cấm so sánh như vậy, vì vậy trình biên dịch sẽ chỉ được quyền so sánh phần bù của con trỏ chứ không phải phần phân đoạn.)

Hừ! Tôi sẽ thử và nghĩ về một số khác.

Chỉnh sửa: Đã thêm một số liên kết làm rõ vào Câu hỏi thường gặp về C tuyệt vời.


2
Tình cờ, một thời gian trước, tôi đã thực hiện một dự án thử nghiệm có tên là Clue ( cluecc.sourceforge.net ) cho phép bạn biên dịch C thành Lua, Javascript, Perl, LISP, v.v. Nó khai thác tàn nhẫn hành vi không xác định trong tiêu chuẩn C để làm cho con trỏ hoạt động . Nó có thể thú vị để thử bài kiểm tra này trên nó.
David Given

1
IIRC C cho phép bạn tăng con trỏ thêm 1 sau phần cuối của một đối tượng, nhưng không tăng thêm nữa. Tuy nhiên, không được phép giảm vị trí trước phần đầu của đối tượng.
R .. GitHub DỪNG TRỢ GIÚP ICE

@R. Tương tự trong C ++. Và việc tăng thêm nữa có thể bị hỏng nếu việc tăng con trỏ gây ra tràn, trên CPU không chỉ coi con trỏ là số nguyên.
jalf

5

Tôi nghĩ bạn nên cố gắng phân biệt giữa hai loại giả định "không chính xác" rất khác nhau. Một nửa tốt (dịch chuyển bên phải và mở rộng dấu, mã hóa tương thích ASCII, bộ nhớ là tuyến tính, dữ liệu và con trỏ hàm tương thích, v.v.) là những giả định khá hợp lý cho hầu hết các nhà mã hóa C và thậm chí có thể được đưa vào như một phần của tiêu chuẩn nếu C đã được thiết kế ngày nay và nếu chúng ta không có phần mềm cũ của IBM. Nửa còn lại (những thứ liên quan đến răng cưa bộ nhớ, hành vi của các hàm thư viện khi bộ nhớ đầu vào và đầu ra chồng chéo lên nhau, các giả định 32-bit như con trỏ phù hợp inthoặc bạn có thể sử dụngmalloc không có nguyên mẫu, quy ước gọi đó giống hệt nhau đối với các hàm đa dạng và không biến thể, ...) hoặc xung đột với tối ưu hóa mà các trình biên dịch hiện đại muốn thực hiện hoặc di chuyển sang máy 64 bit hoặc công nghệ mới khác.


nó không chỉ là "rác của IBM" (mặc dù tôi đồng ý rằng những thứ của IBM là rác). Nhiều hệ thống nhúng ngày nay cũng gặp sự cố tương tự.
rmeador

Để làm rõ, việc sử dụng mallocmà không có nguyên mẫu có nghĩa là không bao gồm <stdlib.h>, nguyên nhân malloclà mặc định int malloc(int), không nếu bạn muốn hỗ trợ 64-bit.
Joey Adams

Về mặt kỹ thuật, bạn có thể tự do không bao gồm <stdlib.h>miễn là bạn bao gồm một tiêu đề khác xác định size_tvà sau đó bạn tự khai báo mallocvới một nguyên mẫu chính xác.
R .. GitHub DỪNG TRỢ GIÚP ICE

5

Đây là một điều thú vị: Có gì sai với chức năng này?

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

[Trả lời (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.)]


Ồ, đó là một điều tốt. clang 2.7 ăn nó và tạo ra những thứ vô nghĩa hoàn toàn mà không có cảnh báo.
Nordic Mainframe,

va_arg mở rộng nếu đó là một macro và vòng lặp while chỉ thực hiện câu lệnh đầu tiên, có lẽ là nhiều?
Maister

Không (nếu điều đó xảy ra, nó sẽ là một lỗi trong quá trình triển khai).
zwol

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

Một cái khác là về chế độ văn bản trong fopen. Hầu hết các lập trình viên giả định rằng văn bản và nhị phân giống nhau (Unix) hoặc chế độ văn bản thêm \rký tự (Windows). Nhưng C đã được chuyển đến các hệ thống sử dụng bản ghi có chiều rộng cố định, trên đó fputc('\n', file)tệp văn bản có nghĩa là thêm khoảng trắng hoặc thứ gì đó cho đến khi kích thước tệp bằng bội số của chiều dài bản ghi.

Và đây là kết quả của tôi:

gcc (Ubuntu 4.4.3-4ubuntu5) 4.4.3 trên 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

Tôi đã thực sự thấy mã kết hợp pow(2, n)với các phép toán bit.
dan04 23/09/10

4

Một số trong số chúng không thể dễ dàng được kiểm tra từ bên trong C vì chương trình có khả năng gặp sự cố trên các triển khai mà giả định không phù hợp.


"Có thể làm bất cứ điều gì với một biến có giá trị con trỏ. Nó chỉ cần chứa một giá trị con trỏ hợp lệ nếu bạn tham khảo nó."

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

Tương tự với các kiểu tích phân và dấu phẩy động (khác unsigned char), được phép có các biểu diễn bẫy.


"Các phép tính số nguyên quấn quanh. Vì vậy, chương trình này in ra một số nguyên âm lớn."

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

(Chỉ C89.) "Không sao để rơi ra cuối main."

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

2
Như một ví dụ cụ thể: Khi được biên dịch với gcc -ftrapv -O, đầu ra được We like to think that:theo sau làAborted
caf

@caf: "Tùy chọn này tạo bẫy đối với lỗi tràn có dấu trên các phép toán cộng, trừ, nhân." Rất vui được biết, cảm ơn.
Gilles 'SO- đừng xấu xa nữa'.

1
Người cuối cùng là ok trong C ++ (98, 03 và 0x) là tốt, và mặc nhiên trả về 0.
jalf

Điều này thật khó chịu vì trước ANSI C đã cho phép điều này và C99 cũng vậy.
Joshua

@Joshua: AFAIK không có sự khác biệt giữa tiền ANSI C và C89 khi trả về mainkhông có giá trị: chương trình đúng nhưng trả về trạng thái kết thúc không xác định (C89 §2.1.2.2). Với nhiều triển khai (chẳng hạn như gcc và các trình biên dịch unix cũ hơn), bạn sẽ có được bất cứ thứ gì có trong một thanh ghi nhất định tại thời điểm đó. Chương trình thường hoạt động cho đến khi nó được sử dụng trong makefile hoặc môi trường khác để kiểm tra trạng thái kết thúc.
Gilles 'SO- đừng xấu xa nữa'.

4

Vâng, các giả định về tính di động cổ điển chưa được đề cập đến là

  • giả định về kích thước của các loại tích phân
  • sự bền bỉ

4
"Endianness", bao gồm "There is an endianness": có những máy trung cấp và tiêu chuẩn cho phép những thứ kỳ lạ như lưu trữ shortgiá trị fedcab9876543210 (đó là 16 chữ số nhị phân) dưới dạng hai byte 0248ace và fdb97531.
Gilles 'SO- đừng xấu xa nữa'.

vâng chắc chắn có endianess bao gồm hỗn hợp / trung tuyến cũng như lớn và nhỏ. nếu bạn sử dụng phần cứng tùy chỉnh, bạn có thể có bất kỳ vật dụng nào bạn thích trên bất kỳ xe buýt nào.
jk.

Kết thúc giữa được gọi là PDP endian. Gilles tiết lộ một cái gì đó thậm chí còn kỳ lạ hơn mặc dù điều đó sẽ gây đau đầu cho việc triển khai TCP / IP.
Joshua

@Gilles: Middle-endian ... Tôi rất vui vì tôi không phát triển trên cái đó. (nhưng bây giờ tôi sẽ nhận được yêu cầu làm một dự án mạng trung về cuối, tôi chắc chắn) ...
Paul Nathan

ARM FPE đã sử dụng các bộ đôi trung cấp, nơi chúng được lưu trữ dưới dạng một cặp <bộ tứ cao> <bộ tứ thấp> nhưng thứ tự của các bit bên trong mỗi bộ tứ bị sai lệch. (Rất may, ARM VFP không làm điều này nữa.)
David Given

4
  • Lỗi rời rạc hóa do biểu diễn dấu phẩy động. Ví dụ: nếu bạn sử dụng công thức chuẩn để giải phương trình bậc hai, hoặc hiệu số hữu hạn để tính đạo hàm gần đúng hoặc công thức chuẩn để tính phương sai, độ chính xác sẽ bị mất do tính toán sự khác biệt giữa các số giống nhau. Thuật toán Gauß để giải quyết các hệ thống tuyến tính là không tốt vì các lỗi làm tròn tích lũy, do đó người ta sử dụng phân rã QR hoặc LU, phân rã Cholesky, SVD, v.v. Việc cộng các số dấu phẩy động không có tính liên kết. Có giá trị bất thường, vô hạn và giá trị NaN. a + b - ab .

  • Chuỗi: Sự khác biệt giữa các ký tự, điểm mã và đơn vị mã. Cách Unicode được thực hiện trên các hệ điều hành khác nhau; Bảng mã Unicode. Không thể mở tệp với tên tệp Unicode tùy ý với C ++ theo cách di động.

  • Điều kiện chạy đua, ngay cả khi không phân luồng: nếu bạn kiểm tra xem tệp có tồn tại hay không, kết quả có thể trở nên không hợp lệ bất kỳ lúc nào.

  • ERROR_SUCCESS = 0


4

Bao gồm kiểm tra kích thước số nguyên. Hầu hết mọi người đều cho rằng int lớn hơn short là lớn hơn char. Tuy nhiên, tất cả những điều này đều có thể sai:sizeof(char) < sizeof(int); sizeof(short) < sizeof(int); sizeof(char) < sizeof(short)

Mã này có thể không thành công (sự cố đối với quyền truy cập chưa được chỉ định)

unsigned char buf[64];

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

mã này có bị lỗi trong C ++ không? IIRC, việc ép con trỏ giữa các kiểu không liên quan là bất hợp pháp, NGOẠI TRỪ đối với char *, có thể được ép kiểu bất kỳ (hay ngược lại?).
rmeador

1
Bạn chỉ có thể làm int *p = (int*)&buf[1];trong c ++, mọi người mong đợi điều đó cũng hoạt động.
nos

@nos, vâng có thể thất bại nhưng lỗi đó là sự cố nên chương trình của anh ấy không thể kiểm tra lỗi đó. :(
Joshua,

1
sizeof(char) < sizeof(int)bắt buộc. Ví dụ, fgetc () trả về giá trị của ký tự dưới dạng ký tự không dấu được chuyển đổi thành int hoặc EOFlà giá trị âm. unsigned charcó thể không có các bit đệm, vì vậy cách duy nhất có thể thực hiện là tạo int lớn hơn char. Ngoài ra, (hầu hết các phiên bản của) đặc tả C yêu cầu bất kỳ giá trị nào từ phạm vi -32767..32767 đều có thể được lưu trữ trong một int.
jilles

@illes vẫn còn, có DSP với ký tự 32 bit và ints 32 bit.
nos

3

Vài điều về kiểu dữ liệu tích hợp:

  • charsigned charthực sự là hai kiểu riêng biệt (không giống intsigned inttham chiếu đến cùng một kiểu số nguyên có dấu).
  • số nguyên có dấu không bắt buộc phải sử dụng phần bù của hai. Phần bù và dấu + độ lớn của Ones cũng là biểu diễn hợp lệ của số âm. Điều này làm cho các hoạt động bit liên quan đến việc triển khai số âm được xác định .
  • Nếu bạn gán một số nguyên nằm ngoài phạm vi cho một biến số nguyên có dấu, thì hành vi được xác định bởi việc triển khai .
  • Trong C90, -3/5có thể trở lại 0hoặc -1. Làm tròn về 0 trong trường hợp một toán hạng là số âm chỉ được đảm bảo trong C99 trở lên và C ++ 0x trở lên.
  • Không có đảm bảo kích thước chính xác cho các loại tích hợp. Tiêu chuẩn chỉ bao gồm các yêu cầu tối thiểu như an intít nhất 16 bit, a longít nhất 32 bit, a long longít nhất 64 bit. A floatcó thể biểu diễn đúng ít nhất 6 chữ số thập phân có nghĩa nhất. A doublecó thể biểu diễn đúng ít nhất 10 chữ số thập phân có nghĩa nhất.
  • IEEE 754 không bắt buộc để biểu diễn số dấu phẩy động.

Phải thừa nhận rằng trên hầu hết các máy, chúng tôi sẽ có hai phần bổ sung và IEEE 754 nổi.


Tôi tự hỏi có giá trị nào khi có các phép gán số nguyên nằm ngoài phạm vi được triển khai xác định thay vì Hành vi không xác định? Trên một số nền tảng, yêu cầu như vậy sẽ buộc trình biên dịch tạo thêm mã cho int mult(int a,int b) { return (long)a*b;}[ví dụ: nếu intlà 32 bit, nhưng đăng ký và longlà 64]. Nếu không có yêu cầu như vậy, hành vi "tự nhiên" của việc triển khai nhanh nhất long l=mult(1000000,1000000);sẽ được đặt lbằng 1000000000000, mặc dù đó là giá trị "không thể" đối với một int.
supercat

3

Còn cái này thì sao:

Không có con trỏ dữ liệu nào có thể giống con trỏ hàm hợp lệ.

Điều này là ĐÚNG đối với tất cả các mô hình phẳng, mô hình MS-DOS TINY, LARGE và HUGE, sai cho mô hình MS-DOS SMALL và hầu như luôn sai đối với các mô hình TRUNG BÌNH và NHỎ GỌN (tùy thuộc vào địa chỉ tải, bạn sẽ cần một DOS thực sự cũ để biến nó thành sự thật).

Tôi không thể viết một bài kiểm tra cho điều này

Và tệ hơn: các con trỏ được truyền tới ptrdiff_t có thể được so sánh. Điều này không đúng với mô hình MS-DOS LARGE (sự khác biệt duy nhất giữa LARGE và HUGE là HUGE thêm mã trình biên dịch để chuẩn hóa con trỏ).

Tôi không thể viết bài kiểm tra vì môi trường nơi điều này ném bom mạnh sẽ không phân bổ bộ đệm lớn hơn 64K, vì vậy mã chứng minh nó sẽ bị lỗi trên các nền tảng khác.

Thử nghiệm cụ thể này sẽ vượt qua một hệ thống hiện không còn tồn tại (lưu ý rằng nó phụ thuộc vào bên trong của 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

CHỈNH SỬA: Đã cập nhật lên phiên bản cuối cùng của chương trình

Solaris-SPARC

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

và với 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

và với 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

Bạn có thể sử dụng chế độ văn bản ( fopen("filename", "r")) để đọc bất kỳ loại tệp văn bản nào.

Trong khi điều này nên trong công việc lý thuyết tốt, nếu bạn cũng sử dụng ftell()trong mã của bạn, và tập tin văn bản của bạn có UNIX kiểu dòng kết thúc, trong một số phiên bản của thư viện chuẩn của Windows, ftell()thường sẽ trở lại giá trị không hợp lệ. Giải pháp là sử dụng chế độ nhị phân thay thế ( fopen("filename", "rb")).


1

gcc 3.3.2 trên AIX 5.3 (vâng, chúng tôi cần cập nhật 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

Một giả định mà một số có thể làm trong C ++ là a structbị giới hạn ở những gì nó có thể làm trong C. Thực tế là, trong C ++, a structgiống như một classngoại trừ rằng nó có mọi thứ công khai theo mặc định.

Cấu trúc C ++:

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


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

protected:
  void Testing2();
};

1

Các hàm toán học tiêu chuẩn trên các hệ thống khác nhau không cho kết quả giống nhau.


1

Visual Studio Express 2010 trên x86 32-bit.

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

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

Lưu ý rằng Codepad không có stddef.h. Tôi đã xóa thử nghiệm 9 do codepad sử dụng cảnh báo là lỗi. Tôi cũng đã đổi tên countbiến vì nó đã được xác định vì một số lý do.

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

Làm thế nào về việc dịch chuyển sang phải bởi số lượng quá nhiều - điều đó được tiêu chuẩn cho phép hay đáng để thử nghiệm?

Tiêu chuẩn C có chỉ định hành vi của chương trình sau đây không:

void print_string (char * st)
{
  char ch;
  while ((ch = * st ++)! = 0)
    putch (ch); / * Giả sử điều này được xác định * /
}
int main (void)
{
  print_string ("Xin chào");
  trả về 0;
}

Trên ít nhất một trình biên dịch tôi sử dụng, mã đó sẽ không thành công trừ khi đối số của print_string là "char const *". Tiêu chuẩn có cho phép hạn chế như vậy không?

Một số hệ thống cho phép một hệ thống tạo ra các con trỏ tới int không được đánh dấu và những hệ thống khác thì không. Có thể đáng để thử nghiệm.


C89 §3.3.7: “Nếu giá trị của toán hạng bên phải là số âm hoặc lớn hơn hoặc bằng độ rộng tính bằng bit của toán hạng bên trái được thăng hạng, thì hành vi là không xác định.” (áp dụng cho cả <<>>). C99 có ngôn ngữ giống hệt nhau trong §6.5.7-3.
Gilles 'Somali dừng tà ác'

Ngoài putch(tại sao bạn không sử dụng tiêu chuẩn putchar?), Tôi không thể thấy bất kỳ hành vi không xác định nào trong chương trình của bạn. C89 §3.1.4 chỉ định rằng “một chuỗi ký tự có […] loại 'mảng ký tự'” (lưu ý: không const) và “nếu chương trình cố gắng sửa đổi một chuỗi ký tự […], hành vi là không xác định” . Trình biên dịch đó là gì, và nó dịch chương trình này như thế nào?
Gilles 'SO- đừng ác nữa'

2
Trong C ++, các hằng ký tự không phải là char [], chúng là const char []. Tuy nhiên ... đã từng có một lỗ hổng cụ thể trong hệ thống kiểu để cho phép bạn sử dụng một hằng chuỗi trong ngữ cảnh mà char * được mong đợi và không gặp lỗi kiểu. Điều này dẫn đến tình huống print_string ("foo") sẽ hoạt động nhưng print_string ("foo" +0) thì không. Điều này gây nhầm lẫn sâu sắc, đặc biệt là trong môi trường mà tệp C được biên dịch bằng trình biên dịch C ++ theo mặc định. Lỗ hổng đã được loại bỏ trong các trình biên dịch mới nhưng vẫn còn rất nhiều những cái cũ xung quanh. AFAIK C99 vẫn định nghĩa hằng số chuỗi là char [].
David Given

1
Trên các trình biên dịch HiTech cho loạt bộ điều khiển Microchip PIC, một con trỏ không có bộ định lượng lưu trữ chỉ có thể trỏ tới RAM. Một con trỏ đủ điều kiện const có thể trỏ đến RAM hoặc ROM. Các con trỏ không đủ điều kiện được tham chiếu trực tiếp trong mã; con trỏ đủ điều kiện const được tham chiếu qua quy trình thư viện. Tùy thuộc vào loại PIC cụ thể, các con trỏ không đủ điều kiện const là 1 hoặc 2 byte; Các hằng số đủ điều kiện là 2 hoặc 3. Vì ROM dồi dào hơn nhiều so với RAM, nên việc có hằng số trong ROM nói chung là một điều tốt.
supercat

@David Given: Cũng lưu ý nhận xét trước đây của tôi. Tôi thích các trình biên dịch sử dụng các định nghĩa khác với "const" để biểu thị lớp lưu trữ phần cứng; trình biên dịch HiTech có một số vấn đề khá khó chịu với việc phân bổ lớp lưu trữ của nó (ví dụ: các mục dữ liệu có "kích thước thành phần" là một byte hoặc các mục dữ liệu trên 256 byte, đi trong một phân đoạn "lớn". Các mục dữ liệu khác đi trong " bss" phân khúc cho các mô-đun chúng được xác định, tất cả các 'bss' mục trong một mô-đun phải phù hợp trong phạm vi 256 byte mảng rằng đang có hơi ngắn của 256 byte có thể là một phiền toái thực sự..
supercat

0

FYI, Đối với những người phải dịch các kỹ năng C của họ sang Java, đây là một vài mẹo nhỏ.

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

Trong Java, char là 16-bit và có dấu. byte là 8-bit và có dấu.

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

long luôn là 64 bit, tham chiếu có thể là 32 bit hoặc 64 bit (nếu bạn có nhiều ứng dụng hơn 32 GB) JVM 64 bit thường sử dụng tham chiếu 32 bit.

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

Sự thay đổi được che để tôi << 64 == i == i << -64, tôi << 63 == i << -1

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

ByteOrder.nativeOrder () có thể là BIG_ENDIAN hoặc LITTLE_ENDIAN

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

i = i++ không bao giờ thay đổi i

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

Kích thước của bộ sưu tập và mảng luôn là 32-bit bất kể JVM là 32-bit hay 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 là 16 bit, short là 16 bit, int là 32 bit và dài là 64 bit.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.