深入理解指针(指针练习)

目录

1.strlen和sizeof的区别

1.1sizeof

1.2strlen

1.3sizeof和strlen对比

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

2.1一维数组(sizeof)

2.2字符数组(sizeof和strlen)

sizeof

strlen

加入指针变量(sizeof和strlen)

2.3二维数组(sizeof求大小)

2.4理解数组名的意义

3.指针运算笔试题

3.1题目一

3.2题目二

3.3题目

3.4题目四

3.5题目五

3.6题目六

**++cpp

*- -  * ++cpp + 3 

*cpp[-2] + 3

cpp[-1][-1] + 1


1.strlen和sizeof的区别

1.1sizeof

sizeof 计算变量所占内存内存空间⼤⼩的,单位是 字节,如果操作数是类型的话,计算的是使⽤类型创建的变量所占内存空间的⼤⼩。

sizeof 只关注占⽤内存空间的⼤⼩,不在乎内存中存放什么数据。

int arr2[] = { 0,1,2,3 };
printf("%d  ", sizeof(arr2));//求的是该数组的字节总长度
printf("%d  ", sizeof(arr2[0]));//求数组中一个元素所占的字节大小
printf("%d  ", sizeof(int));//求数组类型的字节大小。

1.2strlen

1.3sizeof和strlen对比

sizeof

1. sizeof是操作符

2. sizeof计算操作数所占内 存的⼤⼩,单位是字节

3. 不关注内存中存放什么数 据

strlen

1. strlen是库函数,使⽤需要包含头⽂件 string.h

2. srtlen是求字符串⻓度的,统计的是 \0 之前字符的隔个数

3. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能 会越界

char arr[20] = "abcdef" ;
int ret = strlen(arr);
int ret2 = sizeof(arr);
printf("%zd \n", ret);
printf("%d \n", ret2);
char arr[20] = { "abcdef" };
int ret = strlen(arr); 
int ret2 = sizeof(arr); 
	printf("%zd \n", ret); 
printf("%d \n", ret2);

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

2.1一维数组(sizeof)

分为三类

int arr[] = { 1,2,3,4 };

printf("%d \n", sizeof(arr));//16	求的是整个数组的总长度
printf("%d \n", sizeof(*&arr));//16 
//两个角度来看
//1.& 来 ,* 回,相当于来回,相互抵消了,最后得到sizeof(arr);
//2.从类型来看,比如 int(*p)[10] = &arr;,所以在这里&arr是数组指针,要解引用一个数组指针,那么大小就是16
printf("%d \n", sizeof(arr+0));//4/8  arr+0 在这里arr不是单独出现,所以arr代表的是首元素地址,arr+0还是首元素的地址,是地址那么就是4/8
printf("%d \n", sizeof(arr + 1));//4/8  这个同上,arr+1是数组第二个元素的地址,因此是4/8

printf("%d \n", sizeof(&arr)); // 4/8 取的是数组的地址,数组地址也是地址,所以是4/8
printf("%d \n", sizeof(&arr + 1)); //4/8 同上  取的是数组的下一个数组地址,也是地址 

printf("%d \n", sizeof(&arr[0])); //4/8 取得是数组的第一个元素的地址 
printf("%d \n", sizeof(&arr[0] + 1)); //4/8 取得是数组的第二个元素的地址 
printf("%d \n", sizeof(*arr));//4 arr在这里是首元素的地址,*arr就是数组第一个元素,所以字节大小就是4

printf("%d \n", sizeof(arr[1]));//4 arr[1]是数组的第二个元素,大小是4
printf("%d \n", sizeof(arr[0]));//4 同理arr[0]是数组的第一个元素,大小是4

2.2字符数组(sizeof和strlen)

sizeof

