【指针三:穿越编程边界的超能力】

news2024/10/5 14:24:50

本章重点

        9.指针和数组面试题的解析

        10. 指针笔试题

九、指针和数组面试题的解析

1、一维数组的sizeof

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

数组名就是数组首元素的地址
    两个特殊情况
        1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节


        2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址


  • printf("%d\n", sizeof(a));

特殊情况1,计算整个数组的大小,4 * 4 = 16

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

数组名不是单独出现,a/(a + 0)表示数组首元素的地址,地址就是指针,4/8

  • printf("%d\n", sizeof(*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));

特殊情况2,数组名表示整个数组,取出的是整个数组的地址,地址是指针,4/8

  • printf("%d\n", sizeof(*&a));

解法一:sizeof(*&a) <==> sizeof(a),16

解法二:&a -> int(*p)[4]数组指针 - 解引用*访问的就是数组,16

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

&a取出的是整个数组的地址,&a+1是跳过这个数组的地址,地址是指针,4/8

  • printf("%d\n", sizeof(&a[0]));

&a[0])获取的是数组首元素的地址,地址是指针,4/8

  • printf("%d\n", sizeof(&a[0] + 1));

&a[0])获取的是数组首元素的地址,&a[0] + 1获取的是数组第二元素的地址,地址是指针,4/8   

代码图解

运行结果 

 2、字符数组的sizeof和strlen

#include<stdio.h>
#include<string.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));

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

数组名就是数组首元素的地址
    两个特殊情况
        1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节


        2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址


  • printf("%d\n", sizeof(arr));

数组名单独放在sizeof内部,arr表示整个数组,计算整个数组的大小,6 * sizeof(char) = 6

  • printf("%d\n", sizeof(arr + 0));

数组名不是单独出现,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




strlen统计的是在字符串中\0之前的字符的个数,如果没有\0,就会一直往后找

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

字符数组arr中没有\0,所以求字符串长度就会一直往后找,随机值

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

arr+0是首元素的地址,字符数组arr中没有\0,所以求字符串长度就会一直往后找,随机值

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

strlen(参数是地址),*arr是首元素-'a'-对应的ASCII码值为97,把97当作地址,非法访问,error

  • printf("%d\n", strlen(arr[1]));

arr[1] - 'b' - 对应的ASCII码值为98,把98当作地址传参,非法访问,error

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

&arr取出的是整个数组的地址,&arr的类型数组指针char(*)[6]
strlen(const char *str)类型是字符指针,此时会发生数组指针转为字符指针的一个过程
传过去的值不会发生改变,只是类型发生改变
数组的地址和首元素的地址是一样滴,传递给strlen函数后,依然是从数组的第一个元素的位置寻找\0,所以大小仍然是随机值

  • printf("%d\n", strlen(&arr + 1));

&arr+1是跳过这个数组的地址,从数组的第七个元素的位置寻找\0,随机值,比strlen(&arr)小6

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

&arr[0]+1是取出第二个字符的地址,从数组的第二个元素的位置寻找\0,随机值,比strlen(&arr)小1

代码图解

运行结果:

#include<stdio.h>
#include<string.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));

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

数组名就是数组首元素的地址
    两个特殊情况
        1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节


        2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址

char arr[] = "abcdef";//这种写法数组的内容是[a b c d e f \0]


  • printf("%d\n", sizeof(arr));

数组名单独放在sizeof内部,arr表示整个数组,计算整个数组的大小,7 * sizeof(char) = 7

  • printf("%d\n", sizeof(arr + 0));

数组名不是单独出现,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




strlen统计的是在字符串中\0之前的字符的个数,如果没有\0,就会一直往后找

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

字符数组arr中有\0,字符串长度是\0之前的字符,不包括\0,6

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

arr+0是首元素的地址,字符数组arr中有\0,字符串长度是\0之前的字符,不包括\0,6

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

strlen(参数是地址),*arr是首元素-'a'-对应的ASCII码值为97,把97当作地址,非法访问,error

  • printf("%d\n", strlen(arr[1]));

arr[1] - 'b' - 对应的ASCII码值为98,把98当作地址传参,非法访问,error

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

&arr取出的是整个数组的地址,&arr的类型数组指针char(*)[6]
strlen(const char *str)类型是字符指针,此时会发生数组指针转为字符指针的一个过程
传过去的值不会发生改变,只是类型发生改变
数组的地址和首元素的地址是一样滴,传递给strlen函数后,依然是从数组的第一个元素的位置寻找\0,字符数组arr中有\0,字符串长度是\0之前的字符,不包括\0,6

  • printf("%d\n", strlen(&arr + 1));

