十五、数组和指针笔试题解析

1 一维数组

例:

#include <stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a + 0));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(a[1]));
	printf("%d\n", sizeof(&a));
	printf("%d\n", sizeof(*&a));
	printf("%d\n", sizeof(&a + 1));
	printf("%d\n", sizeof(&a[0]));
	printf("%d\n", sizeof(&a[0] + 1));
	return 0;
}

输出结果(x86平台):

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

首先我们来复习一下对数组名的理解。数组名是数组首元素的地址,但是有两个例外:

  1. sizeof(数组名)时,数组名表示整个数组,计算是整个数组的大小,单位是字节。
  2. &数组名时,数组名表示整个数组,取出的是数组的地址。

下面我们逐个 printf 语句来进行分析:

  1. 由于 sizeof(数组名)计算的是整个数组的大小,而1个 int 类型是4个字节,所以4个就是16字节。
  2. 由于a+0不是数组名,所以 a 表示的就是数组首元素的地址,所以sizeof(a+0)计算的是首元素地址大小,是地址大小就是4或者8个字节。
  3. 由于*a不是数组名,所以 a 表示的就是数组首元素的地址,而*a其实就是第一个元素,也就是 a[0],所以大小为4个字节。
  4. 此处的情况和2一样,sizeof(a+1)计算的是数组第二个元素地址大小,是地址大小就是4或者8个字节。
  5. 显然sizeof(a[1])计算的是数组第二个元素的大小,所以大小为4个字节。
  6. 由于&数组名时,数组名表示整个数组,取出的是数组的地址,但是数组的地址也是地址,是地址大小就是4或者8个字节。
  7. 虽然*&a不是数组名,但是&a取出的是数组的地址。对数组的地址进行解引用操作那么实际上*&就可以抵消掉,也就是说sizeof(*&a)实际上等价于sizeof(a)。除此之外,我们还可以从另外一个角度进行分析,&a的类型实际上是数组指针,而对这个数组指针进行解引用实际上访问的就是整个数组,那么整个数组的大小自然而然就是16字节。
  8. 7的分析不难知道,&a + 1表示的是跳过整个数组 a 后的一个地址,是地址大小就是4或者8个字节。
  9. 显然,&a[0]就是首元素的地址,是地址大小就是4或者8个字节。
  10. 同理,&a[0] + 1是第二个元素的地址,其实就是a[1]的地址,是地址大小就是4或者8个字节。

2 字符数组

例:

#include <stdio.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(arr + 0));
	printf("%d\n", sizeof(*arr));
	printf("%d\n", sizeof(arr[1]));
	printf("%d\n", sizeof(&arr));
	printf("%d\n", sizeof(&arr + 1));
	printf("%d\n", sizeof(&arr[0] + 1));
	return 0;
}

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面我们逐个 printf 语句来进行分析:

  1. 由于 sizeof(数组名)计算的是整个数组的大小,而1个 char 类型是1个字节,所以6个就是6字节。
  2. 由于arr+0不是数组名,所以arr表示的就是数组首元素的地址,所以sizeof(arr+0)计算的是首元素地址大小,是地址大小就是4或者8个字节。
  3. 由于*arr不是数组名,所以arr表示的就是数组首元素的地址,而*arr其实就是第一个元素,也就是arr[0],所以大小为1个字节。
  4. 显然,arr[1]表示的是第二个元素,大小为1个字节。
  5. &arr取出的是数组的地址,是地址大小就是4或者8个字节。
  6. &arr + 1表示的是跳过整个数组arr后的一个地址,是地址大小就是4或者8个字节。
  7. 显然,&arr[0] + 1是第二个元素的地址,其实就是arr[1]的地址,是地址大小就是4或者8个字节。

例:

