1.sizeof和strlen的对比
sizeof计算变量所占内存空间的大小的,单位是字节
sizeof只关注内存空间的大小,不在乎内存中方的什么数据
sizeof是操作符不是函数
sizeof会将 算进去的
代码语言:javascript复制// sizeof计算变量所占内存空间的大小的,单位是字节
int main()
{
int a = 10;
printf("%dn", sizeof(int));//输出结果是4
printf("%dn", sizeof(a));//输出结果是4
int arr[10] = { 0 };
printf("%dn", sizeof(arr));//输出的结果是40
return 0;
}//sizeof只管新占了多大的内存空间大小
那么strlen呢?
strlen是c语言里面的库函数,是函数
作用的求字符串长度的,只能针对字符串
strlen统计的是字符串 之前的字符个数
只能指针字符串,求字符串长度
不能求整形数组的
strlen直到遇到 ,不然是不会停下来的
代码语言:javascript复制int main()
{
char arr1[] = { 'a','b','c' };
char arr2[] = "abc";//字符串末尾隐藏着一个
printf("%zdn", strlen(arr1));//15---往后找 ,直到找到 才停止--那么这个15就是随机值
printf("%zdn", strlen(arr2));//3--- 前面有三个字符
printf("%zdn", sizeof(arr1));//3---只关乎arr1占了多大的空间
printf("%zdn", sizeof(arr2));//4---放了4个字符,包括 ,总共占了4个字节
//strlen是用来求字符串长度的,不能用来求整型数组
return 0;
}
2.数组和指针笔试题解析
sizeof---一维数组
代码语言:javascript复制//恢复:数组名是数组首元素的地址
//但是有两个例外
//sizeof(数组名)
//&数组名
//除了这两种,其他的都是数组首元素的地址
int main()
{
int a[] = { 1,2,3,4 };
printf("%zdn", sizeof(a));//16---4个元素,每个元素4个字节
//数组名a单独放在sizeof内部,a表示的是整个数组,计算的是整个数组的大小,单位是字节
printf("%zdn", sizeof(a 0));//4
//a是数组名,数组首元素的地址,a 0还是首元素的地址
//a i就是数组内下标为i的数组元素
//这里的sizeof计算的是首元素地址的大小
printf("%zdn", sizeof(*a));//4
//这里的a依然是首元素的地址,*a就是首元素,就是a[0]
//因为arr[0]是一个整数,就是4个字节
printf("%zdn", sizeof(a 1));//4
//因为数组名表示的是首元素的地址,那么首元素加1
//就是下标为1的数字的地址,就是这个数组内第二个元素的地址
//arr[1]的地址
//这里计算的是地址的大小
printf("%zdn", sizeof(a[1]));//4
//a[1]就是数组内第二个元素,就是4个字节的大小
printf("%zdn", sizeof(&a));//4
//取出整个数组的地址
//数组的地址也是一个地址啊,是地址就是4个字节的长度
printf("%zdn", sizeof(*&a));//16
//取出a的地址,再进行解引用,就是整个数组,
//*和&抵消了,就是整个数组
// sizeof(*&a)==sizeof(a)
//&a是数组的地址,类型是int(*)[4]---数组指针类型
//那么*&a就是访问真个数组
printf("%zdn", sizeof(&a 1));//4
//&a就是整个数组的地址,地址 1就是跳过整个数组后的那个地址,
// 但是仍然是地址啊
//是地址就是4个字节
printf("%zdn", sizeof(&a[0]));//4
//arr[0]是数组首元素的地址,再将其取出
//取出数组首元素的地址
printf("%zdn", sizeof(&a[0] 1));//4
//将首元素的地址取出,再 1就是第二个元素的地址
return 0;
}
sizeof--字符数组
代码语言:javascript复制int main()
{
char arr[] = { 'a','b','c','d','e','f' };//没有
printf("%dn", sizeof(arr));//6
//整个数组的大小,6个字符就是6
printf("%dn", sizeof(arr 0));//4
//arr是首元素的地址,arr 0还是首元素的地址,地址的字节就是4
printf("%dn", sizeof(*arr));//1
//对数组首元素地址进行解引用就是数组首元素,字符就是1个字节
printf("%dn", sizeof(arr[1]));//1
//数组中下标为1的字符,大小是1个字节
printf("%dn", sizeof(&arr));//4
//取出整个数组的地址,仍然是地址,地址就是4个字节的大小
printf("%dn", sizeof(&arr 1));//4
//取出整个数组的地址, 1跳过整个数组,还是地址,就是4个字节的大小
printf("%dn", sizeof(&arr[0] 1));//4
//取出数组首元素的地址,再 1就是第二个元素的地址,地址是4个字节大小
return 0;
}
strlen---大括号--一堆字符
代码语言:javascript复制int main()
{
char arr[] = { 'a','b','c','d','e','f' };//没有
printf("%zdn", strlen(arr));//随机值
//这里的arr表示的是首元素的地址
//从首元素开始数字符,一直数到 之前才停止
//那么这里的值就是随机值
printf("%zdn", strlen(arr 0));
//arr 0还是数组名-----首元素的地址
//那么返回的值就是随机值,这个代码和上面的代码没有区别
//并且这两个随机值是一样的
printf("%zdn", strlen(*arr));
//arr是首元素的地址,那么对其进行解引用就是数组第一个元素,就是'a'字符
//strlen(a)---'a'的ASCII的大小是97,strlen不能运行
//a的值传递给strlen,strlen会认为97是地址,然后会访问内存
//这个代码是有问题的
printf("%zdn", strlen(arr[1]));
//arr[1]是数组下标为1的元素
//strlen('b')---'b'的ASCII大小是98
//这个程序也会崩溃
printf("%zdn", strlen(&arr));//随机值
//将数组的整个数组的地址传过去了
//strlen依然从首元素往后数
//依然没有遇到
//那么返回值就是随机值
//&arr的类型是----char(*)[6]
printf("%zdn", strlen(&arr 1));//随机值
//取出整个数字的地址,然后 1跳过整个数组
//也是随机值,不知道什么时候遇到
printf("%zdn", strlen(&arr[0] 1));//随机值
//arr[0]是数组首元素, 1就是b,那么strlen往后直到遇到 才停止,那么
//这个代码返回的也是随机值
return 0;
}
sizeof--字符串
代码语言:javascript复制//数组名是数组首元素的地址
//两个例外:
//sizeof(arr)
//&arr---这里的数组名表示整个数组,取出的是数组的地址
int main()
{
char arr[] = "abcdef";//字符串初始化
//这个字符串末尾还有一个
printf("%zdn", sizeof(arr));//7
//arr作为数组名,这里计算的是这个数组的大小
//一个字符一个字节,那么这里就是7个字节
//这里的数组名是单独的放在sizeof里面的
printf("%zdn", sizeof(arr 0));//4
//数组名--就是数组首元素的地址
//这里的arr并不是单独的放在sizeof里面
//那么这里的arr 0是首元素的地址
//既然是地址,那么大小就是4个字节
printf("%zdn", sizeof(*arr));//1
//这里的arr是数组名--首元素的地址
//对arr进行解引用达到的就是首元素
//首元素的大小是1个字节--每个字符的大小是1个字节
printf("%zdn", sizeof(arr[1]));//1
//数组下标为1的元素,字符,所以大小也是1
printf("%zdn", sizeof(&arr));//4
//取出整个数组的地址,地址就是4个字节
printf("%zdn", sizeof(&arr 1));//4
//取出的是整个数组的地址,再 1,得到的就是跳过整个数组
//得到的也是地址,既然是地址就是4个字节的大小
printf("%zdn", sizeof(&arr[0] 1));//4
//arr[0]是数组首元素,取出数组首元素的地址再 1
//得到的就是数组内的第二元素的地址,地址就是4个字节大小
//首元素的地址类型是char*
return 0;
}
strlen---字符串
代码语言:javascript复制int main()
{
char arr[] = "abcdef";//字符串末尾放着 的
printf("%dn", strlen(arr));//6
//这里的arr是首元素的地址
// 从首元素开始数,数到 之前就停止
//strlen是求字符串长度的
//因为末尾放着一个 ,那么strlen数的就是 前面的字符个数
//strlen是从第一个元素开始统计 之前的字符个数
printf("%dn", strlen(arr 0));//6
//arr是首元素的地址,那么arr 0还是首元素的地址
//那么把数组首元素的地址传给strlen,就是从首元素开始统计,直到遇到
printf("%dn", strlen(*arr));
//arr是首元素的地址,那么解引用得到的就是首元素字符'a'
//'a'的大小是97,将97当成地址传递给strlen,97这个地址不能被访问,这个程序就崩溃了
printf("%dn", strlen(arr[1]));
//arr[1]是数组下标为1的元素,第二个元素,访问的是'b'
//这个代码的道理和上面的一样,一样会崩溃的
printf("%dn", strlen(&arr));//6
//取出数组的地址,将取出的地址存放至数组指针变量里面这么写
//char(*pa)[7]=&arr
//那么pa的类型是char(*)[7]
//但是对于strlen这个库函数来说
//size_t strlen (const char* str)
//硬要将char(*)[7]这个地址传过去,因为char(*)[7]与const char* str不一致,会导致将传过来的指向强制转换为strlen所需要的类型
//
//数组的地址和数组首元素的地址是指向同一个位置的
//那么strlen也是从第一个元素的位置开始向后访问
//所以这个代码实现的数据是6
printf("%dn", strlen(&arr 1));//随机值
//取出数组的地址,再 1就是跳过整个数组,那么strlen遇到 才会停止
//那么这里的就是随机值
printf("%dn", strlen(&arr[0] 1));//5
//取出的是首元素的地址,那么首元素的地址 1就是数组第二个元素的地址
//那么strlen就从第二个元素开始访问进行统计的
//那么这里的 数据就是5
return 0;
}
//在这些代码运行的时候
//会出现很多警告
//因为我们在传参的时候类型不一致
//&arr的类型是char(*)[7],而strlen只能接受const char* str这个类型的指针
sizeof---指针
代码语言:javascript复制int main()
{
char* p = "abcdef";//将首字符的地址存放在p中
printf("%zdn", sizeof(p));//4
//p是指针变量。那么sizeof(p)计算的就是指针变量p的大小
//因为一个指针变量的大小是4个字节--x86的情况下,x64的情况下是8个字节
printf("%zdn", sizeof(p 1));//4
//p存的是首元素的地址,p的类型是char*
//那么p 1得到的是b的地址,就是相当于跳过了一个字符
//地址的大小就是4个字节
printf("%zdn", sizeof(*p));//1
//p是字符串首元素的地址,对p进行解引用,得到的就是首元素,首元素是字符a
//a的字节大小是1
//因为p的类型是char*,只能访问一个字节,就是a
printf("%zdn", sizeof(p[0]));//1
//p[0]-->*(p 0)---得到的就是字符a---大小为1个字节
printf("%zdn", sizeof(&p));//4
//取出p的地址,&p是p的地址,对p的地址进行大小计算,地址的大小就是4个字节
printf("%zdn", sizeof(&p 1));//4
//取出p的地址进行 1操作
//
//
// char* *ptr=&p ptr指向的p是一个类型为char*类型的指针,后面的*说明ptr是一个指针
//&p的指针类型是char * *,对&p 1就是跳过一个char* * 类型的对象
//举例:int *p 对p 1跳过的就是一个整型对象
//&p是p的地址,&p 1是跳过了p变量,指向了p的后面
//因为&p 1是地址,就是4个字节
printf("%zdn", sizeof(&p[0] 1));//4
//p[0]是*(p 0)---就是首元素a
//前面加上&得到的就是a的地址, 1就是b的地址
//&p[0]就是p
return 0;
}
strlen---指针
代码语言:javascript复制int main()
{
char* p = "abcdef";//将这个字符串首元素的地址存储在p中,末尾有
printf("%zdn", strlen(p));//6
//p里面放的是a的地址,
//strlen(p)就是从a的地址开始统计这个字符串的长度
//得到的数据是6
printf("%zdn", strlen(p 1));//5
//p存的是a的地址,因为p是char*的指针,那么p 1就是指向b的地址
//就是从b的地址位置开始统计这个字符串的个数
//那么得到的数据就是5
printf("%zdn", strlen(*p));//程序崩溃
//*p是a,a是97
//将97作为地址传给strlen,程序会崩溃的
printf("%zdn", strlen(p[0]));//程序崩溃
//p[0]--->*(p 0)----*p---a----97
//那么这个程序就会崩溃的
printf("%zdn", strlen(&p));//随机值
//取出指针变量p的地址,那么strlen就从指针变量p地址开始统计,在p这块空间进行统计
//那么得到的就是随机值
printf("%zdn", strlen(&p 1));//随机值
//&p是指针p的地址, 1就是跳过p的地址,开始进行统计
//所以数据就是随机值
printf("%zdn", strlen(&p[0] 1));//5
//p[0]--->*(p 0)----*p---a
//那么&p[0]就相当于p,
//p里面存的是a的地址, 1得到的是b的地址
//那么我们就从b的位置开始统计
//得到的数据是5
return 0;
}
//注意:p[0]--->*(p 0)----*p---a
sizeof---二维数组
代码语言:javascript复制//二维数组
int main()
{
int a[3][4] = { 0 };//3行4列----每一行都输一维数组
//第一行的数组名叫arr[0]---每个元素的访问a[0][i]
//第一行的数组名叫arr[1]---每个元素的访问a[1][i]
//第一行的数组名叫arr[2]---每个元素的访问a[2][i]
printf("%dn", sizeof(a));//48
//a是二维数组的数组名,单独放在sizeof内部,a表示的是整个数组,计算的就是整个数组的大小,单位是字节
//12个元素,每个元素4个字节,总共就是48个字节大小
printf("%dn", sizeof(a[0][0]));//4
//a[0][0]就是第一行第一个元素,大小是4个字节
printf("%dn", sizeof(a[0]));//16
//a[0]是这个二维数组的第一行的数组名
//第一行的数组名单独放在sizeof内部,计算的就是整个一维数组的大小,就是4*4=16个字节的大小
printf("%dn", sizeof(a[0] 1));//4
//a[0]是第一行的数组名,但是没有单独放在sizeof里面,就说明这里的a[0]是第一行首元素的地址,那么首元素地址 1就是第一行第二个元素的地址
//就是a[0][0]的地址
//那么a[0] 1--->&arr[0][0] 1--->&a[0][1]
//因为这里是地址,那么就是4个字节大小
printf("%dn", sizeof(*(a[0] 1)));4
//上面说a[0] 1得到的是第一行第二个元素的地址
//那么我们解引用得到的就是第一行第二个元素,是一个整型,大小是4个字节
printf("%dn", sizeof(a 1));//4
//a 1
//a是二维数组的数组名,并且没有单独放在sizeof内
//说明这个a是这个二维数组的首元素地址
//就是第一行的地址,那么a 1就是第二行的地址
//因为a 1是第二行的地址,传给sizeof,因为是地址,所以大小是4个字节
//
printf("%dn", sizeof(*(a 1)));//16
//*(a 1)--->a[1]--第二行的数组名,单独放在sizeof内部,那么sizeof计算的就是第二行的大小
//因为a 1得到的是第二行的地址,那么对其进行解引用,得到的就是第二行整个数组
//a 1是第二行的地址,类型是int(*)[4],数组指针,基因用访问的是这个数组,大小是16个字节
printf("%dn", sizeof(&a[0] 1));//4
//a[0]是第一行的数组名,&数组名就是第一行的地址,&a[0]就是取出了第一行的地址
//那么&a[0] 1得到的就是第二行的地址
//因为是地址,那么大小就是4个字节
printf("%dn", sizeof(*(&a[0] 1)));//16
//上面说到&a[0] 1得到的是第二行的地址,那么对第二行的地址进行解引用得到的就是第二行数组
//
printf("%dn", sizeof(*a));//16
//a作为数组名没有单独放在sizeof内,那么a就不是整个数组
//a就是这个二维数组的首元素的地址
//对其进行解引用得到的就是第一行整个数组
//4*4=16
//*a-->*(a 0)-->a[0]
printf("%dn", sizeof(a[3]));//16
//这里是否越界
//sizeof内部的表达式是不会真实计算的,也不会真实去访问的
// 仅仅只是根据类型来推断长度的
//a[3]是第四行的数组名---那么计算的是第四行的大小
//第四行的类型是int[4]
return 0;
}
//数组名在sizeof内单独使用,表示的就是整个数组
//如果不是单独使用,我们就要考虑一下这个数组名是数组首元素的地址
//sizeof内部的表达式是不会进行运算的
int main()
{
short s = 8;//short类型的数据是2个字节
int n = 12;//4个字节
printf("%zdn", sizeof(s = n 5));//2
//sizeof内部的表达式并没有进行运算
//sizeof(s = n 5)这个表达式 最终结果取决于s
printf("%zdn", s);//8
return 0;
}
数组名的意义:
1.sizeof(数组名),这里的数组名表示的是整个数组,计算的是整个数组的大小
2.&数组名,这里的数组名表示整个数组,取出的是整个数组的地址
3.除此之外所有的数组名都表示数组首元素的地址
3.指针运算笔试题解析
题目一
代码语言:javascript复制#include <stdio.h>
int main()
{
int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a 1);
//&a是取出真个数组的地址,那么&a 1就是跳过了整个数组,指向的是这个数组的末尾的位置
//&a 1的地址类型是数组类型的地址int(*)[5]
//将这个地址强制类型转换为int*类型的
//准换位int*类型的地址再赋值给ptr,那么ptr指向的也是这个数组的末尾位置
printf("%d,%d", *(a 1), *(ptr - 1));
//这里的a是数组名--数组首元素的地址,那么a 1得到的就是第二个元素的地址,就是2的地址
//解引用得到的就是2
//因为ptr现在是整型指针,那么-1就是往回退,那么指向的就是5的位置
//*(ptr - 1)得到的就是5
return 0;
}
//&a 1就是取出整个数组再跳过这个数组,就是那么这个&a 1指向的就是这个数组的末尾
//再将这个强制类型转换为int *类型的地址,再赋值给ptr
//那么ptr也是指向这个数组的末尾的位置
//因为ptr的类型是int *,-1就是往后退一个整型的距离,那么就是指向的5的位置
题目二
代码语言:javascript复制//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结果是啥?
struct Test
{
int Num;
char* pcName;
short sDate;
char cha[2];
short sBa[4];
}*p = (struct Test*)0x100000;
//在这里括号前面的是结构体,加上*就是结构体指针类型,创建变量p
//先将0x100000强制转换为结构体指针,再为b进行赋值,现在p里面放的就是0x100000这个地址
int main()
{
printf("%pn", p 0x1);//00100014
//指针 1和类型有关系
//整数 1就是 1
//0x1是16进制的1
//因为p是一个结构体指针,因为前面说了结构体的大小是20字节
// 那么p 1就是就是跳过跳过一个结构体20个字节
//0x100014
//因为这个是16进制的,所以加20个字节就是在这个基础上 14
//4*16的0次方 1*16的1次方就是20
//00100014
printf("%pn", (unsigned long)p 0x1);//0x00100001
//现在将p强制类型转换为整型,那么整型 1就是 1
//那么得到的就是0x00100001
printf("%pn", (unsigned int*)p 0x1);//00100004
//将p强制类型转换为int *
// 1跳过一个unsigned int*类型的指针变量,是4个字节
return 0;
}
题目三
代码语言:javascript复制#include <stdio.h>
int main()
{
int a[3][2] = { (0, 1), (2, 3), (4, 5) };
//仔细看,这里用的是小括号不是大括号
//那么这里就成逗号表达式了
//(0, 1)的结果就是1
//(2, 3)的结果就是3
//(4, 5)的结果就是5
//int a[3][2] = { 1,3,5 };
//那么这个二维数组剩下的位置就被0给填充了
//1 3
//5 0
//0 0
int* p;//指针
p = a[0];
//a[0]是第一行的数组名,
// 没有sizeof
// 没有&
//
// 那么表示的就是首元素的地址
//那么p里面存的就是1的地址
printf("%d", p[0]);
//p[0]===*p(0)--指向的还是1的位置
//那么打印出来的就是1
return 0;
}
题目四
代码语言:javascript复制//假设环境是x86环境,程序输出的结果是啥?
#include <stdio.h>
int main()
{
int a[5][5];//5行5列
//每一行的数组名分别是a[0]、a[1]、a[2]、a[3]、a[4]
int(*p)[4];//p是一个数组指针,p指向的数组有4个元素,每个元素是int
p = a;
//a是数组名,这个二维数组的数组名,就是第一行的地址,第一行整个一维数组的地址
//第一行的地址的类型就是一个数组指针int(*)[5]
//p的类型是int(*)[4] a的类型是int(*)[5]
//不管a原来是什么类型,现在赋值给p,那么现在就是p说的算
//因为a指向的是第一行的位置,那么p同样也指向那里
//对于p来说,一行只有4个元素
//p 1就是跳过一行,每行四个元素
printf("%p,%dn", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
//FFFFFFFC,-4
//p[4][2]==*(*(p 4) 2)-----解引用p 4就相当于拿到那一行的数组名
//两个地址相减是地址直接的元素个数,这中间差了4个元素
//小地址减去大地址得到的是-4
//第一个代码是打印地址的,因为相减的结果是-4,存在内存里面,那么直接把存放-4的地址打印出来了
//-4在内存里面存的是补码
//11111111111111111111111111111100
//打印地址直接将内存里面的这个二进制补码直接当成地址
//4个二进制的1就是一个F,最后剩下的1100就是12,转换成6进制就是C
return 0;
}
//考察的是指针的运算,数组指针 1跳过的是多少
//就是跳过一个指针类型,在这里p 1就是跳过一行
题目五
代码语言:javascript复制#include <stdio.h>
int main()
{
int aa[2][5] = { 1, 2, 3, 4, 5,
6, 7, 8, 9, 10 };//两行5列
int* ptr1 = (int*)(&aa 1);
//&aa就是取出整个二维数组的地址, 1就是跳过整个二维数组
//&aa 1得到的还是二维数组的地址,将这个地址强制类型转换为int *
//因为&aa 1跳过的是整个数组,那么指向的位置就是这个二维数组的末尾
//因为被强制转换为int *后再赋值给ptr1,那么赋值后ptr1指向的位置也是这个二维数组的末尾
//
int* ptr2 = (int*)(*(aa 1));//aa 1--->aa[1]---第二行的数组名---第二行首元素的地址
//aa是数组名,表示的是首元素的地址,就是第一行的地址
//那么aa 1就是第一行的地址 1指向的就是第二行的地址
//那么对其进行解引用得到的就是第二行整个一维数组
//因为这个第二行的一维数组本身就是整型,那么前面的int *可以忽略
//现在将(int*)(*(aa 1))赋值给ptr2,那么ptr2也是指向的第二行的数组
//aa 1可以理解为aa[1],就是第二行的数组名,就是第二行数组首元素的地址,就是指向的6
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));//10,5
//在这里,因为ptr1指向的是数组的末尾,并且ptr1是整型指针,那么ptr1-1指向的就是10的位置
//那么对其进行解引用得到的就是10
//因为ptr2指向的是6,那么ptr2-1得到的就是5的地址,对其进行解引用得到的就是5
return 0;
}
题目六
代码语言:javascript复制#include <stdio.h>
int main()
{
char* a[] = { "work","at","alibaba" };
//正常是不能这么放的,那么就是把每个字符串的首元素地址存在这个指针数组里面了
//这里定义的指针数组里面存的分别是w、a、a的地址
char** pa = a;//a是数组名,表示的是首元素的地址,就是第一个元素的地址
//首元素的地址是char*类型的,那么存在pa里面,那么pa就是一个二级指针
//那么char**pa指向的就是首元素
pa ;
//pa指向的是第一个元素,那么 就是指向的第二个元素,就是跳过提个char*类型的指针
//那么下面的*pa得到的就是第二个元素的地址
printf("%sn", *pa);//at
//得到了地址,那么就从地址开始打印对饮的字符串
return 0;
}
题目七--最难的
代码语言:javascript复制#include <stdio.h>
int main()
{
char* c[] = { "ENTER","NEW","POINT","FIRST" };//每个元素是char*
//存放的是每个字符串首字符的地址,
//分别是E、N、P、F
char** cp[] = { c 3,c 2,c 1,c };
//c是上面的指针数组的数组名,指向的就是首元素的地址,指向的就是第一个字符串
//c指向的就是"ENTER"
//c 1指向的就是"NEW"
//c 2指向的就是"POINT"
//c 3指向的就是"FIRST"
//将c里面首元素的地址存放在cp里面,那么cp的类型就是一个二级指针
char*** cpp = cp;
//cp是数组名,表示的是首元素的地址,将二级指针的地址放在三级指针内
//cpp指向的是首元素的地址,就是c 3
//下面的 和--会有副作用的,会改变指
//int a=5 a 这个动作完了之后a的值就变为6了
printf("%sn", ** cpp);//POINT
//* * cpp
//先算 cpp,cpp本来放的是c 3的地址,那么现在进行 之后,就指向了c 2的地址
// 然后解引用,因为cpp现在指向的是c 2的地址,那么第一次解引用就得到了c 2
// 第二次解引用就是对c 2解引用,得到的就是"POINT"
//
//
//
printf("%sn", *-- * cpp 3);//ER
//因为加法的优先级比较低,所以先算前面的
// 所以先算 ,再解引用,再--,再解引用
//
// 因为上面的cpp已经进行 操作了,指向的是c 2,那么再次 就是指向的c 1了
// 那么现在已经得到了c 1的地址了
// 现在进行解引用通过地址找到c 1,
//
// 原本放的是c 1,现在--就变成c了,就没有c 2了,--就是自减,c 1变成c了
// c是指向的是"ENTER"这个字符串首元素E的地址,那么解引用完 3指向的就是E
// //那么就是从E开始打印,所以只能打印ER
//
//
//
//
//
//
printf("%sn", *cpp[-2] 3);//ST
//*cpp[-2]是**(cpp (-2))就是**(cpp-2)
//
// 一开始cpp指向的是原先的c 1的地址,但是c 1自减了1,就成c了,那么现在cpp指向的就是c的地址了
// 那么cpp-2得到的就是c 3的地址
// 这里的cpp-2不会导致cpp的改变,还是指向c
//
// 那么cpp-2得到的是c 2的地址,解引用得到的就是c 3
// c 3指向的是这串字符串首元素字符F的地址---"FIRST"
//
// 最后F的地址 3就是S
// 那么打印出来的就是ST
//
// 这里最关键的就是*cpp[-2]这个的理解
//
printf("%sn", cpp[-1][-1] 1);// EW
//cpp[-1][-1] 1
//转换形式
// *(*(cpp-1)-1) 1
// 这里的cpp指向的是c的位置
// 那么cpp-1就指向了c 2的位置
//
// *(cpp-1)拿到了c 2
// 那么就变成下面这样了
// *((c 2)-1) 1
// c 2-1得到了c 1,就是现在的cpp指向的位置
//
// c 1指向的就是"NEW"
//对c 1进行解引用就拿到了"NEW"--N的地址
//然后N的地址 1得到的就是E的地址
//然后打印出来的就是EW
return 0;
}