指针(六)带你搞定闻之色变的指针——超详细版!(指针笔试题目保姆级解析)

系列文章目录

指针(二)带你搞定闻之色变的指针——超详细版!(指针与数组及其应用冒泡排序)
指针(三)带你搞定闻之色变的指针——超详细版!(指针、函数、数组及其应用转移表)
指针(四)带你搞定闻之色变的指针——超详细版!(指针的应用之库函数qsort的实现)
指针(五)带你搞定闻之色变的指针——超详细版!(指针相关练习)



前言

很多题目是涉及到操作符sizeof和库函数strlen的,所以我们先结合题目对二者进行分析,包括数组名的理解等内容。

1. sizeof与strlen

sizeof和strlen返回数据类型都是size_t,用变量接收时,变量的类型也要是size_t,且打印用%zd.

1.1 sizeof

sizeof是操作符,sizeof经常用来计算与数组有关的内容。
数组名的理解:只有sizeof(数组名)&数组名这两种情况下指的是整个数组,其余都是指数组名
sizeof(数组名)指的是整个数组,计算的是整个数组的大小
&数组名指的是整个数组,取出的是整个数组的地址,但是存储的是数组首元素的地址,大小只与环境有关,86x是4bytes,64x是8个bytes,区别在于,&数组名+1跳过的是整个数组;
指针+1,指针指向的数据是多大,就跳过多少个字节;
地址的大小只与环境有关,86x是4bytes,64x是8个bytes;
*与&抵消.
sizeof返回的数据类型是size_t,打印要用%zd
sizeof计算变量类型计算变量的大小,单位为字节

1.2 strlen

strlen是库函数,在使用strlen的时候要引入头文件#include <string.h>,strlen接收一个指针作为参数,计算从指针指向元素开始,到’\0’之间的元素个数,字符串末尾有隐藏的’\0’,但是单个字符初始化的字符数组没有’\0’,因此使用strlen可能导致越界访问,计算结果为随机值。


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

2.1 一维数组

2.1.1 整型数组

#include <stdio.h>
int main()
{
	int a[] = { 1,2,3,4 };
	printf("%zd\n", sizeof(a)); 
	//sizeof(数组名)指的是整个数组,计算的是整个数组的大小,4*4=16bytes
	printf("%zd\n", sizeof(a + 0)); //不是sizeof(数组名)和&数组名这两种情况下,指数组首元素的地址
	//计算的是数组首元素的地址的大小,地址的大小只与环境有关,86x是4bytes,64x是8个bytes
	printf("%zd\n", sizeof(*a)); //不是sizeof(数组名)和&数组名这两种情况下,指数组首元素的地址
	//计算的是数组首元素的地址指向的数组元素的大小,int整型数据大小是4bytes
	printf("%zd\n", sizeof(a + 1)); //不是sizeof(数组名)和&数组名这两种情况下,指数组首元素的地址
	//a+1,a是数组首元素的地址,指向数组首元素,类型是整型,+1跳过一个整型,指向第二个元素
	//但本质还是地址,地址的大小,只与环境有关,86x是4bytes,64x是8个bytes
	printf("%zd\n", sizeof(a[1]));//a[1]是以下标访问数组元素,数组元素类型是整型,大小是4bytes
	printf("%zd\n", sizeof(&a));//&数组名指的是整个数组,取出的是整个数组的地址
	//但是存储的是数组首元素的地址,大小只与环境有关,86x是4bytes,64x是8个bytes
	//区别在于,&数组名+1跳过的是整个数组
	printf("%zd\n", sizeof(*&a));//*与&抵消,sizeof(a),指的是整个数组
	//计算的是整个数组的大小,4*4=16bytes 
	printf("%zd\n", sizeof(&a + 1));//&数组名指的是整个数组,取出的是整个数组的地址
	//&a+1跳过整个数组,但还是地址,大小只与环境有关,86x是4bytes,64x是8个bytes
    printf("%zd\n", sizeof(&a[0]));//取出的是下标为0的数组元素的地址,大小只与环境有关,86x是4bytes,64x是8个bytes
    printf("%zd\n", sizeof(&a[0] + 1));//&a[0]取出的是下标为0的数组元素的地址,&a[0]+1,跳过4个字节,指向a[1],但还是地址,大小只与环境有关,86x是4bytes,64x是8个bytes
	
	return 0;
}

