指针!!C语言 字符串篇(第四篇)

news2024/11/16 19:34:47

目录

一. sizeof和strlen的对比

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

2.1 一维数组

2.2 字符数组

2.3 二维数组


一. sizeof和strlen的对比

在C语言中有两个比较相似的知识点,就是sizeofstrlen,下面我们来讲一下它们两者之间有什么不同之处?

🎉sizeof:首先sizeof是操作符而不是函数,而且sizeof计算的是变量所占空间的大小,单位是字节,只关注占内存空间的大小,不在乎内存中放的是什么内存数据。

strlenstrlen是一个函数,只针对字符串或字符数组,统计的是"\0"之前的元素个数

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

我们通过一些具体的题目来理解sizeof和strlen对于数组或者字符的计算,同时通过这题目加强我们对于数组名以及变量名的理解和它们与指针的关系,以及指针的移动等等问题。

2.1 一维数组

int main()
{
	int a[] = { 1,2,3,4 };
	printf("%zd\n", sizeof(a));
	printf("%zd\n", sizeof(a + 0));
	printf("%zd\n", sizeof(*a));
	printf("%zd\n", sizeof(a + 1));
	printf("%zd\n", sizeof(a[1]));
	printf("%zd\n", sizeof(&a));
	printf("%zd\n", sizeof(*&a));
	printf("%zd\n", sizeof(&a + 1));
	printf("%zd\n", sizeof(&a[0]));
	printf("%zd\n", sizeof(&a[0] + 1));
	return 0;
}

这些运行结果是怎么出来的呢?让我们来逐个分析一下:

1:首先a是一个数组名,那么我们要知道当数组名单独放在sizeof中计算的是整个数组的长度,所以这个毋庸置疑是4*4=16个,第一个结果计算16。
2 : 此时a并没有单独放在sizeof内部,所以a指的是第一个元素的地址,(a+0)任然是第一个元素的地址,是地址就是4/8个字节。(这个我们就要看是在什么环境下了)。
3 :我们说过只要a不单独放在sizeof中就表示首元素地址,此时*a就是解引用a,现在指的是第一个元素1,是int类型,所以结果是4。
4 :同 2 一样,不过这个表示的是第二个元素的地址,是地址就是4/8个字节。
5 :a[1]就相当于*(a+1),所以表示的是第二个元素,结果就是4。
6 :我们前面也讲过,如果是&a的话表示的也是整个数组,但是&a表示的也是一个地址,是地址就是4/8个字节。
7 :*&a就表示了整个数组元素,所以此时结果是16。
8 :同理我们的&a+1表示跳过一个数组,但任然是一个地址,是地址就是4/8个字节。
9 :&a[0]表示的就是首元素的地址,是地址就是4/8个字节。
10 :任然同上,&a[0]+1表示第二个元素的地址,是地址就是4/8个字节。

 2.2 字符数组

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

 

sizeof计算的是字节,我们根据第一个例也能推算出来这些结果,需要注意的是当数组名单独放在sizeof中的时候,计算的是整个数组的字节长度,另外就是如果是地址的话,不管怎样,都是4/8个字节。🍾下面让我们用strlen函数来计算一下,代码如下:

int mian()
{
	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;
}

通过运行我们可以发现,这段代码是会出现大量的报错,原因是什么呢?首先我们要知道strlen函数是计算"\0"之前的元素个数,而这里的arr数组只是一个字符数组,不是以'\0'结束的字符串。所以第一行中strlen(arr)就会出现报错,同样(arr+0)也不是字符串,同样会报错,再比如arr[1]表示的是一个元素'a',结尾并没有'\0',所以都会报错,所以我们会得到只要不是结尾以'\0'结尾,我们strlen的要不然是随机值,要不就是报错,归根结底都是没有找到'\0',没有找到结束标志。

🎃下面我们就将字符数组换成一个字符串来重新使用strlen函数:

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

我们都知道,如果是字符串的话,在末尾都会有'\0',就像"abcdef",在程序中则是"a b c d e f /0",所以这样的话使用strlen函数就会找到一个结束标志