char arr[] = { 'a','b','c','d','e','f' };//这是六个元素,没有/0
printf("%d \n", sizeof(arr));//6
printf("%d \n", sizeof(arr+0));//4/8 arr为首元素地址,arr+0也是首元素地址
printf("%d \n", sizeof(*arr));//1 这是首元素地址然后解引用,得到第一个元素
printf("%d \n", sizeof(arr[1]));//1 第一个元素
printf("%d \n", sizeof(&arr));//4/8 取数组的地址
printf("%d \n", sizeof(&arr+1));//4/8 取数组的下一个数组地址,虽然下一个是空,但也是地址 
printf("%d \n", sizeof(&arr[0]+1)); //4 / 8第二个元素的地址 

strlen

strlen的参数是地址,不接受值,不然会错误。

char arr[] = { 'a','b','c','d','e','f' };//这是六个元素,没有/0
printf("%d \n", strlen(arr));//随机值,strlen只能找到abcdef找不到'/0'会往前或者向后找,值是不确定的
printf("%d \n", strlen(arr + 0));//随机值,这个同上,也是首元素地址
printf("%d \n", strlen(*arr));//错误,传过去的是‘a’因为strlen接收到的是地址,这里传的是值,是错误的
printf("%d \n", strlen(arr[1]));//错误,同上
printf("%d \n", strlen(&arr));//随机值,strlen只能找到abcdef找不到'/0'会往前或者向后找,值是不确定的
printf("%d \n", strlen(&arr + 1));//随机值,这个随机值跟上面可能不同,访问的是跳过了一个数组的大小,可能越界,不确定能不能找到/0
printf("%d \n", strlen(&arr[0] + 1));//随机值,只是从‘b’开始,但也是找不到/0.
//上述情况,主要因为数组没有/0原因。

换一个数组写法

//对于strlen函数,strlen(const char *s)接收的是地址
char arr[] = "abcdef";//这是六个元素,有/0
printf("%d \n", strlen(arr));//6,传过去的是首元素地址
printf("%d \n", strlen(arr + 0));//6,arr是首元素地址,arr+0也是
printf("%d \n", strlen(*arr));//错误,传的是‘a’这是不对的,只能是地址
printf("%d \n", strlen(arr[1]));//错误,同上原理,传的是‘b’
printf("%d \n", strlen(&arr));//6,传的是数组地址,但确实是首元素地址,会报错,但是可以实现的
printf("%d \n", strlen(&arr + 1));//随机值,跳过了数组arr,那么后面无法确定能找到/0
printf("%d \n", strlen(&arr[0] + 1));//5,从‘b’开始计算

加入指针变量(sizeof和strlen)

char* p = "abcdef";
/*char arr = "abcdef";
char* p = &arr;*/
printf("%zd\n", sizeof(p)); // 4/8 ,算的是指针变量p的大小
printf("%zd\n", sizeof(p + 1));//4/8,p+1是'b'的地址,是地址那么就是4/8
printf("%zd\n", sizeof(*p)); //1,*p代表是‘a’,大小是1
printf("%zd\n", sizeof(p[0]));//1,这个可以理解成char * p=arr,p=arr,所以p[0] = arr[0],指的就是‘a’大小是1,
//或者是p[0]->*(p+0) = *p 
printf("%zd\n", sizeof(&p)); //4/8,取的是指针变量p的地址,char * *p1 = &p;
但也是地址,所以是4/8
printf("%zd\n", sizeof(&p + 1));//4/8,取的是指针变量p地址,
然后跳过一个指针变量,得到的是指针变量后面的地址,也是地址
printf("%zd\n", sizeof(&p[0] + 1));//4/ 8 ,p[0]=*(p+0)  ,&*(p+0)得到a地址,然后+1得到‘b’的地址

要明白,p指针变量也有自己的地址空间,字符串也有自己的地址空间,是两块不同的空间。

char* p = "abcdef";

