มีอะไรอยู่ในเครื่องมือทางการศึกษาเพื่อแสดงให้เห็นถึงสมมติฐานที่ไม่มีเหตุผลที่ผู้คนตั้งไว้ใน C / C ++


121

ฉันต้องการเตรียมเครื่องมือทางการศึกษาเล็ก ๆ น้อย ๆ สำหรับ SO ซึ่งจะช่วยให้โปรแกรมเมอร์มือใหม่ (และระดับกลาง) รับรู้และท้าทายสมมติฐานที่ไม่ได้รับการรับรองใน C, C ++ และแพลตฟอร์มของพวกเขา

ตัวอย่าง:

  • "จำนวนเต็มพันรอบ"
  • "ทุกคนมี ASCII"
  • "ฉันสามารถจัดเก็บตัวชี้ฟังก์ชันในความว่างเปล่า *"

ฉันคิดว่าโปรแกรมทดสอบขนาดเล็กสามารถทำงานบนแพลตฟอร์มต่างๆได้ซึ่งเรียกใช้สมมติฐานที่ "เป็นไปได้" ซึ่งจากประสบการณ์ของเราใน SO มักสร้างขึ้นโดยนักพัฒนากระแสหลักที่ไม่มีประสบการณ์ / กึ่งมีประสบการณ์จำนวนมากและบันทึกวิธีที่พวกเขาทำลายบนเครื่องต่างๆ

เป้าหมายของสิ่งนี้ไม่ใช่เพื่อพิสูจน์ว่า "ปลอดภัย" ที่จะทำบางสิ่ง (ซึ่งคงเป็นไปไม่ได้ที่จะทำการทดสอบจะพิสูจน์อะไรก็ได้ถ้ามันแตก) แต่แทนที่จะแสดงให้เห็นว่าแม้แต่คนที่ไม่เข้าใจที่สุดว่าการแสดงออกที่ไม่เด่นชัดที่สุด หยุดทำงานบนเครื่องอื่นหากมีพฤติกรรมที่กำหนดไว้โดยไม่ได้กำหนดหรือการนำไปใช้งาน .

เพื่อให้บรรลุสิ่งนี้ฉันอยากจะถามคุณ:

  • ความคิดนี้จะปรับปรุงได้อย่างไร?
  • แบบทดสอบใดที่จะดีและควรมีลักษณะอย่างไร?
  • คุณจะทำการทดสอบบนแพลตฟอร์มที่คุณสามารถทำได้และโพสต์ผลลัพธ์เพื่อที่เราจะได้ฐานข้อมูลของแพลตฟอร์มแตกต่างกันอย่างไรและเหตุใดจึงอนุญาตให้ใช้ความแตกต่างนี้ได้

นี่คือเวอร์ชันปัจจุบันสำหรับของเล่นทดสอบ:

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

โอ้และฉันสร้างวิกิชุมชนนี้ตั้งแต่เริ่มต้นเพราะฉันคิดว่ามีคนต้องการแก้ไขคนพูดไม่ชัดของฉันเมื่อพวกเขาอ่านสิ่งนี้

อัปเดตขอบคุณสำหรับข้อมูลของคุณ ฉันได้เพิ่มบางกรณีจากคำตอบของคุณและจะดูว่าฉันสามารถตั้งค่า github สำหรับสิ่งนี้ตามที่ Greg แนะนำได้หรือไม่

UPDATE : ฉันได้สร้าง github repo สำหรับสิ่งนี้ไฟล์คือ "gotcha.c":

โปรดตอบที่นี่ด้วยแพตช์หรือแนวคิดใหม่ ๆ เพื่อให้สามารถพูดคุยหรือชี้แจงได้ที่นี่ ฉันจะรวมมันเป็น gotcha.c แล้ว


7
พิจารณาโมเดลขนาดกลางใน DOS ฟังก์ชันสามารถจัดเก็บไว้ในหลายส่วนได้ดังนั้นตัวชี้ฟังก์ชันจึงมีความยาว 32 บิต แต่ข้อมูลของคุณจะถูกเก็บไว้ในกลุ่มเดียวเท่านั้นดังนั้นตัวชี้ข้อมูลจึงมีความยาวเพียง 16 บิต เนื่องจาก void * เป็นตัวชี้ข้อมูลจึงมีความกว้าง 16 บิตดังนั้นคุณจึงไม่สามารถใส่ตัวชี้ฟังก์ชันในตัวเดียวได้ ดูc-jump.com/CIS77/ASM/Directives/D77_0030_models.htm
David Given

6
บางทีคุณอาจโยนรหัสนี้ขึ้นบนgithub.comหรือบางสิ่งบางอย่างจากนั้นผู้คนก็สามารถสนับสนุนแพตช์ได้อย่างง่ายดาย
Greg Hewgill

1
สิ่งต่างๆที่นี่น่าจะช่วยได้: stackoverflow.com/questions/367633/…
Martin York

4
POSIX ต้องการให้พอยน์เตอร์ของฟังก์ชันมีการแสดงเหมือนกับโมฆะ * และสามารถแปลงได้ (ด้วยการร่าย) โดยไม่สูญเสียข้อมูล หนึ่งในเหตุผลนี้คือdlsym()ส่งคืนโมฆะ * แต่มีไว้สำหรับทั้งข้อมูลและตัวชี้ฟังก์ชัน ดังนั้นจึงอาจไม่เลวร้ายนักที่จะขึ้นอยู่กับสิ่งนี้
jilles

