int main()
{
int a[3][4] = { 0 };
printf("%d\n", sizeof(a)); //48 ps:3*4*sizeof(int)
printf("%d\n", sizeof(a[0][0])); //4 ps:a[0][0] - 第一行第一个元素
printf("%d\n", sizeof(a[0])); //16 ps:a[0]可以理解为第一行的数组名
printf("%d\n", sizeof(a[0] + 1)); //4 ps:a[0]作为数组名不是单独放在sizeof内部,是表示首元素的地址
// a[0] + 1表示第一行第二列的地址,因为是地址所以长度为4
printf("%d\n", sizeof(*(a[0]+ 1))); //4 ps:*(a[0]+ 1)是第一行第二个元素
printf("%d\n", sizeof(a + 1)); //4 ps:a表示的是首元素的地址,即第一行的地址,+1就是第二行的地址,因为是地址所以是4
printf("%d\n", sizeof(*(a + 1))); //16 ps:解引用后就是第二行
printf("%d\n", sizeof(&a[0] + 1)); //4 ps:&a[0]是取第一行的地址,+1就是第二行的地址。所以为4
printf("%d\n", sizeof(*(&a[0] + 1)));//16 ps:解引用...
printf("%d\n", sizeof(*a)); //16 ps:a是第一行的地址,解引用就是第一行
printf("%d\n", sizeof(a[3])); //16 ps:见下面
//表达式有两个属性 3+5 a[3]
//1.值属性 8
//2.类型属性 int int [4] so sizoef(a[3]) = 16
//sizeof() 内部的表达式不计算的!
return 0;
}
为了更好理解最后一个代码,可以再看一段代码
int main()
{
short s = 5;
int a = 4;
printf("%d\n", sizeof(s = a + 6)); //2
printf("%d\n", s); //5
return 0;
}
因为sizeof不计算表达式,只看类型属性,所以sizeof(s = a + 6)可以等价为sizeof(s).
好了,现在我们来做一些笔试题吧~
int main()
{
int a[5] = { 1,2,3,4,5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));//2 5
return 0;
}
解释: result - 2 5
* (a + 1) :a是首元素的地址,+1是第二个元素的地址,那它们解引用后就是 2
* (ptr - 1):&a是数组的地址,+1就是指向数组后面一个地址,再强制类型转换下,所以ptr就是地址后面一个元素并且是int*的类型,那么它 -1 (注意是整形指针噢~)就是5的地址了
struct Test
{
int Num;
char* poName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//假设p 的值为0x100000,如下表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
printf("%p\n", p + 0x1);
printf("%p\n", (unsigned long)p + 0x1);
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
解释: result - 0x100014 0x100001 0x100004
第一行:p是指结构体指针地址,那么它 + 1就是加20,即0x100000(十六进制) + 20(十进制) = 0x100014
第二行:unsigned long是整数,那么+1 就是加1,所以为 0x100001
第三行:unsigned int*是个指针,那么+1 是加一个整形,所以+4 为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;
}
解释: result - 2000000,4
ptr1:&a是指a的地址,+1就是a后面一个地址,强制类型转换成int*,那么ptr[-1]是a倒数第三个地址
ptr2:a是首元素地址,那么它+1是第二个地址,再强制类型转换成int,*ptr2就是解引用第二个开始的4个地址
01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 (小端)
| |
ptr1 ptr2
0x 02 00 00 00 0x 00 00 00 04
result: 2000000 4
int main()
{
int a[3][2] = { (0,1),(2,3),(3,5) };
int* p;
p = a[0];
printf("%d", p[0]);
return 0;
}
解释: result - 1
{ (0,1),(2,3),(3,5) } ()里的是逗号表达式,所以可以理解为{1,3,5}
那p = a[0]可以理解为指向a第一个元素的地址,p[0]也是一样的意思,所以输出为1
int main()
{
const char* a[] = { "work","at","alibaba" };
const char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
解释: result - at
pa指向的是a的第一个元素的地址(work),那么pa++指向的就是(at)
那么*pa就是at啦
int main()
{
const char* c[] = { "ENTER","NEW","POINT","FIRST" };
const char** cp[] = { c + 3,c + 2,c + 1,c };
const char*** cpp = cp;
printf("%s\n", **++cpp); //POINT
printf("%s\n", *-- *++cpp + 3); //ER
printf("%s\n", *cpp[-2] + 3); //ST
printf("%s\n", cpp[-1][-1] + 1); //EW
return 0;
}
解释: result - point er st ew
int main()
{
const char* c[] = { "ENTER","NEW","POINT","FIRST" };
const char** cp[] = { c + 3,c + 2,c + 1,c };
const char*** cpp = cp;
printf("%s\n", **++cpp); //POINT
printf("%s\n", *-- *++cpp + 3); //ER
printf("%s\n", *cpp[-2] + 3); //ST
printf("%s\n", cpp[-1][-1] + 1); //EW
return 0;
}
解释: result - POINT ER ST EW
第一行:*++cpp ->cp[c + 2] -> c[point], 那么把它解引用就是point了
第二行:++cpp是指 c + 1(前面加过了),解引用后 -- 变成c
再解引用是指向ENTER中的第四个E的地址,所以结果为ER
第三行:*cpp[-2] + 3可以理解为**(cpp-2)+3 cpp现在指向的是c+1
那么它-2就是指向c+3,再解引用就是FIRST,它再+3就是S的地址,所以结果为ST
第四行:cpp[-1][-1]+1可以理解为*(*(cpp-1)-1)+1,cpp现在是指向c+3,那么它-1指向的是c+2
那么把它解引用后就是POINT的地址,那么它-1就是NEW的地址,再解引用就是NEW 首元素的地址再把它+1就是E的地址,所以输出的结果为EW
如果喜欢我的文章可以点赞收藏下,后面将会有更多优质的文章,希望大家喜欢~