在VS2022 64x的测试结果如下

16
8
4
8
4
8
16
8
8
8

2.1.2 字符数组

字符数组的初始化方式有多种,包括字符、字符串、指针等,每种初始化方式的含义有所不同,我们分别进行分析。一共分三种初始化方式,每种初始化方式分别对sizeof和strlen有关笔试题目的解析。

2.1.2.1 单个字符初始化的数组
2.1.2.1.1 sizeof相关题目
#include <stdio.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%zd\n", sizeof(arr));//sizeof(数组名) 计算的是整个数组的大小,1*6=6bytes
	printf("%zd\n", sizeof(arr + 0));//arr指数组首元素的地址,arr+1,跳过一个字节,指向第二个元素,但本质还是地址,地址的大小只与环境有关,86x是4bytes,64x是8个bytes
	printf("%zd\n", sizeof(*arr));//数组名arr指数组首元素的地址,计算的是数组首元素的地址指向的数组元素的大小,char字符型数据大小是1byte
	printf("%zd\n", sizeof(arr[1]));//计算下标为1数组元素的大小,char字符型数据大小是1byte
	printf("%zd\n", sizeof(&arr));//&数组名取出的是整个数组的地址,地址的大小只与环境有关,86x是4bytes,64x是8个bytes
	printf("%zd\n", sizeof(&arr + 1)); //&数组名取出的是整个数组的地址,& arr + 1跳过整个数组,但还是地址,大小只与环境有关,86x是4bytes,64x是8个bytes
	printf("%zd\n", sizeof(&arr[0] + 1));//&a[0]取出的是下标为0的数组元素的地址,&a[0]+1,跳过1个字节,指向a[1],但还是地址,大小只与环境有关,86x是4bytes,64x是8个bytes
	return 0;
}

在VS2022 64x的测试结果如下

6
8
1
1
8
8
8
2.1.2.1.2 strlen相关题目

单个字符初始化的字符数组没有’\0’,因此使用strlen可能导致越界访问,计算结果为随机值。

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%zd\n", strlen(arr));//数组中没有'\0',strlen接收指针作为参数,从数组首元素的地址arr开始计数,遇'\0'停止,数组中没有'\0',结果为随机值
	printf("%zd\n", strlen(arr + 0));//arr是数组首元素的地址,arr+0,跳过0个元素,还是数组首元素的地址,逻辑与上述相同
	//printf("%zd\n", strlen(*arr));//数组名arr指数组首元素的地址,将数组首元素的地址指向的数组元素a的ASCII码值作为地址传给strlen,此处地址不是当前程序能够访问的,系统崩溃
	//printf("%zd\n", strlen(arr[1]));//数组下标为1的元素b的ASCII码值作为地址传给strlen,此处地址不是当前程序能够访问的,系统崩溃
	printf("%zd\n", strlen(&arr));//&数组名指的是整个数组,取出的是整个数组的地址,但是存储的是数组首元素的地址,与传参arr的逻辑一致
	printf("%zd\n", strlen(&arr + 1));//&数组名取出的是整个数组的地址,& arr + 1跳过整个数组,将字符f后面的地址传入strlen,开始计数,遇'\0'停止,比参数为arr的结果小了6
	printf("%zd\n", strlen(&arr[0] + 1));//&arr[0]取出的是下标为0元素的地址,+1跳过1个元素,指向第二个元素,但数组中没有'\0',结果为随机值,比比参数为arr的结果小了1
	return 0;
}

在VS2022 64x的测试结果如下

42
42
//程序崩溃,无法验证
//程序崩溃,无法验证
42
36
41
2.1.2.2 字符串初始化的数组