3
@tristopia: จุดที่ 15 อยู่ที่นี่เนื่องจากผู้เริ่มต้นหลายคนมักจะประหลาดใจที่ได้ทราบว่าข้อมูลไม่ได้ถูกบรรจุอย่างต่อเนื่อง แต่จะจัดให้สอดคล้องกับขอบเขตที่แน่นอนแทน พวกเขางงงวยเมื่อเปลี่ยนลำดับสมาชิกและได้ขนาดวัตถุที่แตกต่างกัน นอกจากนี้การบรรจุยังเป็นโหมดเริ่มต้นที่มีไมโครคอนโทรลเลอร์หรืออุปกรณ์ฝังตัวร่วมสมัยจำนวนมาก เอาท์พุต AVR Atmega และ TurboC / MSDOS ของฉันก็อัดแน่นไปด้วย MSDOS ยังคงใช้ในงานอุตสาหกรรม
Nordic Mainframe

คำตอบ:


91

ลำดับของการประเมินนิพจน์ย่อย ได้แก่

  • อาร์กิวเมนต์ของการเรียกใช้ฟังก์ชันและ
  • ตัวถูกดำเนินการของผู้ประกอบการ (เช่น+, -, =, *, /) มีข้อยกเว้นของ:
    • ตัวดำเนินการตรรกะไบนารี ( &&และ||)
    • ตัวดำเนินการเงื่อนไขด้านท้าย ( ?:) และ
    • ตัวดำเนินการลูกน้ำ ( ,)

คือยังไม่ระบุ

ตัวอย่างเช่น

  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
ฉันรู้มาตลอดว่าเกี่ยวกับพารามิเตอร์ฟังก์ชัน แต่ฉันไม่เคยคิดถึงมันในแง่ของตัวดำเนินการ ... ... และถ้าฉันเคยเห็นคุณเขียนโค้ดแบบนั้นในสภาพแวดล้อมการผลิตฉันจะตบคุณด้วยบะหมี่เปียก
riwalk

3
@ บิลลี่: แต่สำหรับตัวดำเนินการเวอร์ชันดั้งเดิมเท่านั้น
Dennis Zickefoose