printf("%zd\n", strlen(p)); //6 ,第一个元素地址。
printf("%zd\n", strlen(p + 1));//5,取的是第二个元素地址
//printf("%zd\n", strlen(*p)); //错误
//printf("%zd\n", strlen(p[0]));//错误
printf("%zd\n", strlen(&p)); //随机值,取的是指针p的地址空间,里面不清楚有没有/0
printf("%zd\n", strlen(&p + 1));//随机咯,找不到/0
printf("%zd\n", strlen(&p[0] + 1));//5,p[0]是第一个元素,&p[0]+1就是取数组第二个元素的地址。

2.3二维数组(sizeof求大小)

二维数组也遵循,除了以下两种情况,其他情况数组名都是首元素地址
1.sizeof(数组名) 是代表求整个数组总长度大小字节
2.&数组名,这是取的是整个数组地址
二维数组,数组名[0]代表的是一行
不是单独放的话数组名就是首元素地址,比如a[0]-> a[0][0],a->a[0]

int a[3][4] = { 0 };
printf("%zd\n", sizeof(a));//3*4*4=48,数组名单独放到sizeof里面求的是总大小
printf("%zd\n", sizeof(&a+1));//4/8,&a = &a[0]+1  -> &a[1],取的是第二行的地址
printf("%zd\n", sizeof(a[0][0]));//4,就是第一行第一列元素,大小为4
printf("%zd\n", sizeof(a[0])); //16,a[0]是第一行的一维数组的数组名,所以求的是这一行的大小,4*4
printf("%zd\n", sizeof(a[0] + 1));//4/8,a[0]不是单独放,所以是首元素地址,就是a[0][0],即a[0][0]+1->a[0][1]地址
printf("%zd\n", sizeof(*(a[0] + 1)));//4,*(a[0][1])  这是一个元素,大小是4
printf("%zd\n", sizeof(a + 1)); //4/8,a为首元素地址a[0],a[0]+1 -> a[1],所以是a[1]的地址
printf("%zd\n", sizeof(*(a + 1)));//16,求的是*(a[1])一行的大小
printf("%zd\n", sizeof(&a[0] + 1));//4/8,这里是&a[0]的地址,然后+1,就是&a[1]的地址
printf("%zd\n", sizeof(*(&a[0] + 1)));//16,*(&a[1])的大小,16
printf("%zd\n", sizeof(*a));//16,*a,在这里是首元素,*a=*(a+0) =a[0],所以求的是第一行的大小
printf("%zd\n", sizeof(a[3]));//16,不会越界,size并不是真实的计算,他只需要知道数组的类型,然后就可以算出来,这里可以
//a[3] =a[0],在这里是数组名,所以是16

2.4理解数组名的意义

数组名的意义:

1. sizeof(数组名),这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩。

2. &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。

3. 除此之外所有的数组名都表⽰⾸元素的地址。

3.指针运算笔试题

3.1题目一

int a[5] = { 1, 2, 3, 4, 5 };
int* ptr = (int*)(&a + 1); 
//&a是取整个数组地址,然后+1就是跳过这个数组,强制类型转换为int*型指针
printf("%d,%d", *(a + 1), *(ptr - 1));
//a+1指向的是2的位置,然后解引用得到2;ptr指向的是数组后面,所以往前进一位就是指向5位置。

3.2题目二

int a[3][2] = { (0, 1), (2, 3), (4, 5) };//这里有坑,初始化用(),说明是逗号表达式,从做到右取最大
//上面数组简化int a[3][2] = { {
  {1,3},{5,0},{0,0} };
int* p;
p = a[0];
printf("%d", p[0]);//p[0]就是数组首元素地址就是1


 

3.3题目三

在X86环境下
假设结构体的⼤⼩是20个字节
程序输出的结构是啥?

struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p = (struct Test*)0x100000;//结构体指针
int main()
{
//0x1是1
//指针+整数  =》指针+指针的本身大小*整数
printf("%p\n", p + 0x1);//p是结构体指针 +1就是加20,在十六进制下20 = 14(16),所以=0x100000+14=0x100014
printf("%p\n", (unsigned long)p + 0x1);//在这里p不是指针类型而是整数,整数相加直接加,0x100001.
printf("%p\n", (unsigned int*)p + 0x1);//在这里p为int类型的指针,大小为4,所以+1相当于加上4,=0x100004
return 0;
}

3.4题目四

假设环境是x86环境,程序输出的结果是啥?

指针-指针=整数,同一个数组之间,两个指针(地址)相减绝对值得到指针与指针之间的元素个数,小的地址-大的地址得到负数。
地址存放的是补码。要经过源码-反码-补码
 数组随着地址下标的增长是由低到高的

int a[5][5];
int(*p)[4];//这个是一个数组指针,大小为4,[4]其实就是每行四列
p = a;
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//这里要画图找到对应的位置
//-4  源码100000000000000000000100  反码  11111111111111111111111111111011  补码(16进制)FFFFFFFC
指针-指针=整数,同一个数组之间,两个指针(地址)相减绝对值得到指针与指针之间的元素个数,
// 小的地址-大的地址得到负数。

3.5题目五

//这题主要是要会画图分析
char* a[] = { "work","at","alibaba" };
char** pa = a;  //这是二级指针
pa++;
printf("%s\n", *pa); //pa指向a的,类型是char* ,所以当pa++的时候就是跳过一个char * 大小

3.6题目六

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);//考虑优先级,最后的是+3
printf("%s\n", *cpp[-2] + 3);
printf("%s\n", cpp[-1][-1] + 1);

由上诉题目可以得出下面的图

**++cpp

得到下面此图,然后通过解引用,找到c+2,最后找到POINT的首地址P。

*- -  * ++cpp + 3 

,先进行++,使cpp指向cp的下一个指针,即char **c+1,进行解引用找到c的char*c+1,然后让char *c+1 减1,得到 char *c,就是c数组的首元素指针地址,然后+3就是指向字符'E'的地址,然后打印出ER.

*cpp[-2] + 3

首先cpp[- 2 ] -> *(cpp -2)就是cpp往前退两位置,回到原处就是指向cp,然后解引用找到c+3,然后*(c+3),这个的话就是继续解引用找到 char * c+3位置,然后c+3 指向的字符串,进行+3,此时就是指向S,所以打印的ST。

cpp[-1][-1] + 1

这个的话要会一个一个的去[ ],先出cpp[ -1 ] --> *(cpp -1  ),这里没-1之前是保持在*- -  * ++cpp + 3  这个位置,也就是指向char **c+1 ,然后cpp-1指向c+2位置,然后进行解引用找到 char *c+2位置,此时  是  char * (c+2)[ -1 ]  ==》 *(c+2-1 )  得到--> *(c+1) 位置,然后再解引用找到char *c+1 的首地址 N ,然后加 1 ,此时指向E,最后打印  EW 


 

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-01-28 03:10:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-01-28 03:10:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-01-28 03:10:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-01-28 03:10:01       18 阅读

热门阅读

  1. 题目 1022: [编程入门]筛选N以内的素数

    2024-01-28 03:10:01       32 阅读
  2. 【模板】拓扑排序

    2024-01-28 03:10:01       36 阅读
  3. ·迭代器模式

    2024-01-28 03:10:01       29 阅读
  4. 特殊类的设计

    2024-01-28 03:10:01       32 阅读
  5. Canvas图像与视频基础,离屏19

    2024-01-28 03:10:01       29 阅读
  6. KY115 后缀子串排序

    2024-01-28 03:10:01       28 阅读
  7. 【Vue】1-3、Webpack 中的 loader

    2024-01-28 03:10:01       29 阅读
  8. 技术周总结 2024.01.22-01.28

    2024-01-28 03:10:01       39 阅读
  9. 蓝桥杯省一题单

    2024-01-28 03:10:01       34 阅读