&arr+1是跳过这个数组的地址,从数组的第八个元素的位置寻找\0,随机值

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

&arr[0]+1是取出第二个字符的地址,从数组的第二个元素的位置寻找\0,5

代码图解:

 运行结果:

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

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

数组名就是数组首元素的地址
    两个特殊情况
        1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小,单位是字节


        2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址

"abcdef" - 常量字符串 - [a,b,c,d,e,f,\0]
char* p = "abcdef";//这里指针变量p存放的是'a'的地址


  •     printf("%d\n", sizeof(p));

p是一个指针变量,4/8

  •     printf("%d\n", sizeof(p + 1));

p+1是第二个字符'b'的地址,地址就是指针,4/8

  •     printf("%d\n", sizeof(*p));

*p就是字符'a',字符'a'的数据类型是char,sizeof(char), 1

  •     printf("%d\n", sizeof(p[0]));

p[0]就是字符'a',字符'a'的数据类型是charsizeof(char), 1

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

p是指针变量,&p就是二级指针,同样是地址,4/8

  •     printf("%d\n", sizeof(&p + 1));

p是指针变量,&p就是二级指针,&p+1就是跳过这个二级指针变量,同样是地址,4/8

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

&p[0]就是取字符'a'的地址,&p[0]+1就是第二个字符'b'的地址,地址就是指针,4/8




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

常量字符串中有\0,字符串长度是\0之前的字符,不包括\0,6

  •     printf("%d\n", strlen(p + 1));

p+1就是字符'b'的地址, 从常量字符串的第二个元素的位置寻找\0,5

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

*p就是字符'a'-对应的ASCII码值为97,把97当作地址,非法访问,error

  •     printf("%d\n", strlen(p[0]));

p[0]就是字符'a'-对应的ASCII码值为97,把97当作地址,非法访问,error

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

&p是二级指针,存放的是指针变量p的地址,不知道\0的位置,随机值

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

&p+1是跳过这个二级指针变量,不知道\0的位置,随机值

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

&p[0]+1就是第二个字符'b'的地址,从常量字符串的第二个元素的位置寻找\0,5

代码图解:

运行结果:

3、二维数组的sizeof

#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("%d\n", sizeof(a));

数组名单独放在sizeof内部,a表示整个数组,计算整个数组的大小,12*sizeof(int)=48

  • printf("%d\n", sizeof(a[0][0]));

a[0][0]表示第一行第一列的数据,数据类型是整型,4

  • printf("%d\n", sizeof(a[0]));

表示二维数组的第一行元素,该行有4个元素,可以当作一个一维数组存放四个数据,a[0]是第一行这个一维数组的数组名,数组名单独出现,计算的是整个数组的大小,16

  • printf("%d\n", sizeof(a[0] + 1));

a[0]是第一行这个一维数组的数组名,数组名不单独出现,a[0]表示第一行数组的首元素地址,也就是a[0][0]的地址,所以a[0]+1是第一行第二个元素的地址,4/8

  • printf("%d\n", sizeof(*(a[0] + 1)));

a[0]+1是第一行第二个元素的地址,解引用就是a[0][1],数据类型是整型,4

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

a是这个二维数组的数组名,数组名不单独出现,a表示这个二维数组的首元素地址,a+1就表示第二行这个一维数组的地址,4/8

  • printf("%d\n", sizeof(*(a + 1)));

*(a + 1)) <==> a[1],表示二维数组的第二行元素,该行有4个元素,可以当作一个一维数组存放四个数据,a[1]是第二行这个一维数组的数组名,数组名单独出现,计算的是整个数组的大小,16

  • printf("%d\n", sizeof(&a[0] + 1));

&a[0]表示取出第一行一维数组的地址,&a[0]+1就表示第二行这个一维数组的地址,4/8

  • printf("%d\n", sizeof(*(&a[0] + 1)));

*(&a[0] + 1) <==> *(a+1) <==> a[1],16

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

数组名不单独出现,a表示首元素地址,也就是第一行的地址,*a <==> *(a+0) <==> a[0],16

  • printf("%d\n", sizeof(a[3]));

越界,但是sizeof并不会访问内存,a[3] <==> a[2] <==> a[1] <==> a[0],1

代码图解:

运行结果:

上面的sizeof(a[3])越界了,但是程序依然没报错,依然可以运行,为什么呢?