字符串初始化的字符数组,有隐藏的’\0’,char arr[] = "abcdef";初始化的数组arr如下图所示。
在这里插入图片描述

2.1.2.2.1 sizeof相关题目
#include <stdio.h>
int main()
{
	char arr[] = "abcdef";
	printf("%zd\n", sizeof(arr));//整个数组,包含隐藏的'\0',1*7=7bytes
	printf("%zd\n", sizeof(arr + 0));//地址大小,只与环境有关,86x是4bytes,64x是8个bytes
	printf("%zd\n", sizeof(*arr));//数组首元素的地址指向的数组元素大小,字符是1bytes
	printf("%zd\n", sizeof(arr[1]));//下标为1元素的大小,字符是1bytes
	printf("%zd\n", sizeof(&arr));//&数组名指的是整个数组,取出的是整个数组的地址,但是存储的是数组首元素的地址,与传参arr的逻辑一致
	printf("%zd\n", sizeof(&arr + 1));//&数组名取出的是整个数组的地址,& arr + 1跳过整个数组,存储的是地址,大小只与环境有关,86x是4bytes,64x是8个bytes
	printf("%zd\n", sizeof(&arr[0] + 1)); // &arr[0]取出的是下标为0元素的地址, + 1跳过1个元素,指向第二个元素,是第二个元素的地址,大小只与环境有关,86x是4bytes,64x是8个bytes
	return 0;
}

在VS2022 64x的测试结果如下

7
8
1
1
8
8
8
2.1.2.2.2 strlen相关题目
#include <stdio.h>
#include <string.h>
int main()
{
	char arr[] = "abcdef";
	printf("%zd\n", strlen(arr));//arr表示数组首元素的地址,strlen接收指针作为参数,从数组首元素的地址arr开始计数,遇'\0'停止,数组中有隐藏的'\0',结果为1*6=6bytes
	printf("%zd\n", strlen(arr + 0));//arr是数组首元素的地址,arr+0,跳过0个元素,还是数组首元素的地址,逻辑与上述相同,结果为6bytes
	//printf("%zd\n", strlen(*arr));//数组名arr指数组首元素的地址,将数组首元素的地址指向的数组元素a的ASCII码值作为地址传给strlen,此处地址不是当前程序能够访问的,系统崩溃
	//printf("%zd\n", strlen(arr[1]));//数组下标为1的元素b的ASCII码值作为地址传给strlen,此处地址不是当前程序能够访问的,系统崩溃
	printf("%zd\n", strlen(&arr));//&数组名指的是整个数组,取出的是整个数组的地址,但是存储的是数组首元素的地址,与传参arr的逻辑一致
	printf("%zd\n", strlen(&arr + 1));//&数组名取出的是整个数组的地址,& arr + 1跳过整个数组,将'\0'后面的地址传入strlen,开始计数,遇'\0'停止,结果为随机值
	printf("%zd\n", strlen(&arr[0] + 1));//&arr[0]取出的是下标为0元素的地址, + 1跳过1个元素,指向第二个元素,从第二个元素开始计数,6-1=5bytes
	return 0;
}

在VS2022 64x的测试结果如下

6
6
//程序崩溃,无法验证
//程序崩溃,无法验证
6
26
5
2.1.2.3 字符指针初始化的数组

我们在之前的文章中提到过char* p = "abcdef";意为,将字符串首字符a的地址存在指针变量p中,并且*(p+i)等价于p[i],所以字符指针初始化的字符串也能用首字符地址[下标]的形式进行访问,但是不能进行更改。

