系列文章目录
指针(二)带你搞定闻之色变的指针——超详细版!(指针与数组及其应用冒泡排序)
指针(三)带你搞定闻之色变的指针——超详细版!(指针、函数、数组及其应用转移表)
指针(四)带你搞定闻之色变的指针——超详细版!(指针的应用之库函数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
总结
听完课之后很久做的总结,有些知识点感觉课上理解,这两天写文章的时候感觉有很多不理解的地方,慢慢思考、以及一些迷惑的地方通过问老师、查资料得到解决。感觉可能上课还不够用心,要专心听课;同时及时梳理知识点,不然忘得快哦~及时写博客,知识点经过自己的整理、梳理、加工才有可能变成自己的!
内容很多,希望读到这篇文章的你有所收获,星光不负赶路人,岁月不负有心人
,加油鸭!