一个程序需要经过编译+链接->可执行程序->运行,sizeof不访问内存,在编译期间就已经执行了,而b = a + 1是在运行期间执行的。

总结: 数组名的意义:

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表示首元素的地址。

10、指针笔试题

demo1:

#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;
}
//程序的结果是什么?
  1. int a[5] = { 1, 2, 3, 4, 5 }; 定义了一个整数数组 a,并初始化其元素为 1、2、3、4 和 5。

  2. int* ptr = (int*)(&a + 1); 定义了一个指针 ptr,它指向数组 a 的后一个位置。在这里,&a 获取整个数组的地址,然后 +1 使指针指向下一个内存位置,即数组末尾后面的位置。

  3. printf("%d,%d", *(a + 1), *(ptr - 1)); a是数组名,数组名不单独出现,a表示数组首元素的地址,a+1表示第二个元素的地址,*(a + 1)表示第二个元素,prt是int* 类型,此时表示跳过整个数组的地址,ptr - 1就指向了数组的第五个元素的地址,*(ptr - 1)就是5

  4. 程序的结果是什么?2,5

demo2:
 

#include<stdio.h>
//由于还没学习结构体,这里告知结构体的大小是20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;
//假设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. printf("%p\n", p + 0x1); 这里对指针 p 进行加法操作,p + 0x1 实际上是将指针 p 往后移动了一个结构体 Test 的大小(20个字节),所以结果为 0x100000 + 0x14 = 0x100014

  2. printf("%p\n", (unsigned long)p + 0x1); 这里强制将指针 p 转换为无符号长整型后再进行加法操作,即对数值进行计算,0x100000 + 0x1 = 0x100001

  3. printf("%p\n", (unsigned int*)p + 0x1); 这里强制将指针 p 转换为整型指针后再进行加法操作,整型指针的大小是4个字节,所以在进行加法操作时会跳过4个字节,即 0x100000 + 0x4 = 0x100004

  4. 程序的结果是什么?0x10014,0x10001,0x10004

demo3:

#include<stdio.h>
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;
	//程序的结果是什么?
}

代码图解:

  1. int* ptr1 = (int*)(&a + 1); 定义了一个指针 ptr1,它指向数组 a 后一个位置。在这里,&a 获取整个数组的地址,然后 +1 使指针指向下一个整数数组,即在数组 a 之后的位置。

  2. int* ptr2 = (int*)((int)a + 1); 这一行定义了一个指针 ptr2,它指向数组 a 的首地址后一个字节的位置。在这里,(int)a 将数组 a 的首地址转换为整数类型,然后 +1 使指针指向首地址后一个字节的位置。

让我们来计算这两个值的具体结果:

  • ptr1[-1]: ptr1 指向了数组 a 后面的位置,而 ptr1[-1] <==> *(ptr1-1),也就是从数组的后面的位置减1,由于ptr1的类型是指针类型,-1操作的步长是所指向数据类型,也就是sizeof(int)4个字节,指向元素3的位置。

  • *ptr2: ptr2 指向数组 a 的首地址后一个字节的位置,所以 *ptr2 取出了元素1的00 00 00三个字节,取出来元素2的02字节,00 00 00 02,由于是小端存储,实际数据是0x02 00 00 00.

  • 程序的结果是什么?4,2000000

demo4:

#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;
	//程序的结果是什么?
}

  1. int a[3][2] = { (0, 1), (2, 3), (4, 5) }; 这一行定义了一个二维整数数组 a,其中有 3 行 2 列,每个元素被初始化为 (0, 1)(2, 3)(4, 5)。请注意,(0, 1)(2, 3)(4, 5) 都是逗号运算符的结果,实际上只有后面的值 (1, 3, 5) 会被用于初始化。

  2. int* p; 这一行定义了一个整型指针 p

  3. p = a[0]; 这里将指针 p 指向二维数组 a 的第一行的首地址,即数组 a[0] 的首地址。

  4. printf("%d", p[0]); 这一行使用 printf 函数打印 p[0] 的值。由于 p 指向了数组 a[0] 的首地址,所以 p[0] 相当于访问了数组 a[0] 的第一个元素,即 (1, 3, 5) 中的第一个元素,也就是 1

  5. 程序的结果是什么?1

demo5:

#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;
	//程序的结果是什么?
}

