前言:本人为C语言初学者,学识尚浅,研究程度存在很大的局限性,眼界很窄。以下所有观点仅代表个人见解和思路,各位游刃有余的前辈可以给予批评和指正!各位与鄙人同路的学子可相互探讨、发表看法,交换观点!
目录
sizeof,耳熟能详的操作符,用于计算数组的空间大小、类型大小、数组元素个数时都非常实用,就以上三点,对应以下代码:
int arr[10] = {0};
sizeof(arr); //计算整个数组的大小
sizeof(arr[0]); //计算数组单个元素的大小
sizeof(int); //计算整数类型大小
strlen,是C语言的一个库函数,用于返回字符串的长度,例如:
char str[] = "abcde";
strlen(str);
到这里,你似乎觉得并没有什么可说的,但是你是否注意到,对于 strlen 函数只能接收地址,且其声明是这样的:size_t strlen( const char *string ); 也就是说,其地址会被强制装进字符类型指针
而对于sizeof,你会发现,你可以把地址丢给它,类型丢给它,数组也可以直接丢给它,真是和void垃圾桶有得一拼
既然都涉及到了地址,如果不玩玩指针,怎么让你学到新游戏呢?啊不对,新东西。
STRLEN
首先,我们定义一个这样的数组:
char arr[] = "abcdef";
1.直接放数组
int ret = strlen(arr);
strlen不是sizeof,数组名在这里就是首元素地址,所以它从首元素开始读取,直到碰到 '\0' 才会停下来,所以结果是6。故由此也可知,你可不要写出这样的数组:
char arr[] = {'a', 'b', 'c'};
没有‘\0’是十分危险的!
2.数组名加法运算
int ret = strlen(arr + 0);
同理,这里也是数组首元素地址加上0,依然为数组首元素地址,结果依然为6。
3.取地址数组名
int ret = strlen(&arr);
首先要明白的是,我们这里是取出了整个数组的地址,但是如果你细心就会发现其实它的地址和数组首元素地址是一样的,只是它们一个是 char[7] 类型的,一个是 char 类型的,这就导致它们的访问权限不同,但是地址仍然是相同的,所以仍然为6。
但是如果你这样写:
int ret = strlen(&arr + 1);
那这个加1就跳过了整个数组,导致后内容未知:
所以其应该为随机值,我们并不知道它什么时候找到 ‘\0’
4.取数组和下标引用的地址
int ret = strlen(&arr[0]);
这样获得的还是首元素地址嘛,等同于:strlen(arr);
如果你写成这样:
int ret = strlen(&arr[0] + 1);
其指向的是后一个元素,即等同于:strlen(&arr[1]); 所以结果是5。
5.错误写法
(1)
strlen(*arr);
数组名本就是首元素地址,如果取值,则得到了字符 a,但 strlen 前文已述,只能接收地址,如果你硬是传一个 a 过去,最后被读取成地址,strlen就跑到了:0x00000061 去了,咋不出错呢?
(2)
strlen(arr[0]);
和(1)是异曲同工之妙,只是另一种表达方式
总而言之,对于 strlen,首先要清楚,传进去的一定是地址;其次,要明确该地址的指向!
SIZEOF
注:使用64位系统,方便区分int类型大小和地址大小!
首先,我们定义一个数组如下,记好了,记好了,记好了:
int arr[4] = {1 , 2, 3, 4};
我们最常见的情况是:
1.直接放数组:
int ret = sizeof(arr);
这种情况实际是比较特殊的,其实我们知道,数组名如果直接使用的话是当作首元素地址的,唯独两种情况:(1)sizeof,(2)&arr
很不幸,sizeof就居其一,在这里,sizeof 计算的是整个数组的大小,即我们计算其为 int 类型,然后有4个元素,总共是 4*4 = 16byte,当然,其实我认为更正确的理解方法是:其类型为 int[4],所以占16个字节。当然,你喜欢哪一种都可以。
2.数组名加法运算
int ret = sizeof(arr + 0);
数组名加上一个数字,这个数组名当然就不能被当作整个数组使用了,而是露出了它的真实面目,变为了首元素的地址,所以这里其实等同于:sizeof(&arr[0]); 所以结果其实计算的是地址的长度,因为arr参与了运算,所以自然就当作了数组名的本来用途:首元素的地址。所以这里不管你加几,其实都是8,因为都是地址嘛!(当然,我劝你还是不要越界)
3.解引用数组名
int ret = sizeof(*arr);
arr是啥,懵了吧~再次强调,这里 arr 相当于和*先结合了,所以arr是被当作了首元素地址,例2中也是殊途同归,因为 arr+2 先运算,所以 arr 也被当作了首元素地址。所以这里就等同于: sizeof(*&arr[0]); 好像有点怪?*&是一对,所以抵消了,你可以这样看。当然如果你先取地址再取值也是一个道理,所以这里进一步等价于:sizeof(1); 1是啥,1是啥!?1是整数啊喂,所以计算的实际上是int的大小,也就是4byte
4.数组名和下标引用
int ret = sizeof(arr[1]);
说的挺玄乎,其实就是这玩意儿,arr[1] 表示的是2呀,一个整数,所以同理,计算的还是 int 的大小,即4byte。
5.取地址数组名
int ret = sizeof(&arr);
呃,你就告诉我取地址得到的是什么吧!(唏嘘不已,小学生都知道)所以地址的大小肯定是8byte了呗。当然这里也有值得我们深入解析的地方:arr 和 & 先结合,所以 arr 被当作首元素地址,但是这又是一种特殊情况,就是 arr 和 & 结合取出的是数组的地址,这一点就是刚开始提到的数组名特殊用法的第二种。所以如果我们写成这样:
int ret = sizeof(&arr + 1);
虽然结果还是8(因为还是地址),但是其实地址是跳过了整个数组的哦!
6.取地址和解引用
int ret = sizeof(*&arr);
如果你嫌麻烦,* & 一抵消,自然就是计算的整个数组的大小,为16byte。
但是如果打破砂锅问到底,这里的 & 和 arr 结合得到的是整个数组的地址,然后再解引用,得到的就是 arr 数组名,但是有人就要问了,这里不是有操作符和 arr 结合嘛,那arr不应该最后被当作首元素地址?实则不然,因为&和*都实施后,只剩下了 arr,故严格意义上来讲,只能说,它曾经有过(为什么我没有),但是最后还是没有。(我**一直没有!)
7.取数组和下标引用的地址
int ret = sizeof(&arr[0]);
当然,看到取地址,直接答8!(前提语法是对的嗷)这里即取出了数组首元素地址~如果对其进行加法,则表明指向后几位元素,但是仍是地址,为8byte。
当然,如果我再用一个指针变量存放arr首元素地址或者诸如此类其它,都只是换汤不换药,逐步分析即可求解!
不知道以上内容有没有帮助你更深入的理解strlen和sizeof呢?如果遗漏,请务必指出哦!
END