C语言中关于 strlen 和 sizeof 的用法及区别(含例题及解析)

2024-04-30 10:18

本文主要是介绍C语言中关于 strlen 和 sizeof 的用法及区别(含例题及解析),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、前言

首先我们需要知道的是,sizeof既是一个单目操作符,也是一个关键字,其作用是求操作数的类型长度(以字节为单位)。

 

而strlen是一个字符串操作函数,是一个参数为指针类型返回值为size_t(unsigned int)的函数,求的是字符串的长度。

所以现在我们知道sizeof是一个求操作数类型长度的操作符(关键字),而strlen是一个求字符串长度的字符串操作函数 。

 

二、sizeof和strlen的用法

2.1  sizeof操作符在简单变量中的用法

int a = 10;
char c = 'c';printf("%d\n",sizeof(a));   //答案是4(操作数a的类型为整型,32位机器中占4个字节,64位机器中占8个字节)
printf("%d\n",sizeof(int)); //答案是4
printf("%d\n",sizeof a);  //答案是4(求变量的大小时可以去掉括号)
printf("%d\n",sizeof int);  //错误(求类型的大小时不能去掉括号)printf("%d\n",sizeof(c));  //答案是1(操作数c的类型为字符型,占1个字节)
printf("%d\n",sizeof(char));  //答案是1
printf("%d\n",sizeof c);   //答案是1(求变量的大小时可以去掉括号)
printf("%d\n",sizeof char);  //错误((求类型的大小时不能去掉括号))

2.2  strlen函数在字符指针变量中的简单用法

char *p = "abcdef";
printf("%d\n",strlen(p)); //答案是6

我们知道strlen()函数的参数是一个指针变量,存放的是一个地址。而strlen()函数就是从这个地址开始往后数,直到遇到字符串结束标志'\0' ,停止计数('\0'不纳入计数范围),并返回字符串的长度。

在上面例子中,p是一个指针变量,存放的是字符串首元素'a'的地址。所以将p作为参数传给strlen()函数后,该函数从a开始计数,直到遇到'f'后边的'\0',停止计数,此时字符串长度为6。

2.3sizeof和strlen在数组中的用法

注意:

1.数组名单独放在sizeof内部时,此时数组名表示整个数组,sizeof求的是整个数组的大小。

2.&数组名,此时数组名也表示整个数组,取出的是整个数组的地址。

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

 一维整型数组中:

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

例1:

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

解析:此时数组名a单独放在sizeof内部,表示整个数组,所以求的是整个数组的大小 = 4*4=16。


例2:

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

解析:此时数组名并未单独放在sizeof内部,也没有取地址,所以表示首元素地址,+0后仍表示首元素地址。我们知道只要是地址,在32位机器下便占4个字节,在64位机器下便占8个字节。


例3:

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

解析:此时数组名a表示首元素地址,解引用(*)后表示首元素1,而1是一个整型,32位机器下占4个字节。


例4:

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

解析:此时数组名表示首元素地址,首元素地址+1指向第2个元素,表第二个元素的地址。


例5:

int a[] = {1,2,3,4};
printf("%d\n",sizeof(a[1]));  //4

解析:a[1]表示第二个元素2(整型)。


例6:

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

解析:&数组名取的是整个数组的地址(此时的数组名表示整个数组)。


例7:

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

解析:因为&a中,a表示整个数组,所以*&a,解引用a后计算的是整个数组的大小。


例8:

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

解析:由于&a取出的是整个数组的地址,所以&a+1跳过的是整个数组,但仍表示一个地址。


例9:

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

解析:&a[0]取出的是首元素1的地址。


例10:

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

解析:&a[0]取出的是首元素1的地址,加1后指向第二个元素2,表第二个元素的地址。

 

字符数组中:

sizeof的用法:

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

例1:

char c[] = {'a','b','c','d','e','f'};
printf("%d\n",sizeof(c));   //6

解析:此时数组名c单独放在sizeof内部,表整个数组,所以求的是整个数组的大小 。


例2:

char c[] = {'a','b','c','d','e','f'};
printf("%d\n",sizeof(c+0));   //4

解析:此时数组名并未单独放在sizeof内部,也没有取地址,所以此时的数组名表示首元素'a'的地址,加0后仍表示首元素的地址。


例3:

char c[] = {'a','b','c','d','e','f'};
printf("%d\n",sizeof(*c));   //1

解析:此时的数组名表示首元素的地址,解引用后表示首元素(字符型)。


例4:

