What belongs in an educational tool to demonstrate the unwarranted assumptions people make in C/C++?

我想为 SO 准备一个小小的教育工具,它可以帮助初学者(和中级)程序员识别和挑战他们在 C、 C + + 和他们的平台中的不必要的假设。

例子:

  • “整数缠绕”
  • "everyone has ASCII"
  • “我可以把函数指针储存在虚空中”

我认为一个小型的测试程序可以在不同的平台上运行,这些平台运行“合理的”假设,根据我们在 SO 中的经验,这些假设通常是由许多缺乏经验/经验不足的主流开发人员做出的,并记录他们在不同机器上的崩溃方式。

The goal of this is not to prove that it is "safe" to do something (which would be impossible to do, the tests prove only anything if they break), but instead to demonstrate to even the most uncomprehending individual 如果最不明显的表达式具有未定义的或实现定义的行为,那么它在不同的机器上是如何破坏的。.

为了实现这一目标,我想问你们:

  • 这个想法怎样才能改进呢?
  • 哪些测试是好的,它们应该是什么样子的?
  • 您是否会在您可以使用的平台上运行测试并发布结果,以便我们最终得到一个平台数据库,它们之间的差异以及为什么允许这种差异?

下面是测试玩具的最新版本:

#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。

UPDATE: I've created a github repo for this, the file is "gotcha.c":

请在这里回答补丁或新的想法,以便他们可以在这里讨论或澄清。那我就把它们合并到 Gotcha.c 里。

9834 次浏览

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

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

Open644.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 (vice emulator)

alt text


我稍后会更新这些:


Windows XP 上的 Borland C + + Builder 6.0

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

(必须编译为 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预释)

—— 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位 Windows 使用 LLP64模型: intlong都被定义为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位的 PC 机,但是代码和数据在不同的地址空间中。更大的 Atmegas 有一个22位 PC!.


MacOSX 10.6上的 gcc 4.2.1,使用-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

  • 由于浮点表示引起的离散化错误。例如,如果你使用标准公式来解二次方程,或者用有限差分来近似导数,或者用标准公式来计算方差,由于计算相似数字之间的差异,精度将会丢失。求解线性系统的高斯算法是不好的,因为舍入误差累积,因此使用 QR 或 LU分解、乔列斯基分解、奇异值分解等。浮点数的加法不是关联的。有反常值、无限值和 NaN 值。a + B-aB

  • 字符串: 字符、代码点和代码单元之间的差异。如何在各种操作系统上实现 Unicode; Unicode 编码。使用 C + + 以可移植的方式打开任意 Unicode 文件名的文件是不可能的。

  • 竞争条件,即使没有线程: 如果您测试一个文件是否存在,结果可能在任何时候变得无效。

  • ERROR_SUCCESS = 0

您需要包括人们所做的 ++--假设。

a[i++]= i;

例如,在句法上是合法的,但是由于需要推理的事情太多而产生不同的结果。

任何包含 ++(或 --)和一个多次出现的变量的语句都是一个问题。

非常有趣!

