九、指针和数组笔试题解析
数组名:
通常是数组首元素地址但是有两个例外
1.sizeof(数组名),这里数组名表示整个数组,计算的是整个数组的大小
2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址
sizeof时一个操作符计算的是对象所占内存的大小,单位是字节,不在乎内存中存放的是什么只在乎内存大小
strlen是库函数,求字符串长度,从给定的地址向后访问字符,统计\0之前出现的字符个数
1.一维数组
int main()
{
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
//a表示整个数组,计算的是整个数组的大小4*4=16
printf("%d\n",sizeof(a+0));
//sizeof里不是数组名,a是数组首元素地址,a+0是1的地址,地址的大小4或8看几位平台
printf("%d\n",sizeof(*a));
//a表示数组首元素地址,*a表示数组的第一个元素,计算的是第一个元素的大小是4
printf("%d\n",sizeof(a+1));
//a表示数组首元素地址,a+1数组第二个元素地址,计算的是数组中第二个元素的地址的大小4或8
printf("%d\n",sizeof(a[1]));
//a[1]数组中的第二个元素,求第二个元素的大小4
printf("%d\n",sizeof(&a));
//&a取出的是数组的地址,数组的地址也是地址,是地址4或8字节
printf("%d\n",sizeof(*&a));
//&a取的是整个数组的地址,解引用得到的是整个数组,计算的是整个数组的大小4*4=16
printf("%d\n",sizeof(&a+1));
//&a取的是整个数组的地址,数组的地址+1跳过整个数组,还是地址大小4或8
printf("%d\n",sizeof(&a[0]));
//&a[0]取的是数组第一个元素的地址4/8
printf("%d\n",sizeof(&a[0]+1));
//&a[0]+1取的是数组第二个元素的地址4/8
}
2.字符数组
int main()
{
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
//计算的是整个数组的大小6*1=6
printf("%d\n", sizeof(arr+0));
//sizeof里不是数组名,arr是数组首元素地址,arr+0还是首元素地址,地址的大小4或8
printf("%d\n", sizeof(*arr));
//arr是首元素地址,解引用是首元素,首元素的大小是1
printf("%d\n", sizeof(arr[1]));
//arr[1]是数组第二个元素大小是1
printf("%d\n", sizeof(&arr));
//&arr是整个数组的地址,是地址4/8
printf("%d\n", sizeof(&arr+1));
//&arr是数组的地址,&arr+1是从数组的地址开始向后跳过了整个数组得到的地址还是地址,是地址4/8
printf("%d\n", sizeof(&arr[0]+1));
//&arr[0]是数组首元素地址,&arr[0]+1是数组第二个元素的地址是地址4/8
printf("%d\n", strlen(arr));
//arr是数组首元素地址,arr数组中没有'\0'strlen会继续向后找'\0'所以是随机值
printf("%d\n", strlen(arr+0));
//arr+0还是数组首元素地址,没有'\0',随机值
printf("%d\n", strlen(*arr));
//arr是首元素地址,*arr是数组的首元素,'a'的ASCII是97,97被当成地址从97向后数直到/0
//可能非法访问,错误代码
printf("%d\n", strlen(arr[1]));
//arr[1]是'b',ASCII 98同上
printf("%d\n", strlen(&arr));
//&arr数组的地址和数组首元素地址相同,随机值
printf("%d\n", strlen(&arr+1));
//从数组后面开始数,随机值
printf("%d\n", strlen(&arr[0]+1));
//&arr[0]+1从第二个元素地址开始往后数,随机值
char arr[] = "abcdef";//a b c d e f \0
printf("%d\n", sizeof(arr));
//计算的是整个数组的大小7
printf("%d\n", sizeof(arr+0));
//计算的是首元素的地址,是地址4/8
printf("%d\n", sizeof(*arr));
//*arr是数组首元素,计算a的大小为1
printf("%d\n", sizeof(arr[1]));
//arr[1]数组第二个元素大小为1
printf("%d\n", sizeof(&arr));
//&arr是数组的地址,是地址4/8
printf("%d\n", sizeof(&arr+1));
//&arr是数组的地址,数组的地址+1跳过整个数组是\0后面的地址,是地址4/8
printf("%d\n", sizeof(&arr[0]+1));
//&arr[0]是数组首元素地址+1是数组第二个元素地址,是地址4/8
printf("%d\n", strlen(arr));
//strlen统计\0之前的字符个数6
printf("%d\n", strlen(arr+0));
//从首元素向后数6
printf("%d\n", strlen(*arr));
//错误,传97非法访问
printf("%d\n", strlen(arr[1]));
//错误,传98非法访问
printf("%d\n", strlen(&arr));
//从首元素向后数6
printf("%d\n", strlen(&arr+1));
//从\0后面开始数,随机值
printf("%d\n", strlen(&arr[0]+1));
//从第二个元素地址向后数5
char *p = "abcdef";a b c d e f \0
//p里存放的是a的地址
printf("%d\n", sizeof(p));
//p里是地址,计算的是指针变量的大小4/8
printf("%d\n", sizeof(p+1));
//p+1是第二个元素的地址,是地址4/8
printf("%d\n", sizeof(*p));
//*p是首元素大小为1
printf("%d\n", sizeof(p[0]));
//p[0]是首元素大小为1
printf("%d\n", sizeof(&p));
//&p取的指针变量p的地址,是地址4/8
printf("%d\n", sizeof(&p+1));
//&p+1是跳过p之后的地址,是地址4/8
printf("%d\n", sizeof(&p[0]+1));
//&p[0]第一个元素的地址+1是第二个元素的地址,是地址4/8
printf("%d\n", strlen(p));
//p是首元素地址,从首元素开始数6
printf("%d\n", strlen(p+1));
//p+1是第二个元素地址,从第二个元素开始数5
printf("%d\n", strlen(*p));
//*p传的是a,97,非法访问,错误代码
printf("%d\n", strlen(p[0]));
//p[0]首元素a,97,非法访问,错误代码
printf("%d\n", strlen(&p));
//从p的地址开始向后数,随机值
printf("%d\n", strlen(&p+1));
//从跳过p的地址开始向后数随机值
printf("%d\n", strlen(&p[0]+1));
//从第二个元素开始向后数5
}
3.二维数组
int main()
{
int a[3][4] = {0};
printf("%d\n",sizeof(a));
//计算整个数组的大小3*4*4=48
printf("%d\n",sizeof(a[0][0]));
//第一行第一个元素大小4
printf("%d\n",sizeof(a[0]));
//第一行元素大小4*4=16
printf("%d\n",sizeof(a[0]+1));
//a[0]作为第一行数组名并没有单独放在sizeof内部a[0]是第一行第一个元素地址,a[0]+1是第一行第二个元素地址,是地址4/8
printf("%d\n",sizeof(*(a[0]+1)));
//第一行第二个元素4
printf("%d\n",sizeof(a+1));
//a表示首元素的地址,首元素地址就是第一行地址,a+1是第二行的地址,是地址4/8
printf("%d\n",sizeof(*(a+1)));
//第二行元素大小4*4=16
printf("%d\n",sizeof(&a[0]+1));
//第二行地址,是地址4/8
printf("%d\n",sizeof(*(&a[0]+1)));
//第二行元素大小4*4=16
printf("%d\n",sizeof(*a));
//第一行元素大小4*4=16
printf("%d\n",sizeof(a[3]));
//a[3]的类型是一行的数组名,一行数组名的大小是4*4=16
}
十、指针笔试题
1.第一题
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);
printf( "%d,%d", *(a + 1), *(ptr - 1));//2,5
}
2.第二题
//结构体的大小是20个字节,x86环境
struct Test
{
int Num;
char *pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//已知,结构体Test类型的变量大小是20个字节
int main()
{
p=(struct Test*)0x100000;
printf("%p\n", p + 0x1);//00100014结构体指针+1跳过20字节换成16进制是14
printf("%p\n", (unsigned long)p + 0x1);//00100001,p被强制类型转换成整型+1跳过1字节
printf("%p\n", (unsigned int*)p + 0x1);//00100004,p被强制类型转换成整型指针+1跳过4字节
}
3.第三题
int main()
{
int a[4] = { 1, 2, 3, 4 };
int *ptr1 = (int *)(&a + 1);//ptr1[-1]等于*(ptr1-1)=4
int *ptr2 = (int *)((int)a + 1);//首元素地址被转换成整型
printf( "%x,%x", ptr1[-1], *ptr2);
假设小端:01000000 02000000 03000000 04000000
a被转换成整型+1再转换成整型指针相当于向后跳了1个字节再解引用访问4个字节
00000002
因为是小端所以打印02000000
}
4.第四题
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };//逗号表达式{1,3,5}
int *p;
p = a[0];
printf( "%d", p[0]);//1
}
5.第五题
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]);//FFFFFFFC -4
}
&p[4][2]等于*(*(p+4)+2)
p是整型指针+1跳过4字节
0 | 1 | 2 | 3 |
2位置前是*(*(p+4)+2),找到&a[4][2],指针-指针得到的是指针之间元素的个数,小地址-大地址得到-4
%d打印-4
%p打印-4
原码 10000000000000000000000000000100
反码 1111111111111111111111111111111111011
补码 1111111111111111111111111111111111100—>FFFFFFFC
6.第六题
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *ptr1 = (int *)(&aa + 1);//&aa+1跳过整个数组
int *ptr2 = (int *)(*(aa + 1));//aa+1跳过一行
printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10 5
}
7.第七题
int main()
{
char *a[] = {"work","at","alibaba"};//指针数组,把字符串首字符的地址存入
char**pa = a;//a是指针数组首元素地址,pa是二级指针
pa++;//pa++指向第二行首字符地址
printf("%s\n", *pa);//解引用得到第二行首字符地址打印at
}
8.第八题
int main()
{
char *c[] = {"ENTER","NEW","POINT","FIRST"};
char**cp[] = {c+3,c+2,c+1,c};
char***cpp = cp;
printf("%s\n", **++cpp);//POINT
printf("%s\n", *--*++cpp+3);//ER
printf("%s\n", *cpp[-2]+3);//**(cpp-2)+3 ST
printf("%s\n", cpp[-1][-1]+1);//*(*(cpp-1)-1)+1 EW
}