代码图解

 

  1. int a[5][5]; 定义了一个二维整数数组 a,有 5 行 5 列。由于没有初始化,数组 a 的元素值是未定义的。

  2. int(*p)[4]; 定义了一个指向包含 4 个整数的一维数组的指针 p

  3. p = a; 这里将指针 p 指向二维数组 a 的第一行的首地址,即数组 a[0] 的首地址。

  4. &p[4][2] - &a[4][2]这一表达式用来计算 &p[4][2]&a[4][2] 之间的距离,也就是这两个地址之间相差的元素个数。

  5. &p[4][2] - &a[4][2] &p[4][2] 为指针 p 所指向的位置的第 4 行第 2 列的元素地址,而 &a[4][2] 为数组 a 的第 4 行第 2 列的元素地址。这两个地址之间相差 4 个 int 类型的元素,因为 p 指向的一维数组有 4 个元素。

  6. 程序的结果是什么?FFFFFFFC,-4

demo6:

#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;
	//程序的结果是什么?
}
  1. int* ptr1 = (int*)(&aa + 1); 定义了一个指针 ptr1,它指向数组 aa 后一个位置。在这里,&aa 获取整个数组 aa 的地址,然后 +1 使指针指向下一个二维整数数组的位置,即在数组 aa 之后的位置。

  2. int* ptr2 = (int*)(*(aa + 1)); 定义了一个指针 ptr2,它指向数组 aa 的第二行的首地址。*(aa + 1) 实际上是取数组 aa 的第二行的首元素的地址。

  • 现在,让我们计算这两个值的具体结果:

  • *(ptr1 - 1): ptr1 指向了数组 aa 后面的位置,而 ptr1 - 1 则是指向数组 aa 之前一个整数数组的首元素。这个整数数组是 {6, 7, 8, 9, 10},所以 *(ptr1 - 1) 取出了这个整数数组的最后一个元素,即 10
  • *(ptr2 - 1): ptr2 指向数组 aa 的第二行的首地址,而 ptr2 - 1 则是指向数组 aa 第二行之前一个整数的位置。这个整数是 5,所以 *(ptr2 - 1) 取出了 5
  • 程序的结果是什么?10,5

demo7:

#include <stdio.h>
int main()
{
	char* a[] = { "work","at","mihayou" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
	//程序的结果是什么?
}

  1. char* a[] = { "work","at","mihayou" }; 这一行定义了一个字符指针数组 a,其中包含三个元素,分别指向字符串常量 "work"、"at" 和 "mihayou"。

  2. char** pa = a; 这一行定义了一个指向字符指针的指针 pa,并将其指向字符指针数组 a 的第一个元素,即 "work" 的地址。

  3. pa++; 这里将指针 pa 向后移动一个位置,现在 pa 指向字符指针数组 a 的第二个元素,即 "at" 的地址。

  4. printf("%s\n", *pa); 这一行使用 printf 函数打印指针 pa 指向的位置的值,也就是字符指针 "at" 所指向的字符串。

demo8:

#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;
	//程序的结果是什么?
}

参考图:

  1. char* c[] = { "ENTER","NEW","POINT","FIRST" }; 定义了一个字符指针数组 c,其中包含四个元素,分别指向字符串常量 "ENTER"、"NEW"、"POINT" 和 "FIRST"。

  2. char** cp[] = { c + 3,c + 2,c + 1,c }; 定义了一个字符指针指针数组 cp,其中包含四个元素,每个元素是一个字符指针,并分别指向 c 数组的不同位置。具体地说,cp[0] 指向 c 数组的第四个元素 "FIRST" 的地址,cp[1] 指向 c 数组的第三个元素 "POINT" 的地址,cp[2] 指向 c 数组的第二个元素 "NEW" 的地址,cp[3] 指向 c 数组的第一个元素 "ENTER" 的地址。

  3. char*** cpp = cp; 定义了一个字符指针指针指针 cpp,并将其指向字符指针指针数组 cp 的第一个元素,即 cp[0],也就是指向 "FIRST" 的地址。

  4. printf("%s\n", **++cpp); 这一行先执行 ++cpp,将 cpp 指向 cp 数组的第二个元素,即 cp[1],然后再执行 **++cpp,取出指针 cpp 指向的位置的值,也就是字符指针 "POINT" 所指向的字符串。所以这一行输出 "POINT"。

  5. printf("%s\n", *-- * ++cpp + 3); 先执行 ++cpp,将 cpp 指向 cp 数组的第三个元素,即 cp[2],然后再执行 *++cpp,取出指针 cpp 指向的位置的值,接着执行 -- * ++cpp,先对指针 cpp 指向的位置进行解引用,得到 "c+1",然后再执行 -- 操作,指针 cp 指向 cp 数组的第一个元素,即 c[0]。接下来,执行 * c[0] + 3,取出 "ENTER" 的地址并加上 3,所以这一行输出 "ER".

  6. printf("%s\n", *cpp[-2] + 3); 访问 cpp[-2] <==> **(cpp-2),即 cp 数组的第一个元素,指向 "c+3" 的地址。然后执行 * cpp[-2] + 3,取出 "FIRST" 的地址并加上 3,所以这一行输出 "ST".

  7. printf("%s\n", cpp[-1][-1] + 1); 访问 cpp[-1],即 cp 数组的第一个元素,指向 "c+2" 的地址。然后再访问 cpp[-1][-1] <==> *(*(cpp-1)-1),即指向 "c+2" 的指针减去 1,指向 "NEW" 的地址。最后执行 cpp[-1][-1] + 1,取出 "NEW" 的地址并加上 1,所以这一行输出 "EW".

  8. 程序的结果是什么?POINT,ER,ST,EW

