目录
前言
本文是对指针和数组的一些题的归纳。
一、一维整型数组相关题
#include <stdio.h>
int main()
{
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));//16 sizeof(a)算的是数组的大小
printf("%d\n",sizeof(a+0));//4/8 sizeof(a+0)不是sizeof(a),所以此处不属于那两种特殊情况,此处的a是数组首元素的地址,地址的大小在32位(大小为4)或64位(大小为8)平台不同
printf("%d\n",sizeof(*a));//4 *a找到的是首元素,该数组的每个元素的类型是int,所以大小为4
printf("%d\n",sizeof(a+1));//4/8 同理sizeof(a+1)中的a是数组首元素的地址,+1后是第一个元素的地址,地址的大小在没有特定声明的情况下是4/8
printf("%d\n",sizeof(a[1]));//4 a[1]是数组的第一个元素,元素的大小是4
printf("%d\n",sizeof(&a));//4/8 &a是一个数组指针(类型为int(*)[4])
printf("%d\n",sizeof(*&a));//16 &a,取出了数组的地址,*&a访问了数组,此处计算的是数组的大小
printf("%d\n",sizeof(&a+1));//4/8 &a是一个数组指针,+1,跳过整个数组,但还是一个数组指针
printf("%d\n",sizeof(&a[0]));//4/8 &a[0]取出了数组首元素的地址
printf("%d\n",sizeof(&a[0]+1));//4/8 &a[0]+1数组第一个元素的地址
return 0;
}
二、字符数组相关题
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));//6 sizeof(arr)算的是数组的大小
printf("%d\n", sizeof(arr+0));//4/8 sizeof(arr+0)不是sizeof(a),所以此处不属于那两种特殊情况,此处的arr是数组首元素的地址,地址的大小在32位(大小为4)或64位(大小为8)平台不同
printf("%d\n", sizeof(*arr));//1 *arr找到的是首元素,该数组的每个元素的类型是char,所以大小为1
printf("%d\n", sizeof(arr[1]));//1 arr[1]是第一个字符'b'
printf("%d\n", sizeof(&arr));//4/8 &arr是一个数组指针(类型为char(*)[6]),大小为4/8
printf("%d\n", sizeof(&arr+1));//4/8 &arr是一个数组指针,+1跳过一整个数组,但还是一个数组指针
printf("%d\n", sizeof(&arr[0]+1));//4/8 &arr[0]+1是数组第一个元素的地址
printf("%d\n", strlen(arr));//随机值 arr是数组首元素的地址,strlen()会从第一个元素的地址处开始计数,知道遇到'\0'停止计数,但是字符数组中没有'\0',所以会一直计数下去,我们也不知道在何处会遇到'\0'。
printf("%d\n", strlen(arr+0));//随机值 同上式理
printf("%d\n", strlen(*arr));//error *arr访问的是数组的第一个元素'a',它的ASCII码值为97,strlen()以为97就是地址,但是它是一个非法地址
printf("%d\n", strlen(arr[1]));//error 同上式理
printf("%d\n", strlen(&arr));//随机值 &arr是一个数组指针,类型为char(*)[6],它会强制类型转换成(char*),同理,我们也不知道什么时候会遇到'\0',所以为随机值
printf("%d\n", strlen(&arr+1));//随机值,与上式相差6 &arr+1跳过一整个字符数组,少算了6个元素的个数
printf("%d\n", strlen(&arr[0]+1));//随机值,与上上式相差1 &arr[0]+1跳过一个元素的地址,与上上式相比少算了1个元素
return 0;
}
三、字符串数组相关题
#include <stdio.h>
#include <string.h>
int main()
{
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));//7 字符串后面还有一个'\0'
printf("%d\n", sizeof(arr+0));//4/8 sizeof(arr+0)不是sizeof(a),所以此处不属于那两种特殊情况,此处的arr是数组首元素的地址,地址的大小在32位(大小为4)或64位(大小为8)平台不同
printf("%d\n", sizeof(*arr));//1 *arr访问的是数组首元素,大小为1
printf("%d\n", sizeof(arr[1]));//1 arr[1]代表数组的第一个元素,大小为1
printf("%d\n", sizeof(&arr));//4/8 &arr是一个数组指针
printf("%d\n", sizeof(&arr+1));//4/8 &arr+1跳过了一个数组,但还是一个数组指针
printf("%d\n", sizeof(&arr[0]+1));//4/8 &arr[0]+1第1个元素的地址
printf("%d\n", strlen(arr));//6 arr是数组首元素的地址,strlen()不计算'\0'的长度
printf("%d\n", strlen(arr+0));//6 arr+0还是数组首元素的地址,strlen()不计算'\0'的长度
printf("%d\n", strlen(*arr));//error
printf("%d\n", strlen(arr[1]));//error
printf("%d\n", strlen(&arr));//6
printf("%d\n", strlen(&arr+1));//随机值 &arr+1跳过了一个数组,连'\0'也跳过去了
printf("%d\n", strlen(&arr[0]+1));//5 从第一个元素的地址开始计算,直到遇到'\0'。
return 0;
}
#include <stdio.h>
#include <string.h>
int main()
{
char *p = "abcdef";
printf("%d\n", sizeof(p));//4/8 p是'a'的地址
printf("%d\n", sizeof(p+1));//4/8 p+1是'b'的地址
printf("%d\n", sizeof(*p));//1 *p访问的是'a'
printf("%d\n", sizeof(p[0]));//1 p[0]代表'a'
printf("%d\n", sizeof(&p));//4/8 取出了p的地址
printf("%d\n", sizeof(&p+1));//4/8
printf("%d\n", sizeof(&p[0]+1));//4/8 找到'b'的地址
printf("%d\n", strlen(p));//6 p是'a'的地址,从该地址开始计算数组长度
printf("%d\n", strlen(p+1));//5 p+1是'b'的地址,从该地址开始计算数组长度
printf("%d\n", strlen(*p));//error
printf("%d\n", strlen(p[0]));//error
printf("%d\n", strlen(&p));//随机值
printf("%d\n", strlen(&p+1));//随机值
printf("%d\n", strlen(&p[0]+1));//5 &p[0]+1是'b'的地址,从该地址开始计算数组长度
return 0;
}
注意:这两个随机值之间没有大小关系,不一定相差4,因为'\0'的位置未知
四、二维数组相关题
#include <stdio.h>
int main()
{
int a[3][4] = {0};
printf("%d\n",sizeof(a));//48 计算的是整个数组的大小:3*4*4
printf("%d\n",sizeof(a[0][0]));//4 第0行,第0列的元素,大小为4
printf("%d\n",sizeof(a[0]));//16 a[0]是第0行的数组名,sizeof(数组名)算的是数组的大小,所以第0行有4个元素,每个元素的大小是4个字节,因此4*4
printf("%d\n",sizeof(a[0]+1));//4/8 a[0]+1第一行第二个元素的地址
printf("%d\n",sizeof(*(a[0]+1)));//4 访问第一行第二个元素的值
printf("%d\n",sizeof(a+1));//4/8 a+1是第一行的地址
printf("%d\n",sizeof(*(a+1)));//16 a+1是第一行的地址,*(a+1)访问的是第一行的元素
printf("%d\n",sizeof(&a[0]+1));//4/8 &a[0] + 1就是第一行的地址,第一行的地址的类型为int(*)[4]
printf("%d\n",sizeof(*(&a[0]+1)));//16 &a[0]+1是第一行的地址,*(&a[0]+1))访问的是第一行的元素
printf("%d\n",sizeof(*a));//16 a是第0行的地址,*a访问的是第0行的元素
printf("%d\n",sizeof(a[3]));//16 第三行的数组名,尽管数组越界了,但是sizeof括号里面的表达式是不参与运算的,所以此处的16是根据a[0]或a[1]或a[2]推演出来的
return 0 ;
}
总结:
五、一些指针的笔试题
1、
#include <stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int *ptr = (int *)(&a + 1);//&a+1的类型是int(*)[5],强制转换成int*
printf( "%d %d", *(a + 1), *(ptr - 1));//2 5
//解析:
//①a是数组首元素的地址,a+1是数组第一个元素的地址,*(a+1)访问的是数组的第一个元素2
//②&a + 1跳过了一个数组的大小,*(ptr - 1)等价于ptr[-1],意思是向前偏移一个单位,访问到的元素是5
return 0;
}
2.
#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
int *p;
p = a[0];
printf( "%d", p[0]);//1
//解析:
//逗号表达式,所以放进去数组的只有1 3 5
return 0;
}
3.
#include <stdio.h>
int main()
{
char* a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);//at
return 0;
}
解析如下:
4.
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;
}
解析如下:
它们之间的关系如左图所示:
(1)**++cpp:++cpp后,cpp便指向了c+2处,*++cpp相当于拿到的是c+2的内容,c+2指向的是③,相当于拿到了③的地址,再解引用,相当于拿到③的内容,③指向"POINT"的"P",所以相当于拿到的是"P"的地址,所以最终可以打印出POINT。
(2)*--*++cpp + 3:++cpp后,cpp便指向c+1处,*++cpp相当于拿到的是c+1的内容,c+1指向的是②,相当于拿到了②的地址,--*++cpp,注意:此时c+1变成c,并且它不再指向②,它指向①,如右图所示。现在我们相当于拿到了①的地址,再解引用,我们找到了①的内容,①指向"NNTER"的"E",所以相当于拿到的是"E"的地址,*--*++cpp + 3,此时我们拿到的是"E"的地址,所以最终可以打印出ER
(3)*cpp[-2] + 3:cpp[-2]相当于*(cpp-2)的意思是想前偏移2个单位,此时cpp指向的是c+3处(注意:此处没有副作用的产生),访问的是c+3处的内容,c+3指向的是④,相当于拿到了④的地址,解引用,找到的是④的内容,而④指向的是"FIRST"的"F",所以相当于拿到"F"的地址,*cpp[-2] + 3,此时我们拿到的是"S"的地址,所以最终可以打印出ST。
(4)cpp[-1][-1] + 1:注意:由于(3)中没有产生副作用,所以此时cpp指向c处,如右图所示。cpp[-1][-1]相当于*(*(cpp-1)-1),cpp-1,所以此时cpp指向的是c+2处,*(cpp-1)相当于拿到的是c+2的内容,c+2指向的是③,相当于拿到了③的地址,*(cpp-1)-1,此时c+2指向的是②,相当于拿到了②的地址,再解引用,相当于拿到②的内容,而②指向的是"NEW"的"N",所以相当于拿到"N"的地址,cpp[-1][-1] + 1,此时我们拿到的是"E"的地址,所以最终可以打印出EW。
5.
#include <stdio.h>
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p;
//该结构体大小为20byte
int main()
{
p = (struct Test*)0x100000;
printf("%p\n", p + 0x1);//00100014
//0x1就是16进制的1,此处跳过一个结构体 0x100000+20=0x100014
printf("%p\n", (unsigned long)p + 0x1);//00100001
//p不再是结构体,而是一个整数,整数+1就是+1,所以00100001
printf("%p\n", (unsigned int*)p + 0x1);//00100004
//p强制类型转换为一个整型指针,+1,跳过4byte。所以00100004
return 0;
}
6.
#include <stdio.h>
int main()
{
int a[4] = { 1, 2, 3, 4 };
int* ptr1 = (int *)(&a + 1);
int* ptr2 = (int *)((int)a + 1);
//举个例子:0x0012ff40转换为10进制整数:1244992
//1244992+1=1244993 1244993转换为16进制为0x0012ff41
//(int *)((int)a + 1);可见此处地址加1
printf( "%x %x", ptr1[-1], *ptr2);//4 2000000
return 0;
}
解析如下:
7.
#include <stdio.h>
int main()
{
int a[5][5];//类型:int(*)[5]
int(*p)[4];//类型:int(*)[4]
p = a;
printf( "%p %d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//FFFFFFFC -4
//指针与指针相减得到的是它们之间的元素个数
//%p--打印地址(16进制),地址没有原反补码的概念,把-4的补码当成原码打印出来
//-4的原码:10000000000000000000000000000100
// 反码:11111111111111111111111111111011
// 补码:1111 1111 1111 1111 1111 1111 1111 1100
// F F F F F F F C
return 0;
}
题的图如下:
8.
#include <stdio.h>
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));//10 5
//解析:
//10 如图
//5 aa是数组首元素的地址即a[0],aa+1即数组第一行的地址,解引用,得到的是数组第一行第0个元素:6,ptr2指向6,ptr2-1,指向的是前一个元素的地址,即5的地址,所以*(ptr2 - 1)访问的是5
return 0;
}
如图: