浅学指针(5)sizeof和strlen的进阶理解

news2025/1/15 17:45:27

系列文章目录


文章目录

  • 系列文章目录
  • 前言
  • 1. sizeof和strlen的对⽐
    • 1.1 sizeof
    • sizeof不是函数,是运算符
  • 1.2 strlen
    • 1.3 sizeof 和 strlen的对⽐
  • 2. 数组和指针笔试题解析
    • • sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩, 单位是字节 • &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素 的地址是有区别的) 除此之外,任何地⽅使⽤数组名,数组名都表⽰⾸元素的地址。
    • 2.1 ⼀维数组
    • 2.2 字符数组
    • 代码4: 这里有重要内容
    • strlen计算内容是地址时,计算的是地址的内容
  • 2.3 ⼆维数组
  • 3. 指针运算笔试题解析
    • 3.2 题⽬2
    • #的作用打印Ox前缀
    • 3.3 题⽬3
    • 3.4 题⽬4
    • 3.5 题⽬5
    • 做这种题最好画图
    • 3.6 题⽬6
    • 3.7 题⽬7


前言

目的:深入学习sizeof和strlen和有关2维数组的指针形式

1. sizeof和strlen的对⽐

1.1 sizeof

在学习操作符的时候,我们学习了 sizeof , sizeof 计算变量所占内存内存空间⼤⼩的,单位是字节,如果操作数是类型的话,计算的是使⽤类型创建的变量所占内存空间的⼤⼩。sizeof 只关注占⽤内存空间的⼤⼩,不在乎内存中存放什么数据。
⽐如:

sizeof不是函数,是运算符

#inculde <stdio.h>
int main()
{
 	int a = 10;
 	printf("%d\n", sizeof(a));
 	printf("%d\n", sizeof a);//也可以这样
 	printf("%d\n", sizeof(int));
 	return 0;
}

输出结果都是一样的,都是4,

1.2 strlen

strlen 是C语⾔库函数,功能是求字符串⻓度。函数原型如下:

	size_t strlen ( const char * str );

统计的是从 strlen 函数的参数 str 中这个地址开始向后, \0 之前字符串中字符的个数。strlen 函数会⼀直向后找 \0 字符,直到找到为⽌,所以可能存在越界查找。

#include <stdio.h>
int main()
{
 	char arr1[3] = {'a', 'b', 'c'};
 	char arr2[] = "abc";
 	printf("%d\n", strlen(arr1));//1
	printf("%d\n", strlen(arr2));//2
 	printf("%d\n", sizeof(arr1));//3
 	printf("%d\n", sizeof(arr1));//4
 return 0;
}

代码1:随机值,没有\0; 代码2:结果是3
代码3:结果3 代码4:结果是4

1.3 sizeof 和 strlen的对⽐

(1)sizeof

  1. sizeof是操作符。
  2. sizeof计算操作数所占内存的⼤⼩,单位是字节。
  3. 不关注内存中存放什么数据。
    (2)strlen
    strlen
  4. strlen是库函数,使⽤需要包含头⽂件 string.h。
  5. srtlen是求字符串⻓度的,统计的是 \0 之前字符的隔个数。
  6. 关注内存中是否有 \0 ,如果没有 \0 ,就会持续往后找,可能会越界。

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

后面所有题都是在下面基础上:

• sizeof(数组名),sizeof中单独放数组名,这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩,
单位是字节
• &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址(整个数组的地址和数组⾸元素
的地址是有区别的)
除此之外,任何地⽅使⽤数组名,数组名都表⽰⾸元素的地址。

2.1 ⼀维数组

写出所占字节数

	int a[] = {1,2,3,4};
	printf("%d\n",sizeof(a));//单单数组名,是整个数组,16
	printf("%d\n",sizeof(a+0));//首元素地址加0,为地址4/8个字节
	printf("%d\n",sizeof(*a));//为首元素内容,4
	printf("%d\n",sizeof(a+1));//首元素地址加1,为第2个元素地址 ,4/8
	printf("%d\n",sizeof(a[1]));//4
	printf("%d\n",sizeof(&a));//地址 4/8
	printf("%d\n",sizeof(*&a));//所有元素地址,解引用为所有元素内容,4*4=16
	printf("%d\n",sizeof(&a+1));//地址,4/8
	printf("%d\n",sizeof(&a[0]));//地址4/8
	printf("%d\n",sizeof(&a[0]+1));//首元素地址加1,第2个元素地址,4/8

2.2 字符数组

代码1:

	char arr[] = {'a','b','c','d','e','f'};
	printf("%d\n", sizeof(arr));//计算的是整个数组大小,6个字节
	printf("%d\n", sizeof(arr+0));//arr+0是数组第一个元素的地址4/8
	printf("%d\n", sizeof(*arr));//*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

代码2:

char arr[] = {'a','b','c','d','e','f'};
	printf("%d\n", strlen(arr));//随机值,因为没有\0
	printf("%d\n", strlen(arr+0));//随机值
	printf("%d\n", strlen(*arr));//err(错误)'b'没有\0
	printf("%d\n", strlen(arr[1]));//err
	printf("%d\n", strlen(&arr));//随机值
	printf("%d\n", strlen(&arr+1));//随机值
	printf("%d\n", strlen(&arr[0]+1));//随机值

代码3:

	char arr[] = "abcdef";
	printf("%d\n", sizeof(arr));//7
	printf("%d\n", sizeof(arr+0));//arr+0是数组首元素的地址,地址的大小4/8
	printf("%d\n", sizeof(*arr));//*arr是数组首元素地址,1
	printf("%d\n", sizeof(arr[1]));//1
	printf("%d\n", sizeof(&arr));//整个数组地址,数组地址也是地址4/8
	printf("%d\n", sizeof(&arr+1));//&arr+1跳过整个数组,指向了数组的后,4/8
	printf("%d\n", sizeof(&arr[0]+1));//&arr[0]+1是第2个元素地址4/8

代码4:
这里有重要内容

	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));//6
	printf("%d\n", strlen(arr+0));//arr+0是首元素地址,6
	printf("%d\n", strlen(*arr));//err(错误)
	printf("%d\n", strlen(arr[1]));//err'b'没有\0
	printf("%d\n", strlen(&arr));//————&arr虽然是数组地址,但是也是指向数组起始位置
	printf("%d\n", strlen(&arr+1));//随机值
	printf("%d\n", strlen(&arr[0]+1));//&arr+1是第2个元素的地址

strlen计算内容是地址时,计算的是地址的内容

代码5:

	char *p = "abcdef";
	printf("%d\n", sizeof(p));//p是指针变量,是地址4/8
	printf("%d\n", sizeof(p+1));//p+1是'b' 的地址,4/8
	printf("%d\n", sizeof(*p));//*p计算'a'大小1个字节
	printf("%d\n", sizeof(p[0]));//p[0] = *(p+0) 1个字节
	printf("%d\n", sizeof(&p));//&p是地址,这里相当于2级指针4/8
	printf("%d\n", sizeof(&p+1));//&p+1是指向p指针变量后面的空间,也是地址,是4/8字节
	printf("%d\n", sizeof(&p[0]+1));//&p[0]+1是'b'的地址,是地址就是4/8个字节

代码6:

	char *p = "abcdef";
	printf("%d\n", strlen(p));//6
	printf("%d\n", strlen(p+1));//跳过1个字节,计算后面的为5
	printf("%d\n", strlen(*p));//err
	printf("%d\n", strlen(p[0]));//err一个字节
	printf("%d\n", strlen(&p));//随机值
	printf("%d\n", strlen(&p+1));//随机值
	printf("%d\n", strlen(&p[0]+1));//5

