C语言进阶之指针(三)

目录

9.指针和数组面试题的解析

10.指针笔试题


9.指针和数组面试题的解析

在进行面试题的解析之前,我想先介绍一下 sizeof  和 strlen 函数

  • sizeof 计算大小,主要看类型,根据类型来判断字节大小
  • strlen 是库函数,求字符串的长度,统计的是在字符串中\0之前的字符的个数
                                如果没有\0,就会一直往后找……
    使用strlen函数传参传的是地址

一维数组 

#include<stdio.h>
int main()
{
	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));
	return 0;
}

 运行结果:

解释如下:

	printf("%d\n", sizeof(a));//->16
	//在sizeof中,数组名表示整个数组,因此大小是4*4

	printf("%d\n", sizeof(a + 0));//->4/8
	//在sizeof中,数组名表示整个数组,但此时+0,代表不是整个数组,而是首元素的地址
	//首元素地址+0,还是首元素地址,大小是4/8个字节

	printf("%d\n", sizeof(*a));//->4
	//a此时不属于特殊情况,因此a代表首元素地址
	//*解引用操作符,*a代表首元素类型是int,大小是4个字节

	printf("%d\n", sizeof(a + 1));//->4/8
	//在sizeof中,数组名表示整个数组,但此时+0,代表不是整个数组,而是首元素的地址
	//首元素地址+1,是第二个元素地址,大小是4/8个字节

	printf("%d\n", sizeof(a[1]));//->4
	//a[1]表示第二个元素,类型是int,大小是4个字节

	printf("%d\n", sizeof(&a));//->4/8
	//&a是数组的地址,是地址就是4/8个字节

	printf("%d\n", sizeof(*&a));//->16
	//*和&相互抵消,所以相当于sizeof(a) 
	//在sizeof中,数组名表示整个数组,因此大小是4*4
	// 理解二:
	//&a-->int(*)[4]
	//对数组指针解引用,其实就是访问数组

	printf("%d\n", sizeof(&a + 1));//->4/8
	//&a是数组的地址,&a + 1还是地址,是地址就是4/8个字节

	printf("%d\n", sizeof(&a[0]));//->4/8
	//a[0]代表数组元素,&取地址符,即&a[0]的地址大小是4/8个字节

	printf("%d\n", sizeof(&a[0] + 1));//->4/8
	//a[0]代表数组元素,&取地址符
	//&a[0] + 1代表第二个元素的地址,大小是4/8个字节

字符数组

1.将单个字符放入数组中

1.1使用sizeof

#include<stdio.h>
int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	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));
	return 0;
}

运行结果:

解释如下:

	printf("%d\n", sizeof(arr));//->6
	//在sizeof中,数组名表示整个数组,因此大小是1*6

	printf("%d\n", sizeof(arr + 0));//->4/8
	//在sizeof中,数组名表示整个数组,但此时+0,代表不是整个数组,而是首元素的地址
	//首元素地址+0,还是首元素地址,大小是4/8个字节

	printf("%d\n", sizeof(*arr));//->1
	//a此时不属于特殊情况,因此a代表首元素地址
	//*解引用操作符,*a代表首元素类型是char,大小是1个字节

	printf("%d\n", sizeof(arr[1]));//->1
	//arr[1]代表第二个元素,类型是char,大小是1个字节

	printf("%d\n", sizeof(&arr));//->4/8
	//&arr代表整个数组的地址,是地址大小就是4/8个字节

	printf("%d\n", sizeof(&arr + 1));//->4/8
	//&arr代表整个数组的地址,&arr + 1后,仍然表示地址,是地址大小就是4/8个字节

	printf("%d\n", sizeof(&arr[0] + 1));//->4/8
	//&arr[0]表示数组首元素的地址,&arr[0] + 1表示第二个元素的地址,是地址大小就是4/8个字节

 2.使用 strlen 库函数

#include<stdio.h>
#include<string.h>
int main()
{
	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));
	return 0;
}

运行结果:

