写在前面

sizeof、strlen、字符串、数组,提到这些概念,相信学过C语言的人都能耳熟能详,也能谈得头头是道,但是,在实际运用中,当这些内容交织在一起时,大家却不一定能搞地清清楚楚,本文的目的正是帮助大家将相关知识总结清楚。

正文

先看一段代码

 #include <stdio.h>
#include <stdlib.h> void testchar(char str[])
{
printf("%d %d\n", sizeof(str), strlen(str));
} void testint(int arr[])
{
printf("%d\n", sizeof(arr));
} int main()
{
char str[] = "abc";
printf("%d %d\n", sizeof(str), strlen(str)); //4 3 char str1[] = "abc";
printf("%d %d\n", sizeof(str1), strlen(str1)); //10 3 char dog[] = "wangwang\0miao";
printf("%d %d\n", sizeof(dog), strlen(dog)); //14 8
testchar(dog); //4 8 char *cat = "wangwang\0miaomiao";
printf("%d %d\n", sizeof(cat), strlen(cat)); //4 8 int arr[] = { };
printf("%d %d\n", sizeof(arr), sizeof(arr[])); //40 4
testint(arr); // return ;
}

结果

在解释上面的例子之前,我们先来说一说sizeof和strlen。

语法上的本质不同:

sizeof是运算符,strlen是函数。

适用范围不一样:

对sizeof(name)而言,name可以是变量名也可以是类型名,对strlen而言,参数必须是char*类型的,即strlen仅用于字符串。

重中之重——从底层看本质

strlen(ptr)的执行机理是:从参数ptr所指向的内存开始向下计数,直到内存中的内容是全0(即’\0’)为止(不会对’\0’进行计数)。用strlen测量字符串的长度,其实就是基于这个原理。

sizeof(name)的执行机理是:如果name是一个类型名,得到的是该类型的大小(所谓类型的大小,指的是:如果存在一个该类型的变量,这个变量在内存中所占用的字节数),如果name是一个变量名,那么,sizeof(name)并不会真正访问该变量,而是先获知该变量的类型,然后再返回该类型的大小(即便是struct这样的复杂类型,编译器在编译时也会根据它的各个域记录其大小,所以,由类型得到类型大小,不是一件难事)。换句话说,本质上,sizeof的运算对象是类型。如果name是一个变量名,那么,sizeof如何“看待”name的类型,将是一个关键问题。(后面我们会对这一点有深刻的体会)

上面提到的这一点,是理解好sizeof和strlen的不二法门,是放之四海皆准的准则。下面,我们就以这样的准则来分析上面的例子。

a.

char str[] = "abc";
printf("%d %d\n", sizeof(str), strlen(str)); //4 3

这里,是用数组的形式声明字符串,编译器会自动在字符串后面加上'\0',所以,数组的元素个数是4而不是3。对于sizeof(str)而言,sizeof将str视为char [4]l类型的变量,所以,sizeof(str)的结果就是整个数组所占有的空间大小。对于strlen(str)来说,它从str指向的内存开始计数,直到遇到全0的内存('\0'),所以最后得到结果3。

b.

char str1[] = "abc";
printf("%d %d\n", sizeof(str1), strlen(str1)); //10 3

编译器为char str1[10]分配10个数组元素大小的空间,这与初始化它的字符串没有关系,所以sizeof(str1)得到10。

c.

char dog[] = "wangwang\0miao";
printf("%d %d\n", sizeof(dog), strlen(dog)); //14 8
testchar(dog); //4 8

前两句和a中的情况相同,sizeof(dog)输出整个数组所占的内存大小(包括编译器加上去的'\0'),strlen(dog)遇到'\0'就停止,所以输出8。

再看后面的函数调用,数组名dog作为函数实参传入,我们再来回顾一下testchar函数

void testchar(char str[])
{
printf("%d %d\n", sizeof(str), strlen(str));
}