char c[] = {'a','b','c','d','e','f'};
printf("%d\n",sizeof(c[1]));   //1

解析:c[1]表示第二个元素'b'(字符型)。


例5:

char c[] = {'a','b','c','d','e','f'};
printf("%d\n",sizeof(&c));   //4

解析:&数组名取出的是整个数组的地址。


例6:

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

解析:&数组名取出的是整个数组的地址,&数组名+1跳过整个数组。


例7:

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

解析:&c[0]取的是首元素的地址,加1后表第二个元素的地址。

 

strlen()的用法:

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

例1:

char c[] = {'a','b','c','d','e','f'};
printf("%d\n",strlen(c));   //随机值

解析:由于strlen()函数求的是字符串长度,而字符串又是以'\0'作为字符串结束标志 。此时的数组中并无'\0'元素,所以当strlen()函数从'a'开始往后数,遇到'\0'停下来的时候我们并不知道确切的字符串长度。


例2:

char c[] = {'a','b','c','d','e','f'};
printf("%d\n",strlen(c+0));   //随机值

解析:给首元素地址加0后仍从首元素开始往后数,此时仍不知道何时会遇到'\0'并停止计数。


例3:

char c[] = {'a','b','c','d','e','f'};
printf("%d\n",strlen(*c));   //错误

解析:在文章最开始时我们就已经知道strlen()函数需要的参数是一个指针类型,而*c表示的是首元素,并不是一个地址,强行调用会引发程序中断。


例4:

char c[] = {'a','b','c','d','e','f'};
printf("%d\n",strlen(c[1]));   //错误

解析:此时c[1]也不是指针类型,而是字符型。


例5:

char c[] = {'a','b','c','d','e','f'};
printf("%d\n",strlen(&c));   //随机值

解析:当取得了整个数组的地址后,strlen()函数从该地址开始往后数,由于不知道什么时候会遇到'\0',所以此字符串长度仍为随机值。


例6:

char c[] = {'a','b','c','d','e','f'};
printf("%d\n",strlen(&c+1));   //随机值

解析:给整个数组的地址加1跳过的是整个数组,即从元素'f'后边开始计数,且字符串长度仍为随机值。但我们可以确定的是strlen(&c) 和 strlen(&c+1)相差的是整个数组的大小6。


例7:

char c[] = {'a','b','c','d','e','f'};
printf("%d\n",strlen(&c[0]+1));   //随机值

解析:首元素地址加1表第二个元素的地址,此时函数从'b'处往后数,直到遇到'\0'停止计数。

 

sizeof的用法

char c[] = "abcdef";
printf("%d\n",sizeof(c));  //7
printf("%d\n",sizeof(c+0));  //4
printf("%d\n",sizeof(*c));  //1
printf("%d\n",sizeof(c[1]));  //1
printf("%d\n",sizeof(&c));  //4
printf("%d\n",sizeof(&c+1));  //4
printf("%d\n",sizeof(&c[0]+1));  //4

例1:

char c[] = "abcdef";
printf("%d\n",sizeof(c));  //7

解析:通过调试并监视c,我们发现此时由于数组是一个字符串,所以会将'\0'存入该数组中,计算字符串长度时,由于'\0'也是数组的一个元素,所以最后字符串长度为7而不是6。 


例2:

char c[] = "abcdef";
printf("%d\n",sizeof(c+0));  //4

解析:首元素地址加0仍表示首元素地址。


例3:

char c[] = "abcdef";
printf("%d\n",sizeof(*c));  //1

解析:此时数组名表示首元素地址,解引用后表示首元素(字符型)。


例4:

char c[] = "abcdef";
printf("%d\n",sizeof(c[1]));  //1

解析:c[1]表示第二个元素(字符型)。


例5:

char c[] = "abcdef";
printf("%d\n",sizeof(&c));  //4

解析:&数组名取的是整个数组的地址。


例6:

char c[] = "abcdef";
printf("%d\n",sizeof(&c+1));  //4

解析:&数组名+1跳过整个数组。


例7:

char c[] = "abcdef";
printf("%d\n",sizeof(&c[0]+1));  //4

解析:首元素地址加1指向第二个元素,表第二个元素的地址。

 

strlen的用法:

char c[] = "abcdef";
printf("%d\n",strlen(c));  //6
printf("%d\n",strlen(c+0));  //6
printf("%d\n",strlen(*c));  //错误
printf("%d\n",strlen(c[1]));  //错误
printf("%d\n",strlen(&c));  //6
printf("%d\n",strlen(&c+1));  //随机值
printf("%d\n",strlen(&c[0]+1));  //5