1:strlen从arr所指向位置开始,直到遇见'\0',计算字符个数。
2:(arr+0)等同于arr,所以仍然是输出6。
3:*arr就表示第一个字符'a',不是字符串,会报错。
4:同理arr[1]表示的是字符'b',不是字符串,会报错。
5:同理&arr和&arr+1都是一个地址,会报错。
6:而对于&arr[0]+1是指向'b',所以从b开始计算到'\0'的长度为5 

🍾当我们把数组换成指针变量的话,再来计算他们的字节长度或者字符个数会有什么变化吗?首先使用sizeof计算字节长度,代码如下:

int main()
{
	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;
}

 

我们逐步分析一下:char* p = "abcdef"; 定义一个字符指针 p,指向字符串常量 "abcdef"
1:sizeof(p) 计算的是指针变量 p 本身所占的字节数,,p是一个地址,通常在 32 位系统为 4 字节,64 位系统为 8 字节,不会报错。
2:sizeof(p + 1),p + 1 仍然是一个指针,所以 sizeof(p + 1) 计算的也是指针所占的字节数,结果与 sizeof(p) 相同,不会报错。
3:sizeof(*p) 表示指针 p 所指向的第一个字符,即 'a',sizeof(*p) 相当于 sizeof(char),结果为 1 字节,不会报错。
4:p[0] 等价于 *p,所以 sizeof(p[0]) 也为 1 字节,不会报错
5:sizeof(&p)是指针 p 的地址,其类型为指向指针的指针,在 32 位系统通常为 4 字节,64 位系统为 8 字节,不会报错。
6:printf("%d\n", sizeof(&p + 1));  // &p + 1 是指向指针的指针的移动,sizeof(&p + 1) 计算的是这种移动后的指针的大小,结果与 sizeof(&p) 相同,不会报错。对于这个大家可能会疑惑有没有可能会越界,但其实sizeof不会真实计算里面的值,不会出先越界情况。
7:printf("%d\n", sizeof(&p[0] + 1));  // &p[0] 等价于 p,&p[0] + 1 仍然是一个指针,所以 sizeof(&p[0] + 1) 计算的也是指针所占的字节数,不会报错。

🎉:使用strlen函数计算,代码如下:

int main()
{
	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;
}

 

为什么只有6和5两个数值呢?逐步分析:

-  printf("%d\n", strlen(p));  :输出 6。从  p  所指向的位置(即字符  'a' )开始,一直到遇到  '\0' ,正好有 6 个字符。
-  printf("%d\n", strlen(p + 1));  :输出 5。 p + 1  使得指针向后移动一位,指向了字符  'b' ,从  'b'  开始到  '\0'  的长度为 5。
-  printf("%d\n", strlen(*p));  :这里会报错。 *p  表示指针  p  所指向的字符,即  'a' 。 strlen  函数需要的是一个字符指针作为参数,而不是单个字符。这里将字符  'a' (其 ASCII 值为 97)当作指针传递给  strlen  函数,会导致错误的内存访问。
-  printf("%d\n", strlen(p[0]));  :与  strlen(*p)  类似,会报错。 p[0]  等价于  *p ,也是字符  'a' ,不能作为  strlen  的参数。
-  printf("%d\n", strlen(&p));  :会产生随机值或报错。 &p  是指针  p  的地址,而不是一个有效的字符串起始位置,所以  strlen  函数的结果是不确定的。
-  printf("%d\n", strlen(&p + 1));  :同样会产生随机值或报错。 &p + 1  是指针的地址再向后移动一位,也不是一个有效的字符串起始位置。
-  printf("%d\n", strlen(&p[0] + 1));  :输出 5。 &p[0]  等价于  p , &p[0] + 1  就相当于  p + 1 ,从该位置开始到  '\0'  的长度为 5。

2.3 二维数组

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

 

printf("%d\n", sizeof(a));  // 输出 48。a 是一个 3 行 4 列的二维数组,int 类型通常占 4 字节,所以 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 个 int 元素的一维数组,所以 4×4 = 16 字节,不会报错

    printf("%d\n", sizeof(a[0] + 1));  // 输出 4 或 8(取决于系统是 32 位还是 64 位)。a[0] + 1 是一个指向 int 的指针,所以其大小为指针大小,不会报错

    printf("%d\n", sizeof(*(a[0] + 1)));  // 输出 4。*(a[0] + 1) 是一个 int 类型的值,所以大小为 4 字节,不会报错

    printf("%d\n", sizeof(a + 1));  // 输出 4 或 8(取决于系统是 32 位还是 64 位)。a + 1 是一个指向二维数组的指针,所以其大小为指针大小,不会报错

    printf("%d\n", sizeof(*(a + 1)));  // 输出 16。*(a + 1) 指向第二行,相当于一个包含 4 个 int 元素的一维数组,大小为 16 字节,不会报错

    printf("%d\n", sizeof(&a[0] + 1));  // 输出 4 或 8(取决于系统是 32 位还是 64 位)。&a[0] + 1 是一个指向第二行的指针,所以其大小为指针大小,不会报错

    printf("%d\n", sizeof(*(&a[0] + 1)));  // 输出 16。*(&a[0] + 1) 指向第二行,相当于一个包含 4 个 int 元素的一维数组,大小为 16 字节,不会报错

    printf("%d\n", sizeof(*a));  // 输出 16。*a 指向第一行,相当于一个包含 4 个 int 元素的一维数组,大小为 16 字节,不会报错

    printf("%d\n", sizeof(a[3]));  // 未定义行为。因为数组 a 只有 3 行,访问 a[3] 超出了数组的边界,不会报错,但结果是不可预测的

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

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

相关文章

python脚本制作循环执行命令行

python import subprocess import sysif __name____main__:ret 1while ret!0:ret subprocess.call(sys.argv[1:], textTrue)pack pip install pyinstaller pyinstaller --onefile loop.py pyinstaller -i *.ico loop.py #指定ico图标 使用场景 使用上面生成的loop.exe调用c…

前端开发者必备:揭秘谷歌F12调试的隐藏技巧!

前言 使用断点(breakpoint)是调试 JavaScript 代码的一种非常有效的方式。通过在代码的关键位置设置断点,可以阻止页面的状态变化,从而方便地检查和修改页面的当前状态。 1. 使用 setTimeout 配合 debugger 和 console.log setTi…

调用百度的大模型API接口实现AI对话!手把手教程!

本文介绍如何使用百度的大模型API接口实现一个AI对话项目 1 注册百度云 2 获取API接口 3 配置环境 4 代码编写与运行 5 chat models 1 注册百度云 搜索百度云,打开官网注册,充值一点点大米(收费很低,大概生成几个句子花费一毛…

立仪光谱共焦传感器应用测量之:汽车连接器高度差测量

01 检测要求,要求测量汽车连接器的高度差 02 检测方式 根据观察,我们采用立仪科技光谱共焦H4UC控制器搭配D65A52系列镜头,角度最大,外径最大,量程大,可以有效应用于测量弧面,大角度面等零件。 0…

【嵌入式之RTOS】什么是著名的食客问题

目录 一、问题描述 二、四个条件 三、实际应用中的意义 著名的“食客问题”(Dining Philosophers Problem)是一个经典的计算机科学问题,用来说明并发编程中的资源竞争和死锁问题。这个问题最初是由荷兰计算机科学家Edsger Dijkstra提出的…

秘密实验室开服教程(SCP: Secret Laboratory)

1、购买后登录服务器(百度莱卡云) 购买服务器后,如下图👇,面板信息都在产品详情页面内 注意:请不要用你的莱卡云账号信息去登陆服务器面板 进入控制面板后会出现正在安装的界面,安装时长约5分…

【排序算法(二)】——冒泡排序、快速排序和归并排序—>深层解析

前言: 接上篇,排序算法除了选择排序(希尔排序)和插入排序(堆排序)之外,还用交换排序(冒泡排序、快速排序)和归并排序已经非比较排序,本篇来深层解析这些排序算…

2 卷积神经网络CNN

文章目录 LeNet-5AlexNetGoogLeNetResNet 本章代码均在kaggle上运行成功 LeNet-5 import torch import torch.nn as nn from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pyplot as plt from matplotlib_inline impo…

木马后门实验

实验拓扑 实验步骤 防火墙 配置防火墙—充当边界NAT路由器 边界防火墙实现内部 DHCP 分配和边界NAT需求,其配置如下 登录网页 编辑接口 配置e0/0 配置e0/1 编辑策略 测试:内部主机能获得IP,且能与外部kali通信 kali 接下来开启 kali 虚…

【视频讲解】后端增删改查接口有什么用?

B站视频地址 B站视频地址 前言 “后端增删改查接口有什么用”,其实这句话可以拆解为下面3个问题。 接口是什么意思?后端接口是什么意思?后端接口中的增删改查接口有什么用? 1、接口 概念:接口的概念在不同的领域中…

BUGKU-WEB-好像需要密码

如果点击start attrack 后出现 Payload set 1: Invalid number settings 的提示,先点hex 后点 decimal 再开始start attrack,这是一个软件bug,需要手动让它刷新。 解题思路 先随便输入测试:admin看看源码吧那就爆破了 据说&…

项目比赛经验分享:如何抓住“黄金一分钟”

项目比赛经验分享:如何抓住“黄金一分钟” 前言引起注意:用事实和故事开场明确痛点:描述问题和影响介绍解决方案:简明扼要激发兴趣:使用视觉辅助概述演讲结构:清晰的路线图我的开场白示例结语 前言 在创新的…

【小超嵌入式】 交叉编译工具安装过程

1、下载交叉编译工具链 ● 确定目标平台: 首先,你需要确定你的目标平台是什么,比如ARM、MIPS等。不同的目标平台需要不同的交叉编译工具链。 ● 获取工具链: 官方网站:通常可以从交叉编译器的官方网站下载适用于你的…

一番赏小程序开发,为消费者带来更多新鲜体验

一番赏作为经典的潮玩方式,深受消费者的喜爱,一番赏还会与不同的热门IP合作,不断推出新的赏品,吸引众多粉丝。赏品的内容非常丰富,从手办、公仔玩具等,还设有隐藏款和最终赏商品,对玩家拥有着非…

人工智能大模型发展的新形势及其省思

自2022年底OpenAI发布ChatGPT以来,大模型产业发展先后经历了百模大战、追求更大参数、刷榜竞分,直到近期各大厂商相继加入价格战,可谓热点纷呈。大模型的技术形态也从单纯文本发展到了多模态,从模拟人类大脑的认知功能发展到操控机…

暂存篇:高频面试题基本总结回顾(含笔试高频算法整理)

干货分享,感谢您的阅读! (暂存篇---后续会删除,完整版和持续更新见高频面试题基本总结回顾(含笔试高频算法整理)) 备注:引用请标注出处,同时存在的问题请在相关博客留言…

韦东山瑞士军刀项目自学之分析部分GPIO_HAL库函数代码

GPIO_HAL部分库函数分析 主要是分析了宏定义,这些宏定义可以被写入到对应的寄存器之中,从引脚到GPIO组再到模式速度等等,每一个参数都对应着寄存器的一位或几位。以后自己还是根据库函数来开发吧,太麻烦了。

《浅谈如何培养树立正确的人工智能伦理观念》

目录 摘要: 一、引言 二、《机械公敌》的情节与主题概述 三、人工智能伦理与法律问题分析 1.伦理挑战 2.法律问题 四、培养正确的人工智能伦理观念的重要性 五、培养正确的人工智能伦理观念的途径与方法 1.加强教育与宣传 2.制定明确的伦理准则和规范 3.…

Java学习Day16:基础篇6

1.静态和非静态 2.调用静态和非静态的过程 注:在Java中,同类中,确实可以使用类的对象来调用静态方法,尽管这不是推荐的做法。静态方法属于类本身,而不是类的任何特定实例。因此,理论上讲,你应该…

分隔链表(LeetCode)

题目 给你一个链表的头节点 和一个特定值 ,请你对链表进行分隔,使得所有 小于 的节点都出现在 大于或等于 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 示例1: 输入:, 输出: 示例2&a…