#include <stdio.h>
#include <string.h>
int main()
{
	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));
	return 0;
}

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面我们逐个 printf 语句来进行分析:

  1. 由于 strlen 函数计算的是\0之前字符串中字符的个数,而数组中没有明确给出\0,所以计算出来的结果应该是个随机值。
  2. arr + 0表示的是首元素的地址,而arr的地址正是从首元素的地址开始,也就是说strlen(arr)strlen(arr + 0)中传给strlen的地址其实是一模一样的,那么它的输出结果应该也是随机值且和上面的结果一致。
  3. *arr表示对首元素的地址进行解引用,而strlen接收的参数是地址,也就是说在这里相当于把首元素也就是a的ASCII码值作为地址传给strlen,而这个地址是否有被分配其实是不可知的,所以会造成非法访问,无法输出结果。
  4. 3arr[1]表示的是第二个元素,把元素的ASCII码值作为地址传给strlen会造成非法访问。
  5. &arr表示的是数组的地址,但是这个地址也是指向数组的起始位置,也就是说这里的参数实际上和12是一样的,那么输出的结果应该也是一样的随机值。
  6. &arr + 1表示跳过整个数组后的地址,从这里开始找\0的话,由于arr后的地址存放的内容是随机的,那么输出结果应该也是随机值,且和125的随机值不同。
  7. &arr[0] + 1表示的是第二个元素的地址,从第二个元素开始找\0的话,那么和125相比,7统计到的个数就应该少一个,且是随机值。

由于上面34两条语句造成非法访问后程序就已经停止运行了,所以程序运行时只会看到12输出的结果,我们把34注释掉之后再来运行就可以看到下面的结果了:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

例:

#include <stdio.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(arr + 0));
	printf("%d\n", sizeof(*arr));
	printf("%d\n", sizeof(arr[1]));
	printf("%d\n", sizeof(&arr));
	printf("%d\n", sizeof(&arr + 1));
	printf("%d\n", sizeof(&arr[0] + 1));
	return 0;
}

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面我们逐个 printf 语句来进行分析:

  1. sizeof(数组名)计算的是整个数组的大小。由于用字符串初始化字符数组时,字符串最后会隐藏一个\0,而sizeof并不关心数组中存放了什么,只关心存放了几个元素,所以把\0算上就是7个元素。
  2. arr + 0表示的是首元素的地址,是地址大小就是4或者8个字节。
  3. *arr表示的是首元素,一个char类型是1个字节。
  4. 3sizeof(arr[1])计算的是数组中第一个元素的大小,所以结果为1.
  5. &arr表示数组的地址,是地址大小就是4或者8个字节。
  6. &arr + 1 表示跳过整个数组后的地址,是地址大小就是4或者8个字节。
  7. &arr[0] + 1)表示的是第二个元素的地址,是地址大小就是4或者8个字节。

例:

#include <stdio.h>
#include <string.h>
int main()
{
	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));
	return 0;
}

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面我们逐个 printf 语句来进行分析:

  1. 由于用字符串初始化字符数组时,字符串最后会隐藏一个\0,而strlen函数计算的就是\0之前字符串中字符的个数,所以结果为6。
  2. arr + 0表示的是首元素的地址,而arr的地址正是从首元素的地址开始,也就是说strlen(arr)strlen(arr + 0)中传给strlen的地址其实是一模一样的,那么它的输出结果应该也是6。
  3. *arr表示对首元素的地址进行解引用,而strlen接收的参数是地址,也就是说在这里相当于把首元素也就是a的ASCII码值作为地址传给strlen,而这个地址是否有被分配其实是不可知的,所以会造成非法访问,无法输出结果。
  4. 3arr[1]表示的是第二个元素,把元素的ASCII码值作为地址传给strlen会造成非法访问。
  5. &arr表示的是数组的地址,但是这个地址也是指向数组的起始位置,也就是说这里的参数实际上和12是一样的,那么输出的结果应该也是6。
  6. &arr + 1表示跳过整个数组后的地址,从这里开始找\0的话,由于arr后的地址存放的内容是随机的,那么输出结果应该是随机值。
  7. &arr[0] + 1表示的是第二个元素的地址,从第二个元素开始找\0的话,那么和125相比,7统计到的个数就应该少一个,即为5。

由于上面34两条语句造成非法访问后程序就已经停止运行了,所以程序运行时只会看到12输出的结果,我们把34注释掉之后再来运行就可以看到下面的结果了:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

例:

#include <stdio.h>
int main()
{
	char* p = "abcdef";
	printf("%d\n", sizeof(p));
	printf("%d\n", sizeof(p + 1));
	printf("%d\n", sizeof(*p));
	printf("%d\n", sizeof(p[0]));
	printf("%d\n", sizeof(&p));
	printf("%d\n", sizeof(&p + 1));
	printf("%d\n", sizeof(&p[0] + 1));
	return 0;
}

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面我们逐个 printf 语句来进行分析:

  1. 由于p是一个指针变量,而指针变量的大小为4或者8个字节,所以结果应该为4或者8.
  2. 同理,p+1表示的是b的地址,是地址大小就为4或者8个字节。
  3. *p表示的是对首元素的地址进行解引用,也就是a,所以大小为1个字节。
  4. 实际上,p[0]从计算的角度会被转换成*(p + 0),所以p[0]表示的还是首元素,所以大小为1个字节。
  5. &p表示的是指针变量p的地址,是地址大小就为4或者8个字节。
  6. &p + 1表示的是跳过指针变量p后的地址,是地址大小就为4或者8个字节。
  7. &p[0] + 1表示的是b的地址,是地址大小就为4或者8个字节。

例:

#include <stdio.h>
#include <string.h>
int main()
{
	char* p = "abcdef";
	printf("%d\n", strlen(p));
	printf("%d\n", strlen(p + 1));
	printf("%d\n", strlen(*p));
	printf("%d\n", strlen(p[0]));
	printf("%d\n", strlen(&p));
	printf("%d\n", strlen(&p + 1));
	printf("%d\n", strlen(&p[0] + 1));
	return 0;
}

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面我们逐个 printf 语句来进行分析:

  1. 由于用字符串初始化字符数组时,字符串最后会隐藏一个\0,且p中存放的是a的地址,所以strlen函数就是从a的地址开始找\0,所以结果为6。
  2. p + 1表示的是b的地址,所以strlen函数是从b的地址开始找\0,所以结果为5。
  3. *p表示的是a,而strlen接收的参数是地址,也就是说在这里相当于把首元素也就是a的ASCII码值作为地址传给strlen,而这个地址是否有被分配其实是不可知的,所以会造成非法访问,无法输出结果。
  4. 3p[0]表示的还是a,把元素的ASCII码值作为地址传给strlen会造成非法访问。
  5. &p表示的是p的地址,而\0p所占空间中的具体位置是不可知的,所以结果应该是个随机值。
  6. 5&p + 1表示的是跳过指针变量p后的地址,而\0在其所占空间中的具体位置是不可知的,所以结果也是个随机值。
  7. &p[0] + 1表示的是第二个元素的地址,从第二个元素开始找\0的话,那么和1相比,7统计到的个数就应该少一个,即为5。

由于上面34两条语句造成非法访问后程序就已经停止运行了,所以程序运行时只会看到12输出的结果,我们把34注释掉之后再来运行就可以看到下面的结果了:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

3 二维数组

例:

#include <stdio.h>
int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a[0][0]));
	printf("%d\n", sizeof(a[0]));
	printf("%d\n", sizeof(a[0] + 1));
	printf("%d\n", sizeof(*(a[0] + 1)));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(*(a + 1)));
	printf("%d\n", sizeof(&a[0] + 1));
	printf("%d\n", sizeof(*(&a[0] + 1)));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a[3]));
	return 0;
}

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