2.3 ⼆维数组

	int a[3][4] = {0};
	printf("%d\n",sizeof(a));//4*12=48
	printf("%d\n",sizeof(a[0][0]));//4
	printf("%d\n",sizeof(a[0]));//a[0]是第一行这个一维数组的数组名,数组名单独放在sizeof了,计算的是第一行大小,单位是字节,16
	printf("%d\n",sizeof(a[0]+1));//a[]第一行这个一维数组的数组名,这里表示数组首元素,也就是a[0][0]的地址,a[0] + 1是a[0][1]的地址
	printf("%d\n",sizeof(*(a[0]+1));//a[0][1] - 4个字节
	printf("%d\n",sizeof(a+1));//a是二维数组的数组名,但是没有&,也没有单独放在sizeof内部,所以这里的a是数组收元素的地址,应该是第一行的地址,a+1是第二行的地址
	printf("%d\n",sizeof(*(a+1)));//*(a + 1) ==> a[1] - 第二行的数组名,单独放在sizeof内部,计算的是第二行的大小,为16
	printf("%d\n",sizeof(&a[0]+1));//&a[0]是第一行的地址,&a[0]+1就是第二行的地址,4/8
	printf("%d\n",sizeof(*(&a[0]+1)));//访问的是第二行,计算的是第二行的大小,16个字节,相当于放在int(*p)[4] = &a[o] + 1;
	printf("%d\n",sizeof(*a));//这里的a是第一行的地址,*a就是第一行,sizeof(*a)计算的是第一行的大小-16
	printf("%d\n",sizeof(a[3]));//这里不存在越界,因为sizeof内部的表达式不会真实计算的,计算的是第四行的类型大小-16

数组名的意义:

  1. sizeof(数组名),这⾥的数组名表⽰整个数组,计算的是整个数组的⼤⼩。
  2. &数组名,这⾥的数组名表⽰整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表⽰⾸元素的地址。

3. 指针运算笔试题解析

3.1 题⽬1:

#include <stdio.h>
int main()
{
 	int a[5] = { 1, 2, 3, 4, 5 };
 	int *ptr = (int *)(&a + 1);
 	printf( "%d,%d", *(a + 1), *(ptr - 1));
 return 0;
}
//程序的结果是什么?

在这里插入图片描述
*(ptr-1)解析:
在这里插入图片描述

3.2 题⽬2

指针+整数

//在X86环境下
//假设结构体的⼤⼩是20个字节
//程序输出的结构是啥?
struct Test
{
 	int Num;
 	char *pcName;
 	short sDate;
 	char cha[2];
 	short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{
 	printf("%p\n", p + 0x1);//0x100000+20 == x100014
 	printf("%p\n", (unsigned long)p + 0x1);//0x100000+1 == 0x100001
 	printf("%p\n", (unsigned int*)p + 0x1);//0x100000+1 == 0x100004
 	return 0;
}

结果:
在这里插入图片描述
也可以以16进制打印:

struct Test
{
 	int Num;
 	char *pcName;
 	short sDate;
 	char cha[2];
 	short sBa[4];
}*p = (struct Test*)0x100000;
int main()
{
 	printf("%#x\n", p + 0x1);//0x100000+20 == x100014
 	printf("%#x\n", (unsigned long)p + 0x1);//0x100000+1 == 0x100001
 	printf("%#x\n", (unsigned int*)p + 0x1);//0x100000+1 == 0x100004
 	return 0;
}

#的作用打印Ox前缀

结果:
在这里插入图片描述

3.3 题⽬3

重点看

#include <stdio.h>
int main()
{
 	int a[3][2] = { (0, 1), (2, 3), (4, 5) };//这里是(),是逗号表达式,不是{},逗号表达式结果是运算到最右边的结果
 	//int a[3][2] = { 1, 3, 5 };
 	int *p;
 	p = a[0];
 	printf( "%d", p[0]);
 	return 0;
}

分析:
在这里插入图片描述
输出结果:
在这里插入图片描述
为什么是1,其实是把a[0](数组第一行)给p,其实给的是第一行首元素地址。

3.4 题⽬4

//假设环境是x86环境,程序输出的结果是啥?
#include <stdio.h>
int main()
{
 	int a[5][5];
 	int(*p)[4];
 	p = a;
 	printf( "%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);//-4,-4(元素个数)
 	return 0;
}

分析:
在这里插入图片描述
在这里插入图片描述
输出结果:
在这里插入图片描述

3.5 题⽬5

#include <stdio.h>
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));//相当于a[1],第2行,ptr得到的是第2行首元素地址
 	printf( "%d,%d", *(ptr1 - 1), *(ptr2 - 1));
	return 0;
}

做这种题最好画图

在这里插入图片描述

输出结果:
在这里插入图片描述

3.6 题⽬6

#include <stdio.h>
int main()
{
 	char *a[] = {"work","at","alibaba"};//指针数组
 	char**pa = a;
 	pa++;
 	printf("%s\n", *pa);
 return 0;
}

分析:

在这里插入图片描述

输出结果:
在这里插入图片描述

3.7 题⽬7

#include <stdio.h>
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;
}

分析:
解引用cpp是找到cp[ ]里面的内容如:c + 3
在这里插入图片描述
在这里插入图片描述
输出结果:
在这里插入图片描述
好了,指针的学习就到这里就结束了,都看到这里了,点一个赞,谢谢。

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

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

相关文章

一款自动帮你生成UI界面和代码的AI神器

我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 只要描述你想要的UI是什么样的&#xff0c;它就能帮你生成&#xff0c;是不是很神奇&#xff1f; v0使用 AI 模型根据简单的文本提示生成用户界面和代码&#xff…

有序表常见题型

给定一个数组arr和两个整数a和b求arr中有多少个子数组累加和在a到b这个范围上返回达标的子数组数量 如【3&#xff0c;6&#xff0c;1&#xff0c;9&#xff0c;2】达标的子数组通过暴力求解的方式时间复杂度为O&#xff08;N的三次方&#xff09;【找每个子数组占用O&#xf…

MQ - KAFKA 基础篇

##1、KAFKA的核心组件/API Producer API&#xff0c;它允许应用程序向一个或多个 topics 上发送消息记录 Consumer API&#xff0c;允许应用程序订阅一个或多个 topics 并处理为其生成的记录流 Streams API&#xff0c;它允许应用程序作为流处理器&#xff0c;从一个或多个主…

Ubuntu中安装IDEA,并配置桌面快捷方式

1、首先自己下载linux版本的idea 这一步省略不说了 2、在/usr/local/路径下新建安装目录IDEA&#xff1a; mkdir -p /usr/local/IDEA3、执行如下命令&#xff0c;解压下载的压缩包到指定目录&#xff1a; tar -zxvf ideaIU-2022.3.3.tar.gz -C /usr/local/IDEA 注意&#x…

QT 中 QDateTime::currentDateTime() 输出格式备查

基础 QDateTime::currentDateTime() //当前的日期和时间。 QDateTime::toString() //以特定的格式输出时间&#xff0c;格式 yyyy: 年份&#xff08;4位数&#xff09; MM: 月份&#xff08;两位数&#xff0c;07表示七月&#xff09; dd: 日期&#xff08;两位数&#xff0c…

Siemens-NXUG二次开发-新建与保存prt文件[Python UF][20231204]

Siemens-NXUG二次开发-新建与保存prt文件[Python UF][20231204] 1.python uf函数1.1 NXOpen.UF.Part.New1.2 NXOpen.UF.Part.Save1.3 NXOpen.UF.Ui.OpenListingWindow1.4 NXOpen.UF.Ui.IsListingWindowOpen1.5 NXOpen.UF.Ui.WriteListingWindow1.6 NXOpen.UF.Ui.SaveListingWin…

深入了解汉字转拼音转换工具:原理与应用

一、引言 汉字作为世界上最古老、最具象形意的文字之一&#xff0c;承载了数千年的历史文明。然而&#xff0c;在现代信息技术环境下&#xff0c;汉字的输入、输出和检索等方面存在一定的局限性。拼音作为汉字的一种音标表达方式&#xff0c;能够有效地解决这些问题。本文将为…

MYSQL练题笔记-排序和分组-全7题已完成

排序和分组这部分共7道题&#xff0c;如下&#xff0c;只说一说3道&#xff0c;其他都写对了&#xff0c;也不难&#xff0c;只有最后一题难一点点&#xff0c;没想到那种解法&#xff0c;一看到主键和外键就想利用连接。 1.销售分析的题目和表相关内容如下 就是利用product_id…

会话 cookie 及隐私的那些事

什么是会话 Cookie? 会话 Cookie 的概念非常简单。 会话 Cookie,也称为临时 Cookie 或内存 Cookie,是网站在浏览会话期间存储在用户计算机或设备上的小数据片段。 它是由网站生成并由您的浏览器存储和使用的多种 Cookie 之一。 常规 Cookie 或“持久”Cookie 是通常在您的…

周周清(1)

项目进度&#xff1a; 最近一直在搭建环境&#xff0c;都没写什么&#xff1a;登陆页面采用登陆注册在同一个界面&#xff0c;用v-if进行渲染&#xff0c;并且借助validation插件中的yup神器进行校验&#xff0c; <script setup> // import { ref } from vue import * …

机器学习 - 导论

简单了解 机器学习关于数据集的概念 、

免费AI洗稿软件【2023最新】

很多时候我们需要通过文字来表达观点、推广产品或服务。然而&#xff0c;长时间的文稿创作不仅费时费力&#xff0c;还容易陷入表达瓶颈。许多写手和从业者纷纷寻找一款方便、高效的AI洗稿工具。 文心一言洗稿软件。这款软件以其独特的文风生成和洗稿功能而备受瞩目。用户只需…

思维模型 搭便车效应

本系列文章 主要是 分享 思维模型&#xff0c;涉及各个领域&#xff0c;重在提升认知。合理坐享其成。 1 搭便车效应的应用 1.1 搭便车效应在商业竞争中的应用 1 被搭便车的AT&T 在 20 世纪 80 年代&#xff0c;美国电信市场由美国电话电报公司&#xff08;AT&T&…

DNA模糊匹配(动态规划)

我做动态规划还是少的 只会做那些显而易见的动态规划题&#xff08;这题是看了给出来的解题思路做的&#xff09; 以后可能就会做与这类似的了 代码如下&#xff1a; #include<stdio.h> #include<string.h> int get_min(int a, int b, int c); int min_l[301][…

利用STM32内置温度传感器实现温度监测系统

STM32微控制器是一款强大的嵌入式处理器&#xff0c;具有广泛的应用领域。其中&#xff0c;一些STM32微控制器内置了温度传感器&#xff0c;可以方便地实现温度监测功能。本文将详细介绍如何利用STM32内置温度传感器实现温度监测系统&#xff0c;并提供相应的示例代码。 一、硬…

【C++】类和对象——explicit关键字,友元和内部类

这篇博客已经到了类和对象的最后一部分了&#xff0c;下面我们先看一下explicit关键字 我们还是先来引入一个例子&#xff0c;我们的代码是可以这么写的 class A { public:A(int aa 0) {_a aa;cout << "A(int aa 0)" << endl;} private:int _a; }; i…

【Vue】Vue CLI 脚手架(Vue Command Line Interface)安装教程(通过npm)

前言 Vue CLI&#xff08;Vue Command Line Interface&#xff09;是一个基于Vue.js的官方脚手架工具&#xff0c;用于快速搭建和管理Vue.js项目。它提供了一套完整的开发工具和配置&#xff0c;包括项目初始化、开发服务器、热重载、构建和打包等功能。 Vue CLI使用了Webpac…

华为手环关闭智能适时测量

问题 使用华为手环并使用华为创新研究APP后&#xff0c;会自动打开智能适时测量开关&#xff0c;此开关开启后&#xff0c;手环会在睡眠时间自动测量血氧&#xff0c;增加手环功耗从而影响续航&#xff0c;用户可根据自身需求决定是否开启&#xff0c;下文介绍如何找到此开关。…

逻辑漏洞测试靶场实验

任务一&#xff1a; 突破功能限制漏洞&#xff0c;要求突破查询按钮disabled限制&#xff0c;获取编号&#xff1a;110010的查询内容&#xff08;弹框中的flag&#xff09; 任务二&#xff1a;用户信息泄露漏洞&#xff0c;通过回显信息&#xff0c;以暴力破解法方式猜测系统中…

SpringBoot整合Kafka

SpringBoot整合Kafka 文章目录 SpringBoot整合Kafka下载与安装创建topic&#xff0c;测试生产消费程序SpringBoot整合Kafka导坐标做配置做客户端 下载与安装 下载地址&#xff1a; https://kafka.apache.org/downloads 下载2的版本&#xff0c;3.的版本会报错 解压安装&#x…