本章结束啦!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/816846.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探索运营商渠道佣金数字化运营

当前全球经济增长放缓&#xff0c;行业竞争持续加剧已是常态&#xff0c;用户需求越发苛刻、经营成本不断上升。内忧外患&#xff0c;企业经营如何突围&#xff1f;越来越多的企业发现&#xff0c;融合数字化技术的IT解决方案为企业提供了一种解决问题的可能。 数字化运营可以帮…

反转链表(JS)

反转链表 题目 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]示例 2&#xff1a; 输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]示例 3&…

809协议

809协议 目录概述需求&#xff1a; 设计思路实现思路分析1.809协议数据流——链路管理类 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better result,wait for change,…

【吐槽贴】项目居然因为采购管理失控被迫暂停了?

最近&#xff0c;手上的一个大型项目好不容易解决了进度延误、范围蔓延、质量不过关等难点&#xff0c;结果差点掉进了成本失控的坑里。没想到咱项目经理还要全权负责项目的采购管理&#xff0c;必须要分享出来&#xff0c;让大家避避雷。 先给大家介绍一下背景&#xff1a; 我…

ns3.39编译时报错与解决_包括netanim-3.109(NetAnim)

ns&#xff08;来源于“network simulator”&#xff09;是一系列离散事件网络模拟器&#xff0c;包括ns-1、ns-2和ns-3。他们主要应用于研究和教学。ns-3是自由软件&#xff0c;以GNU GPLv2协议分发。​——百度百科 熟悉ns的朋友都知道&#xff0c;使用build.py编译时会先编…

【HttpRunnerManager】搭建接口自动化测试平台实战

目录 一、需要准备的知识点 二、我搭建的环境 三、搭建过程 四、访问链接 五、两个问题点 【整整200集】超超超详细的Python接口自动化测试进阶教程&#xff0c;真实模拟企业项目实战&#xff01;&#xff01; 一、需要准备的知识点 1. linux: 安装 python3、nginx 安装和…

ssl证书费用

SSL证书是一种广泛应用于网站的安全协议&#xff0c;可以确保网站上所有传输的数据加密&#xff0c;避免数据的泄露和篡改。因此&#xff0c;许多网站都会申请SSL证书来保护用户数据的安全性。不过&#xff0c;SSL证书的申请会涉及到费用问题&#xff0c;因此需要考虑一些费用方…

真空电子管、晶体管DRAM

文章目录 1. 导读读完这篇文章&#xff0c;你可以回答什么&#xff1f; 2. 起源3. Q1->A14. Q2->A25. Q3->A36. Reference7. 声明 1. 导读 读完这篇文章&#xff0c;你可以回答什么&#xff1f; 什么是真空电子管&#xff1f;真空电子管的基本原理&#xff1f;为什么…

【MIPI协议 D-PHY基础介绍】

MIPI协议 D-PHY基础介绍 前言一、MIPI介绍1.1 D-PHY MIPI 简单介绍1.2 C-PHY MIPI 简单介绍1.3 M-PHY MIPI 简单介绍 二、D-PHY具体介绍2.1 DSI分层结构2.2 D-PHY电气特性介绍2.3 D-PHY 工作模 三、LINE线上的模式3.1 line线上 state code3.2 high speed data line传输3.3 Low …

后端技术趋势指南|如何选择自己的技术方向