我能想到的其他可能有用的信息:

  • do function pointers and data pointers exist in the same address space? (Breaks in Harvard architecture machines like DOS small mode. Don't know how you'd test for it, though.)

  • 如果取一个 NULL 数据指针并将其强制转换为适当的整数类型,它的数值是否为0?(在一些非常古老的机器上断裂——参见 http://c-faq.com/null/machexamp.html。)函数指针也一样。而且,它们可能是不同的值。

  • 增加一个指针超过它对应的存储对象的末尾,然后再回来,是否会导致有意义的结果?(我不知道有什么机器会出现这种情况,但是我相信 C 规范不允许你在指针不指向(a)数组的内容或者(b)数组后面的元素或者(c) NULL 的情况下使用 好好想想。见 http://c-faq.com/aryptr/non0based.html)

  • does comparing two pointers to different storage objects with < and > produce consistent results? (I can imagine this breaking on exotic segment-based machines; the spec forbids such comparisons, so the compiler would be entitled to compare the offset part of the pointer only, and not the segment part.)

我再想想。

Edit: Added some clarifying links to the excellent C FAQ.

其中一些不能很容易地从 C 内部进行测试,因为程序很可能在假设不成立的实现上崩溃。


"It's ok to do anything with a pointer-valued variable. It only needs to contain a valid pointer value if you dereference it."

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

编辑: 更新到程序的最后版本

Solaris-SPARC

32位的 gcc 3.4.6

We like to think that:
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09 overshifting is *always* okay
but '(1<<BITS_PER_INT)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits always come first
but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..19-3 int<long
but 'sizeof(int)<sizeof(long)' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 72% mainstream

64位的 gcc 3.4.6

We like to think that:
..05 int has the size of pointers
but 'sizeof(int)==sizeof(void*)' is false.
..08 overshifting is okay
but '(1<<bits_per_int)==0' is false.
..09 overshifting is *always* okay
but '(1<<BITS_PER_INT)==0' is false.
..09a minus shifts backwards
but '(t=-1,(15<<t)==7)' is false.
..13 The smallest bits always come first
but '(t=0x1234,0x34==*(char*)&t)' is false.
..14 i++ is strictly left to right
but '(i=0,a[i++]=i,a[0]==1)' is false.
..15 structs are packed
but 'sizeof(char_int)==(sizeof(char)+sizeof(int))' is false.
..17 size_t is unsigned int
but 'sizeof(size_t)==sizeof(unsigned int)' is false.
..22 floating point is always IEEE
but 'STDC_IEC_559_is_defined' is false.
From what I can say with my puny test cases, you are 68% mainstream

and with 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

和 SUNStudio 1164位

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

经典的可移植性假设还没有被提及

  • 关于积分类型大小的假设
  • Endianness

我认为你应该努力区分两类非常不同的“不正确”假设。好的一半(右移和符号扩展,ASCII 兼容编码,内存是线性的,数据和函数指针是兼容的,等等)对于 大部分 C 程序员来说是相当合理的假设,甚至可能包括在标准的一部分,如果 C 是今天设计的,如果我们没有遗留的 IBM 垃圾祖父级。另一半(与内存别名有关的事情,当输入和输出内存重叠时库函数的行为,32位假设,如指针适合于 int,或者你可以在没有原型的情况下使用 malloc,调用约定对于可变和非可变函数是相同的,...)要么与现代编译器想要执行的优化冲突,要么与迁移到64位机器或其他新技术冲突。

子表达式的计算顺序,包括

  • 函数调用和
  • 运算符的操作数(例如,+-=*/) ,但不包括:
    • the binary logical operators (&& and ||),
    • 条件运算符(?:) ,以及
    • 逗号运算符(,)

未指明

比如说

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

包括整数大小的检查。 大多数人认为 int 是 大于空头就是大于空头 然而,这些可能都是 False: < code > 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 的课本

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

作为样本问题。对于一个学生来说,它失败了,因为 sizeof产生的是 size_t类型的值,而不是 int,在这个实现中,int是16位的,size_t是32,而且它是 big-endian。(平台是基于680x0的 Macintosh 上的 Lightspeed C。我说那是很久以前的事了。)

这个怎么样:

任何数据指针都不可能与有效的函数指针相同。

对于所有的平面模型,MS-DOS TINY,LARGE,和 HUGE 模型都是正确的,对于 MS-DOS SMALL 模型是错误的,对于 MEDIUM 和 COMPACT 模型几乎总是错误的(取决于加载地址,你将需要一个真正老的 DOS 来实现它)。

我不能为此编写测试

更糟糕的是: 可以比较强制转换为 ptrdiff _ t 的指针。这对于 MS-DOS LARGE 模型是不正确的(LARGE 和 HUGE 之间唯一的区别是 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.");

关于内置数据类型的一些事情:

  • char and signed char are actually two distinct types (unlike int and signed int which refer to the same signed integer type).
  • signed integers are not required to use two's complement. Ones's complement and sign+magnitude are also valid representations of negative numbers. This makes bit operations involving negative numbers 实现定义.
  • 如果将超出范围的整数赋给有符号整数变量,则行为为 实现定义
  • 在 C90中,-3/5可以返回 0-1。如果一个操作数为负,则向零舍入只能在向上的 C99和向上的 C + + 0x 中得到保证。
  • 内置类型没有精确的大小保证。该标准只涵盖最低要求,如 int至少16位,long至少32位,long long至少64位。float至少可以正确地表示6个最高有效小数位。double至少可以正确地表示10个最高有效小数位。
  • IEEE754对于表示浮点数并不是强制性的。

不可否认,在 大部分机器上我们将有两个补充和 IEEE 754浮点。

AIX 5.3上的 gcc 3.3.2(是的,我们需要更新 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

有些人可能会在 C + + 中做的一个假设是,struct仅限于它能在 C 中做的事情。事实上,在 C + + 中,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();
};

不同系统上的标准数学函数不会给出相同的结果。

32位 x86上的 VisualStudioExpress2010。

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

Via Codepad.org(< code > C + + : g + + 4.1.2 标志:-O-std = c + + 98-迂腐-错误-致命-错误-Werror-Wall-Wtra-Wno-miss-field-initializer-Wwrite-string-Wno-depreated-Wno-used-Wno-non-virtual-dtor-Wno-variad-Macros-fmessage-length = 0- ftemplate-deep-128-fno-merge-Constants-fno-nonansi-builtins-fno-gnu-keywords-fno-elide-structors-fstric- 别名-fstack-protected-all-Wvalid- pch )。译注:

Note that Codepad did not have stddef.h. I removed test 9 due to codepad using warnings as errors. I also renamed the count variable since it was already defined for some reason.

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

Here's a fun one: What's wrong with this function?

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

[回答(rot13) : Inevnqvp nethzragf borl gur byq X & E cebzbgvba ehyrf,juvpu zrnaf lbnaabg hfr‘ sybng’(be‘ pune’be‘ fubeg’) va in _ net!如果没有这个问题,我们就无法找到答案。(TPP qbrf rzvg n jneavat,gubhtu.)]

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

可以使用 text-mode (fopen("filename", "r"))读取任何类型的文本文件

虽然这个 应该在理论上工作得很好,但是如果你也在代码中使用 ftell(),并且你的文本文件有 UNIX 风格的行结束符,在某些版本的 Windows 标准库中,ftell()经常会返回无效的值。解决方案是改用二进制模式(fopen("filename", "rb"))。

过量的右移怎么样——这是标准所允许的,还是值得测试的?

Does Standard C specify the behavior of the following program:

void print_string(char *st)
{
char ch;
while((ch = *st++) != 0)
putch(ch);  /* Assume this is defined */
}
int main(void)
{
print_string("Hello");
return 0;
}

在我使用的至少一个编译器上,除非 print _ string 的参数是“ char 康斯特 *”,否则该代码将失败。标准是否允许这样的限制?

有些系统允许生成指向未对齐 int 的指针,而有些系统则不允许。

顺便说一句,对于那些需要将 C 技能转换成 Java 的人来说,这里有一些陷阱。

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

In Java, char is 16-bit and signed. byte is 8-bit and signed.

/* 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)64位 JVM 通常使用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));

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

无论 JVM 是32位还是64位,集合和数组的大小总是32位的。

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位,short 为16位,int 为32位,long 为64位。