指针练习题目

1.sizeof和strlen的区别

sizeof是操作符,不是函数

strlen是函数,求的是字符串的长度,统计的是字符中\0之前的字符个数,只能针对字符串(字符数组)

2.数组和指针笔试题目解析

2.1 一维数组

数组名都是首元素的地址,但是有两个例外

1.sizeof(数组名)

2.&数组名

int a[] = {1,2,3,4};
printf("%zd\n",sizeof(a));
printf("%zd\n",sizeof(a+0));
printf("%zd\n",sizeof(*a));
printf("%zd\n",sizeof(a+1));
printf("%zd\n",sizeof(a[1]));
printf("%zd\n",sizeof(&a));
printf("%zd\n",sizeof(*&a));
printf("%zd\n",sizeof(&a+1));
printf("%zd\n",sizeof(&a[0]));
printf("%zd\n",sizeof(&a[0]+1));

以上代码结果和解析:

printf("%zd\n",sizeof(a));

数组名a单独放在sizeof里面,表示的是整个元素的大小,单位是字节,有4个int,int类型有4个字节,所以答案为16个字节,输出16.

printf("%zd\n",sizeof(a+0));

a表示首元素地址,a+0也是首元素地址,在32位平台下是4个字节,62位平台下8个字节,输出4/8.

printf("%zd\n",sizeof(*a));

a表示首元素地址,结引用a就是第一个元素,就是a[0],一个int,4个字节,输出4.

printf("%zd\n",sizeof(a+1));

a表示首元素地址,a+1是第2个元素的地址,输出4/8

printf("%zd\n",sizeof(a[1]));

第二个元素大小,4个字节,输出4
printf("%zd\n",sizeof(&a));

&a就是首元素的地址,输出4/8
printf("%zd\n",sizeof(*&a));

这里的*和&相互抵消了,所以等同于sizeof(a),输出16

printf("%zd\n",sizeof(&a+1));

反正是地址,输出为4/8
printf("%zd\n",sizeof(&a[0]));

还是地址,输出4/8
printf("%zd\n",sizeof(&a[0]+1));

这是第二个元素的地址,输出还是4/8

全部结果如下:

2.2字符数组

2.2.1代码1

char arr[] = {'a','b','c','d','e','f'};
printf("%zd\n", sizeof(arr));
printf("%zd\n", sizeof(arr+0));
printf("%zd\n", sizeof(*arr));
printf("%zd\n", sizeof(arr[1]));
printf("%zd\n", sizeof(&arr));
printf("%zd\n", sizeof(&arr+1));
printf("%zd\n", sizeof(&arr[0]+1));

printf("%zd\n", sizeof(arr));

arr单独放在sizeof里面,所以表示整个数组,char为1个字节,输出为6
printf("%zd\n", sizeof(arr+0));

a表示首元素地址,a+0也是首元素地址,在32位平台下是4个字节,62位平台下8个字节,输出4/8.
printf("%zd\n", sizeof(*arr));

第一个元素,输出1
printf("%zd\n", sizeof(arr[1]));

第二个元素,输出1
printf("%zd\n", sizeof(&arr));

指针,输出4/8
printf("%zd\n", sizeof(&arr+1));

一样是指针,不过是跳过整个数组的地址,输出4/8
printf("%zd\n", sizeof(&arr[0]+1));

第二个元素的地址,输出4/8

2.2.2代码2

char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

printf("%d\n", strlen(arr));

strlen计算的是\0之前的值,随机值
printf("%d\n", strlen(arr+0));

strlen计算的是\0之前的值,随机值

printf("%d\n", strlen(*arr));

arr是首元素的地址,*arr是首元素---'a'---97

strlen会认为97是地址,会一直向后访问,会崩溃
printf("%d\n", strlen(arr[1]));

这个也会崩溃
printf("%d\n", strlen(&arr));

随机值
printf("%d\n", strlen(&arr+1));

随机值
printf("%d\n", strlen(&arr[0]+1));

随机值

2.2.3代码3

char arr[] = "abcdef";
printf("%zd\n", sizeof(arr));
printf("%zd\n", sizeof(arr+0));
printf("%zd\n", sizeof(*arr));
printf("%zd\n", sizeof(arr[1]));
printf("%zd\n", sizeof(&arr));
printf("%zd\n", sizeof(&arr+1));
printf("%zd\n", sizeof(&arr[0]+1));

字符串后面还有个'\0'

printf("%zd\n", sizeof(arr));

arr单独放在sizeof里面,表示整个数组的大小,包括\0,所以输出7
printf("%zd\n", sizeof(arr+0));

指针,4/8
printf("%zd\n", sizeof(*arr));

*arr是'a',1
printf("%zd\n", sizeof(arr[1]));

'b',1
printf("%zd\n", sizeof(&arr));

&arr表示的是整个数组的地址,但是一样是地址,4/8
printf("%zd\n", sizeof(&arr+1));

跳过了整个数组的地址,指的是\0后面的地址,4/8
printf("%zd\n", sizeof(&arr[0]+1));

第二个元素的地址,4/8

2.2.4代码4

char arr[] = "abcdef";
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

字符串后面还有个'\0'

printf("%d\n", strlen(arr));

strlen计算的是\0之前的个数,6
printf("%d\n", strlen(arr+0));

6
printf("%d\n", strlen(*arr));

*arr='a'=97,无法访问,报错·
printf("%d\n", strlen(arr[1]));

报错
printf("%d\n", strlen(&arr));

&arr的地址是首元素的地址,类型为char(*)[7],strlen是size_t strlen(const char*str),会强制转换为char*,6
printf("%d\n", strlen(&arr+1));

跳过了整个数组,随机值
printf("%d\n", strlen(&arr[0]+1));

是第二个元素的地址,5

2.2.5代码5

char *p = "abcdef";

printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));

printf("%d\n", strlen(&p));

这里的&p是二级指针,跟后面的字符串没有关系,随机值
printf("%d\n", strlen(&p+1));

这里和上面一样,+1,跳过了&p的地址,一样是随机值
printf("%d\n", strlen(&p[0]+1));

这里就不同了,取得是p[0]的地址,也就是a的地址,5

答案

这里的6纯属巧合,我们可以加几个字符来验证

如图所示。

2.3二维数组

int a[3][4] = {0};
printf("%zd\n",sizeof(a));
printf("%zd\n",sizeof(a[0][0]));
printf("%zd\n",sizeof(a[0]));
printf("%zd\n",sizeof(a[0]+1));
printf("%zd\n",sizeof(*(a[0]+1)));
printf("%zd\n",sizeof(a+1));
printf("%zd\n",sizeof(*(a+1)));
printf("%zd\n",sizeof(&a[0]+1));
printf("%zd\n",sizeof(*(&a[0]+1)));
printf("%zd\n",sizeof(*a));
printf("%zd\n",sizeof(a[3]));

printf("%zd\n",sizeof(a));

a作为二维数组的数组名,单独放在sizeof内部,a表示的是整个数组,计算的是整个数组的大小,单位是字节,4/8
printf("%zd\n",sizeof(a[0][0]));

第一行第一个元素,4
printf("%zd\n",sizeof(a[0]));

a[0]是第一行的数组名,单独放在sizeof内部,计算的是整个数组的大小,16
printf("%zd\n",sizeof(a[0]+1));

a[0]是第一行数组名,但是没有单独放在sizeof内部,那么只能表示为数组首元素的地址,也就是第一行第一个元素的地址,也就是&a[0][0],+1就是&a[0][1],地址是4/8
printf("%zd\n",sizeof(*(a[0]+1)));

由上一个可知,a[0]+1==&a[0][1],结引用就是a[0][1],就是一个int,4
printf("%zd\n",sizeof(a+1));

a是二维数组数组名,没有单独放在sizeof内部,所以a表示的是首元素的地址,也就是&a[0],第一行的地址,类型为int(*)[4],a+1就是第二行的地址,地址就是4/8.
printf("%zd\n",sizeof(*(a+1)));

1*(a+1)==a[1],a[1]就是第二行的数组名,单独放在sizeof内部,表示整个数组的大小,16

2.a+1是第二行的地址,类型为int (*)[4],结引用访问的是整个数组的大小,16
printf("%zd\n",sizeof(&a[0]+1));

a[0]是第一行的数组名,&a[0]+1是第二行的地址,4/8
printf("%zd\n",sizeof(*(&a[0]+1)));

解引用访问的是第二行的数组,16
printf("%zd\n",sizeof(*a));

1.a没有单独放在sizeof内部,a是首元素的地址,也就是&a[0],解引用相当于访问第一行的数组,16

2.*a==*(a+0)==a[0]
printf("%zd\n",sizeof(a[3]));

不会越界访问,a[3]的类型为int(*)[4],sizeof知道了这个之后就会运算了,16

运行结果

3.指针运算笔试题目解析

3.1代码1

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没有单独放在sizeof内部,表示首元素地址,a+1是第二个元素的地址。

ptr被强转为int*类型,-1就向后倒退一个字节

运行输出

3.2代码2

//x86环境下
//这里告知结构体的大小是20个字节
struct Test
{
int Num;
char *pcName;
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;
}

提示:

指针+1和类型有关,整数+1就是+1

p + 0x1

p是结构体类型指针,一个结构体指针类型20个字节,+1跳过就是20个字节,又因为输出形式是16进制,所以为0x100014
(unsigned long)p + 0x1

强制为整形,整形+1就是+1,0x100001
(unsigned int*)p + 0x1

+1跳过一个unsigned int类型的变量,是4个字节,0x100004

3.3代码3

#include <stdio.h>
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,3,5),因为这个是逗号表达式,输出的是后一位的结果

a[0]是第一行的地址,没有放在sizeof和&,表示的是首元素的地址,所以p指向的是第一行第一个元素,打印出1.

3.4代码4

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;
}

a的类型为int(*)[5],p的类型为int(*)[4],p=a把a的地址给了p。

&p[4][2] - &a[4][2]计算的是两个地址之间元素的个数,又因为低地址-高地址,为-4

1000000000000000000000000000100--原码

11111111111111111111111111111111011--反码

11111111111111111111111111111111100--补码

%d打印的是有符号的值,但存的是补码,即打印的是原码,-4

%p在内存中存的是无符号的整数,也就是原反补相同,但此时存的是补码,原码也是补码,

每四个二进制位得一个16进制位,0xFF FF FF FC

3.5代码5

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是首元素的地址,也就是int(*)[5]类型,+1跳过第一行数组,强转为int*。

*(aa+1)==aa[1],aa[1]表示的是首元素的地址,即&aa[1][0]。

3.6代码6

#include <stdio.h>
int main()
{
char *a[] = {"work","at","alibaba"};
char**pa = a;
pa++;
printf("%s\n", *pa);
return 0;
}

a中存的是字符串的首元素地址,pa存的是a首元素的地址,pa++指向了a中第二个元素。*pa找到的是a中第二个元素的地址。

3.7代码7

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;
}

**++cpp

先算++cpp,cpp本来指向的是cp中第一个元素,++之后就指向第二个,**之后访问的是c中第三个元素的地址。POINT


 *--*++cpp+3

上一个影响了cpp的指向,此时cpp指向的是cp中第二个元素的地址,+的优先级最低,最后运算,++cpp指向cp中第三个元素,*解引用之后找到了cp中的c+1,再运算--*++cpp,c=(c+1)-1就指向了c中首元素的地址,然后+3指向c中首元素的第三个字符的地址,可能有人疑惑为什么不是c中第四个元素的地址,经过了前面一系列运算,最后就相当于char*c了,要想变成c+3的地址,c的类型为char**,c的类型也要是char**。所以此时+3和下列一样,ER


 *cpp[-2]+3

*cpp[-2]+3==**(cpp-2)+3,由于经过上列的运算,此时cpp指向了cp中第三个元素的地址,-2就指向cp中第一个元素的地址,**两次解引用访问的是c中第四个元素的地址,再算+3就是ST


 cpp[-1][-1]+1

cpp[-1][-1]+1==*(*(cpp-1)-1)+1,cpp还是指向cp的第三个元素的地址,因为上一步没有改变,cpp-1指向的是cp中第二个元素的地址,*解引用就是c+2,此时-1=c+1,*再解引用,指向的是c中第二个元素的地址,+1又向后1位地址,EW

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值