例1:

char c[] = "abcdef";
printf("%d\n",strlen(c));  //6

解析:此时c在内存中的存储形式如下。由于字符串计算长度时不将'\0'计算在内,所以此字符串长度为6。 


例2:

char c[] = "abcdef";
printf("%d\n",strlen(c+0));  //6

解析:首元素地址加0仍从首元素开始往后数,不将'\0'纳入长度计算范围内。


例3:

char c[] = "abcdef";
printf("%d\n",strlen(*c));  //错误

 解析:所传参数*c是字符型而不是指针类型。


例4:

char c[] = "abcdef";
printf("%d\n",strlen(c[1]));  //错误

解析:所传参数c[1]是字符型而不是指针类型。


例5:

char c[] = "abcdef";
printf("%d\n",strlen(&c));  //6

解析:&数组名取的是整个数组的地址,该地址在数值上跟首元素地址一样,所以也从字符串首元素开始往后数。


例6:

char c[] = "abcdef";
printf("%d\n",strlen(&c+1));  //随机数

解析:&数组名加1后跳过整个数组,从该数组的'\0'后面开始往后数。


例7:

char c[] = "abcdef";
printf("%d\n",strlen(&c[0]+1));  //5

解析:&c[0]加1表示从第二个元素开始往后数。

 

sizeof的用法:

char *p = "abcdef";
printf("%d\n",sizeof(p));  //4
printf("%d\n",sizeof(p+1));  //4
printf("%d\n",sizeof(*p));  //1
printf("%d\n",sizeof(p[0]));  //1
printf("%d\n",sizeof(&p));  //4
printf("%d\n",sizeof(&p+1));  //4
printf("%d\n",sizeof(&p[0]+1));  //4

例1:

char *p = "abcdef";
printf("%d\n",sizeof(p));  //4

解析:此时p指向字符串所在空间,所以p是一个指针变量,存的是该字符串首元素的地址。 


例2:

char *p = "abcdef";
printf("%d\n",sizeof(p+1));  //4

 解析:给p加1后,指向第二个元素,表第二个元素的地址。


例3:

char *p = "abcdef";
printf("%d\n",sizeof(*p));  //1

解析:因为p指向首元素,所以解引用后*p='a'(字符型)。


例4:

char *p = "abcdef";
printf("%d\n",sizeof(p[0]));  //1

解析:p[0]表首元素(字符型)。


例5:

char *p = "abcdef";
printf("%d\n",sizeof(&p));  //4

解析:&p表示将存有字符串首元素地址的变量p的地址取出来,此时的指针变量char **pp = &p(第二颗*表示pp是一个指针变量,char *表示pp变量所存地址的类型)。


例6:

char *p = "abcdef";
printf("%d\n",sizeof(&p+1));  //4

解析:char **pp = &p    ,所以&p+1相当于跳过一个char *型的字节,即4个字节。


例7:

char *p = "abcdef";
printf("%d\n",sizeof(&p[0]+1));  //4

解析:&p[0]取出的是首元素的地址,加1表示第二个元素的地址。

 

strlen的用法:

char *p = "abcdef";
printf("%d\n",strlen(p));  //6
printf("%d\n",strlen(p+1));  //5
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));  //5

例1:

char *p = "abcdef";
printf("%d\n",strlen(p));  //6

解析:从首元素'a'开始往后数,遇到'f'后面的'\0'停止计数。


例2:

char *p = "abcdef";
printf("%d\n",strlen(p+1));  //5

解析:从第二个元素开始往后数。


例3:

char *p = "abcdef";
printf("%d\n",strlen(*p));  //错误

解析:strlen()函数要求所传参数必须为指针类型。


例4:

char *p = "abcdef";
printf("%d\n",strlen(p[0]));  //错误

解析:strlen()函数要求所传参数必须为指针类型。


例5:

char *p = "abcdef";
printf("%d\n",strlen(&p));  //随机数

解析:由于p是一个指针变量,从该变量的地址开始往后数计算字符串长度。


例6:

char *p = "abcdef";
printf("%d\n",strlen(&p+1));  //随机值

解析:char **pp = &p    ,所以&p+1相当于跳过一个char *型的字节之后开始计数字符串的长度。


例7:

char *p = "abcdef";
printf("%d\n",strlen(&p[0]+1));  //5

解析:&p[0]+1后指向第二个元素,所以从'b'开始往后计数字符串的长度。

 