2.1.2.3.1 sizeof相关题目
#include <stdio.h>
#include <string.h>
int main()
{
	char* p = "abcdef";
	printf("%zd\n", sizeof(p));//p是指针,指向a,4/8个bytes
	printf("%zd\n", sizeof(p + 1));//p+1是指针,指向b,4/8个bytes
	printf("%zd\n", sizeof(*p));//*p是a,计算a的大小,1bytes
	printf("%zd\n", sizeof(p[0]));//p[0]等价于*(p+0),等价于*p,1bytes
	printf("%zd\n", sizeof(&p));//&p,p是指针变量,大小是4/8个字节
	printf("%zd\n", sizeof(&p + 1));//&p+1,p是指针变量,大小是4/8个字节,&p+1跳过4/8个字节;&p+1是指针变量,大小是4/8个字节
	printf("%zd\n", sizeof(&p[0] + 1)); // &p[0] + 1,&p[0]是指针变量,大小是4 / 8个字节,&p[0] + 1跳过4 / 8个字节,指向p[1], & p[0] + 1是指针变量,大小是4 / 8个字节
	return 0;
}

在VS2022 64x的测试结果如下

8
8
1
1
8
8
8
2.1.2.3.2 strlen相关题目
#include <stdio.h>
#include <string.h>
int main()
{
    char* p = "abcdefgh";
	printf("%zd\n", strlen(p));//p表示字符串首元素的地址,strlen接收指针作为参数,从字符串首元素开始计数,遇'\0'停止,数组中有隐藏的'\0',结果为1*8=6bytes
	printf("%zd\n", strlen(p + 1));//p+1是指针,指向b,从第二个元素开始计数,8-1=7bytes
	//printf("%zd\n", strlen(*p));//p指字符串首元素的地址,*p表示将数组首元素的地址指向的数组元素a的ASCII码值作为地址传给strlen,此处地址不是当前程序能够访问的,系统崩溃
	//printf("%zd\n", strlen(p[0]));//p[0]等价于*(p+0),等价于*p,逻辑与上述相同,程序崩溃
	printf("%zd\n", strlen(&p));//p是char*一级指针,&p是char**二级指针,将&p这个变量的地址传给strlen,指向p,从p开始计数,遇'\0'停止,随机值
	printf("%zd\n", strlen(&p + 1));//&p是指针变量,大小是&p+14/8个字节,跳过4/8个字节,将&p+1的地址传给strlen,指向p后面的char*指针变量,从此处开始计数,遇'\0'停止,随机值
	printf("%zd\n", strlen(&p[0] + 1)); //&p[0]等价于*(p+0),取出的是a的地址, + 1跳过1个元素,指向第二个元素,从第二个元素开始计数,8 - 1 = 7bytes
	return 0;
}

在VS2022 64x的测试结果如下

8
7
//程序崩溃,无法验证
//程序崩溃,无法验证
6
30
7

2.2 二维数组

二维数组在内存中连续存放,二维数组还是数组,数组名除sizeof(数组名)和&数组名两种情况外,均表示首元素的地址。
二维数组可以看做一维数组构成的一维数组,如下图所示,每一行看做一个一维数组,arr[3][4]可以看做{arr[0],arr[1],arr[2]}.
在这里插入图片描述

那么此时的数组名arr表示首元素地址时,也就是表示arr[0]数组的地址。

