C语言中数组和指针的sizeof和strlen详解

        数组和指针是常用的数据类型,了解它们的sizeof和strlen的使用是编写高效程序的关键。本文将通过一系列示例代码详细解释数组和指针在不同场景下的sizeof和strlen的计算方式。

一维数组

首先,我们来看一维数组的情况:

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

在这个例子中,a是一个包含4个整数的数组。下面是对上述代码各行的详细解释:

  • sizeof(a):返回整个数组所占的字节数,这里是4个整数 * 4字节/整数 = 16字节。
  • sizeof(a + 0):数组名加0等于数组本身,但在这里被解释为数组指针,所以返回指针大小,通常是4字节。
  • sizeof(*a):返回数组的第一个元素的大小,这里是4字节,因为a是int类型数组。
  • sizeof(a + 1):数组名加1表示指向数组下一个元素的指针,所以返回指针大小,通常是4字节。
  • sizeof(a[1]):返回数组的第二个元素的大小,同样是4字节,因为a[1]是int类型。
  • sizeof(&a):返回数组的地址的大小,通常是4字节。
  • sizeof(&a + 1):数组地址加1,跳过整个数组,返回地址大小,通常是4字节。
  • sizeof(&a[0] + 1):数组第一个元素的地址加1,同样返回地址大小,通常是4字节。

这里需要注意的是,sizeof(a)计算的是整个数组的大小,而sizeof(&a)计算的是数组的地址的大小。

字符数组

接下来,我们看看字符数组的情况:

int main() {char arr[] = {'a', 'b', 'c', 'd', 'e', 'f'};printf("%d\n", sizeof(arr));       // 6printf("%d\n", sizeof(arr + 0));   // 4printf("%d\n", sizeof(*arr));      // 1printf("%d\n", sizeof(arr[1]));    // 1printf("%d\n", sizeof(&arr));      // 4printf("%d\n", sizeof(&arr + 1));  // 4printf("%d\n", sizeof(&arr[0] + 1));// 4return 0;
}

在这个例子中,arr是一个包含6个字符的数组。下面是对上述代码各行的详细解释:

  • sizeof(arr):返回整个字符数组所占的字节数,这里是6个字符 * 1字节/字符 = 6字节。
  • sizeof(arr + 0):数组名加0等于数组本身,但在这里被解释为数组指针,所以返回指针大小,通常是4字节。
  • sizeof(*arr):返回字符数组的第一个元素的大小,这里是1字节,因为arr是char类型数组。
  • sizeof(arr[1]):返回字符数组的第二个元素的大小,同样是1字节,因为arr[1]是char类型。
  • sizeof(&arr):返回字符数组的地址的大小,通常是4字节。
  • sizeof(&arr + 1):数组地址加1,跳过整个数组,返回地址大小,通常是4字节。
  • sizeof(&arr[0] + 1):数组第一个元素的地址加1,同样返回地址大小,通常是4字节。

这里需要注意的是,sizeof(arr)计算的是整个字符数组的大小,而sizeof(&arr)计算的是数组的地址的大小。

字符串数组

接下来,我们看看字符串数组的情况:

int main() {char arr[] = "abcd";printf("%d\n", strlen(arr));        // 4printf("%d\n", strlen(arr + 0));    // 4// 下面两行是非法的,因为strlen函数需要一个指向字符串的指针作为参数// printf("%d\n", strlen(*arr));// printf("%d\n", strlen(arr[1]));printf("%d\n", strlen(&arr));       // 随机值,传递整个地址给strlenprintf("%d\n", strlen(&arr + 1));   // 随机值,跳到下一个数组printf("%d\n", strlen(&arr[0] + 1)); // 随机值,第一个元素跳到第二个元素return 0;
}