编程多条路&#xff0c;条条通罗马 后台大佬 后台路线都是面对后台服务器业务&#xff0c;比如web后台服务器&#xff0c;视频后台服务器&#xff0c;搜索后台服务器&#xff0c;游戏后台服务器&#xff0c;直播后台服务器&#xff0c;社交IM后台服务器等等&#xff0c;大部分…

全球宕机!以色列最大的炼油厂遭遇黑客攻击

以色列最大的炼油厂运营商BAZAN Group的网站遭遇黑客入侵&#xff0c;全球大范围宕机。 BAZAN集团总部位于海法湾&#xff0c;前身为石油精炼厂有限公司&#xff0c;每年原油炼化规模约980万吨&#xff0c;年收入超135亿美元&#xff0c;员工超过1800人。 BAZAN网站被切断网络…

GuLi商城-前端基础Vue

MVVM思想 M&#xff1a;即Model&#xff0c;模型&#xff0c;包括数据和一些基本操作 V&#xff1a;即View&#xff0c;视图&#xff0c;页面渲染结果 VM&#xff1a;即View-Model&#xff0c;模型与视图间的双向操作&#xff08;无需开发人员干涉&#xff09; Vue.js - 渐…

每天一个电商API分享:淘宝/天猫关键字搜索店铺列表 API

淘宝/天猫搜索店铺列表 API 是一个强大而灵活的工具&#xff0c;可以帮助开发者在自己的应用中集成淘宝/天猫搜索店铺列表的功能。通过这个 API&#xff0c;开发者可以获取指定关键词下的店铺列表&#xff0c;并根据自己的需求自定义搜索条件。该 API 提供丰富的参数选项&#…

2,认识N(logN)的排序【p3】

认识N( logN} 的排序 2.1归并排序2.1.1代码实现归并排序2.1.1.1自己c实现归并排序2.1.1.2gptc实现归并排序2.1.1.3总结2.1.1.4比较行为 2.1.2归并排序使用master公式2.1.3归并排序的扩展2.1.3.1小和问题2.1.3.2逆序对问题 2.2快排、荷兰国旗问题2.2.1问题一2.2.2问题二(荷兰国旗…

AcWing算法提高课-1.3.16背包问题求方案数

宣传一下算法提高课整理 <— CSDN个人主页&#xff1a;更好的阅读体验 <— 本题链接&#xff08;AcWing&#xff09; 点这里 题目描述 有 N N N 件物品和一个容量是 V V V 的背包。每件物品只能使用一次。 第 i i i 件物品的体积是 v i v_i vi​&#xff0c;价值…

IP 监控软件

IP 监控软件可帮助管理员主动监控网络资源。随着各种设备连接到网络&#xff0c;监控设备和接口可能很复杂&#xff0c;为管理员提供这些设备的IP监控&#xff0c;了解其各种性能指标和问题。 使用有效的 IP 监控软件的优势 使用有效的 IP 监控系统和一套全面的 IP 监控工具&…

华为数通HCIP-IGMP(网络组管理协议)

IGMP&#xff08;网络组管理协议&#xff09; 作用&#xff1a;维护、管理最后一跳路由器以及组播接收者之间的关系&#xff1b; 应用&#xff1a;最后一跳路由器以及组播接收者之间&#xff1b; 原理&#xff1a;当组播接收者需要接收某个组别的流量时&#xff0c;会向最后…

Python自带的命令行调试器pdb介绍

Python自带的命令行调试器pdb介绍 Python自带的命令行调试器是pdb&#xff08;Python Debugger&#xff09;。pdb允许开发者在程序执行过程中进行逐行调试&#xff0c;以便查找和修复代码中的错误。 pdb调试器是Python标准库的一部分&#xff0c;无需额外安装任何软件。直接im…

【Golang 接口自动化08】使用标准库httptest完成HTTP请求的Mock测试

目录 前言 http包的HandleFunc函数 http.Request/http.ResponseWriter httptest 定义被测接口 测试代码 测试执行 总结 资料获取方法 前言 Mock是一个做自动化测试永远绕不过去的话题。本文主要介绍使用标准库net/http/httptest完成HTTP请求的Mock的测试方法。 可能有…

2023年值得推荐的5个数据可视化平台

之前看过一篇介绍20款国外常用的数据可视化工具后&#xff0c;很多朋友在评论区表示国内也有很多很不错的主流数据可视化平台&#xff0c;今天就来给大家介绍国内5个主流的数据可视化平台。 1、阿里云DataV DataV数据可视化是使用可视化应用的方式来分析并展示庞杂数据的产品。…