我们发现,这里sizeof(str)并没有像sizeof(dog)那样得到14,而是得到了4。这是因为,str是函数形参,尽管它是以数组名的形式出现的,传给它的实参也确实是数组名,但sizeof仅仅把它当成一个char*类型的指针看待,所以,sizeof(str)的结果就是char *类型所占的空间4。至于strlen(str),我们前面说过,它执行的机理就是从str指向的内存开始向下计数,直到遇到'\0',所以依然得到8。

d.

char *cat = "wangwang\0miaomiao";
printf("%d %d\n", sizeof(cat), strlen(cat)); //4 8 

由于cat明确声明为char*,所以sizeof将它视为指针,得到4。

e.

int arr[] = {  };
printf("%d %d\n", sizeof(arr), sizeof(arr[])); //40 4
testint(arr); //

前面说过,当数组名作为函数形参出现时,sizeof仅仅将其视为一个指针,否则,sizeof认为它代表整个数组,所以,sizeof(arr)得到整个数组所占的字节数40,而testint(arr)的结果是int*类型的指针的长度4。

sizeof(int[11])中,很明显数组越界了,但并不会出现运行时错误。原因是:依据我们给出的判断准则,sizeof并没有真正访问arr[11],根据arr的声明,sizeof知道arr[11]是int型的,所以返回int类型的大小。

至于testint(arr),道理和c中的testchar(dog)相同。

最后,基于上面的讨论,给出编码准则:

1.永远不要用sizeof来求字符串长度!它不是干这个活的,所以你也永远不会得到正确答案。

2.不要自作聪明地用sizeof(arr)/sizeof(arr[0])这样的代码求数组的长度!sizeof也不是干这个活的。如果arr是函数形参,得到的结果将是错误的(除非你在32位系统下恰好声明int arr[1]或者char arr[4]等,但这纯属巧合)。既然是数组,长度自然是已知的,求数组长度这一本身,就是多此一举的愚蠢行为。

写在后面

本文的目的,就是使读者对C语言的基础知识——sizeof和strlen有一个本质的认识,同时对与之相关的易错、易混问题有一个正确、清晰的判断。由于在下才疏学浅,错误疏漏之处在所难免,希望广大读者积极批评指正,您的批评指正是在下前进的不竭动力。