在这个例子中,arr是一个包含4个字符的字符串数组。下面是对上述代码各行的详细解释:

  • strlen(arr):返回字符串 "abcd" 的长度,结果为4。
  • strlen(arr + 0)arr + 0 等于 arr,因为它们都指向数组的起始位置,所以结果也为4。
  • strlen(*arr):非法。strlen函数需要一个指向字符串的指针作为参数,而 *arr 是一个字符,所以传递 *arr 给 strlen 是错误的。
  • strlen(arr[1]):非法。arr[1] 是一个字符,不能作为 `strlen‘的参数
  • strlen(&arr):随机值。整个地址传给 strlen,这可能会偶然在内存中找到一个 \0 字节,从而返回一个非预期的长度值。
  • strlen(&arr + 1):随机值。跳到下一个数组,同样传递整个地址给 strlen
  • strlen(&arr[0] + 1):随机值。第一个元素的地址加1,相当于跳到第二个元素,比 &arr 少一个元素。
  • 需要注意的是,strlen 函数需要一个指向字符串的指针作为参数,而 &arr&arr + 1&arr[0] + 1 在这里都是地址,而非指向字符串的指针。

字符指针

现在,我们来看字符指针的情况:

int main() {char* p = "abcde";printf("%d\n", sizeof(p));          // 随机值,通常为 4 或 8printf("%d\n", sizeof(p + 0));      // 随机值,通常为 4 或 8printf("%d\n", sizeof(*p));         // 1printf("%d\n", sizeof(p[0]));       // 1printf("%d\n", sizeof(&p));         // 随机值,通常为 4 或 8printf("%d\n", sizeof(&p + 1));     // 随机值,通常为 4 或 8printf("%d\n", sizeof(&p[0] + 1));  // 随机值,通常为 4 或 8printf("%d\n", strlen(p));          // 随机值,可能会找到 \0 字节返回一个非预期的长度值printf("%d\n", strlen(p + 0));      // 随机值,同上// 下面两行是非法的,因为strlen函数需要一个指向字符串的指针作为参数// printf("%d\n", strlen(*p));// printf("%d\n", strlen(p[0]));printf("%d\n", strlen(&p));         // 随机值,整个地址传给strlenprintf("%d\n", strlen(&p + 1));     // 随机值,跳到下一个地址printf("%d\n", strlen(&p[0] + 1));  // 随机值,第一个元素的地址加1,相当于跳到第二个元素return 0;
}

在这个例子中,p 是一个字符指针,指向字符串 "abcde" 的起始位置。下面是对上述代码各行的详细解释:

  • sizeof(p):返回指针的大小,通常为 4 或 8 字节,具体取决于系统位数。
  • sizeof(p + 0):指针加0等于指针本身,返回指针大小。
  • sizeof(*p):返回字符的大小,这里是1字节。
  • sizeof(p[0]):同样返回字符的大小,1字节。
  • sizeof(&p):返回指针的大小,通常为 4 或 8 字节。
  • sizeof(&p + 1):指针地址加1,跳到下一个地址,返回地址大小。
  • sizeof(&p[0] + 1):第一个元素的地址加1,相当于跳到第二个元素,返回地址大小。

strlen 函数的使用情况与前面的例子相似,需要注意传递给 strlen 的参数必须是指向字符串的指针。

二维数组

最后,我们来看二维数组的情况:

int main() {int p[3][4] = {0};printf("%d\n", sizeof(p));          // 48printf("%d\n", sizeof(p[0][0]));    // 4printf("%d\n", sizeof(p[0]));       // 16printf("%d\n", sizeof(p[0] + 1));   // 4printf("%d\n", sizeof(*(p[0] + 1)));// 4printf("%d\n", sizeof(p + 1));      // 4printf("%d\n", sizeof(*(p + 1)));   // 16printf("%d\n", sizeof(&p[0] + 1));  // 4printf("%d\n", sizeof(*(&p[0] + 1)));// 16printf("%d\n", sizeof(*p));         // 16printf("%d\n", sizeof(p[3]));       // 16printf("---------------------\n");// 验证printf("%p ", &p[0][0]);printf("%p ", &p[0][1]);printf("%p ", p[0] + 1);return 0;
}

在这个例子中,p 是一个包含3行4列的二维数组。下面是对上述代码各行的详细解释:

  • sizeof(p):返回整个二维数组所占的字节数,这里是3 * 4 * 4 = 48字节。
  • sizeof(p[0][0]):返回二维数组中的单个元素的大小,这里是4字节。
  • sizeof(p[0]):返回二维数组的第一行的大小,相当于数组名,所以返回的是第一行的大小,16字节。
  • sizeof(p[0] + 1):第一行的地址加1,相当于跳到第二个元素,返回地址大小,4字节。
  • sizeof(*(p[0] + 1)):第一行的地址加1,解引用,相当于取第二个元素的大小,4字节。
  • sizeof(p + 1)p 是二维数组的数组名,这里返回的是第二行的地址大小,4字节。
  • sizeof(*(p + 1)):第二行的地址解引用,相当于取整个第二行的大小,16字节。
  • sizeof(&p[0] + 1):第一行的地址加1,相当于跳到第二行的地址,返回地址大小,4字节。
  • sizeof(*(&p[0] + 1)):第一行的地址加1,解引用,相当于取整个第二行的大小,16字节。
  • sizeof(*p)p 是二维数组的数组名,返回的是第一行的大小,16字节。
  • sizeof(p[3]):其实sizeof并不会真正去访问,只是会通过变量来推导类型,所以并不会真的去访问它。在这里a[3]其实是数组指针类型,int (*)[3],大小为4或8个字节。

接下来,通过验证部分,我们输出一些地址以验证我们的计算:

  // 验证printf("%p ", &p[0][0]);printf("%p ", &p[0][1]);printf("%p ", p[0] + 1);

        这部分代码输出了一些地址,其中 &p[0][0] 是第一个元素的地址,&p[0][1] 是第一个元素的地址加1,而 p[0] + 1 是第一行的地址加1。这些输出的地址应该有助于理解地址的增长。

        在这个例子中,p 被看作是一个包含3行4列的二维数组。需要注意的是,在计算二维数组大小时,sizeof(p) 计算的是整个数组的大小,而 sizeof(p[0]) 计算的是数组的第一行的大小,相当于数组名。此外,sizeof(p[0][0]) 计算的是数组中单个元素的大小。

        通过这一系列的例子,我们深入了解了C语言中数组和指针在不同场景下的 sizeofstrlen 的计算方式,对于理解内存布局和地址的增长是非常有帮助的。希望这篇博客能够对C语言初学者和进阶者有所启发。

总结:

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这里的数组名表示整个数组,去除的是整个数组的地址。
  3. 除此之外所有的数组名都表示首元素的地址。

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

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

相关文章

Cisco模拟器-企业网络部署

某企业园区网有:2个分厂(分别是:零件分厂、总装分厂)1个总厂网络中心 1个总厂会议室; (1)每个分厂有自己的路由器,均各有:1个楼宇分厂网络中心 每个楼宇均包含&#x…

004、变量与可变性

1. 变量与可变性 在Rust中,变量默认是不可变的,这一设计是为了让你安全方便地写出复杂、甚至是并行的代码。 当然,Rust也提供了可使用的可变变量的方法,这个待会讨论。 当一个变量是不可变时,一旦它被绑定到某个值上面…

Java之程序、进程、线程、管程和并发、并行的概念

文章目录 1. 进程与线程1.1 程序1.2 进程1.3 线程1.4 管程 2.并行与并发2.1 并发2.2 并行 1. 进程与线程 1.1 程序 程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。简单的说就是我们写的代码。 1.2 进程 (1&…

分布式系统架构设计之分布式数据存储的分类和组合策略

在现下科技发展迅猛的背景下,分布式系统已经成为许多大规模应用和服务的基础架构。分布式架构的设计不仅仅是一项技术挑战,更是对数据存储、管理和处理能力的严峻考验。随着云原生、大数据、人工智能等技术的崛起,分布式系统对于数据的高效存…

MySQL一些常用命令

1、登录本地MySQL #一种是 mysql -u root -p; #(输入密码后回车)#另一种是 mysql -uroot -p123456; #(在-p后面直接带上密码)2、启动MySQL服务 net start mysql; 3、关闭MySQL服务: net stop mysql; 4、创建数据库 create database 数据库名; 5、创建数据…

科技云报道:开源才是大模型的未来?

科技云报道原创。 一年前,ChatGPT横空出世;7个多月后,Meta宣布开源LLaMA 2,并且可免费商用。 这一天,也成为大模型发展的分水岭。短时间内,LLaMA 2对一些闭源的大模型厂商造成了致命性的打击。 随后&…

【力扣100】207.课程表

添加链接描述 class Solution:def canFinish(self, numCourses: int, prerequisites: List[List[int]]) -> bool:# 思路是计算每一个课的入度,然后使用队列进行入度为0的元素的进出# 数组:下标是课程号,array[下标]是这个课程的入度# 哈希…

轻松调整视频时长,创意与技术的新篇章

传统的视频剪辑工具往往难以精确控制时间,而【媒体梦工厂】凭借其先进的算法和界面设计,让视频时长的调整变得简单而精确,助你释放无限的创意,用技术为你的创意插上翅膀,让每一秒都有意义。 所需工具: 一…

性能优化-如何提高cache命中率

本文主要介绍性能优化领域常见的cache的命中率问题,旨在全面的介绍提高cache命中率的方法,以供大家编写出性能友好的代码,并且可以应对性能优化领域的面试问题。 🎬个人简介:一个全栈工程师的升级之路! &am…

听GPT 讲Rust源代码--src/tools(38)

File: rust/src/tools/clippy/clippy_dev/src/lib.rs rust/src/tools/clippy/clippy_dev/src/lib.rs文件是Clippy开发工具的入口文件,其作用是提供Clippy开发过程中所需的功能和工具。Clippy是一个Rust代码的静态分析工具,用于提供各种有用的代码规范、编…

prometheus-docker 快速安装

镜像加速 sudo mkdir -p /etc/docker sudo tee /ect/docker/daemon.json << "EOF" {"register-mirros": ["http://hub-mirror.c.163.com"] } EOF安装docker export DOWNLOAD_URL"http://mirrors.163.com/docker-ce" curl -fsSl…

C++day4作业

定义一个Person类&#xff0c;私有成员int age&#xff0c;string &name&#xff0c;定义一个Stu类&#xff0c;包含私有成员double *score&#xff0c;写出两个类的构造函数、析构函数、拷贝构造和拷贝赋值函数&#xff0c;完成对Person的运算符重载(算术运算符、条件运算…

dubbo的一些问题思考

1.dubbo是啥 Dubbo 是一个高性能的 Java RPC&#xff08;远程过程调用&#xff09;框架&#xff0c;用于构建分布式服务架构。由阿里巴巴开发并开源&#xff0c;作为一个分布式服务框架&#xff0c;Dubbo 提供了丰富的功能&#xff0c;包括服务治理、远程调用、负载均衡、容错机…

kafka-python简单生产消费数据

kafka-python使用手册 kafka-python 1. 生产者同步发送数据 # 生产者同步发送数据from kafka import KafkaProducer from kafka.errors import KafkaErrorproducer KafkaProducer(bootstrap_servers["192.168.1.6:9092"])try:record_metadata producer.send(&qu…

java企业网站系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Web企业网站系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql5.0&…

科技创新实验室数据管理优选:高效企业网盘推荐

科技创新实验室建设是国家加强科技创新基本能力建设的重要措施&#xff0c;企业网盘等高效办公工具的应用是保证科技创新实验室正常运行、提高科研项目团队合作效率的重要手段。 本文将介绍企业网盘Zoho WorkDrive提供的解决方案&#xff1a; 行业痛点1&#xff1a;分散的数据…

听GPT 讲Rust源代码--src/tools(39)

File: rust/src/tools/rustfmt/src/config/config_type.rs 在Rust代码中&#xff0c;rust/src/tools/rustfmt/src/config/config_type.rs文件的作用是定义了与配置相关的数据结构和函数。 Config struct&#xff08;配置结构体&#xff09;&#xff1a;该结构体用于存储rustfmt…

图形化编程(3)之猜拳的加速度计

今天说我们来学习图形化第三节内容&#xff0c;加速度计。加速度传感器是一种能够测量物体加速度的传感器&#xff0c;在运动过程中&#xff0c;通过测量质量的惯性力和牛顿第二定律得到加速度。 根据传感器敏感元件的不同&#xff0c;常见的加速度传感器有电容式、电感式、应变…

zookeeper之集群搭建

1. 集群角色 zookeeper集群下&#xff0c;有3种角色&#xff0c;分别是领导者(Leader)、跟随着(Follower)、观察者(Observer)。接下来我们分别看一下这三种角色的作用。 领导者(Leader)&#xff1a; 事务请求&#xff08;写操作&#xff09;的唯一调度者和处理者&#xff0c;保…

音频播放软件Foobar2000 mac特点介绍

Foobar2000 mac是一款高度可定制的音频播放器&#xff0c;适用于Windows平台。它支持各种音频格式&#xff0c;包括MP3、FLAC、AAC、WMA等&#xff0c;同时也支持各种音频插件和效果器&#xff0c;可以提供更好的音质和用户体验。 Foobar2000 mac软件特点 1. 高度可定制&#…