深度讲解指针的笔试题目

发布于:2022-12-21 ⋅ 阅读:(528) ⋅ 点赞:(0)

主页:C语言的前男友

知识讲解:C语言指针

创作者:C语言的前男友

开发环境:VS2022

前言:前面学了好久的指针,今天来看一些组织的面试题,来帮助我们加深理解。今天主要看一些关于sizeof(),和strlen(),对数组,字符串的操作。通过练习深度理解指针。深刻理解数组与指针,数组名与指针,字符串与指针,二维数组指针之间的关系。欢迎大家前来指正,如果觉得作者写的还不错的话,请麻烦动动发财的小手,关注,点赞,收藏,评论

 

目录

一.一维数组

(1) sizeof()的操作对象是数组名

(2)sizeof()操作对象是指针

 (3)sizeof()的操作对象是普通变量或者普通变量类型

 二.指针类型对指针的影响

1.指针的类型决定了指针在加减一个整数,指针移动的空间长度。

2.指针的类型决定了指针在解引用时,一次能访问多少字节的空间。

 三.字符数组

 (1)strlen()的底层原理

(2)strlen()和字符数组

 四.字符串数组

(1)sizeof()和字符串数组

 (2)strlen()和字符串数组

六.二维数组

 (1)二维数组的数组名

 最后:

月是人间散客,卿是人间绝色,亦是人间难得。


一.一维数组

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

(1) sizeof()的操作对象是数组名

数组名表示的是数组首元素的地址,但是有两个例外:

1.数组名是 sizeof 的操作对象

2. & + 数组名 ,

上述两种情况比较特殊,此时的数组名表示的是,整个数组。也就是说,sizeof(数组名)计算的是整个数组的大小,& + 数组名,代表是的取出整个数组的地址。

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

 &a是数组的地址,是数组指针类型,*&a是都数组指针解引用,访问一个数组的大小。

(2)sizeof()操作对象是指针

针对 sizeof 这个操作符,是用来求类型的大小的(单位:字节),当然也可以求指针的大小,我们知道一个普通指针在内存中占 4 个字节或者 8 个字节,至于到底是几个字节,取决于计算机平台,在32位平台是 4 字节,在 64 位平台是 8 字节。

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

这里(a+0)如果是 a 单独是sizeof的操作对象,那么此时 a 就是代表整个数组,但是(a+0)就不代表整个数组。(a+0)就是一个普通指针,指向数组的第一个元素。也就是数组的首元素的地址。(a+1)就是数组第二个元素的地址。(&a)也是一个指针,是一个数组指针类型的指针,但仍然是一个指针。(&a +1 ): &a 是取出了整个数组的地址,是一个数组指针类型的指针,(&a +1 )跳过整个数组的地址。(这里的针对指针加上一个整数的运算,后面会单拎出来好好讲讲)所以(&a+1)在实际上还是一个指针。是指针大小就是,4 或 8 字节。&arr[0]实际上也是一样,拿到是首元素的地址,(&a[0] + 1):就是在首元素的指针上往后跳过一个元素,也就是第二个数组元素的地址,无论 &a[0] , &a[0] + 1都是指针类型。所以大小自然都是 4 或者 8 字节。

32平台(X86)展示:

 64平台(X64 )展示:

 (3)sizeof()的操作对象是普通变量或者普通变量类型

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

a并没有单独放在sizeof()里面,所以 a 是首元素的地址,(*a)也就是首元素了,a[1],数组访问第二个元素,都是每一个数组元素又都是 int 类型。所以这两个结果都是 4 。

 二.指针类型对指针的影响

刚才我们遇到了,&a + 1 是越过了整个数组的地址,那到底为什么呢?下面我们就来好好分析一下。

1.指针的类型决定了指针在加减一个整数,指针移动的空间长度。

举例:

int main()
{

	int a = 0;
	char ch = 0;
	int arr[3] = { 0 };
	int* pa = &a;
	char* pc = &ch;
	int(*parr)[3] = &arr;
	printf("pa=%p,pa+1=%p\n", pa, pa + 1);
	printf("pc=%p,pc+1=%p\n", pc, pc + 1);
	printf("parr=%p,parr+1=%p\n", parr, parr + 1);
	
	return 0;
}

这个地方明显看出,pa 是整形指针,pa + 1只越过了 4 个字节。pc 是一个字符型指针,pc + 1越过了 1 个字节。parr 是一个数组指针,数组有三个元素,每个元素都是一个 int 型数据。所以数组的大小是 4*3=12,所以 parr + 1 越过了 12 个字节。

2.指针的类型决定了指针在解引用时,一次能访问多少字节的空间。

举例:

int main()
{

    int a = 0x11223344;
    int* pa = &a;
    char* pc = &a;
    short* ps = &a;
    printf("%x\n", *pa);
    printf("%x\n", *ps);
    printf("%x\n", *pc);

    return 0;
}

