zoukankan      html  css  js  c++  java
  • sizeof、strlen、字符串、数组,整到一块,你还清楚吗?

    写在前面

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

    正文

    先看一段代码

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 
     4 void testchar(char str[])
     5 {
     6     printf("%d %d
    ", sizeof(str), strlen(str));
     7 }
     8 
     9 void testint(int arr[])
    10 {
    11     printf("%d
    ", sizeof(arr));
    12 }
    13 
    14 int main()
    15 {
    16     char str[] = "abc";
    17     printf("%d %d
    ", sizeof(str), strlen(str)); //4 3
    18 
    19     char str1[10] = "abc";
    20     printf("%d %d
    ", sizeof(str1), strlen(str1)); //10 3
    21 
    22     char dog[] = "wangwangmiao";
    23     printf("%d %d
    ", sizeof(dog), strlen(dog)); //14 8
    24     testchar(dog); //4 8
    25 
    26     char *cat = "wangwangmiaomiao";
    27     printf("%d %d
    ", sizeof(cat), strlen(cat)); //4 8
    28     
    29     int arr[10] = { 0 };
    30     printf("%d %d
    ", sizeof(arr), sizeof(arr[11])); //40 4
    31     testint(arr); //4
    32 
    33     return 0;
    34 }

     结果

     

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

    语法上的本质不同:

    sizeof是运算符,strlen是函数。

    适用范围不一样:

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

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

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

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

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

    a.

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

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

    b.

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

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

    c.

    char dog[] = "wangwangmiao";
    printf("%d %d
    ", sizeof(dog), strlen(dog)); //14 8
    testchar(dog); //4 8

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

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

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

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

    d.

    char *cat = "wangwangmiaomiao";
    printf("%d %d
    ", sizeof(cat), strlen(cat)); //4 8 

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

    e.

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

    前面说过,当数组名作为函数形参出现时,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有一个本质的认识,同时对与之相关的易错、易混问题有一个正确、清晰的判断。由于在下才疏学浅,错误疏漏之处在所难免,希望广大读者积极批评指正,您的批评指正是在下前进的不竭动力。

  • 相关阅读:
    bzoj1015星球大战(并查集+离线)
    bzoj1085骑士精神(搜索)
    bzoj1051受欢迎的牛(Tarjan)
    左偏树学习
    hdu1512 Monkey King(并查集,左偏堆)
    左偏树(模板)
    PAT (Basic Level) Practice (中文) 1079 延迟的回文数 (20分) (大数加法)
    PAT (Basic Level) Practice (中文) 1078 字符串压缩与解压 (20分) (字符转数字——栈存放)
    PAT (Basic Level) Practice (中文) 1077 互评成绩计算 (20分) (四舍五入保留整数)
    PAT (Basic Level) Practice (中文) 1076 Wifi密码 (15分)
  • 原文地址:https://www.cnblogs.com/zpcdbky/p/5857656.html
Copyright © 2011-2022 走看看