解释如下:

	char arr[] = { 'a','b','c','d','e','f' };
	//strlen是库函数
	//求字符串的长度,统计的是在字符串中\0之前的字符的个数
	// 如果没有\0,就会一直往后找……
	printf("%d\n", strlen(arr));//->随机值
	//因为字符数组中没有\0,所以在求字符串长度时,会一直往后找,因此是随机值

	printf("%d\n", strlen(arr + 0));//->随机值
	//数组名是首元素地址,arr + 0还是首元素地址,所以会一直往后找\0,因此是随机值

	//printf("%d\n", strlen(*arr));//->error
	//arr是数组首元素的地址,*arr就是数组首元素,即'a'->97
	//strlen 函数参数部分需要传一个地址,当我们传递的是'a'时,字符'a'的ASCII值是97
	//那就是将97作为地址传参
	//strlen就会从97这个地址开始统计字符串长度,这就属于非法访问内存

	//printf("%d\n", strlen(arr[1]));//->error
	//arr[1]是数组第二个元素,即'b'->98
	//strlen 函数参数部分需要传一个地址,当我们传递的是'b'时,字符'b'的ASCII值是98
	//那就是将98作为地址传参
	//strlen就会从98这个地址开始统计字符串长度,这就属于非法访问内存

	printf("%d\n", strlen(&arr));//随机值
	//&arr是数组的地址,类型是 char(*)[6],数组的地址和数组首元素的地址,值是一样的
	//那么传递给strlen 函数后,依然是从数组的第一个元素的位置开始往后统计
	//strlen 的参数是 const char * str

	printf("%d\n", strlen(&arr + 1));//->随机值
	//&arr是指向首元素的地址,&arr + 1跳过整个数组,然后开始向后数

	printf("%d\n", strlen(&arr[0] + 1));//->随机值
	//&arr[0]是第一个元素的地址,&arr[0] + 1是第二个元素的地址
	//从'b'的位置开始向后数

2.将字符串放在数组中 

2.1使用 sizeof

#include<stdio.h>
int main()
{
	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));
	return 0;
}

 运行结果:

解释如下:

	char arr[] = "abcdef";
	//数组是[a b c d e f \0]  字符串数组隐藏着 \0

	printf("%d\n", sizeof(arr));//->7
	//在sizeof中,数组名表示整个数组,类型是char [7]
	//因此大小是7

	printf("%d\n", sizeof(arr + 0));//->4/8
	//在sizeof中,数组名表示整个数组,但此时+0,代表不是整个数组,而是首元素的地址
	//首元素地址+0,还是首元素地址,大小是4/8个字节

	printf("%d\n", sizeof(*arr));//->1
	//arr是首元素地址,*arr表示数组元素,类型是char,大小是1个字节

	printf("%d\n", sizeof(arr[1]));//->1
	//arr[1]代表数组第二个元素,类型是char,大小是1个字节

	printf("%d\n", sizeof(&arr));//->4/8
	//&arr 表示整个数组的地址,是地址就是4/8

	printf("%d\n", sizeof(&arr + 1));//->4/8
	//&arr 表示整个数组,&arr + 1表示跳过整个数组后的地址
	//但还是属于地址,是地址就是4/8

	printf("%d\n", sizeof(&arr[0] + 1));//->4/8
	//&arr[0]表示第一个元素的地址,&arr[0] + 1表示第二个元素的的地址
	//是地址大小就是4/8个字节

2.2 使用 strlen 库函数

#include<stdio.h>
#include<string.h>
int main()
{
	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));
	return 0;
}

运行结果:

解释如下:

	char arr[] = "abcdef";
	//数组是[a b c d e f \0]  字符串数组隐藏着 \0
	//strlen是库函数
	//求字符串的长度,统计的是在字符串中\0之前的字符的个数
	// 如果没有\0,就会一直往后找……

	printf("%d\n", strlen(arr));//->6
	//arr是数组首元素地址,strlen 从数字首元素地址开始向后找 \0
	//即大小是6个字节

	printf("%d\n", strlen(arr + 0));//->6
	//arr是数组首元素地址,arr + 0还是数组首元素地址
	//strlen 从数字首元素地址开始向后找 \0,即大小是6个字节

	//printf("%d\n", strlen(*arr));//->error
	//*arr 相当于 arr[0],即从数组首元素开始向后找 \0
	//strlen 函数参数部分需要传一个地址,当我们传递的是'a'时,字符'a'的ASCII值是97
	//那就是将97作为地址传参
	//strlen就会从97这个地址开始统计字符串长度,这就属于非法访问内存
	 
	//printf("%d\n", strlen(arr[1]));//->error
	//arr[1] 是数组第二个元素,即从数组第二个元素开始找\0
	//strlen 函数参数部分需要传一个地址,当我们传递的是'b'时,字符'b'的ASCII值是98
	//那就是将98作为地址传参
	//strlen就会从98这个地址开始统计字符串长度,这就属于非法访问内存

	printf("%d\n", strlen(&arr));//->6
	//&arr表示数组地址,数组地址的开始就是数组首元素的地址
	//从数组首元素的地址开始向后找\0
	//即大小就是6个字节

	printf("%d\n", strlen(&arr + 1));//->随机值
	//&arr表示数组地址,&arr + 1表示跳过整个数组,一个数组的大小是7
	//从数组后的地址开始找\0 ,此时,并不知道哪儿有\0
	//因此是随机值

	printf("%d\n", strlen(&arr[0] + 1));//->4/8
	//&arr[0]表示数组首元素地址,&arr[0] + 1表示数组第二个元素的地址
	//从第二个元素往后数,即大小是5个字节

3.字符指针

3.1使用 sizeof  

#include<stdio.h>
int main()
{
	const char* p = "abcdef";
	printf("%d\n", sizeof(p));
	printf("%d\n", sizeof(p + 1));
	printf("%d\n", sizeof(*p));
	printf("%d\n", sizeof(p[0]));
	printf("%d\n", sizeof(&p));
	printf("%d\n", sizeof(&p + 1));
	printf("%d\n", sizeof(&p[0] + 1));
	return 0;
}

运行结果:

解释如下: 

	const char* p = "abcdef";
	//字符指针,指针变量指向的是第一个字符的地址

	printf("%d\n", sizeof(p));//->4/8
	//p是指针变量,大小是4/8个字节

	printf("%d\n", sizeof(p + 1));//->4/8
	//p是指针变量,指向第一个字符
	//p 是 char * 类型,+1 跳过一个字节
	//p + 1指向第二个字符,即字符 b  的地址
	//是地址大小就是4/8个字节

	printf("%d\n", sizeof(*p));//->1
	//*p是首元素,字符 a ,大小是 1 个字节

	printf("%d\n", sizeof(p[0]));//->1
	//p[0] ->* (p+0)
	//表示数组首元素,字符 a ,大小是 1 个字节

	printf("%d\n", sizeof(&p));//->4/8
	//&p表示指针变量p的地址,即二级指针
	//同样属于地址,大小是4/8个字节

	printf("%d\n", sizeof(&p + 1));//->4/8
	//&p表示指针变量p的地址,即二级指针
	//&p + 1同样属于地址,大小是4/8个字节

	printf("%d\n", sizeof(&p[0] + 1));//->4/8
	//&p[0]->&(*(p+0))->p
	//p+1代表第二个字符 b 的地址
	//p+1 仍是指针变量,大小是4/8个字节

3.2 使用 strlen 函数

#include<stdio.h>
#include<string.h>
int main()
{
	const char* p = "abcdef";
	printf("%d\n", strlen(p));
	printf("%d\n", strlen(p + 1));
	printf("%d\n", strlen(*p));
	printf("%d\n", strlen(p[0]));
	printf("%d\n", strlen(&p));
	printf("%d\n", strlen(&p + 1));
	printf("%d\n", strlen(&p[0] + 1));
    return 0;
}

运行结果:

解释如下: 

	const char* p = "abcdef";
	//字符指针,指针变量指向的是第一个字符的地址

	printf("%d\n", strlen(p));//->6
	//p是指针变量,存的是 a 的地址,遇到 \0 停下来

	printf("%d\n", strlen(p + 1));//->5
	//p是指针变量
	//p+1存的是 b 的地址,遇到 \0 停下来
	
	//printf("%d\n", strlen(*p));//->error
	//*p表示字符 a ,即从 a 开始向后找 \0
	//strlen 函数参数部分需要传一个地址,当我们传递的是'a'时,字符'a'的ASCII值是97
	//那就是将97作为地址传参
	//strlen就会从97这个地址开始统计字符串长度,这就属于非法访问内存
	
	//printf("%d\n", strlen(p[0]));//->error
	//p[0]->*(p+0),表示数组首元素,字符 a ,,即从 a 开始向后找 \0
	//strlen 函数参数部分需要传一个地址,当我们传递的是'a'时,字符'a'的ASCII值是97
	//那就是将97作为地址传参
	//strlen就会从97这个地址开始统计字符串长度,这就属于非法访问内存

	printf("%d\n", strlen(&p));//->随机值
	//&p表示取出指针变量 p 的地址,即二级指针
	//不知道指针变量 p 的地址,不知道啥时候可以找到\0,因此是随机值

	printf("%d\n", strlen(&p + 1));//->随机值
	//&p表示取出指针变量 p 的地址,即二级指针
	//&p+1表示指针变量 p 向后移动,和字符串没有任何关系
	//不知道指针变量 p 的地址,不知道啥时候可以找到\0,因此是随机值

	printf("%d\n", strlen(&p[0] + 1));//->5
	&p[0]->&(*(p+0))->p
	//p+1代表第二个字符 b 的地址,遇到 \0 停下来

二维数组 

#include<stdio.h>
int main()
{
	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]));
	return 0;
}

运行结果:

解释如下:

	int a[3][4] = { 0 };
	//二维数组相当于每行是一个一维数组

	printf("%d\n", sizeof(a));//->48
	//sizeof直接+数组名,数组名此时表示整个数组,3*4*4=48

	printf("%d\n", sizeof(a[0][0]));//->4
	//a[0][0]表示二维数组第一行第一个元素,类型是 int,大小是 4 个字节

	printf("%d\n", sizeof(a[0]));//->16
	//a[0]表示二维数组的第一行,计算的是整个数组的大小
	//第一行有四个元素,即4*4=16

	printf("%d\n", sizeof(a[0] + 1));//->4/8
	//a[0]是二维数组中的第一行(即一维数组的数组名),此时并没有单独放在 sizeof 内部,也没有&
	//因此,a[0]表示数组首元素地址,即a[0][0]的地址
	//a[0] + 1表示一维数组的第二个元素的地址,是地址就是4/8个字节

	printf("%d\n", sizeof(*(a[0] + 1)));//->4
	//a[0] + 1表示一维数组的第二个元素的地址
	//*(a[0] + 1))表示数组第二行元素,类型是 int ,大小是 4 个字节

	printf("%d\n", sizeof(a + 1));//->4
	//a此时没有单独放在 sizeof 里面,a 表示第一行的一维数组的地址,类型是 int(*)[4]
	//a+1表示第二行的一维数组的地址,地址大小就是4/8个字节

	printf("%d\n", sizeof(*(a + 1)));//->16
	//*(a + 1)表示第二行的一维数组的元素,共四个元素,即4*4=16

	printf("%d\n", sizeof(&a[0] + 1));//->4/8
	//&a[0]表示数组第一行的地址,&a[0] + 1表示数组第二行的地址
	//地址就是4/8个字节

	printf("%d\n", sizeof(*(&a[0] + 1)));//->16
	//&a[0]表示数组第一行的地址,&a[0] + 1表示数组第二行的地址
	//*(&a[0] + 1))表示数组第二行的元素,共四个元素,即4*4=16

	printf("%d\n", sizeof(*a));//->16
	//a此时没有单独放在 sizeof 里面,a 表示第一行的一维数组的地址,类型是 int(*)[4]
	//*a表示一维数组内的元素,一共四个元素,即4*4=16

	printf("%d\n", sizeof(a[3]));//->16
	//a[3]的类型是int [4],即4*4=16

对于最后一个,我想再解释一下:sizeof 只关注类型

10.指针笔试题

笔试题1:

int main()
{
	int a[5] = { 1,2,3,4,5 };
	int* ptr = (int*)(&a + 1);
	printf("%d %d",*(a + 1), *(ptr - 1));
	return 0;
}

运行结果:

 解释如下:

笔试题2:

struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}* p = (struct Test*)0x100000;//p 是结构体指针
//假设p 的值为0x100000。如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	printf("%p\n", p + 0x1);
	printf("%p\n",(unsigned long) p + 0x1);
	printf("%p\n",(unsigned int *) p + 0x1);

	return 0;
}

运行结果:

解释如下:

struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}* p = (struct Test*)0x100000;//p 是结构体指针
//假设p 的值为0x100000。如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	printf("%p\n", p + 0x1);//+1跳过一个结构体(20个字节)
	printf("%p\n",(unsigned long) p + 0x1);//强制转换为长整型,整型加1就是1 0x100000+1->0x100001
	printf("%p\n",(unsigned int *) p + 0x1);//int ,+1 跳过4个字节 0x100004

	return 0;
}

笔试题三:

int main()
{
	int a[4] = { 1,2,3,4 };
	int* ptr1 = (int*)(&a + 1);
	int* ptr2 = (int*)((int)a + 1);
	printf("%x,%x", ptr1[-1], *ptr2);
	return 0;
}

运行结果:

 解释如下: 

笔试题四:

int main()
{
	int a[3][2] = { (0,1),(2,3),(4,5) };
	int* p;
	p = a[0];
	printf("%d", p[0]);
	return 0;
}

运行结果:

解释如下:

笔试题五:

int main()
{
	int a[5][5];
	int(*p)[4];
	p = a;
	printf("%p\n", &p[4][2]-&a[4][2]);
	printf("%d\n", &p[4][2] - &a[4][2]);
	return 0;
}

运行结果:

 解释如下:

 笔试题六:

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

运行结果:

 解释如下:

笔试题七:

int main()
{
	char* a[] = { "work","at","alibaba" };

	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

运行结果:

 解释如下:

笔试题八:

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

运行结果:

 这个题就留给大家自己思考吧!!!!!

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

囚徒玩电脑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值