#include <stdio.h>
int main()
{
	int a[3][4] = { {1,2,3,4},{5,6,7,8},{9,10,11,12} };
	printf("%zd\n", sizeof(a));//sizeof(数组名)指的是整个数组,计算的是整个数组的大小,12*4=48bytes
	printf("%zd\n", sizeof(a[0][0]));//计算第一行第一列数组元素的大小,数据类型为整型,大小是四个字节
	printf("%zd\n", sizeof(a[0]));//arr[0]看做一个数组,计算的是一维数组的大小,4*4=16bytes
	printf("%zd\n", sizeof(a[0] + 1));//a[0]表示数组首元素的地址,+1跳过一个整型,指向a[0][1],a[0] + 1是指针变量,大小是4/8个字节
	printf("%zd\n", sizeof(*(a[0] + 1)));//是2,整型,大小是四个字节
	printf("%zd\n", sizeof(a + 1));//a是二维数组首元素的地址,也就是一维数组arr[0]的地址,+1,跳过一维数组,指向arr[1]的一维数组,类型是int(*)[4],是指针变量,大小是4/8个字节
	printf("%zd\n", sizeof(*(a + 1)));//得到arr[1]数组,大小16字节
	//有两种理解,1.*(arr+1)等价于arr[1],是第一行数组的数组名,sizeof(数组名)是16字节
	//2.a表示数组首元素的地址,也就是a[0]的地址,+1也就是a[1]的地址,解引用得到a[1]数组,大小16字节
	printf("%zd\n", sizeof(&a[0] + 1));//取出arr[0]一维数组的地址,&a[0]+1指向a[1],但还是指针变量,大小是4/8个字节
	printf("%zd\n", sizeof(*(&a[0] + 1)));//得到arr[0],大小16字节
	printf("%zd\n", sizeof(*a));//得到a[0],大小16字节
	//同理,*(二维数组名+n)有两个理解角度,一是直接等价a[n],二是数组名表示首元素,第一行的地址,+n得到第n行地址,解引用得到第n行;
	printf("%zd\n", sizeof(a[3]));//sizeof内部的表达值是不会真实计算的,会根据数据类型判断大小,调试可以看到a[3]的数据类型是int[4],大小是16字节
	//也可以这样理解,a[3]等价于*(a+3),a表示数组首元素的地址,也就是a[0]整个一维数组的地址,+3跳过3个一维数组,类型还是数组指针,解引用得到数组,与原来的a[0]数组大小一致
	return 0;
}

在VS2022 64x的测试结果如下

48
4
16
8
4
8
16
8
16
16
16

关于sizeof内部的表达值是不会真实计算的,下面有一个实例,左边sizeof内部的s=n+5并没有执行,s的结果还是4;但右边s=n+5执行了,s的结果是17.

在这里插入图片描述


3. 指针运算笔试题解析

3.1 数组名理解 指针加减整数 强制类型转换

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

解析如下

#include <stdio.h>
int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);//&a取出整个数组的地址,&a+1跳过整个数组,指向5后面的位置,&a+1是int*[5],(int*)将其强制转化为整型指针
	printf("%d,%d", *(a + 1), *(ptr - 1));//a+1表示数组首元素的地址跳过一个整型,指向2,*(a+1)也就是2
	//ptr-1,往回退1个整型,指向5,*(ptr-1)表示5
	return 0;
}
//程序的结果是什么?2,5

在VS2022 64x的测试结果如下

2,5

3.2 结构体指针 指针加减整数 强制类型转换

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

解析如下

//在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);//p指向结构体,大小为20字节,p+1跳过20个字节,十六进制表示为0x100014
	printf("%p\n", (unsigned long)p + 0x1);//p被强制转化为无符号长整型,整型+1,结果是0x100001
	printf("%p\n", (unsigned int*)p + 0x1);//p被强制转化为无符号整型指针,+1跳过4个字节,结果是0x100004
	return 0;
}

在VS2022 64x的测试结果如下

0x100014
0x100001
0x100004

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

解析如下

#include <stdio.h>
int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//逗号表达式的值为其计算结果,因此a为{{1,3},{5,0},{0,0}}
	int* p;//p是整型指针
	p = a[0];//a[0]是一维数组的数组名,表示数组首元素的地址,也就是a[0][0]的地址
	printf("%d", p[0]);//p[0]也就是*(p+0),也就是对p指向的内容解引用,a[0][0],也就是1
	return 0;
}

在VS2022 64x的测试结果如下

1

3.4 数组名理解 内存分布 进制转换

//假设环境是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和a

a表述数组首元素也就是a[0]的地址,类型为int(* )[5],p是int(* )[4]数组指针,所以p只能存a[0]~a[4]的地址,p[4]等价于*(p+4),p是数组指针,p+4跳过4个大小为4的数组,且数组在内存中是连续存放的,我们可以得到下面的内存图,
在这里插入图片描述
&p[4][2] - &a[4][2],指针-指针,前小后大,得到的是相差元素的个数的相反数,也就是-4,而指针是无符号整型,无符号整型原码反码补码一致,当以"%p"打印-4时,-4在内存中的补码如下,会被当成无符号整型打印,原反补相同