我们将十六进制数 11223344 存入a的四个字节空间,因为pa是整形指针,(*pa)一次就可以访问四个字节,所以,十六进制打印出来的(*pa)就是原数据11223344,ps 是短整型指针,(*ps)一次就可以访问两个字节,由于我的机器采用的是小端存储模式,所以在存储时是将数据的权重低位,放在了地址的低地址处,而读取内存还是从地址在高地址读取,所以十六进制打印出来的恢复数据的高低位就是 3344 ;pc是 char * 类型,(*pc)一次访问一个字节,也就是内存低位的一个字节,在我的机器上就是数据的权重低位,也就是44;

 三.字符数组

    char arr[] = { 'a','b','c','d','e','f' };
	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));

 (1)strlen()的底层原理

strlen()是一个计算字符串长度的库函数,计算的原理是:接受字符串的首字符指针,直到找到字符串的结束标志也就是‘  \0 ’ 结束。

(2)strlen()和字符数组

由于字符数组的没有 ‘ \0 ’作为结束标志,所以strlen()函数在什么位置寻找到 ‘ \0 ’也是不能知道的。所以在计算长度的时候,自然也就是一个随机值。

    char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", strlen(arr));
	// 从 arr 出开始找‘ \0 ’
	printf("%d\n", strlen(arr + 0));
	//仍然是从 arr 处开始找‘\0’  
	printf("%d\n", strlen(&arr));
	// 由于 &arr 和 arr 虽然类型不同,但是都是指向数组的首元素地址,
	// 都是从数组的首元素地址处开始的寻找‘ \0 ’
	printf("%d\n", strlen(&arr + 1));
	// &arr + 1 从越过整个数组后的地址处开始寻找‘ \0 ’
	printf("%d\n", strlen(&arr[0] + 1));
	// &arr[0]仍然是指向首元素的地址,&arr[0] + 1 是从数组第一个元素开始寻找‘\0’

 因为&arr+1 跨过了一个数组,所以strlen(&arr + 1)的随机值会比strlen(arr),

strlen(arr + 0),strlen(&arr) 小 6.而&arr[0]+1只只是跨过了一个字符,strlen(&arr[0]+1)所以会比strlen(arr),strlen(arr + 0),strlen(&arr) 小 1。

代码运行结果:

 由于strlen()参数需要的是指针,所以下面的代码就是错误的,由于内存访问冲突引起的。

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

代码运行结果:

 四.字符串数组

1)sizeof()和字符串数组

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", sizeof(arr));

这里的arr是整个数组,而这里的数组有在最后多存了一个‘ \0 ’。所以加上‘ \0 ’一共七个字节。

其他的还是和数组一样的分析。

运行结果:

X86环境

 X64环境:

 (2)strlen()和字符串数组

char arr[] = "abcdef";
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));

这里大家只要注意,strlen()求字符串的长度的原理就行了。

六.二维数组

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

 (1)二维数组的数组名

二维数组的数组名,还是和一维数组差不过,除了sizeof( 数组名 )和 & + 数组名。其他的数组名都是,首元素的地址。注意:这个时候二维数组的首元素,不再是第一个元素,而是把二维数组看成一维数组,每一行看作一个一维数组,那么数组名是首元素的地址,也就是第一行一维数组的地址。

int main()
{
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));
	//sizeof(a)数组名代表整个数组,
	//代表求整个数组的大小。
	printf("%d\n", sizeof(a[0][0]));
	//代表是求第一个数组元素的大小。
	printf("%d\n", sizeof(a[0]));
	//a是二维数组的数组名,是数组的第一行的地址,
	// a[0]相当于*(a+0),找到了数组第一行。
	//所以sizeof(a[0])算的是二维数组第一行的大小。
	printf("%d\n", sizeof(a[0] + 1));
	//a[0]其实就是二维数组第一行的数组名,
	// (a[0]+1)就是一个指向二维第一行第二个元素的指针。
	//算的是一个指针的大小。
	printf("%d\n", sizeof(*(a[0] + 1)));
	//在(a[0]+1)的基础上在解引用,
	//就是二维第一行第二个元素。
	printf("%d\n", sizeof(a + 1));
	//a是一个指向第一行数组的指针,
	// a+1就是一个指向第二行数组的指针。
	//算的是一个指针的大小
	printf("%d\n", sizeof(*(a + 1)));
	//(a+1)是指向二维数组第二行的地址,
	// 解引用以后就是找到了第二行
	//所以算的是第二行的大小
	printf("%d\n", sizeof(&a[0] + 1));
	//&a[0]取出第一行的地址,
	//等价于数组名 a ,所以等价于 a + 1
	printf("%d\n", sizeof(*(&a[0] + 1)));
	//等价于*(a+1)
	printf("%d\n", sizeof(*a));
	//等价于a[0]
	printf("%d\n", sizeof(a[3]));
	//等价于 *(a+3),虽然数组没有第四行,
	// 但是*(a+3)类型能够确定,
	// 就是 int [4],就是整形四元素数组。

	return 0;
}

运行效果(X86):

 最后:

月是人间散客,卿是人间绝色,亦是人间难得。

 

 

本文含有隐藏内容,请 开通VIP 后查看