下面我们逐个 printf 语句来进行分析:

  1. sizeof(a)计算的是整个二维数组的大小,这个二维数组的大小为三行四列,也就是说这个数组中有12个int类型的元素,那么大小应该是48个字节。
  2. a[0][0]表示的第一行第一个元素,大小为4个字节。
  3. a[0]表示的是第一行的数组名,数组名单独放在sizeof内部,计算的就是第一行的大小,也就是16个字节。
  4. a[0] + 1a[0]是第一行的数组名,但是数组名并没有单独放在sizeof内部,所以这里的a[0]表示的是第一行第一个元素的地址,那么a[0] + 1表示的是第一行第二个元素的地址,是地址大小就是4或者8个字节。
  5. 4可知,a[0] + 1表示的是第一行第二个元素的地址,那么*(a[0] + 1)就是第一行第二个元素,大小为4个字节。
  6. a + 1a没有单独放在sizeof内部,也没有&,那么a就是首元素的地址,也就是第一行的地址,所以a + 1就是第二行的地址,是地址大小就是4或者8个字节。
  7. 由于a + 1是一个数组指针,指向的是第二行的地址,对数组指针进行解引用,访问的是一个数组的大小,所以*(a + 1)的大小就是16个字节。
  8. &a[0] + 1)a[0]是第一行的数组名,那么&a[0]取出的就是第一行的地址,那么&a[0] + 1)就是第二行的地址,是地址大小就是4或者8个字节。
  9. 由于&a[0] + 1)指向第二行的地址,对地址进行解引用,访问的是一个数组的大小,所以*(&a[0] + 1)的大小就是16个字节。
  10. 由于数组名a就是数组首元素的地址,也就是第一行的地址,所以*a就是第一行的元素,大小为16个字节。
  11. 虽然a[3]这一行实际上并不存在,但是由于sizeof不会真实计算表达式里面的值,而是根据类型进行推断,所以sizeof根据推断得到a[3]a[0]a[1]a[2]为同一个类型,所以结果为16个字节。

4 指针

例:

#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表示首元素地址,那么a + 1表示的就是第二个元素的地址,对地址进行解引用,所以第一个输出的值为2.

&a表示的是取出整个数组的地址,所以&a + 1表示的就是跳过数组a后一个位置的地址,那么int* ptr = (int*)(&a + 1)表示的就是把跳过数组a后一个元素的地址强制转化为int*类型后再赋给指针变量ptr,那么*(ptr - 1)实际上就是数组最后一个元素,所以第二个输出的值为5。

例:

#include <stdio.h>
//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结构是啥?
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

题目中定义了一个类型为struct Test的结构体指针变量p,并把0x100000作为地址赋给了p。由于题目已经告诉我们结构体的大小是20个字节,那么p + 1加的就应该是20,而%p表示的是以十六进制的形式打印地址,所以结果为0010014

(unsigned long)p + 0x1中,由于p被强制转换为了unsigned long也就是无符号整型,所以此时p不再是指针变量,那么p + 1表示的就是整型加1,所以结果为00100001

类似地,(unsigned int*)p + 0x1中,p被强制转换为(unsigned int*)p类型的指针,那么p + 1加的就应该是4,所以结果为00100004

例:

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

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

由于逗号表达式是从左到右依次计算,返回的是括号里面最右边的值,所以实际上初始进数组的值只有135,剩下的空间被初始化为0p = a[0]中的a[0] 是第一行的数组名,而数组名又是首元素的地址,所以就相当于把第一个元素也就是1的地址赋给p,也就是说p = a[0]等价于p = &a[0][0],而p[0]又等价于*(p + 0),所以输出的值自然就是1

例:

//假设环境是x86环境,程序输出的结果是啥?
#include <stdio.h>
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;
}

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

不难发现,&p[4][2] - &a[4][2]本质就是指针的加减,而指针 - 指针值的绝对值是指针之间元素的个数。我们不难找到a[4][2]的位置,p的首元素地址是a,而p指向的是一个4个整型元素的数组,也就是说它每加一次只会跳过4个整型元素,那么p[4][2]的位置就应该如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从图中可以看出,a[4][2]的地址比p[4][2]要大,那么&p[4][2] - &a[4][2]的值就应该是个负数,而两个指针之间元素的个数为4,所以结果应为-4,但是%p表示的是按十六进制的形式打印地址,又由于-4在内存中是以补码的形式存储,而地址其实是一个无符号数,所以这里就相当于把-4的补码按十六进制的形式打印出来,10000000000000000000000000000100-4的原码,11111111111111111111111111111100-4的补码,那么把二进制转换成十六进制结果就为FFFFFFFC.

例:

#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));
 	return 0;
}

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

