9、指针和数组面试题(续)
https://mp.csdn.net/mp_blog/creation/editor/118405531
一、总结
A、一位数组-sizeof()
数组名是首元素地址
例外:
1、sizeof(数组名)-表示整个数组
2、&数组名-表示整个数组
#include<stdio.h>
int main()
{
//数组名是首元素地址
//例外:
//1、sizeof(数组名)-表示整个数组
//2、&数组名-表示整个数组
//一位数组
int arr[] = { 4, 5, 6, 7 };
printf("%d\n", sizeof(arr));//16-数组名-计算整个数组的大小
printf("%d\n", sizeof(arr+0));//4-首元素地址
printf("%d\n", sizeof(*arr));//4-首元素
printf("%d\n", sizeof(arr+1));//4-第二个元素地址
printf("%d\n", sizeof(arr[1]));//4-第二个元素
printf("%d\n", sizeof(&arr));//4-整个数组的地址
printf("%d\n", sizeof(*&arr));//16-整个数组
printf("%d\n", sizeof(&arr+1));//4-下一个数组的地址
printf("%d\n", sizeof(&arr[0]));//4-首元素地址
printf("%d\n", sizeof(&arr[0]+1));//4-第二个元素的地址
return 0;
}
B、字符数组-sizeof()
#include<stdio.h>
int main()
{
//字符数组
char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
printf("%d\n", sizeof(arr));//6-数组大小
printf("%d\n", sizeof(arr+0));//4-首元素地址
printf("%d\n", sizeof(*arr));//1-首元素
printf("%d\n", sizeof(arr[1]));//1-第二个元素
printf("%d\n", sizeof(&arr));//4-整个数组的地址
printf("%d\n", sizeof(&arr+1));//4-下一个数组的地址
printf("%d\n", sizeof(&arr[0]+1));//4第二个元素的地址
return 0;
}
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7-数组大小+'\0'
//其余都一样
C、字符数组-strlen()
#include<stdio.h>
#include<string.h>
int main()
{
//字符数组
char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
printf("%d\n", strlen(arr));//随机值-直到找到'\0'
printf("%d\n", strlen(arr + 0));//随机值
printf("%d\n", strlen(*arr));//error-'a'-97-非法访问内存
printf("%d\n", strlen(arr[1]));//error-‘b’-98
printf("%d\n", strlen(&arr));//随机值
printf("%d\n", strlen(&arr + 1));//随机值
printf("%d\n", strlen(&arr[0] + 1));//随机值
}
#include<string.h>
int main()
{
//字符数组
char arr[] = "abcdef";
printf("%d\n", strlen(arr));//6-整个数组大小-直到'\0'
printf("%d\n", strlen(arr + 0));//6-整个数组大小
//printf("%d\n", strlen(*arr));//error-'a'-97-非法访问内存
//printf("%d\n", strlen(arr[1]));//error-‘b’-98
printf("%d\n", strlen(&arr));//6-整个数组大小
printf("%d\n", strlen(&arr + 1));//随机值
printf("%d\n", strlen(&arr[0] + 1));//5-从第二个数直到'\0'
}
D、字符数组-sizeof()
int main()
{
char* p = "abcdef";
printf("%d\n", sizeof(p));//4-指针变量p
printf("%d\n", sizeof(p+1));//4-(p+1)为指向b的地址
printf("%d\n", sizeof(*p));//1-字符串首元素a
printf("%d\n", sizeof(p[0]));//1-首元素 int arr[10];arr[0]==*(arr+0);p[0]==*(p+0);
printf("%d\n", sizeof(&p));//4-p的地址
printf("%d\n", sizeof(&p + 1));//4-p的下一个地址
printf("%d\n", sizeof(&p[0] + 1));//4-字符b的地址
return 0;
}
#include<string.h>
int main()
{
char *p = "abcdef";
printf("%d\n", strlen(p));//6-指针变量p指向首元素地址开始直至'\0'
printf("%d\n", strlen(p + 1));//5-(p+1)为指向b的地址开始直至'\0'
//printf("%d\n", strlen(*p));//error-*p-字符串首元素'a'-97,非法访问内存
//printf("%d\n", strlen(p[0]));//error-p[0]字符串首元素'a'-97,非法访问内存
printf("%d\n", strlen(&p));//随机值
printf("%d\n", strlen(&p + 1));//随机值
printf("%d\n", strlen(&p[0] + 1));//5-从b开始直至'\0'
return 0;
}
E、二维数组
sizeof(数组名)、&(数组名)才表示数组,其余都为数组首元素地址
int main()
{
//sizeof(数组名)、&(数组名)才表示数组,其余都为数组首元素地址
int a[3][4] = { 0 };
printf("%d\n", sizeof(a));//48-二维数组大小
printf("%d\n", sizeof(a[0][0]));//4-a[0][0]的大小
printf("%d\n", sizeof(a[0]));//16-a[0]相当于第一行作为一维数组的数组名,
//sizeof(a[0])把a[0]单独放在里面,所以计算的是第一行数
printf("%d\n", sizeof(a[0]+1));//4-第一行第二个元素的地址,a[0][1]的地址
printf("%d\n", sizeof(*(a[0] + 1)));//4-第一行第二个元素的大小,a[0][1]的大小
printf("%d\n", sizeof(a+1));//4-第二行的地址,
//a是首元素地址,把二维数组看成一位数组,a就是第一行地址
printf("%d\n", sizeof(*(a + 1)));//16-第二行的数组元素大小
printf("%d\n", sizeof(&a[0]+1));//4-第二行的地址
printf("%d\n", sizeof(*(&a[0] + 1)));//16-第二行数组元素的大小
printf("%d\n", sizeof(*a));//16-第一行数组元素的大小
printf("%d\n", sizeof(a[3]));//16-因为sizeof并不会访问,
//第四行数组元素(有无都不重要)的大小吧
return 0;
}
总结:
数组名的意义:
1 . sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3.除此之外所有的数组名都表示首元素的地址。
笔试题:
eg1:
#include<stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1);
printf("%d,%d", *(a + 1), *(ptr - 1));
return 0;
}
分析:
a指的是元素1的地址,a+1就是元素二的地址;
&a表示取出数组的地址,&a+1就是图示位置,所以ptr-1就是指向元素5的地址;
然后对它们进行解引用操作
代码实现:
eg2:
已知,结构体Test类型变量大小为20个字节,假设p的值为0x100000
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//已知,结构体Test类型变量大小为20个字节
//假设p的值为0x100000
int main()
{
p = (struct Test*)0x100000;
//p是指向结构体的指针,初值为0x100000,
//p + 1跳过一个结构体Test变量大小为20个字节,所以第一项输出0x00100014;
printf("%p\n", p + 0x1);
//强制类型转换成无符号长整型+1就是0x00100001
printf("%p\n",(unsigned long) p + 0x1);//十进制1048576+1=1048577转换成十六进制
//强制类型转换成无符号int*,+1跳过一个无符号整型(4个字节)就是0x00100004
printf("%p\n", (unsigned int*)p + 0x1);
return 0;
}
代码实现:
eg3:
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;
}
分析:
(&a+1)-指向a数组的下一个数组,强制类型转化为(int*),ptr1就指向数组a的下一个数组首元素,ptr1[-1]==*(ptr1-1),所以就是元素4;
a就是首元素地址,为00 00 00 01,但是在内存中为小端存储(如图所示),然后将a强制类型转换为int,((int)a + 1)再强制类型转换为(int*),等价于向后移动一个字节,指向01后面的00,指针ptr2就指向00,*ptr2就是00 00 00 02,从内存还原为02 00 00 00
代码实现:
eg4:
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int* p ;
p = a[0];
printf("%d\n", p[0]);
return 0;
}
分析:
因为是数组内部为(),所以只执行最后一个元素/命令,所以数组元素为{1,3,5,0,0,0};
a[0]为数组第一行首元素地址,所以指针p是元素1的地址,p[0]==*(p+0)==*p,为1
代码实现:
eg5:
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]);
}
分析:
数组a为5行5列,a单独为首元素地址,a的类型为int(*)[5],p类型为int(*)[4],将a赋给p同指向数组首元素;
&a[4][2]就是指向数组第5行第3列的(图示红色)元素,&p[4][2]因为p数组指针指向的数组有4个元素,所以就是(图示绿色)元素;
%d:两地址相减为中间元素的个数,前面为低地址,所以为-4;
%p:将-4转化为补码存储在内存中,因为地址为无符号数,原码就是补码,所以将补码还原为十六进制为FFFFFFFC
//-4-10000000 00000000 00000000 00000100-原码
// 11111111 11111111 11111111 11111011-反码
// 11111111 11111111 11111111 11111100-补码
代码实现:
eg6:
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;
}
分析:&aa(aa为数组名),+1跳过一个数组;
*(aa+1)aa为数组首元素地址,+1把它看作一维数组就相当于跳过一行
代码实现:
eg7:
int main()
{
char* a[] = { "work", "at", "alibaba" };
char** pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}
分析:
a为字符指针数组,存放数组首字母地址“w”、“a”、“a”的地址,数组每个元素为char*类型,pa存放a数组首元素地址,自增,指向“a”元素地址,解引用输出字符串为“at”
代码实现:
eg8:
#include<stdio.h>
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);//ST
printf("%s\n",cpp[-1][-1]+1 );//EW
return 0;
}
分析:
c数组为char*类型分别指向"ENTER"、"NEW"、"POINT"、"FIRST"首元素地址;
cp数组为char**类型,它们分别指向c数组的不同首元素地址;
cpp指针类型为char***类型指向cp数组首元素,如图所示:
①**++cpp——先执行++cpp,cpp指向c+2,再进行两次解引用操作,POINT;
②(上一步操作完成后cpp指向c+2)*--*++cpp+3——先执行++cpp操作,cpp指向c+1,再进行解引用指向c+1对应的数组c内元素地址,再进行--操作,指向c对应的数组c内元素地址,再进行解引用操作指向ENTER,+3操作,ER;
③(上一步操作完成后cpp指向c+1)*cpp[-2]+3)——cpp[-2]=*(cpp-2),为(cpp-2)指向c+3地址,**(cpp-2)指向FIRST,+3后,ST;
④cpp[-1][-1]+1——cpp[-1][-1]=*(*(cpp-1)-1),cpp-1指向c+2,*(cpp-1)指向c+2对应的数组c内元素地址,*(*(cpp-1)-1)指向NEW,+1,EW
代码实现: