C语言-指针进阶-常见笔试面试题详解(9.4)

news2024/11/28 2:40:55

目录

思维导图:

指针和数组笔试题

指针笔试题

写在最后:


思维导图:

指针和数组笔试题

只有多刷题,才能巩固提高所学的知识。

例1:

#include <stdio.h>

int main()
{
	//一维数组
	int a[] = { 1,2,3,4 };
    //求出下列打印结果及原因

	printf("%d\n", sizeof(a));
	//sizeof(数组名)数组名代表整个数组,数组每个元素是整形,整形大小4个字节,所以打印16

	printf("%d\n", sizeof(a + 0));
	//数组名表示首元素地址,+0还是首元素地址,指针大小4/8,x86环境打印4
	                                                     //我使用的是x86环境 
	printf("%d\n", sizeof(*a));
	//a是首元素地址,*a是首元素,整形大小4个字节,打印4

	printf("%d\n", sizeof(a + 1));
	//a是首元素地址,+1跳过一个整形,代表第二个元素地址,指针大小4/8,打印4

	printf("%d\n", sizeof(a[1]));
	//a[1]是数组第二个元素,就是*(a+1),整形大小4个字节,打印4

	printf("%d\n", sizeof(&a));
	//&a取出的是整个数组的地址,指针大小4/8,打印4

	printf("%d\n", sizeof(*&a));
	//&a取出的是整个数组的地址,*&a解引用出的是整个数组,数组四个整形元素,打印16

	printf("%d\n", sizeof(&a + 1));
	//&a取出整个数组的地址,+1跳过一整个数组,但他还是指针,指针大小4/8,打印4

	printf("%d\n", sizeof(&a[0]));
	//&a[0]取出数组首元素地址,指针大小4/8,打印4

	printf("%d\n", sizeof(&a[0] + 1));
	//&a[0]取出数组首元素地址,+1跳过一个整形,是数组第二个元素的地址,指针大小4/8,打印4

	return 0;
}

输出:

输出:
16
4
4
4
4
4
16
4
4
4

学完上述题目,我们发现了一些特点:

1.sizeof(数组名),数组名表示整个数组,

2.&数组名,取出的是整个数组,

3.除上述两种情况外,数组名都表示数组首元素地址。

例2:

#include <stdio.h>

int main()
{
	//字符数组
	char arr[] = { 'a','b','c','d','e','f' };
	//求出下列打印结果及原因

	printf("%d\n", sizeof(arr));
	//sizeof(arr),arr代表整个数组,数组每个元素是字符,打印6            

	printf("%d\n", sizeof(arr + 0));
	//arr是首元素地址,+0后还是地址,指针大小4/8,打印4

	printf("%d\n", sizeof(*arr));
	//arr是首元素地址,*arr解引用后得到首元素,数组元素是字符,打印1

	printf("%d\n", sizeof(arr[1]));
	//arr[1]是数组的第二个元素,数组每个元素是字符,打印1

	printf("%d\n", sizeof(&arr));
	//&arr取出的是整个元素的地址,指针大小4/8,打印4

	printf("%d\n", sizeof(&arr + 1));
	//&arr取出的是整个元素的地址,+1跳过一整个数组,但还是地址,指针大小4/8,打印4

	printf("%d\n", sizeof(&arr[0] + 1));
	//&arr[0]取出的数组首元素地址,+1跳过一个字节指向数组第二个元素地址,指针大小4/8,打印4

	return 0;
}

输出:

输出:
6
4
1
1
4
4
4

例三:

#include <stdio.h>
#include <string.h>

int main()
{
	//字符数组
	char arr[] = "abcdef";//该字符串在内存中存储的是:"abcdef\0"
	//求出下列打印结果及原因

	//sizeof
	printf("%d\n", sizeof(arr));
	//sizeof(arr)代表整个数组,数组有七个元素,类型是字符,打印7

	printf("%d\n", sizeof(arr + 0));
	//arr是首元素地址,+0后还是首元素地址,指针大小4/8,打印4

	printf("%d\n", sizeof(*arr));
	//arr是首元素地址,*解引用后得到首元素,类型是字符,打印1

	printf("%d\n", sizeof(arr[1]));
	//arr[1]是首元素,类型是字符,打印1

	printf("%d\n", sizeof(&arr));
	//&arr取出的整个数组的地址,指针大小4/8,打印4

	printf("%d\n", sizeof(&arr + 1));
	//&arr取出的整个数组的地址,+1跳过一整个数组,但还是地址,指针大小4/8,打印4

	printf("%d\n", sizeof(&arr[0] + 1));
	//&arr[0]取出的数组首元素地址,+1跳过一个字节指向数组第二个元素地址,指针大小4/8,打印4

	printf("\n");

	//strlen
	printf("%d\n", strlen(arr));
	//arr是首元素地址,strlen通过首元素地址查找整个字符串,遇到\0停下,求出字符长度,打印6

	printf("%d\n", strlen(arr + 0));
	//arr是首元素地址,+0还是首元素地址,打印6

	//printf("%d\n", strlen(*arr));//错误代码
	//arr是首元素地址,*解引用后得到首元素‘a’,‘a’不是地址,程序非法访问内存空间

	//printf("%d\n", strlen(arr[1]));//错误代码
	//arr[1]是数组第二个元素'b','b'不是地址,程序非法访问内存空间

	printf("%d\n", strlen(&arr));
	//&arr取出的是整个数组的地址,但内存中保存的是首元素地址,所以打印6

	printf("%d\n", strlen(&arr + 1));
	//&arr取出整个数组的地址,+1跳过整个数组,指针指向数组后的空间,
	//strlen只有找到\0才会停下,所以程序打印结果是随机值

	printf("%d\n", strlen(&arr[0] + 1));
	//&arr[0]取出的数组首元素地址,+1跳过一个字节指向数组第二个元素地址,
	//strlen将数组第二个元素的地址作为数组的首元素地址,打印5

	return 0;
}

输出:

输出:
7
4
1
1
4
4
4

6
6
6
12
5

tips:

1.strlen 函数将传来的地址作为起始地址。

2.strlen 函数只有遇到'\0'时才会停下来。

例4:

#include <stdio.h>
#include <string.h>

int main()
{
	//字符指针
	char* p = "abcdef";
	//求出下列打印结果及原因

	//sizeof
	printf("%d\n", sizeof(p));
	//p是个指针变量,指针大小4/8,打印4

	printf("%d\n", sizeof(p + 1));
	//p是个字符类型的指针变量,+1跳过一个字节,指针大小4/8,打印4

	printf("%d\n", sizeof(*p));
	//p是个指向字符串首字符的指针,*解引用得到字符串首字符,打印1

	printf("%d\n", sizeof(p[0]));
	//p[0]其实也能写成*(p+0),得到字符串首字符,打印1

	printf("%d\n", sizeof(&p));
	//p是个指针变量,&p是取p的地址,指针大小4/8,打印4

	printf("%d\n", sizeof(&p + 1));
	//p是个指针变量,&p+1还是地址,指针大小4/8,打印4

	printf("%d\n", sizeof(&p[0] + 1));
	//&p[0]取出的是个地址,+1还是地址,指针大小4/8,打印4

	printf("\n");

	//strlen
	printf("%d\n", strlen(p));
	//p是个指向字符串首字符的指针,字符串有6个字符,打印6

	printf("%d\n", strlen(p + 1));
	//p是个指向字符串首字符的指针,+1跳过一个字节,指向字符串第二个字符的地址,打印5

	//printf("%d\n", strlen(*p));//错误代码
	//p是个指向字符串首字符的指针,*解引用得到字符‘a’,'a'不是地址,程序非法访问内存

	//printf("%d\n", strlen(p[0]));//错误代码
	//p[0]代表字符串首字符‘a’,‘a’不是地址,程序非法访问内存

	printf("%d\n", strlen(&p));
	//p是个指针变量,&p取出p的地址,strlen找到\0才停下,打印随机值

	printf("%d\n", strlen(&p + 1));
	//和上一个类似,只是地址+1,打印随机值

	printf("%d\n", strlen(&p[0] + 1));
	//&p[0]取出的字符串首字符地址,+1跳过一个字节,打印5

	return 0;
}

输出:

输出:
4
4
1
1
4
4
4

6
5
15
11
5

接下来是一道有关二维数组的题目:

例5:

#include <stdio.h>
#include <string.h>

int main()
{
	//二维数组
	int a[3][4] = { 0 };
	//求出下列打印结果及原因

	printf("%d\n", sizeof(a));
	//sizeof(a)表示的是整个数组,数组大小是3*4*每个元素4个字节=48,打印48

	printf("%d\n", sizeof(a[0][0]));
	//arr[0][0]代表的是数组首元素,元素类型是整形,打印4

	printf("%d\n", sizeof(a[0]));
	//arr[0]是a[3][4]数组第一行的元素,也就是第一行的数组名,代表整个第一行数组,打印16
	
	printf("%d\n", sizeof(a[0] + 1));
	//arr[0]是数组第一行的元素,+1跳过一行,所以arr[0]+1是数组第二行的数组名,
	//数组名表示首元素地址,指针大小4/8,打印4

	printf("%d\n", sizeof(*(a[0] + 1)));
	//arr[0]+1是数组第二行的首元素地址,*解引用的到首元素,类型是整形,打印4

	printf("%d\n", sizeof(a + 1));
	//a代表数组首行地址,+1跳过一行,代表数组第二行地址,指针大小4/8,打印4

	printf("%d\n", sizeof(*(a + 1)));
	//a + 1代表数组第二行的地址,*解引用得到第二行,打印16

	printf("%d\n", sizeof(&a[0] + 1));
	//&a[0]取出数组第一行地址,+1跳过一行,代表数组第二行地址,指针大小4/8,打印4

	printf("%d\n", sizeof(*(&a[0] + 1)));
	//&a[0] + 1代表数组第二行地址,*解引用得到得到第二行,打印16

	printf("%d\n", sizeof(*a));
	//a是首行的地址,*解引用后得到第一行,打印16

	printf("%d\n", sizeof(a[3]));
	//arr[3]不在数组内,但是sizeof判断大小不会真的去访问内存,他会通过类型判断
	//arr[3]看起来像数组的一行,所以sizeof默认他为数组一行的大小,打印16

	return 0;
}

输出:

输出:
48
4
16
4
4
4
16
4
16
16
16

总结:

二维数组的数组名代表首行地址。

做完上面的题目,相信你一定对数组有了更深层次的理解,

也对sizeof 和strlen 的理解更透彻了,

那么我们接下来就要尝试做一做指针的题目了。

指针笔试题

例1:

#include <stdio.h>

int main()
{
	int a[5] = { 1, 2, 3, 4, 5 };
	int* ptr = (int*)(&a + 1);
	//&a + 1跳过一整个数组,然后强制类型转换成(int*)
	//所以指针变量ptr存放的是数组往后一个元素

	printf("%d,%d", *(a + 1), *(ptr - 1));
	//a时首元素地址,+1得到第二个元素的地址,*解引用得到2,打印2
	//(ptr-1)想前跳一个整形的大小,*解引用得到5,打印5

	return 0;
}

输出:

输出:2,5

例2:

#include <stdio.h>

//已知,结构体Test类型的变量大小是20个字节
struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;

int main()
{
	p = (struct Test*)0x100000;
	printf("%p\n", p + 0x1);
	//结构体Test的指针+1,跳过20个字节,转换成十六进制是0x00100014

	printf("%p\n", (unsigned long)p + 0x1);
	//p被强制类型转换成(unsigned long),整形+1,结果是0x00100001

	printf("%p\n", (unsigned int*)p + 0x1);
	//p被强制类型转换成(unsigned int*),无符号整形指针+1,跳过4个字节,结果是0x00100004

	return 0;
}

输出:

输出:
00100014
00100001
00100004

注:

指针+1才是跳过类型所占的字节大小,

整形加整形科是直接相加的,可别被绕进去了哦。

例3:

#include <stdio.h>

int main()
{
	int a[4] = { 1, 2, 3, 4 };

	int* ptr1 = (int*)(&a + 1);
	//(&a + 1)跳过整个数组,ptr1指向的是数组后一个元素

	int* ptr2 = (int*)((int)a + 1);
	//a是首元素地址,强制类型转换成整形后+1,代表整形1的地址从起始位置往后跳了一个字节
	//所以ptr2指针指向的是整形1的地址从起始位置往后跳了一个字节的位置

	printf("%x,%x", ptr1[-1], *ptr2);
	//prt1[-1]就是*(ptr1-1),向前跳一个整形,指向数组最后一个元素,*解引用后,打印4
	//我是小端环境,数据在内存中是倒着放的,指针指向整形1往后一个字节,访问的时候
	//指针访问四个字节,就将数组下一个元素整形2的第一个字节也包含了,所以打印20000000

	return 0;
}

输出:

输出:4,2000000

例4:

#include <stdio.h>

int main()
{
	int a[3][2] = { (0, 1), (2, 3), (4, 5) };
	//(0,1),看清楚,这是个逗号表达式,所以
	//数组a其实存放的是{ 1, 3, 5 }
	int* p;
	p = a[0];
	//指针p中存储的是数组第一行地址
	printf("%d", p[0]);
	//p[0]是第一行的首元素,所以打印1
	return 0;
}

输出:

输出:1

例5:

#include <stdio.h>

int main()
{
	int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
	//数组第一行:1 2 3 4 5
	//数组第二行:6 7 8 9 10

	int* ptr1 = (int*)(&aa + 1);
	//&aa+1跳过整个数组

	int* ptr2 = (int*)(*(aa + 1));
	//aa是数组首行地址,+1得到第二行地址

	printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1));
	//ptr-1向前跳一个整形,得到数组最后一个元素地址,*解引用得到10
	//ptr2-1向前跳一个整形,得到数组第一行最后一个元素地址,*解引用得到5

	return 0;
}

输出:

输出:10,5

例6:

#include <stdio.h>

int main()
{
	char* a[] = { "work","at","alibaba" };
	//这个指针数组里存放的是三个字符串的首元素地址

	char** pa = a;
	//二级指针pa接收了数组a首元素地址

	pa++;
	//指针++,跳过4个字节,

	printf("%s\n", *pa);
	//*pa指向第二个字符串的首元素地址,打印at

	return 0;
}

输出:

输出:at

 例7:

#include <stdio.h>

int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	//数组中存放四个字符串的首元素地址

	char** cp[] = { c + 3,c + 2,c + 1,c };
	//这个二级指针数组存放的字符串数组分别是:
	//{ "FIRST", "POINT", "NEW", "ENTER" }

	char*** cpp = cp;
	//三级指针cpp指向cp数组的首元素地址

	printf("%s\n", **++cpp);
	//cpp++,cpp指向cp数组第二个元素地址,*第一次解引用得到cp数组第二个元素(c+2)
	//*第二次解引用cp数组第二个元素,得到c数组第三个元素,打印 POINT

	printf("%s\n", *-- * ++cpp + 3);
	//cpp++,cpp指向cp数组第三个元素地址,*第一次解引用得到cp数组第三个元素(c+1)
	//(c+1)--后等于c,再解引用得到c数组第一个元素,然后+3跳过三个字节,打印 ER

	printf("%s\n", *cpp[-2] + 3);
	//*cpp[-2]就是**(cpp-2),cpp-2得到cp数组首元素地址,*第一次解引用得到cp数组第二个元素(c+3)
	//*第二次解引用cp数组第一个元素,得到c数组第四个元素,+3跳过三个字节,打印 ST

	printf("%s\n", cpp[-1][-1] + 1);
	//cpp[-1][-1]就是*(*(cpp-1)-1),cpp-1指向cp数组第二个元素地址,*得到cp数组第二个元素(c+2)
	//然后-1等于(c+1),*得到c数组第二个元素,+1跳过一个字节,打印 EW

	return 0;
}

输出:

输出:
POINT
ER
ST
EW

学到这里,相信你对指针的理解一定更上一层楼了!

写在最后:

以上就是本篇文章的内容了,感谢你的阅读。

如果喜欢本文的话,欢迎点赞和评论,写下你的见解。

如果想和我一起学习编程,不妨点个关注,我们一起学习,一同成长。

之后我还会输出更多高质量内容,欢迎收看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/153253.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

「精研科技」× 企企通,全球MIM龙头借助采购供应商数字化向多领域突破

近日&#xff0c;金属粉末注射成型&#xff08;MIM&#xff09;龙头企业江苏精研科技股份有限公司&#xff08;以下简称“精研科技”&#xff09;与企企通达成合作。双方将共同构建完整的采购管理和供应商协同平台&#xff0c;加强供应商管理&#xff0c;提高采购效率&#xff…

Netty源码性能分析 - ThreadLocal PK FastThreadLocal

序 既然jdk已经有ThreadLocal&#xff0c;为何netty还要自己造个FastThreadLocal&#xff1f;FastThreadLocal快在哪里&#xff1f;这需要从jdk ThreadLocal的本身说起。在java线程中&#xff0c;每个线程都有一个ThreadLocalMap实例变量&#xff08;如果不使用ThreadLocal&…

使用Alexnet实现CIFAR100数据集的训练

如果对你有用的话&#xff0c;希望能够点赞支持一下&#xff0c;这样我就能有更多的动力更新更多的学习笔记了。&#x1f604;&#x1f604; 使用Alexnet进行CIFAR-10数据集进行测试&#xff0c;这里使用的是将CIFAR-10数据集的分辨率扩大到224X224&#xff0c;因为在测试…

【ROS】—— ROS常用组件_TF坐标变换_多态坐标变换与TF坐标变换实操(十一)

文章目录前言1. 多态坐标变换1.1 发布方1.2 订阅方(C)1.3 订阅方(python)2. 坐标系关系查看3. TF坐标变换实操(C)3.1准备3.2 生成新的乌龟3.3 增加键盘控制3.4 发布方(发布两只乌龟的坐标信息)3.5 订阅方(解析坐标信息并生成速度信息)前言 &#x1f4e2;本系列将依托赵虚左老师…

将git仓库瘦身

一个小工具的仓库居然有7个g了&#xff0c;每次clone都要等好久&#xff0c;在网上找的方法&#xff0c;实际了几个小时才成功瘦身&#xff0c;做一次记录 一、排查是哪些历史文件占用了内存&#xff0c;下面是查询最大的5个文件 git rev-list --objects --all | grep "$(…

使用nvm管理node版本,实现高版本与低版本node之间的转换

第一步&#xff1a;先清空本地安装的node.js版本 1.按健winR弹出窗口&#xff0c;键盘输入cmd,然后敲回车&#xff08;或者鼠标直接点击电脑桌面最左下角的win窗口图标弹出&#xff0c;输入cmd再点击回车键&#xff09; 然后进入命令控制行窗口&#xff0c;并输入where node查…

Linux环境配置

一、安装ubuntu16的步骤 1、新建Ubuntu 右击文件---------新建虚拟机 典型----------------下一步 稍后安装操作系统-------------下一步 Linux&#xff08;L&#xff09;------------------Ubuntu64位-----------------下一步 虚拟机名称&#xff08;随意起&#xff09;----…

OSS简单介绍

OSS 阿里云对象存储OSS&#xff08;Object Storage Service&#xff09;是一款海量、安全、低成本、高可靠的云存储服务&#xff0c;可提供99.9999999999%&#xff08;12个9&#xff09;的数据持久性&#xff0c;99.995%的数据可用性。多种存储类型供选择&#xff0c;全面优化…

197: vue+openlayers 预加载preload瓦片地图,减少过渡期间的空白区域

第197个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers项目中演示瓦片预加载和没有预加载的不同状态。 没有采用预加载 当我们平移和缩放地图时,经常会遇到过渡期间的空白区域(因为内容正在加载),过一会儿,切片图像才出现了。 采用预加载,将预载的值设置为…

深入ReentrantLock锁

1. 前言 今天我们来探讨下另一个核心锁ReentrantLock. 从具体的实现到JVM层面是如何实现的。 我们都会一一进行讨论的&#xff0c;好了&#xff0c;废话不多说了&#xff0c;我们就开始吧 2. ReentrantLock 以及synchronized 核心区别&#xff1a; ReentrantLock 是一个抽象的…

MVC框架知识详解

✅作者简介&#xff1a;热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏&#xff1a;Java案例分…

机器学习课程学习随笔

文章目录本文来源机器学习简介机器学习流程机器学习可以完成如下功能&#xff1a;机器学习应用场景金融领域零售领域机器学习分类机器学习实现基于python等代码自己实现本文来源 本博客 是通过学习亚马逊的官方机器学习课程的随笔。 课程参考链接https://edu.csdn.net/course/…

爬虫与反爬虫 - 道高一尺魔高一丈 - 2013最新 - JS逆向 - Python Scrapy实现 - 爬取某天气网站历史数据

目录 背景介绍 网站分析 第1步&#xff1a;找到网页源代码 第2步&#xff1a;分析网页源代码 Python 实现 成果展示 后续 Todo 背景介绍 今天这篇文章&#xff0c;3个目的&#xff0c;1个是自己记录&#xff0c;1个是给大家分享&#xff0c;还有1个是向这个被爬网站的前…

synchronized锁膨胀(附代码分析)

synchronized锁膨胀 1. 基本概念 Java对象头 Java对象的对象头信息中的 Mark Word 主要是用来记录对象的锁信息的。 现在看一下 Mark Word 的对象头信息。如下&#xff1a; 其实可以根据 mark word 的后3位就可以判断出当前的锁是属于哪一种锁。注意&#xff1a;表格中的…

shell脚本练习2023年下岗版

shell脚本练习 1.判断指定进程的运行情况 #!/bin/bash NAMEhttpd #这里输入进程的名称 NUM$(ps -ef |grep $NAME |grep -vc grep) if [ $NUM -eq 1 ]; thenecho "$NAME running." elseecho "$NAME is not running!" fi2.判断用户是否存在 #!/bin/bash r…

【RabbitMQ】安装、启动、配置、测试一条龙

一、基本环境安装配置 1.英文RabbitMQ是基于erlang开发的所以需要erlang环境,点击以下链接下载安装 Downloads - Erlang/OTP 2.官网下载RabbitMQ安装包并安装 Installing on Windows — RabbitMQ 3.配置erlang本地环境变量(和JAVAHOME类似) 4.cmd查看erlang版本 5.点击以下…

自己看的操作系统

计算机网络冯诺依曼体系进程线程内核和虚拟内存os管理线程冯诺依曼体系 计算机五大组成&#xff1a;输入设备、输出设备、控制器、运算器、存储器 进程线程 这些应用都是进程 进程相当于一个菜谱&#xff0c;读取到内存中去使用。 电脑一时间能运行很多进程。 进程中为什么要…

excel函数技巧:MAX在数字查找中的应用妙招

大家都知道VLOOKUP可以按给定的内容去匹配到我们所需的数据&#xff0c;正因为如此&#xff0c;它在函数界有了很大的名气。但是今天要分享的这三个示例&#xff0c;如果使用VLOOKUP去匹配数据的话&#xff0c;就有些麻烦了。就在VLOOKUP头疼不已的时候&#xff0c;MAX函数二话…

2022 年度总结

1、CSDN 年度总结 2022年的粉丝涨幅比较明显竟然超过了之前几年的总和&#xff0c;这是比较意外的。应该是因为今年研究了一些云原生、元宇宙的原因&#xff0c;方向比努力真的重要的多。 1500的阅读确实没想到~~~说明低头一族还是没白当 涨粉稍微明细&#xff0c;不过还需…

English Learning - L1-11 时态 + 情态动词 2023.1.9 周一

English Learning - L1-11 时态 情态动词 2023.1.9 周一8 时态8.4 完成进行时&#xff08;一&#xff09;现在完成进行时核心思维&#xff1a;动作开始于现在之前&#xff0c;并有限地持续下去&#xff0c;动作到目前为止尚未完成1. 动作从过去某时开始一直持续到现在并可能继…