二维数组:

我们知道二维数组在内存中是以一维数组形式存储的。

以int a[3][4]为例,存储形式如下:

那么我们可以将a[0]当成 第一行元素的数组名,a[1]作为第二行元素的数组名,同理,a[2]就是第三行元素的数组名。

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

例1:

int a[3][4] = {0};
printf("%d\n",sizeof(a));   //48

解析:此时数组名单独放在sizeof内部表示整个数组,所以求的是整个数组的大小。


例2:

int a[3][4] = {0};
printf("%d\n",sizeof(a[0][0]));   //4

解析:a[0][0]表示数组首元素。


例3:

int a[3][4] = {0};
printf("%d\n",sizeof(a[0]));   //16

解析:由于a[0]是二维数组第一行的数组名且单独放在sizeof内部,所以此时求的是二维数组第一行的大小。


例4:

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

解析:由于此时a[0]没有单独放在sizeof内部且没有取地址,所以表示的是第一行首元素即a[0][0]的地址,加1表示a[0][1]的地址。


例5:

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

解析:此时a并没有单独放在sizeof内部且没有取地址,所以表示的是首元素第一行a[0]的地址,加1表示第二行a[1]的地址。


例6:

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

解析:因为&数组名取的是整个数组的地址,所以&a[0]取的是数组第一行的地址,加1将跳过这个数组,即表示数组第二行的地址。


例7:

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

解析:此时a并没有单独放在sizeof内部且没有取地址,所以表示的是首元素第一行a[0]的地址,解引用表示第一行。


例8:

int a[3][4] = {0};
printf("%d\n",sizeof(a[3]));   //16

解析:由于此二维数组并不含有a[3]元素,但sizeof操作符在求操作数的类型长度是在程序编译阶段就已经完成,而且其内部元素并不参与运算,所以a[3]就相当于a[0],a[1],a[2]。

 

总结:在二维数组中,对于数组名a来说,它的首元素就是a[0],即第一行,加1就是第二行;而对于第一行a[0]来说,它的首元素就是a[0][0],加1就是a[0][1]

这篇关于C语言中关于 strlen 和 sizeof 的用法及区别(含例题及解析)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/948600

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

native和static native区别

本文基于Hello JNI  如有疑惑,请看之前几篇文章。 native 与 static native java中 public native String helloJni();public native static String helloJniStatic();1212 JNI中 JNIEXPORT jstring JNICALL Java_com_test_g

bytes.split的用法和注意事项

当然,我很乐意详细介绍 bytes.Split 的用法和注意事项。这个函数是 Go 标准库中 bytes 包的一个重要组成部分,用于分割字节切片。 基本用法 bytes.Split 的函数签名如下: func Split(s, sep []byte) [][]byte s 是要分割的字节切片sep 是用作分隔符的字节切片返回值是一个二维字节切片,包含分割后的结果 基本使用示例: pa

OWASP十大安全漏洞解析

OWASP(开放式Web应用程序安全项目)发布的“十大安全漏洞”列表是Web应用程序安全领域的权威指南,它总结了Web应用程序中最常见、最危险的安全隐患。以下是对OWASP十大安全漏洞的详细解析: 1. 注入漏洞(Injection) 描述:攻击者通过在应用程序的输入数据中插入恶意代码,从而控制应用程序的行为。常见的注入类型包括SQL注入、OS命令注入、LDAP注入等。 影响:可能导致数据泄

从状态管理到性能优化:全面解析 Android Compose

文章目录 引言一、Android Compose基本概念1.1 什么是Android Compose?1.2 Compose的优势1.3 如何在项目中使用Compose 二、Compose中的状态管理2.1 状态管理的重要性2.2 Compose中的状态和数据流2.3 使用State和MutableState处理状态2.4 通过ViewModel进行状态管理 三、Compose中的列表和滚动

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

CSP 2023 提高级第一轮 CSP-S 2023初试题 完善程序第二题解析 未完

一、题目阅读 (最大值之和)给定整数序列 a0,⋯,an−1,求该序列所有非空连续子序列的最大值之和。上述参数满足 1≤n≤105 和 1≤ai≤108。 一个序列的非空连续子序列可以用两个下标 ll 和 rr(其中0≤l≤r<n0≤l≤r<n)表示,对应的序列为 al,al+1,⋯,ar​。两个非空连续子序列不同,当且仅当下标不同。 例如,当原序列为 [1,2,1,2] 时,要计算子序列 [