1
@ เดนนิส: นั่นคือเรื่องจริง (ซึ่งเป็นเหตุผลว่าทำไมจึงเป็นรายการใน C ++ ที่มีประสิทธิภาพ / มีประสิทธิภาพมากขึ้นเพื่อไม่ให้เกินพิกัดเหล่านั้น (เว้นแต่คุณจะเขียนboost::spirit)
Billy ONeal

1
@ แดเนียล: ฉันไม่แน่ใจว่าคุณพยายามจะพูดอะไร ดูเหมือนว่าคุณกำลังแนะนำว่าไม่เป็นไรที่จะโอเวอร์โหลดโอเปอเรเตอร์เพราะมีเพียงผู้ใช้ในชั้นเรียนของคุณเท่านั้นที่อาจเข้าใจผิดและถ้าคุณไม่ได้เขียน C ++ แบบตรงก็ไม่สำคัญ ทั้งสองอย่างไม่สมเหตุสมผลเลย
Dennis Zickefoose

2
@ user420536: พฤติกรรมนี้ไม่ได้ระบุไว้ แต่ไม่ได้ระบุ ใช่ตัวอย่างสามารถพิมพ์ได้ Hello World! หรือโลก! สวัสดี แต่ไม่ได้ระบุเพียงเพราะลำดับของการประเมินตัวถูกดำเนินการของตัวดำเนิน+การไม่ได้ระบุไว้ (ผู้เขียนคอมไพเลอร์ไม่จำเป็นต้องบันทึกพฤติกรรม) ไม่ละเมิดกฎลำดับจุดใด ๆเช่นนี้
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 ขัดข้อง "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

เสียงดัง 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 / หน่วยความจำขนาดเล็ก

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 / หน่วยความจำขนาดกลาง

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 / หน่วยความจำขนาดกะทัดรัด

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 (ตัวจำลองรอง)

ข้อความแสดงแทน


ฉันจะอัปเดตสิ่งเหล่านี้ในภายหลัง:


Borland C ++ Builder 6.0 บน Windows XP

..04 a char is signed
   but 'CHAR_MIN==SCHAR_MIN' is false.
..08 overshifting is okay
   but '(1<<bits_per_int)==0' is false.
..09 overshifting is *always* okay
   but '(1<<BITS_PER_INT)==0' is false.
..09a minus shifts backwards
   but '(t=-1,(15<<t)==7)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..16 malloc()=NULL means out of memory
   but '(malloc(0)!=NULL)' is false.
..19-3 int<long
   but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
   but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 71% mainstream

Visual Studio Express 2010 C ++ CLR, Windows 7 64 บิต

(ต้องคอมไพล์เป็น C ++ เนื่องจากคอมไพเลอร์ CLR ไม่รองรับ C บริสุทธิ์)

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 บิตใช้แบบจำลอง LLP64: ทั้งสองอย่างintและlongกำหนดเป็น 32 บิตซึ่งหมายความว่าไม่มีความยาวเพียงพอสำหรับตัวชี้


avr-gcc 4.3.2 / ATmega168 (Arduino Diecimila)

สมมติฐานที่ล้มเหลวคือ:

..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 มีพีซี 16 บิต แต่รหัสและข้อมูลอยู่ในช่องว่างที่อยู่แยกกัน Atmegas ขนาดใหญ่ขึ้นมีพีซี 22 บิต!.


gcc 4.2.1 บน MacOSX 10.6 คอมไพล์ด้วย -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
และคุณได้ระบุสมมติฐานอีกประการหนึ่งนั่นคือคุณสามารถใส่อักขระได้ 80 ตัวในบรรทัดเทอร์มินัล
Mike Seymour

3
sizeof(void*)>=sizeof(void(*)())จะมีความเกี่ยวข้องมากกว่า == ทั้งหมดที่เราสนใจคือ "เราสามารถเก็บชี้ฟังก์ชั่นในตัวชี้โมฆะ" ดังนั้นสมมติฐานที่คุณจำเป็นต้องทดสอบว่าvoid*เป็นอย่างน้อยเป็นใหญ่เป็นตัวชี้ฟังก์ชั่น
jalf

1
หากสภาพแวดล้อมของคุณเป็นไปตาม POSIX คุณควรจะโอเคsizeof(void*)>=sizeof(void(*)())- ดูที่opengroup.org/onlinepubs/009695399/functions/dlsym.html
Daniel Earwicker

26

นานมาแล้วฉันสอน C จากหนังสือเรียนที่มี

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

เป็นคำถามตัวอย่าง มันล้มเหลวสำหรับนักเรียนเพราะsizeofค่าอัตราผลตอบแทนจากประเภทนี้size_tไม่ได้int, intในการดำเนินการนี้คือ 16 บิตและsize_t32 และมันก็เป็น big- (แพลตฟอร์มนี้คือ Lightspeed C บน Macintoshes ที่ใช้ 680x0 ฉันบอกว่ามันนานมาแล้ว)


7
+1 เพื่อชี้ให้เห็นข้อผิดพลาดที่พบบ่อยที่สุดและมักถูกมองข้ามในประเภทนี้
R .. GitHub STOP HELPING ICE

4
สิ่งนี้ยังเกิดขึ้นในระบบ 64 บิตโดยที่ size_t คือ 64 บิตและ ints จะสั้นกว่าเกือบตลอดเวลา Win64 ยังคงแปลกกว่าเพราะ size_t อยู่ที่unsigned long longนั่น เพิ่มเป็น Test 17.
Nordic Mainframe

น่าเสียดายที่รันไทม์ C ของ Microsoft ไม่รองรับzตัวปรับแต่งสำหรับsize_tจำนวนเต็มขนาดและlong longไม่รองรับในบางแพลตฟอร์มด้วย ดังนั้นจึงไม่มีวิธีพกพาที่ปลอดภัยในการจัดรูปแบบหรือส่งขนาดที่พิมพ์ของวัตถุ
Phil Miller

15

คุณต้องรวม++และ--สมมติฐานที่ผู้คนตั้งขึ้น

a[i++]= i;

ตัวอย่างเช่นเป็นไวยากรณ์ที่ถูกต้องตามกฎหมาย แต่ให้ผลลัพธ์ที่แตกต่างกันไปขึ้นอยู่กับหลายสิ่งที่เกินกว่าที่จะให้เหตุผล

คำสั่งใด ๆ ที่มี++(หรือ--) และตัวแปรที่เกิดขึ้นมากกว่าหนึ่งครั้งเป็นปัญหา


และมันก็เป็นคำถามธรรมดาเช่นกัน!
Matthieu M.

8

น่าสนใจมาก!

สิ่งอื่น ๆ ที่ฉันคิดได้การตรวจสอบอาจเป็นประโยชน์:

  • ตัวชี้ฟังก์ชันและตัวชี้ข้อมูลมีอยู่ในพื้นที่ที่อยู่เดียวกันหรือไม่ (แตกในเครื่องสถาปัตยกรรม Harvard เช่น DOS small mode แม้ว่าคุณจะไม่ทราบว่าคุณจะทดสอบมันอย่างไร)

  • ถ้าคุณใช้ตัวชี้ข้อมูล NULL และส่งไปยังประเภทจำนวนเต็มที่เหมาะสมจะมีค่าตัวเลข 0 หรือไม่ (แตกในเครื่องจักรโบราณจริงๆบางตัว --- ดูhttp://c-faq.com/null/machexamp.html ) Ditto พร้อมตัวชี้ฟังก์ชัน นอกจากนี้อาจเป็นค่าที่แตกต่างกัน

  • การเพิ่มตัวชี้ผ่านจุดสิ้นสุดของอ็อบเจ็กต์จัดเก็บข้อมูลที่เกี่ยวข้องแล้วย้อนกลับอีกครั้งทำให้เกิดผลลัพธ์ที่สมเหตุสมผลหรือไม่? (ฉันไม่ทราบว่ามีเครื่องใดบ้างที่เกิดขึ้นจริง แต่ฉันเชื่อว่าข้อมูลจำเพาะ C ไม่อนุญาตให้คุณคิดถึงตัวชี้ที่ไม่ชี้ไปที่ (a) เนื้อหาของอาร์เรย์หรือ (b) องค์ประกอบ ต่อจากอาร์เรย์หรือ (c) NULL ดูhttp://c-faq.com/aryptr/non0based.html )

  • การเปรียบเทียบพอยน์เตอร์สองตัวกับอ็อบเจ็กต์หน่วยเก็บข้อมูลที่แตกต่างกันด้วย <และ> ให้ผลลัพธ์ที่สอดคล้องกันหรือไม่ (ฉันนึกภาพออกว่านี่คือการทำลายเครื่องจักรแบบแบ่งส่วนที่แปลกใหม่ข้อมูลจำเพาะห้ามการเปรียบเทียบดังกล่าวดังนั้นคอมไพเลอร์จะมีสิทธิ์เปรียบเทียบส่วนออฟเซ็ตของตัวชี้เท่านั้นไม่ใช่ส่วนของส่วน)

อืมมม ฉันจะพยายามคิดให้มากกว่านี้

แก้ไข:เพิ่มลิงก์ชี้แจงบางส่วนไปยังคำถามที่พบบ่อยเกี่ยวกับ C ที่ยอดเยี่ยม


2
อนึ่งเมื่อไม่นานมานี้ฉันได้ทำโครงการทดลองชื่อ Clue ( cluecc.sourceforge.net ) ซึ่งอนุญาตให้คุณคอมไพล์ C เป็น Lua, Javascript, Perl, LISP และอื่น ๆ มันใช้ประโยชน์จากพฤติกรรมที่ไม่ได้กำหนดในมาตรฐาน C อย่างไร้ความปรานีเพื่อให้พอยน์เตอร์ทำงาน . อาจเป็นเรื่องน่าสนใจที่จะลองใช้การทดสอบนี้
David Given

1
IIRC C ช่วยให้คุณสามารถเพิ่มตัวชี้ได้ทีละ1 เลยส่วนท้ายของวัตถุ แต่ไม่สามารถเพิ่มได้อีก อย่างไรก็ตามไม่อนุญาตให้ทำการลดตำแหน่งก่อนจุดเริ่มต้นของวัตถุ
R .. GitHub STOP HELPING ICE

@R เหมือนกันใน C ++ และการเพิ่มขึ้นต่อไปอาจทำลายได้หากการเพิ่มตัวชี้ทำให้เกิดการล้นบน CPU ซึ่งไม่เพียง แต่ถือว่าพอยน์เตอร์เป็นจำนวนเต็ม
jalf

5

ฉันคิดว่าคุณควรพยายามแยกแยะความแตกต่างระหว่างสองชั้นของสมมติฐานที่ "ไม่ถูกต้อง" ที่แตกต่างกันมาก ครึ่งหนึ่งที่ดี (ส่วนขยาย shift และ sign ที่ถูกต้อง, การเข้ารหัสที่เข้ากันได้กับ ASCII, หน่วยความจำเป็นเชิงเส้น, ตัวชี้ข้อมูลและฟังก์ชันเข้ากันได้ ฯลฯ ) เป็นสมมติฐานที่สมเหตุสมผลสำหรับC coders ส่วนใหญ่ที่จะสร้างและอาจรวมเป็นส่วนหนึ่งของมาตรฐานด้วย หาก C ได้รับการออกแบบในวันนี้และหากเราไม่มีขยะ IBM รุ่นเก่าที่มีปู่ย่าตายาย อีกครึ่งหนึ่ง (สิ่งที่เกี่ยวข้องกับนามแฝงหน่วยความจำพฤติกรรมของฟังก์ชันไลบรารีเมื่อหน่วยความจำอินพุตและเอาต์พุตทับซ้อนกันสมมติฐาน 32 บิตเช่นตัวชี้ที่พอดีintหรือที่คุณสามารถใช้ได้malloc หากไม่มีต้นแบบรูปแบบการเรียกนั้นจะเหมือนกันสำหรับฟังก์ชันแบบแปรผันและไม่ใช่ตัวแปร ... ) อาจขัดแย้งกับการเพิ่มประสิทธิภาพคอมไพเลอร์สมัยใหม่ที่ต้องการดำเนินการหรือด้วยการโยกย้ายไปยังเครื่อง 64 บิตหรือเทคโนโลยีใหม่อื่น ๆ


ไม่ใช่แค่ "ขยะของ IBM" (แม้ว่าฉันจะยอมรับว่าของ IBM เป็นขยะก็ตาม) ระบบฝังตัวจำนวนมากในปัจจุบันมีปัญหาคล้ายกัน
rmeador

เพื่อชี้แจงการใช้mallocโดยไม่มีต้นแบบหมายถึงการไม่รวม<stdlib.h>ซึ่งทำให้mallocค่าเริ่มต้นint malloc(int)เป็น no-no หากคุณต้องการรองรับ 64-bit
Joey Adams

ในทางเทคนิคแล้วคุณมีอิสระที่จะไม่รวม<stdlib.h>ตราบใดที่คุณรวมส่วนหัวอื่นที่กำหนดsize_tและจากนั้นคุณจะประกาศmallocด้วยต้นแบบที่ถูกต้องด้วยตัวคุณเอง
R .. GitHub STOP HELPING ICE

5

นี่เป็นความสนุก: มีอะไรผิดปกติกับฟังก์ชั่นนี้?

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


โอ้ยยยยยยยยยย เสียงดัง 2.7 กินสิ่งนี้และก่อให้เกิดเรื่องไร้สาระโดยไม่มีการเตือนล่วงหน้า
Nordic Mainframe

va_arg จะขยายหากเป็นมาโครและ while loop จะเรียกใช้งานคำสั่งแรกเท่านั้นซึ่งอาจมีจำนวนมาก?
Maister

ไม่ (หากเกิดขึ้นจะเป็นข้อบกพร่องในการนำไปใช้งาน)
zwol

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

อีกเรื่องหนึ่งเกี่ยวกับโหมดข้อความในfopen. โปรแกรมเมอร์ส่วนใหญ่คิดว่าข้อความและไบนารีเหมือนกัน (Unix) หรือโหมดข้อความจะเพิ่ม\rอักขระ (Windows) แต่ C ถูกย้ายไปยังระบบที่ใช้เร็กคอร์ดความกว้างคงที่ซึ่งfputc('\n', file)ในไฟล์ข้อความหมายถึงการเพิ่มช่องว่างหรือบางสิ่งบางอย่างจนกว่าขนาดไฟล์จะเป็นทวีคูณของความยาวบันทึก

และนี่คือผลลัพธ์ของฉัน:

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

ฉันเคยเห็นโค้ดที่รวมpow(2, n)กับการดำเนินการบิต
dan04

4

บางส่วนไม่สามารถทดสอบได้ง่ายจากภายใน C เนื่องจากโปรแกรมมีแนวโน้มที่จะเกิดข้อผิดพลาดในการใช้งานโดยที่สมมติฐานไม่ได้


"สามารถดำเนินการใด ๆ กับตัวแปรที่มีค่าตัวชี้ได้เพียง แต่ต้องมีค่าตัวชี้ที่ถูกต้องหากคุณยกเลิกการอ้างอิง"

void noop(void *p); /* A no-op function that the compiler doesn't know to optimize away */
int main () {
    char *p = malloc(1);
    free(p);
    noop(p); /* may crash in implementations that verify pointer accesses */
    noop(p - 42000); /* and if not the previous instruction, maybe this one */
}

เช่นเดียวกับประเภทอินทิกรัลและจุดลอยตัว (นอกเหนือจากunsigned char) ซึ่งได้รับอนุญาตให้มีการแทนกับดัก


"การคำนวณจำนวนเต็มล้อมรอบดังนั้นโปรแกรมนี้จึงพิมพ์จำนวนเต็มลบจำนวนมาก"

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

(C89 เท่านั้น) "ตกลงมาจากจุดสิ้นสุดmain"

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

2
เป็นตัวอย่างที่เป็นรูปธรรม: เมื่อรวบรวมด้วยgcc -ftrapv -Oผลลัพธ์จะWe like to think that:ตามมาด้วยAborted
caf

@caf: "ตัวเลือกนี้สร้างกับดักสำหรับการเซ็นชื่อล้นในการบวกการลบการคูณ" ยินดีที่ได้รู้จักขอบคุณ
Gilles 'SO- หยุดชั่ว'

1
คนสุดท้ายเป็น ok ใน C ++ (98, 03 และ 0x) เช่นกันและโดยปริยายกลับ 0
jalf

ซึ่งน่ารังเกียจเพราะก่อน ANSI C อนุญาตสิ่งนี้และ C99 ก็ทำเช่นกัน
Joshua

@ Joshua: AFAIK ไม่มีความแตกต่างระหว่าง pre-ANSI C และ C89 เมื่อกลับจากmainไม่มีค่า: โปรแกรมถูกต้อง แต่ส่งคืนสถานะการสิ้นสุดที่ไม่ได้กำหนด (C89 §2.1.2.2) ด้วยการใช้งานจำนวนมาก (เช่น gcc และคอมไพเลอร์ยูนิกซ์รุ่นเก่า) คุณจะได้รับสิ่งที่อยู่ในทะเบียนที่แน่นอน ณ จุดนั้น โดยทั่วไปโปรแกรมจะทำงานได้จนกว่าจะใช้ใน makefile หรือสภาพแวดล้อมอื่น ๆ ที่ตรวจสอบสถานะการสิ้นสุด
Gilles 'SO- หยุดชั่ว'

4

สมมติฐานการพกพาแบบคลาสสิกยังไม่มีความหมาย

  • สมมติฐานเกี่ยวกับขนาดของอินทิกรัลประเภท
  • endianness

4
"Endianness" รวมถึง "มี endianness": มีเครื่องจักรระดับกลางและมาตรฐานอนุญาตให้มีสิ่งแปลก ๆ เช่นการจัดเก็บshortค่า fedcab9876543210 (ซึ่งเป็นเลขฐานสอง 16 หลัก) เป็นสองไบต์ 0248ace และ fdb97531
Gilles 'SO- หยุดชั่ว'

ใช่ endianess แน่นอนรวมถึง endian ผสม / กลางและใหญ่และน้อย หากคุณไปที่ฮาร์ดแวร์ที่กำหนดเองคุณอาจมีความพยายามที่คุณชอบบนรถบัสใดก็ได้
jk.

Middle endian เรียกว่า PDP endian Gilles ประกาศบางสิ่งที่แปลกกว่านั้นแม้ว่าจะทำให้ปวดหัวในการใช้ TCP / IP
Joshua

@Gilles: middle-endian ... ฉันดีใจมากที่ไม่ได้พัฒนาในเรื่องนั้น (แต่ตอนนี้ฉันจะถูกขอให้ทำโครงการเครือข่ายระดับกลางฉันแน่ใจ) ...
พอลนาธาน

ARM FPE ใช้คู่ผสมระดับกลางซึ่งถูกเก็บไว้เป็นคู่ <high ​​quad> <low quad> แต่การเรียงลำดับของบิตในแต่ละรูปสี่เหลี่ยมนั้นผิดวิธี (โชคดีที่ ARM VFP ไม่ทำสิ่งนี้อีกแล้ว)
David Given

4
  • ข้อผิดพลาดในการแยกแยะเนื่องจากการแสดงจุดลอยตัว ตัวอย่างเช่นหากคุณใช้สูตรมาตรฐานในการแก้สมการกำลังสองหรือความแตกต่าง จำกัด ของอนุพันธ์โดยประมาณหรือสูตรมาตรฐานในการคำนวณผลต่างความแม่นยำจะหายไปเนื่องจากการคำนวณความแตกต่างระหว่างตัวเลขที่เหมือนกัน อัลกอริทึมGaußในการแก้ปัญหาระบบเชิงเส้นไม่ดีเนื่องจากข้อผิดพลาดในการปัดเศษสะสมดังนั้นจึงใช้การสลายตัวของ QR หรือ LU การสลายตัวของ Cholesky SVD และอื่น ๆ การเพิ่มตัวเลขจุดลอยตัวจะไม่เชื่อมโยง มีค่า denormal, infinite และ NaN a + b - ab .

  • สตริง: ความแตกต่างระหว่างอักขระจุดรหัสและหน่วยรหัส วิธีการติดตั้ง Unicode บนระบบปฏิบัติการต่างๆ การเข้ารหัส Unicode การเปิดไฟล์ที่มีชื่อไฟล์ Unicode โดยพลการไม่สามารถทำได้ด้วย C ++ แบบพกพา

  • เงื่อนไขการแข่งขันแม้ว่าจะไม่มีเธรด: หากคุณทดสอบว่ามีไฟล์อยู่หรือไม่ผลลัพธ์อาจไม่ถูกต้องได้ตลอดเวลา

  • ERROR_SUCCESS = 0


4

รวมการตรวจสอบขนาดจำนวนเต็ม คนส่วนใหญ่คิดว่า int ใหญ่กว่า short นั้นใหญ่กว่า char อย่างไรก็ตามสิ่งเหล่านี้อาจเป็นเท็จ:sizeof(char) < sizeof(int); sizeof(short) < sizeof(int); sizeof(char) < sizeof(short)

รหัสนี้อาจล้มเหลว (ล้มเหลวในการเข้าถึงที่ไม่ตรงแนว)

unsigned char buf[64];

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

รหัสนี้จะล้มเหลวใน C ++ หรือไม่? IIRC การร่ายพอยน์เตอร์ระหว่างประเภทที่ไม่เกี่ยวข้องกันถือเป็นเรื่องผิดกฎหมายยกเว้นสำหรับถ่าน * ซึ่งสามารถโยนเป็นประเภทใดก็ได้ (หรือเป็นวิธีอื่น?)
rmeador

1
คุณสามารถทำได้int *p = (int*)&buf[1];ใน c ++ ผู้คนก็คาดหวังว่าจะได้ผลเช่นกัน
เลขที่

@nos ใช่นั่นอาจล้มเหลว แต่ความล้มเหลวคือความผิดพลาดดังนั้นโปรแกรมของเขาจึงไม่สามารถทดสอบได้ :(
Joshua

1
sizeof(char) < sizeof(int)ต้องระบุ. ตัวอย่างเช่น fgetc () ส่งกลับค่าของอักขระเป็นถ่านที่ไม่ได้ลงชื่อแปลงเป็น int หรือEOFซึ่งเป็นค่าลบ unsigned charอาจไม่มี padding bits ดังนั้นวิธีเดียวที่ทำได้คือทำให้ int มีขนาดใหญ่กว่า char นอกจากนี้ (เวอร์ชันส่วนใหญ่) ข้อมูลจำเพาะ C ต้องการให้เก็บค่าใด ๆ จากช่วง -32767..32767 ใน int
jilles

@illes ยังมี DSP ที่มีอักขระ 32 บิตและอินเทอร์ 32 บิต
เลขที่

3

สองสิ่งเกี่ยวกับประเภทข้อมูลในตัว:

  • charและsigned charเป็นจริงทั้งสองประเภทที่แตกต่าง (เหมือนintและsigned intที่อ้างถึงเดียวกันชนิดจำนวนเต็มลงนาม)
  • จำนวนเต็มที่ลงนามไม่จำเป็นต้องใช้ส่วนเติมเต็มสองส่วน ส่วนประกอบและเครื่องหมาย + ขนาดของคนเป็นตัวแทนที่ถูกต้องของจำนวนลบ นี้จะทำให้การดำเนินงานที่เกี่ยวข้องกับบิตตัวเลขที่ติดลบการดำเนินงานที่กำหนดไว้
  • หากคุณกำหนดจำนวนเต็มนอกช่วงให้กับตัวแปรจำนวนเต็มที่มีการเซ็นชื่อพฤติกรรมจะถูกกำหนดให้ใช้งานได้
  • ใน C90 -3/5สามารถคืนค่า0หรือ-1. การปัดเศษเข้าหาศูนย์ในกรณีที่ตัวถูกดำเนินการหนึ่งตัวเป็นค่าลบจะรับประกันเฉพาะใน C99 ขึ้นไปและ C ++ 0x ขึ้นไป
  • ไม่มีการรับประกันขนาดที่แน่นอนสำหรับชนิดในตัว มาตรฐานครอบคลุมเฉพาะข้อกำหนดขั้นต่ำเช่นintมีอย่างน้อย 16 บิต a longมีอย่างน้อย 32 บิต a long longมีอย่างน้อย 64 บิต floatสามารถอย่างน้อย 6 แทนมากที่สุดที่สำคัญตัวเลขทศนิยมได้อย่างถูกต้อง doubleสามารถอย่างน้อย 10 แทนที่สำคัญที่สุดตัวเลขทศนิยมได้อย่างถูกต้อง
  • IEEE 754 ไม่ได้บังคับสำหรับการแสดงตัวเลขทศนิยม

เป็นที่ยอมรับในเครื่องส่วนใหญ่เราจะมีส่วนเสริมสองตัวและ IEEE 754 ลอยตัว


ฉันสงสัยว่ามีค่าอะไรบ้างในการกำหนดจำนวนเต็มนอกช่วงที่กำหนดให้นำไปใช้งานแทนที่จะเป็นพฤติกรรมที่ไม่ได้กำหนด ในบางแพลตฟอร์มข้อกำหนดดังกล่าวจะบังคับให้คอมไพเลอร์สร้างโค้ดพิเศษสำหรับint mult(int a,int b) { return (long)a*b;}[เช่นถ้าintเป็น 32 บิต แต่รีจิสเตอร์และlongเป็น 64] หากไม่มีข้อกำหนดดังกล่าวพฤติกรรม "ตามธรรมชาติ" ของการนำไปใช้งานที่เร็วที่สุดlong l=mult(1000000,1000000);จะถูกกำหนดlให้เท่ากับ1000000000000แม้ว่าจะเป็นค่าที่ "เป็นไปไม่ได้" สำหรับintไฟล์.
supercat

3

แล้วอันนี้ละ:

ไม่มีตัวชี้ข้อมูลใดจะเหมือนกับตัวชี้ฟังก์ชันที่ถูกต้อง

นี่เป็นความจริงสำหรับรุ่นแบนทั้งหมด MS-DOS TINY รุ่นใหญ่และรุ่นใหญ่เท็จสำหรับรุ่น MS-DOS SMALL และเกือบจะเป็นเท็จสำหรับรุ่น MEDIUM และ COMPACT (ขึ้นอยู่กับที่อยู่โหลดคุณจะต้องมี DOS เก่าจริงๆ ทำให้เป็นจริง)

ฉันเขียนแบบทดสอบสำหรับสิ่งนี้ไม่ได้

และที่แย่กว่านั้น: อาจเปรียบเทียบพอยน์เตอร์ที่ส่งถึง ptrdiff_t ได้ สิ่งนี้ไม่เป็นความจริงสำหรับรุ่น MS-DOS LARGE (ความแตกต่างเพียงอย่างเดียวระหว่าง LARGE และ HUGE คือการเพิ่มโค้ดคอมไพเลอร์เพื่อทำให้ตัวชี้เป็นมาตรฐาน)

ฉันไม่สามารถเขียนการทดสอบได้เนื่องจากสภาพแวดล้อมที่ระเบิดอย่างหนักจะไม่จัดสรรบัฟเฟอร์ที่มากกว่า 64K ดังนั้นโค้ดที่แสดงให้เห็นว่ามันขัดข้องบนแพลตฟอร์มอื่น

การทดสอบเฉพาะนี้จะส่งผ่านระบบหนึ่งที่หมดอายุแล้ว (สังเกตว่าขึ้นอยู่กับภายในของ 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

แก้ไข: อัปเดตเป็นเวอร์ชันล่าสุดของโปรแกรม

Solaris-SPARC

gcc 3.4.6 ใน 32 บิต

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

และด้วย SUNStudio 11 32 บิต

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

และด้วย SUNStudio 11 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.
..13 The smallest bits always come first
   but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is strictly left to right
   but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
   but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
   but 'sizeof(size_t)==sizeof(unsigned int)' is false.
From what I can say with my puny test cases, you are 75% mainstream

2

คุณสามารถใช้โหมดข้อความ ( fopen("filename", "r")) เพื่ออ่านไฟล์ข้อความประเภทใดก็ได้

ขณะนี้ควรในการทำงานเพียงแค่ปรับทฤษฎีถ้าคุณยังใช้ftell()ในรหัสของคุณและไฟล์ข้อความของคุณมี UNIX แบบเส้นตอนจบในบางรุ่นของไลบรารีมาตรฐานของ Windows, ftell()มักจะกลับค่าที่ไม่ถูกต้อง วิธีแก้ปัญหาคือใช้โหมดไบนารีแทน ( fopen("filename", "rb"))


1

gcc 3.3.2 บน AIX 5.3 (ใช่เราต้องอัปเดต 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

ข้อสันนิษฐานที่บางคนอาจทำใน C ++ คือ a structถูก จำกัด ไว้ที่สิ่งที่สามารถทำได้ใน C ความจริงก็คือใน C ++ a structเป็นเหมือนข้อclassยกเว้นที่มีทุกอย่างเป็นสาธารณะตามค่าเริ่มต้น

โครงสร้าง C ++:

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


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

protected:
  void Testing2();
};

1

ฟังก์ชันคณิตศาสตร์มาตรฐานในระบบต่างๆไม่ได้ให้ผลลัพธ์ที่เหมือนกัน


1

Visual Studio Express 2010 บน 32 บิต 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

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

โปรดทราบว่า Codepad ไม่มีstddef.hไฟล์. ฉันลบการทดสอบ 9 เนื่องจาก codepad ใช้คำเตือนเป็นข้อผิดพลาด ฉันยังเปลี่ยนชื่อcountตัวแปรเนื่องจากถูกกำหนดไว้แล้วด้วยเหตุผลบางประการ

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

วิธีการเปลี่ยนขวาด้วยปริมาณที่มากเกินไป - เป็นไปตามมาตรฐานหรือคุ้มค่ากับการทดสอบ?

Standard C ระบุลักษณะการทำงานของโปรแกรมต่อไปนี้หรือไม่:

เป็นโมฆะ print_string (ถ่าน * st)
{
  ถ่าน ch;
  ในขณะที่ ((ch = * st ++)! = 0)
    Putch (CH); / * สมมติว่ามีการกำหนดไว้ * /
}
int หลัก (โมฆะ)
{
  print_string ( "Hello");
  กลับ 0;
}

อย่างน้อยหนึ่งคอมไพเลอร์ที่ฉันใช้รหัสนั้นจะล้มเหลวเว้นแต่อาร์กิวเมนต์ของ print_string จะเป็น "char const *" มาตรฐานอนุญาตให้มีข้อ จำกัด ดังกล่าวหรือไม่?

ระบบบางระบบอนุญาตให้ระบบหนึ่งสร้างพอยน์เตอร์ไปยัง 'int ที่ไม่ตรงแนวและระบบอื่น ๆ ไม่ได้ อาจคุ้มค่ากับการทดสอบ


C89 §3.3.7:“ ถ้าค่าของตัวถูกดำเนินการด้านขวาเป็นลบหรือมากกว่าหรือเท่ากับความกว้างเป็นบิตของตัวถูกดำเนินการด้านซ้ายที่เลื่อนขั้นพฤติกรรมจะไม่ได้กำหนดไว้” (ใช้กับทั้งสอง<<และ>>) C99 มีภาษาที่เหมือนกันใน§6.5.7-3
Gilles 'SO- หยุดชั่ว'

นอกเหนือจากputch(ทำไมคุณถึงไม่ใช้มาตรฐานputchar) ฉันไม่เห็นพฤติกรรมที่ไม่ได้กำหนดในโปรแกรมของคุณ C89 §3.1.4ระบุว่า“ ลิเทอรัลสตริงอักขระมี […] พิมพ์ 'array of char'” (หมายเหตุ: ไม่ใช่const) และ“ ถ้าโปรแกรมพยายามแก้ไขสตริงลิเทอรัล […] พฤติกรรมจะไม่ถูกกำหนด” . คอมไพเลอร์คืออะไรและแปลโปรแกรมนี้อย่างไร
Gilles 'SO- หยุดชั่ว'

2
ในค่าคงที่อักขระ C ++ ไม่ใช่อักขระ [] แต่เป็นค่าคงที่ถ่าน [] อย่างไรก็ตาม ... มีใช้มีรูเฉพาะในระบบ type เพื่อให้คุณใช้ค่าคงที่ของสตริงในบริบทที่คาดว่าถ่าน * และไม่ได้รับข้อผิดพลาดประเภท สิ่งนี้นำไปสู่สถานการณ์ที่ print_string ("foo") ใช้งานได้ แต่ print_string ("foo" +0) จะไม่ทำงาน สิ่งนี้สร้างความสับสนอย่างมากโดยเฉพาะในสภาพแวดล้อมที่ไฟล์ C ถูกคอมไพล์โดยใช้คอมไพเลอร์ C ++ ตามค่าเริ่มต้น ช่องนี้ถูกลบออกในคอมไพเลอร์ใหม่ แต่ยังมีรูเก่าอยู่มากมาย AFAIK C99 ยังคงกำหนดค่าคงที่ของสตริงให้เป็น char []
David Given

1
ในคอมไพเลอร์ HiTech สำหรับชุดควบคุม Microchip PIC ตัวชี้ที่ไม่มีคุณสมบัติการจัดเก็บสามารถชี้ไปที่ RAM เท่านั้น ตัวชี้คุณสมบัติ const อาจชี้ไปที่ RAM หรือ ROM พอยน์เตอร์ที่ไม่ผ่านคุณสมบัติ const จะถูกอ้างถึงโดยตรงในโค้ด พอยน์เตอร์ที่ผ่านการรับรอง const ถูกอ้างอิงผ่านรูทีนไลบรารี ขึ้นอยู่กับประเภทของ PIC ตัวชี้ที่ไม่ใช่ const คือ 1 หรือ 2 ไบต์ คนที่มีคุณสมบัติ const คือ 2 หรือ 3 เนื่องจาก ROM มีจำนวนมากกว่า RAM มากการมีค่าคงที่ใน ROM จึงเป็นสิ่งที่ดี
supercat

@David Given: หมายเหตุความคิดเห็นก่อนหน้าของฉันด้วย ฉันชอบคอมไพเลอร์ที่ใช้คุณสมบัติอื่นที่ไม่ใช่ "const" เพื่อแสดงถึงคลาสหน่วยเก็บฮาร์ดแวร์ คอมไพเลอร์ HiTech มีความแปลกประหลาดที่ค่อนข้างน่ารำคาญกับการจัดสรรคลาสพื้นที่จัดเก็บข้อมูล (เช่นรายการข้อมูลที่มี "ขนาดคอมโพเนนต์" เป็นไบต์หรือรายการข้อมูลที่มีขนาดมากกว่า 256 ไบต์จะอยู่ในเซ็กเมนต์ "ใหญ่" รายการข้อมูลอื่น ๆ จะอยู่ใน " bss "เซ็กเมนต์สำหรับโมดูลที่กำหนดไว้รายการ" bss "ทั้งหมดในโมดูลต้องมีขนาดไม่เกิน 256 ไบต์อาร์เรย์ที่สั้นเพียง 256 ไบต์อาจสร้างความรำคาญได้
supercat

0

FYI สำหรับผู้ที่ต้องแปลทักษะ C เป็น Java ต่อไปนี้เป็น gotcha บางส่วน

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

ใน Java ถ่านเป็น 16 บิตและเซ็นชื่อ ไบต์คือ 8 บิตและเซ็นชื่อ

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

long เป็น 64 บิตเสมอการอ้างอิงอาจเป็น 32 บิตหรือ 64 บิต (หากคุณมีแอปมากกว่า 32 GB) โดยทั่วไป JVM 64 บิตจะใช้การอ้างอิง 32 บิต

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

กะจะถูกมาสก์เพื่อให้ i << 64 == i == i << -64, i << 63 == i << -1

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

ByteOrder.nativeOrder () สามารถเป็น BIG_ENDIAN หรือ LITTLE_ENDIAN

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

i = i++ ไม่เคยเปลี่ยนแปลง i

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

ขนาดของคอลเลกชันและอาร์เรย์จะเป็น 32 บิตเสมอไม่ว่า JVM จะเป็น 32 บิตหรือ 64 บิต

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

ถ่านคือ 16 บิตสั้น 16 บิต int 32 บิตและยาว 64 บิต

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.