&aa取出的是整个数组的地址,那么&aa + 1就应该取的是跳过整个数组后的地址。将&aa + 1这个地址赋给int *类型的ptr1后,那么ptr1 - 1实际上就是回退一个整型,也就是10的地址,解引用自然就是10

aa是数组名,在二维数组中就是第一行的地址,那么aa + 1就是跳过一行也就是第二行的地址,对第二行的地址进行解引用实际上就相当于拿到了第二行的数组名,也就是说*(aa + 1)实际上就等价于aa[1], 而aa[1]在没有sizeof&修饰的情况下表示的是首元素的地址,把aa[1]赋给ptr2,那么ptr2代表的就是6的地址。ptr2 - 1表示回退一个整型,指向的就是5的地址,解引用自然就是5

注意:int *ptr1 = (int *)(&aa + 1)由于两边的类型是一样的,所以这里进行强制类型转换是没有意义的,只是起到干扰的作用,int *ptr2 = (int *)(*(aa + 1))同理。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

例:

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

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

字符串作为元素存储在char *类型的指针数组中时,存放的实际上是首字符的地址,所以数组a中的内存布局实际上如下图所示:

又由于a指向的是首元素的地址,而a数组元素的类型为char*,那么存放char*类型元素的地址就应该用char**类型,所以char**pa = a就是把a中首元素的地址赋给pa。那么pa++指向的位置就应该如下图所示:

那么解引用之后输出的就应该是at

注意:char *a[] = {"work","at","alibaba"}这种写法并不是把字符串存放到数组中去,而是把字符串首字符的地址存放进去,也就是字符指针数组。

例:

#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);
	printf("%s\n", *-- * ++cpp + 3);
	printf("%s\n", *cpp[-2] + 3);
	printf("%s\n", cpp[-1][-1] + 1);
	return 0;
}

输出结果:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

首先我们来看数组c的内存布局:

而数组cp中存放的c + 3,c + 2,c + 1,c,就应该存在下面一个关系:

此时,cp中存放都是char**类型的地址,那么char*** cpp = cp表示的就是把cp首元素的地址赋给cpp,如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

搞清楚了内存布局,这个时候我们再来理解**++cpp

首先我们来看第一个输出。cpp本来指向的是cp首元素的地址,++cpp操作后改为指向cp第二个元素的地址,对cpp进行两次解引用操作后访问到的就是POINT.

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意:这个时候,cpp指向的就已经不是cp首元素的地址了,即后续对cpp进行的加减操作都是基于上一次运算的结果进行的。

第二个输出中,我们首先要理清表达式的运算顺序。*-- * ++cpp + 3中,先进行++cpp操作,解引用一次后得到c+1,然后进行--操作,得到的结果是c,也就是说cp的第三个元素本来指向的是c中的第二个元素,经过--后改为指向了c中的第一个元素。--之后再解引用,拿到的就是ENTER中首字母的地址,这个时候再+3,拿到的就是第四个字母的地址,这个时候再按照%s的形式打印,输出的结果就是从第四个字母开始的字符串,也就是ER

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

第三个输出。*cpp[-2] + 3中,我们要清楚cpp[-2]实际上等价于*(cpp-2),也就是说*cpp[-2] + 3**(cpp-2) + 3是等价的。

那么这条表达式先执行的是cpp-2,让cpp指向c+3的地址,然后进行两次解引用操作,拿到FIRST中首字母的地址,这个时候再+3,拿到的就是第四个字母的地址,这个时候再按照%s的形式打印,输出的结果就是从第四个字母开始的字符串,也就是ST

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

注意:由于cpp-2没有让cpp本身的值发生改变,所以此时的cpp实际上指向的还是cp中第三个元素的位置。

第四个输出。cpp[-1][-1] + 1实际上等价于*(*(cpp-1)-1)+1,那么这条表达式先执行的是cpp-1,让cpp指向c+2的地址,解引用后-1,将c+2改成c+1,然后再进行一次解引用,拿到NEW首字母的地址,这个时候再+1,拿到第二个字母的地址,这个时候再按照%s的形式打印,输出的结果就是从第四个字母开始的字符串,也就是EW

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HackerKevn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值