在这里插入图片描述
在VS2022 86x的测试结果如下

FFFFFFFC,-4

3.5 数组名理解 指针加减整数 强制类型转换

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

解析如下

#include <stdio.h>
int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	int* ptr1 = (int*)(&aa + 1);//&aa取出整个数组的地址,+1跳过整个数组,指向10后面的位置,类型是int(*)[2][5],被强制转化为int*,
	//ptr-1后退一个整型,指向10
	int* ptr2 = (int*)(*(aa + 1));//aa数组首元素aa[0]的地址,+1跳过aa[0]一维数组,指向aa[1],*(aa+1)表示aa[1],aa[1]是数组名,表示数组aa[1]首元素的地址,(int*)强制类型转换,得到首元素aa[1][0]的地址
	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//ptr-1得到aa[0][4]的地址
	return 0;
}

在VS2022 86x的测试结果如下

10,5

3.6 数组名理解 二级指针 指针加减整数

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

看到"alibaba",这个题可能是阿里的面试题!
2.1.2.3 字符指针初始化的数组我们提到char* p = "abcdef";意为,将字符串首字符a的地址存在指针变量p中,所以char* a[] = { "work","at","alibaba" };这句代码意为,将字符串"work", “at”, “alibaba"首字符的地址分别存在a数组中,即char* a[]={&w,&a,&a}
char** pa = a;a是数组首元素的地址,a中存放’w’的地址0x00007ff6d9c19c10,pa存放a的地址0x000000178df8fc28,具体内存分布如下图所示;当pa++,跳过一个数组元素,pa指向a[1],也就是"at"的’a’的地址,打印得到"at”.

在这里插入图片描述
在VS2022 86x的测试结果如下

at

3.7 数组名理解 二级指针 指针加减整数

#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, cpp的对应关系画出来,如下图所示,接着进行分析
在这里插入图片描述

操作符优先级:[]>前置++>*>+
printf("%s\n", **++cpp);如下图所示,cpp中存的是数组名cp,也就是数组首元素的地址,cpp指向cp[0],是二级字符指针类型,cpp++跳过一个字符指针,指向c+2,*cpp++得到c+2, **cpp++等价于 *(c+2),c是字符指针数组的数组名,是首元素的地址,c+2跳过2个字符指针,指向c[2],也就是"POINT"首字符的地址,因此该行代码的打印结果是POINT;

同时我们知道,前置++会改变操作变量的值,因此此次代码改变了cpp的指向,cpp++后指向c+2,如下图所示,

在这里插入图片描述

printf("%s\n", *--*++cpp+3);++cpp跳过一个二级字符指针,指向c+1,解引用得到cp[2],- -后cp[2]变成c,解引用得到"ENTER"首字符"E"的地址,+3得到"T"的地址,打印得到ER;

后置++与前置- -使cpp的指向和cp[2]发生了变化,如下图所示
在这里插入图片描述

printf("%s\n", *cpp[-2]+3);cpp指向cp[2],cpp[-2]等价于*(cpp-2),首先后退两个二级指针,指向cp[0],解引用得到c+3,再解引用得到"FIRST"首字符的地址,+3得到S的地址,打印得到ST;

printf("%s\n", cpp[-1][-1] + 1);cpp[-1]等价于*(cpp-1)得到c+2, *(c+2-1)得到"NEW"首字符的地址,+1得到E的地址,打印得到EW.

在VS2022 64x的测试结果如下

POINT
ER
ST
EW

总结

听完课之后很久做的总结,有些知识点感觉课上理解,这两天写文章的时候感觉有很多不理解的地方,慢慢思考、以及一些迷惑的地方通过问老师、查资料得到解决。感觉可能上课还不够用心,要专心听课;同时及时梳理知识点,不然忘得快哦~及时写博客,知识点经过自己的整理、梳理、加工才有可能变成自己的!

内容很多,希望读到这篇文章的你有所收获,星光不负赶路人,岁月不负有心人,加油鸭!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值