When two pointers are subtracted, both must point to elements of the same array object or just one past the last element of the array object (C Standard, 6.5.6 [ISO/IEC 9899:2011]); the result is the difference of the subscripts of the two array elements. Otherwise, the operation is undefined behavior (see undefined behavior 48).
Similarly, comparing pointers using the relational operators <
, <=
, >=
, and >
gives the positions of the pointers relative to each other. Subtracting or comparing pointers that do not refer to the same array is undefined behavior (see undefined behavior 48 and undefined behavior 53).
"If P points to the last member of array,then P+1 compares higher than P,even though P+1 points outside the array.Otherwise,pointer comparison is undefined".
Comparing two pointers to distinct members of the same struct
object is allowed. Pointers to structure members declared later in the structure compare greater than pointers to members declared earlier in the structure.
Comparing pointers using the equality operators ==
and !=
has well-defined semantics regardless of whether or not either of the pointers is null, points into the same object, or points one past the last element of an array object or function.
编译器报的错是error: incompatible types in assignment
但做左值仍然表示整个数组的存储空间,而不是首元素的存储空间,数组名做左值还有一点特殊之处,
不支持++、赋值这些运算符,但支持取地址运算符&,所以&arr是合法的.
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
int main(
void)
{ char a[ 4][ 3][ 2] = {{{ 'a', 'b'}, { 'c', 'd'}, { 'e', 'f'}}, {{ 'g', 'h'}, { 'i', 'j'}, { 'k', 'l'}}, {{ 'm', 'n'}, { 'o', 'p'}, { 'q', 'r'}}, {{ 's', 't'}, { 'u', 'v'}, { 'w', 'x'}} }; char (*pa)[ 2] = &a[ 1][ 0]; char (*ppa)[ 3][ 2] = &a[ 1]; /* a[1][0]是一个数组,有两个元素,在&a[1][0]这个表达式中,数组名做左值,取整个数组 * 的首地址赋给指针pa.注意,&a[1][0][0] 表示数组a[1][0]的首元素的首地址,而&a[1][0]表示数组a[1][0]的首地址, * 显然这两个地址的数值相同,但这两个表达式的类型是两种不同的指针类型, * 前者的类型是int *,而后者的类型是int (*)[2] ,指针的本质都只是一个内存地址,但指针的类型决定了如pa++此类的表达式跨越的内存字节数 * 以及通过pa可以访问到的内存大小 */ char r1 = (*(pa + 5))[ 1]; /* pa+5 跳过10个字节指向a[2][2],即{'q','r'},(*(pa+5))就表示数组a[2][2] * 所以a[2][2][1]可以用(*(pa+5))[1]来表示 */ char r2 = pa[ 5][ 1]; // (*(pa+5)) 即 pa[5] char r3 = (*(ppa + 1))[ 2][ 1]; char r4 = ppa[ 1][ 2][ 1]; printf( "%c %c\n", pa[ 0][ 0], ppa[ 0][ 0][ 1]); /* *pa 就表示pa 所指向的数组a[1][0],所以取数组的a[1][0][0] 元素可以用表达式(*pa)[0] 。 * 注意到*pa 可以写成pa[0] ,所以(*pa)[0] 这个表达式也可以改写成pa[0][0] */ printf( "%c %c %c %c\n", r1, r2, r3, r4); return 0; } |
1
2 3 4 5 6 7 8 9 |
#include <stdio.h>
int main( void) { int a[ 5]; printf( "%x\n", a); //0Xbfe2e100 printf( "%x\n", a+ 1); // 0Xbfe2e104 printf( "%x\n", &a); // 0Xbfe2e100 printf( "%x\n", &a+ 1); //0Xbfe2e114 } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
struct test{
int i; short c; char *p; char s[ 10]; //char s[0] 同理 }; int main( void) { struct test *pt= NULL; // 下面打印相对地址,分别为0,4,8,c printf( "&i = %x\n", &pt->i); //因为操作符优先级,我没有写成&(pt->i) printf( "&c = %x\n", &pt->c); printf( "&p = %x\n", &pt->p); printf( "&s = %x\n", pt->s); //等价于 printf("%x\n", &(pt->s) ); // crash, 此时结构体尚未存在,何况成员p // printf("the str is %s\n", pt->p); return 0; } |
(1). 一是使预处理的速度变慢了,要处理很多本来不需要处理的头文件。
(3). 三是头文件里有些代码不允许重复出现,虽然变量和函数允许多次声明(只要不是多次定义就行),但头文件里有些代码是不
1
2 3 4 5 6 7 8 9 10 |
#include <stdio.h>
int main( int argc, char *argv[]) { unsigned short a = - 1; short b = a; printf( "%d %d",a,b); return 0; } //结果:65535 -1 |
C语言中常量整数 -1的补码表示为0xFFFFFFFF,截取后面16位FFFF赋值给 变量a(unsigned short),此时 a = 0xFFFF(最高位不是符号位)。
a又将0xFFFF,直接赋值给short b,此时 b = 0xFFFF(最高位是符号位)。
执行printf("%d %d",a,b);的时候,要将 a和b的值先转换为int型:
a没有符号所以转为int型为0x0000FFFF(补码);
b有符号转换为int型为0xFFFFFFFF(补码);
十进制输出值 65535 -1(原码)。
* signed类型转换为unsigned/signed类型的时候,也是将补码直接复制到低位,如果位数不够在高位补1还是补0取决于原来的符号位,这称为符号扩展(Sign Extension)。
例2:下面的代码输出的结果是什么,并简单分析结果。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <stdio.h>
//无符号数与有符号数相加 int main( int argc, char **argv) { unsigned int a = 6; int b = - 12; if(a+b > 0) { printf( "dsds\n"); printf( "a+b=%d\n" , a+b); printf( "a+b=%p\n" , ( void *)(a+b)); } else { printf( "ssss\n"); printf( "a+b=%d\n" , a+b); printf( "a+b=%p\n" , ( void *)(a+b)); } return 0; } |
输出:
dsdsa+b=-6
a+b=0xFFFFFFFA
foobar1(void)和foobar2()是有不同原型的的。我们可以在《ISO/IEC 9899》的C语言规范找到下面两段关于函数声明的描述
10.The special case of an unnamed parameter of type void as the only item in the list specifies that the function has no parameters
14.An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.124)
上面两段话的意思就是:foobar1(void)是没有参数,而foobar2()等于foobar2(…)等于参数类型未知。如果我们这样调用 foobar2(33, ch); 是可以编译通过的。
对于整型溢出,分为无符号整型溢出和有符号整型溢出。
对于unsigned整型溢出,C的规范是有定义的——“溢出后的数会以2^(8*sizeof(type))作模运算”,也就是说,如果一个unsigned char(1字符,8bits)溢出了,会把溢出的值与256求模。例如:
unsigned char x = 0xff;
printf("%d\n", ++x);
上面的代码会输出:0 (因为0xff + 1是256,与2^8求模后就是0)
对于signed整型的溢出,C的规范定义是“undefined behavior”,也就是说,编译器爱怎么实现就怎么实现。对于大多数编译器来说,算得啥就是啥。比如:
signed char x =0x7f; //注:0xff就是-1了,因为最高位是1也就是负数了
printf("%d\n", ++x);
上面的代码会输出:-128,因为0x7f + 0×01得到0×80,也就是二进制的1000 0000,符号位为1,负数,后面为全0,就是负的最小数,即-128。