10个printf,请大家先自己做一遍之后再翻看答案
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));//1
printf("%d\n",sizeof(a+0));//2
printf("%d\n",sizeof(*a));//3
printf("%d\n",sizeof(a+1));//4
printf("%d\n",sizeof(a[1]));//5
printf("%d\n",sizeof(&a));//6
printf("%d\n",sizeof(*&a));//7
printf("%d\n",sizeof(&a+1));//8
printf("%d\n",sizeof(&a[0]));//9
printf("%d\n",sizeof(&a[0]+1));//10
答案解析:
首先第一个printf ,sizeof (数组名) ,数组名单独放在sizeof 操作符中计算的是整个数组的大小,又因为是int类型的数组,有4个元素 ,很容易得出是16个字节。
第二个printf,sizeof(a+0),数组名表示的 是数组首元素的地址,首元素的地址加 0 ,得到的还是首元素的地址, 故就是sizeof 计算首元素的地址,地址的字节大小不论类型,只在乎它的编译环境,64位8个字节32位4个字节。
第三个printf,sizeof(*a), *很数组名a表示的是数组首元素地址,解引用操作,得到首元素1,sizeof (1), 1 是int类型,故得4个字节。
第四个printf,sizeof(a + 1) ,根据第二个printf, 可知 a + 1 是第二个元素的地址,地址的大小不管大小,由编译器决定,故得4 或 8 个字节。
第五个printf , sizeof( a[1]),这就很容易啦, 计算的是第2个元素的大小,第二个元素是int 类型,故答案是4个字节。
第六个printf, sizeof(&a),&a 取的是整个数组的地址,但整个数组的地址仍是地址,得4个字节或8个字节。
第七个printf ,sizeof(*&a),先对a进行取地址,取得的是整个数组的地址, 如果将这个地址存放到指针变量p当中,这个p指针的类型应当是int (*)[],是一个数组指针, 故对这个地址解引用操作,得到的是整个数组,故得答案16。
第八个printf ,sizeof(&a+1),&a 得到的是整个数组的地址,对其加1 ,则越过啦整个数组,但归其本质仍是一个地址,故答案是4 或 8 个字节。
第九个printf , sizeof (&a[0]),[]的优先级高于*,故数组名a 先与[] 结合,a[0] 得到的是第1个元素 ,int类型的1 ,再对其&取地址操作, 得到的是第一个元素的地址,地址的答案就是 4或8个字节。
第十个printf ,sizeof(&a[0] + 1),在第九个printf的基础上,可知&a[0] 是第一个元素的地址, &a[0]+1就是第二元素的地址,但地址还是地址,答案自然就是4或8个字节。
//字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));//1
printf("%d\n", sizeof(arr+0));//2
printf("%d\n", sizeof(*arr));//3
printf("%d\n", sizeof(arr[1]));//4
printf("%d\n", sizeof(&arr));//5
printf("%d\n", sizeof(&arr+1));//6
printf("%d\n", sizeof(&arr[0]+1));//7
答案解析
第一个printf , sizeof (arr),由上题可知,sizeof (数组名),计算的是整个数组的大小,又数组的每个元素是char类型, 故很容易就得到6个字节
第二个printf, sizeof(arr+0) ,arr表示的是数组首元素的地址,arr + 0 还是数组首元素的地址,但地址还是地址,地址大小由编译器决定, 故得到答案4或8个字节。
第三个printf , sizeof(*arr),arr表示的是字符首元素的地址,对其解引用得到的是字符’a’, 对其求大小,固然是1个字节。
第四个printf, sizeof(arr[1]), arr[1] 得到 ‘a ’ ,故答案是1个字节
第五个printf , sizeof(&arr),&arr,对数组名取地址得到的是整个数组的地址,但地址还是地址,不由其类型决定,故答案是4或8个字节
第六个printf, sizeof(&arr+1) , 在第五个printf的基础上,&arr再加1, 得到的是越过整个字符数组的下一个字节的地址,但地址还是地址,答案4或8。
第七个printf , sizeof(&arr[0]+1),&arr[0]取到的是第一个元素的地址,但还是那句话,地址还是地址,不由其类型决定,故答案是4或8个字节
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));//1
printf("%d\n", strlen(arr+0));//2
printf("%d\n", strlen(*arr));//3
printf("%d\n", strlen(arr[1]));//4
printf("%d\n", strlen(&arr));//5
printf("%d\n", strlen(&arr+1));//6
printf("%d\n", strlen(&arr[0]+1));//7
注意,这里输出的是strlen函数,请区别于sizeof操作符,以下源码是strlen库函数的实现。请大家关注3点,第一点传进来的参数是一个char * 类型的指针变量,第二点判断条件是指针遇到’\0’跳出循环,第三点返回值是int 类型, 值为eos - str - 1 ,表示并不把‘\0’计算string字符串长度在内。
size_t __cdecl strlen (const char * str)
{
const char *eos = str;
while( *eos++ ) ;
return( eos - str - 1 );
}
答案解析 :
第一个printf ,arr表示的数组首元素地址 ,当arr传进strlen函数里执行循环体,因为字符串数组本身没有’\0’,故知道指针eos遍历到数组末尾越界还没有停止循环,只有当指针遇到内存中恰好有一块内存是’\0‘,才停止循环,此时因为我们并不知道内存的下一个’\0‘,即我们并不知道指针eos会指向那块内存空间。故返回一个随机值。
第二个printf,arr表示的数组首元素的地址,arr + 0 也还是首元素的地址,传进strlen函数,结果跟第一个一样的结果 , 随机值。
第三个printf, arr是数组首元素地址,对它解引用之后,*arr得到一个’a’ ,因为’a’ 的ASCII码值是97,用二进制表示的是0x00000601,将该值传进strlen函数中,此时指针str将该值当成地址传给eos,eos根据str给定的值访问,将会报访问异常,非法访问,程序出现错误。
第四个printf , 跟第三个printf同样的道理,将arr[1],即第二元素b的传给指针str ,程序将会报错,访问异常。
第五个printf, strlen (&arr) , &arr得到的是整个数组的地址,但是他的值跟数组首元素的地址是一模一样的,故仍然是个随机值
第六个printf, strlen(&arr+1),&arr得到的是整个数组的地址,&arr+1 之后得到的是下一个数组大小相同的地址,尽管这个数组并未定义,但指针就指向那个位置,返回的仍然是个随机值,但这个随机值与第四、第五个随机值有数值关系,相差一个数组大小的字节数,即随机值-6。
第七个printf,取得是第一个元素的地址,&arr[0] + 1 之后是第二个元素的地址,将其传给strlen函数时,得到的值仍然是个随机值,与第四第五的随机值关系是随机值-1。
注意 : 用双引号”abcdef“,与上一组不同的是在字符串的尾部有‘\0‘。
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//1
printf("%d\n", sizeof(arr+0));//2
printf("%d\n", sizeof(*arr));//3
printf("%d\n", sizeof(arr[1]));//4
printf("%d\n", sizeof(&arr));//5
printf("%d\n", sizeof(&arr+1));//6
printf("%d\n", sizeof(&arr[0]+1));//7
答案解析 :
第一个printf ,将arr单独放到sizeof当中 ,计算的是整个字符数组的大小,故得7个字节,别忘啦’\0‘也算一个字节。
第二个printf,arr表示的是arr数组的首元素地址,arr + 0 仍是第一元素的地址,只要是地址,大小由编译器决定得4或8个字节。
第三个printf,arr表示的是数组首元素的地址,若将其存放在一个指针变量当中,该指针变量的类型应是char *类型,对其解引用就是元素a,故答案是1个字节。
第四个printf,arr[1]得到的是第二个char类型元素b,很容易求到他的字节大小1
第五个printf,&arr得到的是整个数组的地址,还是那句话,不管这个地址是什么类型的,他的大小只有4个或8个字节。
第六个printf,&arr + 1 同理得到的是arr后一个同样大小的字符数组的地址,但还是地址,故答案4或8个字节
第七个printf,&arr[0] + 1 表示的是第二个元素b 的地址, 同样是4 或8 个字节。
char arr[] = "abcdef";
printf("%d\n", strlen(arr));//1
printf("%d\n", strlen(arr+0));//2
printf("%d\n", strlen(*arr));//3
printf("%d\n", strlen(arr[1]));//4
printf("%d\n", strlen(&arr));//5
printf("%d\n", strlen(&arr+1));//6
printf("%d\n", strlen(&arr[0]+1));//7
答案解析
第一个printf,根据前面源码代码实现,很容易得到答案6。
第二个printf.arr表示的是数组首元素的地址,arr+0仍然是第一个元素的地址,故答案同第一个printf一样,传给strlen函数一样值,答案6。
第三个printf,arr表示的是数组首元素的地址,对其解引用*arr得到的是字符a ,将其传给strlen 参数str ,程序报错,非法访问。
第四个printf,跟第三个printf一样,得到是第二个字符b,传给strlen,将其二进制的值作为地址传给指针strlen, 程序报错,非法访问。
第五个printf,&arr虽然它取得是整个字符数组的值,但它的值同第一个元素的值是一样的,故答案同第一个printf一样是6个字节。
第六个printf,&arr+1 ,指针越过整个字符数组,取得的是同arr字符数组相同大小的下一数组的地址,故指针不知道在什么地方会停止循环,结果返回随机值。
第七个printf,&arr[0]取得的是第一个元素a的地址,&arr+1就是第二个元素b 的地址。故比第一个printf少1 ,得 5个字节。
char *p = "abcdef";
printf("%d\n", sizeof(p));//1
printf("%d\n", sizeof(p+1));//2
printf("%d\n", sizeof(*p));//3
printf("%d\n", sizeof(p[0]));//4
printf("%d\n", sizeof(&p));//5
printf("%d\n", sizeof(&p+1));//6
printf("%d\n", sizeof(&p[0]+1));//7
答案解析:
第一个printf ,指针就是地址,故指针p的字节大小为4或8个字节。
第二个printf,p指向的是第一个元素a的地址,p+1之后得到的是第二个元素b的地址,但地址还是地址,故得4或8个字节。
第三个printf,p指向的是第一个元素的地址,对其解引用之后,得到的是第一个元素a,而元素a是char类型,故得1个字节。
第四个printf,同第三个printf一样,得到的是元素a,得1个字节。
第五个printf,对指针变量p取地址,取得是二级指针的地址,但二级指针仍然是地址,故得4/8个字节。
第六个printf,在第五的基础上&p + 1,得到的地址可如图所示,得到的是存放指针p地址的相邻的下一个空间。但不管什么地址,字节大小为4/8个字节
第七个printf,p[0]得到的是第一个元素a,又对其取地址,得到的是a的地址,也就是第一个元素的地址,&p[0] + 1 加1之后得到的就是第二个元素的地址,还是那句话,地址始终是地址,得4 / 8 个字节 。
char *p = "abcdef";
printf("%d\n", strlen(p));//1
printf("%d\n", strlen(p+1));//2
printf("%d\n", strlen(*p));//3
printf("%d\n", strlen(p[0]));//4
printf("%d\n", strlen(&p));//5
printf("%d\n", strlen(&p+1));//6
printf("%d\n", strlen(&p[0]+1));//7
第一个printf , p的值是第一个元素a的地址,传给strlen函数后,得到的是字符串的长度6。
第二个printf,p传给strlen函数的是第二个元素b的地址,得到的字符串的长度5。
,第三个printf,p对p解引用,得到的是元素a,将a在内存中的二进制传给strlen 中的char * str 参数时,程序报错,非法访问。
第四个prinf,同第三个printf一样,将a在内存中的二进制的值传给strlen函数,程序报错,非法访问。
第五个printf,&p,得到的一个二级地址,若用指针来存储这地址,应当是char* , 如图7-1,由图易得一个随机值
如图7-2,由图易得一个随机值。
第七个printf,&p[0]得到的是第一个元素a的地址,&p[0]+1之后得到的是第二个元素b的地址,将b的地址传进strlen函数中,得到长度5
//二维数组
int a[3][4] = {0};
printf("%d\n",sizeof(a));//1
printf("%d\n",sizeof(a[0][0]));//2
printf("%d\n",sizeof(a[0]));//3
printf("%d\n",sizeof(a[0]+1));//4
printf("%d\n",sizeof(*(a[0]+1)));//5
printf("%d\n",sizeof(a+1));//6
printf("%d\n",sizeof(*(a+1)));//7
printf("%d\n",sizeof(&a[0]+1));//8
printf("%d\n",sizeof(*(&a[0]+1)));//9
printf("%d\n",sizeof(*a));//10
printf("%d\n",sizeof(a[3]));//11
答案解析 :
第一个printf : a 单独放入sizeof中, 故计算的是整个二维数组的大小,得到答案48个字节。
第二个printf :a[0][0] 是第一行第一列的元素,元素的类型是int型,故sizeof (int) 得 4个字节。
第三个printf: 可以将a[3][4] 看成一个 3个连续的元素个数为4的一维数组,那么a[0] 就是第一个一维数组的数组名,将一维数组的数组名单独放在sizeof中,那么计算就是整个一维数组的字节大小就是 4 * 4 = 16 个字节。
第四个printf: a[0] 表示的是第一行一维数组首元素的地址,故a[0]+1 是第一行第二个元素的地址, 但其仍然是个地址, 得 4/8个字节
第五个printf :由第四个printf可知,a[0]+1 是第一行第二个元素的地址,对其解引用,就得到啦第一行第二个元素, 故得 其大小4 字节。
第六个printf : a表示的是二维数组首元素的地址,即第一行数组的地址, a + 1 表示的是第二行数组的地址,即a[1],故该字节大小为4/8个字节
第七个printf : 由第六个printf知,a + 1 表示的是第二行数组的地址,即a[1]的地址, 若将其用指针将其存起来,指针的类型为int (*) [] ,对它解引用,得到的是整个一维数组元素,故字节大小为 16个字节。
第八个printf: &a[0] 得到的是第一行整个元素的地址,对其加1 ,也就得到啦第二行整个数组的地址,但地址还是地址, 字节大小为== 4/8 ==
第九个printf:在第八的基础上 ,解引用得到的是第二行整个一维数组的元素,字节大小为16。
第十个printf: a 表示二维数组首元素的地址,即第一行一维数组的地址,对其解引用得到的是整个一维数组的元素,故其字节大小 为16。
第十一个printf : a[3] 很明显越界啦, 但程序并不会报错,因为sizeof只需要知道它要计算的元素类型,并不会去访问该内存空间是什么值,故 答案 16个字节。
总结: 数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址。(另外需要提醒一点, 二维数组的数组名
表示的是第一行整个一维数组的地址)。
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
图片解析 :
//程序的结果是什么?
//这里告知结构体的大小是20个字节
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
答案解析:
第一行printf : p为结构体的指针,指针类型为struct Test *,它加1指针地址需要向后移动20个字节,故用%p打印,为0x100014。
第二行printf: p被强转为整形, 故它 加1 就是普通的+ 1 , 用%p 打印就是0x100001。
第三行printf: p被强转成整形指针,故整形指针加1 ,地址向后移动4个字节,用%p打印就是0x100004。
int main()
{
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);
int *ptr2 = (int *)((int)a + 1);
printf( "%x,%x", ptr1[-1], *ptr2);
return 0;
}
#include
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);
return 0;
}
答案解析:
这题主要是考的是逗号表达式,注意{}里面是()小括号,(0,1),(2,3),(4,5),逗号表达式的结果是1 , 2 , 3,将其存入二维数组,如图所示。
int main()
{
int a[5][5];
int(*p)[4];
p = a;
printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
return 0;
}
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)(&aa + 1);
int *ptr2 = (int *)(*(aa + 1));
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
return 0;
}
图片解析:
#include
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
int main()
{
char *c[] = {"ENTER","NEW","POINT","FIRST"};
char**cp[] = {c+3,c+2,c+1,c};
char***cpp = cp;
printf("%s\n", **++cpp);
printf("%s\n", *--*++cpp+3);
printf("%s\n", *cpp[-2]+3);
printf("%s\n", cpp[-1][-1]+1);
return 0;
}