sizeof、strlen、字符串、数组,整到一块,你还清楚吗?的更多相关文章

  1. 使用 sizeof 获取字符串数组的大小

    @2018-11-1 字符串组成的数组存放于指针数组中,使用 sizeof 获取数组大小 [验证] #include <stdio.h> #define BootScreen " ...

  2. 逗号字符的使用、字符数组与字符串数组、sizeof与strlen

    (1)连接两个表达式为一个表达式 for(ux=0,uxt=1;uxt<444;ux++,uxt++) 允许通过编译:他可以给FOR循环更多的初始化值: (2)一般定义的话要区别只有 字符数组 ...

  3. Strcmp(字符串1,字符串2)函数 Sizeof && strlen() Substr(a,b)

    Strcmp(字符串1,字符串2)函数 { strcmp函数是比较两个字符串的大小,返回比较的结果.一般形式是:  i=strcmp(字符串,字符串); 其中,字符串1.字符串2均可为字符串常量或变量 ...

  4. PHP的count(数组)和strlen(字符串)的内部实现

    PHP的count(数组)和strlen(字符串)的内部实现上是直接显示一个长度变量,还是重头依次数一遍有多少个元素?关乎我理解这2个函数的效率..希望高人能从php的c源码上讲一讲.没有源码看过源码 ...

  5. [JavaScript] 将字符串数组转化为整型数组

    var dataStr="1,2,3,4,5";//原始字符串 var dataStrArr=dataStr.split(",");//分割成字符串数组 var ...

  6. C语言数组:C语言数组定义、二维数组、动态数组、字符串数组

    1.C语言数组的概念 在<更加优美的C语言输出>一节中我们举了一个例子,是输出一个 4×4 的整数矩阵,代码如下: #include <stdio.h> #include &l ...

  7. sizeof, strlen区别

    strlen与sizeof的区别 .sizeof操作符的结果类型是size_t,它在头文件中typedef为unsigned int类型. 该类型保证能容纳实现所建立的最大对象的字节大小. .size ...

  8. @清晰掉 Sizeof与字符串

    Sizeof与字符串 1.以字符串形式出现的,编译器都会为该字符串自动添加一个0作为结束符 如在代码中写  "abc",那么编译器帮你存储的是"abc/0" 2 ...

  9. C++:用字符串数组实现大数运算,以两个不高于40位的大数运算为例。

    因为基本数据类型中整型的内存范围有限,所以直接进行大数之间的运算,不仅浪费空间,而且运行缓慢,甚至有些会导致数据溢出. 那怎么办呢? 这时我们就想直接不行,那咱们来间接的. 这就是我们今天主要要讲的: ...

  10. C++下面关于字符串数组的一些操作

    今天在写一个搜索引擎的分词系统,是很简单的那种,但是居然费了我一天的时间还没完成,晚上估计还得弄一会了,但是在这个过程中,遇到了集中关于字符串数组的操作,值得和大家分享一下. 首先是关于统计字符串数组 ...

随机推荐

  1. linux命令日常总结

    1.date 显示系统日期2. mkdir xx 创建xx目录 rmdir xx 删除xx目录(空目录) rm -rf xx 删除xx目录(非空目录) 3. vi xx 创建某文件 写入-->e ...

  2. ssh无密码通信设置

    ■单向登陆配置:1.在本地机器中的~/.ssh/目录下执行下命令#ssh-keygen -t dsa然后全部回车,采用默认值.生成了一对密钥,id_dsa和id_dsa.pub,存放在用户目录的~/. ...

  3. RDIFramework.NET ━ .NET快速信息化系统开发框架 V3.0 版新增消息管理

    在V3.0版本的Web(Mvc.WebForm)与WinForm中我们新增了“消息管理”模块.“消息管理”模块是对框架的所有消息进行管理.通过左侧的消息分类可以查看所选分类的所有消息列表.在主界面上我 ...

  4. Lua学习笔记一

    学习了有一周多了.之前一直不想献丑,但还是记录下这个过程. 第1章  开发软件搭建 1. ubuntu 下lua安装 sudo apt-get install lua5.1 2.win下的环境搭建. ...

  5. linux基础

    用户管理: id:可以查看当前用户 whoami:查看当前的用户 who:看当前已经登录的用户  w:也可以看 添加用户:useradd name 添加用户组: useradd -g groupnam ...

  6. GPS部标监控平台的功能设计(一)-功能列表

    在2011年交通部的796标准推出后,随着各地交管部门的硬性要求,大多数的GPS监控系统或者车辆管理系统或者物流管理系统,无论是旧的,还是新开发的,都必须要以796标准为基础蓝本,首先要满足796的要 ...

  7. union和union all有什么不同?

    union和union all有什么不同? 相同点:用来获取两个或者两个以上结果集的并集 不同点: union会自动去重,排序 union all没有去重,排序

  8. 关于DoesObjectExist

    目录: 我们可以看到这个文件夹目录是存在的 文件: 然后,我们运行测试一下: 文件存在检测成功(正常) 文件夹存在,但检测失败! ??? 明明存在的,为什么检测不到……

  9. NDK开发-Android Studio+gradle-experimental开发ndk

    在最新的Android Studio2.2的preview版中,增加全新的ndk支持,使用了新的gradle,以及DSL语言. 新的NDK需要使用新的Gradle插件和新的Android插件来支持! ...

  10. LDA( Latent Dirichlet Allocation)主题模型 学习报告

    1     问题描述 LDA由Blei, David M..Ng, Andrew Y..Jordan于2003年提出,是一种主题模型,它可以将文档集中每篇文档的主题以概率分布的形式给出,从而通过分析一 ...