The difference between sizeof and strlen

本文详细解析了C语言中的sizeof运算符与strlen函数的区别及使用场景,通过多个实例对比了两者在处理数组和指针时的不同表现。

一、先看看sizeof()


1. sizeof的概念


sizeof是C语言的一种单目操作符,如C语言的其他操作符++、--等。它并不是函数。

sizeof操作符以字节形式给出了其操作数的存储大小。

操作数可以是一个表达式或括在括号内的类型名。操作数的存储大小由操作数的类型决定。

其实可以简单的理解sizeof是针对"类型"的。

sizeof(...)是运算符,在头文件中typedef为unsigned int,其值在编译时即计算好了,参数可以是数组、指针、类型、对象、函数等。

它的功能是:获得保证能容纳实现所建立的最大对象的字节大小。

由于在编译时计算,因此sizeof不能用来返回动态分配的内存空间的大小。实际上,用sizeof来返回类型以及静态分配的对象、结构或数组所占的空间,返回值跟对象、结构、数组所存储的内容没有关系。

具体而言,当参数分别如下时,sizeof返回的值表示的含义如下:

数组——编译时分配的数组空间大小;
指针——存储该指针所用的空间大小(存储该指针的地址的长度,是长整型,应该为4);
类型——该类型所占的空间大小;
对象——对象的实际占用空间大小;
函数——函数的返回类型所占的空间大小。函数的返回类型不能是void。

2. sizeof的使用方法 


1) 用于数据类型 
sizeof使用形式:sizeof(type) 数据类型必须用括号括住。如sizeof(int)。

2) 用于变量 

sizeof使用形式:sizeof(var_name)或sizeof var_name 

变量名可以不用括号括住。如sizeof (var_name),sizeof var_name等都是正确形式。带括号的用法更普遍,大多数程序员采用这种形式。 

注意:sizeof操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。 

3. sizeof的结果


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

1) 若操作数具有类型char、unsigned char或signed char,其结果等于1。因为 ANSI C正式规定字符类型为1字节。

2) int、unsigned int 、short int(short)、unsigned short 、long int(long) 、unsigned long 、float、double、long double类型的sizeof 

在ANSI C中没有具体规定,大小依赖于实现,一般可能分别为2、2、2、2、4、4、4、8、10。


3) 当操作数是指针时,sizeof依赖于编译器。例如Microsoft C/C++7.0中,near类指针字节数为2,far、huge类指针字节数为4。一般Unix的指针字节数为4。

4) 当操作数具有数组类型时,其结果是数组的总字节数,特别要注意字符串数组,

如:Char str[]=“123456” sizeof(str)=7。

5) 联合类型操作数的sizeof是其最大字节成员的字节数。结构类型操作数的sizeof是这种类型对象的总字节数,包括任何垫补在内。

让我们看如下结构: 

struct {char b; double x;} a; 

在某些机器上sizeof(a)=16,而一般sizeof(char)+ sizeof(double)=9。

这是因为编译器在考虑对齐问题时,在结构中插入空位以控制各成员对象的地址对齐。

6) 如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小。

即:int func(char p[100])

{ sizeof(p) = 4; }

C/C++中不能传数组,只能传指针,所以任何数组都会隐式转成指针形式进行操作,所以"类型"还是指针。

7) sizeof是运算符当编译器编译时会自动运算这个变量的大小的并使用它的大小代替sizeof的值如

int len = sizeof(int);编译时编译器计算出int的大小大小为4所以把上面这句变成

int len = 4

4. sizeof与其他操作符的关系


  sizeof的优先级为2级,比/、%等3级运算符优先级高。它可以与其他操作符一起组成表达式。如i*sizeof(int);其中i为int类型变量。

5. sizeof的主要用途


1) sizeof操作符的一个主要用途是与存储分配和I/O系统那样的例程进行通信。例如: 
  void *malloc(size_t size), 
  size_t fread(void * ptr,size_t size,size_t nmemb,FILE * stream)。 


2) sizeof的另一个的主要用途是计算数组中元素的个数。例如: 
  void *memset(void *s,int c,sizeof(s))。



3) 在动态分配一对象时,可以让系统知道要分配多少内存。

如:
int *p=(int *)malloc(sizeof(int)*10);



4) 由于操作数的字节数在实现时可能出现变化,建议在涉及到操作数字节大小时用sizeof来代替常量计算。

5) 如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小。

6. 建议

  由于操作数的字节数在实现时可能出现变化,建议在涉及到操作数字节大小时用ziseof来代替常量计算。


二、下面我们来看看strlen()


1. strlen是函数


strlen只能用char*做参数,且必须是以''\0''结尾的。Strlen将返回它的长度,不包括‘\0’。

strlen(...)是函数,要在运行时才能计算。参数必须是字符型指针(char*)。当数组名作为参数传入时,实际上数组就退化成指针了。

    它的功能是:返回字符串的长度。该字符串可能是自己定义的,也可能是内存中随机的,该函数实际完成的功能是从代表该字符串的第一个地址开始遍历,直到遇到结束符NULL。返回的长度大小不包括NULL。

2. 由几个例子说开去。


例一:
char* ss = "0123456789";
sizeof(ss) 结果 4===>ss是指向字符串常量的字符指针
sizeof(*ss) 结果 1===>*ss是第一个字符

char ss[] = "0123456789";
sizeof(ss) 结果 11 ===>ss是数组,计算到\0位置,因此是10+1
sizeof(*ss) 结果 1 ===>*ss是第一个字符

char ss[100] = "0123456789";
sizeof(ss) 结果是100 ===> ss表示在内存中的大小 100×1
strlen(ss) 结果是10 ===> strlen是个函数内部实现是用一个循环计算到\0为止之前

int ss[100] = "0123456789";
sizeof(ss) 结果 400 ===>ss表示再内存中的大小 100×4
strlen(ss) 错误===>strlen的参数只能是char*且必须是以''\0''结尾的

char q[]="abc";
char p[]="a\n";
sizeof(q),sizeof(p),strlen(q),strlen(p);


结果是 4 3 3 2

例二:
class X{
    int i;
    int j;
    char k;
};

X x;
cout<<sizeof(X)<<endl; //结果 12 ===>内存补齐
cout<<sizeof(x)<<endl; //结果 12 同上


例三:

数组用作参数传递,见上面6


例四:
    char arr[10]= "What?";

    int len_one = strlen(arr);

    intlen_two = sizeof(arr);

    cout << len_one <<" and " << len_two << endl;


    输出结果为:5 and 10

    点评:sizeof返回定义arr数组时,编译器为其分配的数组空间大小,不关心里面存了多少数据。strlen只关心存储的数据内容,不关心空间的大小和类型。

例五:
    char * parr = new char[10];

    int len_one = strlen(parr);

    int len_two = sizeof(parr);

    int len_three = sizeof(*parr);

    cout << len_one <<" and " << len_two << " and " <<len_three << endl;


    输出结果:23 and 4 and 1

    点评:第一个输出结果23实际上每次运行可能不一样,这取决于parr里面存了什么(从parr[0]开始知道遇到第一个NULL结束);第二个结果实际上本意是想计算parr所指向的动态内存空间的大小,但是事与愿违,sizeof认为parr是个字符指针,因此返回的是该指针所占的空间(指针的存储用的是长整型,所以为4);第三个结果,由于*parr所代表的是parr所指的地址空间存放的字符,所以长度为1。

三、重要的区别:


1.sizeof是运算符,strlen是函数。

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

3.sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以''\0''结尾的。

sizeof还可以用函数做参数,比如:
short f();
printf("%d\n", sizeof(f()));


输出的结果是sizeof(short),即2。

4.sizeof后如果是类型必须加括弧,如果是变量名可以不加括弧。这是因为sizeof是个操作符不是个函数。

5.当适用了于一个结构类型时或变量, sizeof 返回实际的大小, 当适用一静态地空间数组, sizeof 归还全部数组的尺寸。

 sizeof 操作符不能返回动态地被分派了的数组或外部的数组的尺寸 。
6.strlen计算的是字符串的长度,sizeof计算的是变量使用的内存大小,不受里面存储的内容改变

7.strlen的结果要在运行的时候才能计算出来,时用来计算字符串的长度,不是类型占内存的大小。

8.数组做sizeof的参数不退化,传递给strlen就退化为指针了。

9.大部分编译程序 在编译的时候就把sizeof计算过了 是类型或是变量的长度这就是sizeof(x)可以用来定义数组维数的原因
char str[20]="0123456789";

int a=strlen(str); //a=10;

int b=sizeof(str); //而b=20;


10.数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,如:
fun(char [8])
fun(char [])

都等价于 fun(char *)
在C++里参数传递数组永远都是传递指向数组首元素的指针,编译器不知道数组的大小
如果想在函数内知道数组的大小, 需要这样做:
进入函数后用memcpy拷贝出来,长度由另一个形参传进去
fun(unsiged char *p1, int len)
{
  unsigned char* buf = new unsigned char[len+1]
  memcpy(buf, p1, len);
}

我们能常在用到 sizeof 和 strlen 的时候,通常是计算字符串数组的长度

11.使用区别,从这个例子可以看得很清楚:
char str[20]="0123456789";

int a=strlen(str); //a=10; >>>>strlen 计算字符串的长度,以结束符 0x00 为字符串结束。

int b=sizeof(str); //而b=20; >>>> sizeof 计算的则是分配的数组 str[20] 所占的内存空间的大小,不受里面存储的内容改变。


上面是对静态数组处理的结果,如果是对指针,结果就不一样了
char* ss = "0123456789";

sizeof(ss) 结果 4 ===》ss是指向字符串常量的字符指针,sizeof 获得的是一个指针的之所占的空间,应该是长整型的,所以是4

sizeof(*ss)结果 1 ===》*ss是第一个字符 其实就是获得了字符串的第一位'0' 所占的内存空间,是char类型的,占了 1 位

strlen(ss)= 10 >>>> 如果要获得这个字符串的长度,则一定要使用 strlen


四、测试题:


1.不要上机测试,否则便没有什么意义。

如下程序的输出是什么?(在intel x86(32-bit) platform.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main( )
{
    char str[10];
    char *p;
    memset(str,0x00,sizeof(str));
    p = (char *)malloc(100);
    printf("%d\n",sizeof(p));
    printf("%d\n",sizeof('p'));
    printf("%d\n",strlen(str));
    exit(0);
} 


答:4/1/0,如果不加memset(),第三行就会打出15(随机数),请问这是为什么?

memset函数是初始化分配的内存空间,使用0、0x00都是0即’\0',不同系统当分配一块内存时,这块内存中的内容是未知的,系统只是根据申请者的要求为其化一块内存并不管他原先的内容是什么(有的系统清零),所以你的是随即数15。

2.你能够正确的说出它们的sizeof和strlen的大小吗?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main( )

{

     char *str1="absde";
       char str2[]="absde";
       char str3[8]={'a',};
       char str4[8]={'a','b','s','e','f','g','h','j'};

     printf("sizeof(str1)=%d\n",sizeof(str1));
       printf("sizeof(str2)=%d\n",sizeof(str2));
       printf("sizeof(str3)=%d\n",sizeof(str3));
       printf("sizeof(str4)=%d\n",sizeof(str4));

     printf("strlen(str1)=%d\n",strlen(str1));
       printf("strlen(str2)=%d\n",strlen(str2));
       printf("strlen(str3)=%d\n",strlen(str3));
       printf("strlen(str4)=%d\n",strlen(str4));

     exit(0);

} 


答:sizeof是计算括号中变量的类型所占的储存空间(不考虑内容);strlen是计算变量值为起点的内存地址到第一个'\0'的距离,以字节为单位,字符串尾部为'\0',0=='\0'(不包括’\0’)。正确答案是:4、6、8、8;5、5、1、9;

3.这个例子可以说明一些问题:
char str[20]="0123456789";
int a=strlen(str); //a=10; >>>> strlen 计算字符串的长度,以结束符 0x00 为字符串结束。
int b=sizeof(str); //而b=20; >>>> sizeof 计算的则是分配的数组 str[20] 所占的内存空间的大小,不受里面存储的内容改变。


上面是对静态数组处理的结果,如果是对指针,结果就不一样了
char* ss = "0123456789";
sizeof(ss) 结果 4 ===>ss是指向字符串常量的字符指针,sizeof 获得的是一个指针的之所占的空间,应该是长整型的,所以是4
sizeof(*ss) 结果 1 ===>*ss是第一个字符 其实就是获得了字符串的第一位'0' 所占的内存空间,是char类型的,占了 1 位strlen(ss)= 10 ===> 如果要获得这个字符串的长度,则一定要使用 strlen



五、网上其他讨论


1.

char * fun(char *str)
{
    memset(str, 0, sizeof(str));  //用strlen和sizeof()有什么区别
    ...
    return str;
}
int main(int argc, char* argv[])
{
    char *a, b[400];
    a = fun(b);
}


strlen()和sizeof()出来的长度是不一样的,但结果好像都一样,memset()有那么聪明吗?


2. sizeof 这个是在汇编里面就存在的一个指令,可以直接返回你要判断的变量战局的内存的大小。

这个是在编译器就确定的,一个要注意的问题是,看下面的代码

char* str=new char[100]
sizeof(str)
这个可是会返回4哦,可不是你要的400
而 char str[100]
sizeof(str)是会返回400的。
但是,无论如何strlen()都是一个函数,它返回的是一个字符串的长度,也就是说从你给的字符串
首地址开始,一直到'\0'为止的这一段长度。
memset真的没有那么智能,但是它确实高效。


3. strlen 返回的是实际串长
sizeof 如果*str是指针则返回 char *的大小 如果是用数组声明 返回的是空间的大小
char *sz = "abcde";
char sz1[20] = "abcde";
cout<<sizeof(sz)<<endl;
cout<<strlen(sz)<<endl;
cout<<sizeof(sz1)<<endl;
cout<<strlen(sz1)<<endl;


输出:
4
5
20
5


4. memset(str, 0, sizeof(str));  //用strlen和sizeof()有什么区别?
答:用sizeof的话,只给str所指向的内存块连续4个字节清0;
      用strlen的话,是给str所指向的字符串全部清0;
     
    sizeof(str)返回str这个指针变量所占的内存字节数目;
    strlen(str) 返回str所指向的字符串的长度


5. sizeof()应该是编译时处理的。strlen是函数,不一样的
char * fun(char *str)
{

    /*
       sizeof(str))求得是指针str的大小,指针占空间是一样的4个字节;

       str指向的是数组的首地址,这样相当于将数组前四个元素至为‘\0’,用printf("%s")的话,遇到第一个'\0',即退出。
       如果用memset(str, 0, strlen(str));就得看运气了,str指向数组b[400]首地址,b[400]没有显示初始化,strlen是遇到'\0'退出,有可能b[0]就是'\0'
       strlen(str)的结果就为0,用printf("%s")就打印不出来了;strlen(str)也有可能是其他值,得看'\0'在b[400]的哪个位置了
   */

   memset(str, 0, sizeof(str));
    return str;
}
int main(int argc, char* argv[])
{
    char *a, b[400];
    a = fun(b);
}


6.
char * fun(char *str)
{
    memset(str, 0, sizeof(str));
    return str;
}
int main(int argc, char* argv[])
{
    char *a, b[400];
    a = fun(b);
}

sizeof(str))求得是指针str的大小
strlen---------测“字符个数”(包括:不可见字符,如:空格等)
sizeof---------测“BYTE个数”


7. sizeof返回对象所占用的字节大小.
strlen返回字符个数.
在使用sizeof时,有一个很特别的情况,就是数组名到指针蜕变,
char Array[3] = {'0'};
sizeof(Array) == 3;
char *p = Array;
sizeof(p) == 1;


在传递一个数组名到一个函数中时,它会完全退化为一个指针
1. List the six main biwise operators in C++ and explain the function of each. 2. Why cannot bitwise operations be applied to variables of floating-point type? 3. Explain the purpose of the << (left shift) and >> (right shift) operators. What is the typical effect on the decimal value of a number when it is shifted left by 1? Shifted right by 1? 4. Describe the process of using a mask to check the value of a specific bit within an
integer. 5. How can you use the bitwise AND operator (&) to check if a number is even or odd?
Explain the logic. 6. What is the difference between the logical AND (&&) and the bitwise AND (&)? Provide an example scenario for each. 7. Explain the purpose of the ~ (bitwise NOT) operator. What is the result of applying it to a mask, and how can this be useful? 1. What is the primary goal of program debugging? What types of errors can it help identify? 2. Describe the difference between Step Over (F10) and Step Into (F11) debugging commands. When would you choose one over the other? 3. What is the purpose of a breakpoint in planned debugging? How do you set and remove a breakpoint in Visual Studio? 4. Explain the utility of the "Watch" window compared to the "Autos" or "Locals" windows during a debugging session. 5. What is the key difference between the Debug and Release configurations when building a project? Why is it necessary to create a Release version after successful debugging? 6. List at least three types of files commonly found in a project's Debug folder and briefly state their purpose (e.g., *.pdb). 7. During debugging, you notice a variable has an incorrect value. How can you change its value during runtime to test a hypothesis without modifying the source code? 8. What command is used to exit the debug mode and stop the current debugging session? 1. What is an array in C++? List its three main characteristics. 2. How are array elements numbered in C++? What is the valid index range for an array declared as int data[25];? 3. Explain the difference between array declaration and initialization. Provide an example of each. 4. What is an initializer list? What happens if the initializer list is shorter than the array size? 5. How can you let the compiler automatically determine the size of an array during initialization? 6. What values do elements of a local array contain if it is declared but not explicitly initialized? How does this differ from a global array? 7. What is an array out-of-bounds error? Why is it dangerous, and what are its potential consequences? 8. How do you calculate the number of elements in an array using the sizeof operator?
Provide the formula. What is a significant limitation of this method? 9. Why is it impossible to copy the contents of one array into another using the assignment
operator (arrayB = arrayA;)? What is the correct way to perform this operation? 10. Why does comparing two arrays using the equality operator (arrayA == arrayB) not check if their elements are equal? How should array comparison be done correctly? 11. What does the name of an array represent in terms of memory? 1. What is a pointer in C++ and what are its two main attributes? 2. Explain the difference between the & and * operators when working with pointers. 3. Why is pointer initialization critical and what dangers do uninitialized pointers pose? 4. What is the fundamental relationship between arrays and pointers in C++? 5. How does pointer arithmetic work and why does ptr + 1 advance by the size of the pointed type rather than 1 byte? 6. What is the difference between an array name and a pointer variable? Why can't you increment an array name? 7. What are the differences between const int*, int* const, and const int* const? 8. How can you safely iterate through an array using pointers, and what are the boundary risks? 9. What is a null pointer and why should you check for nullptr before dereferencing? 10. How do you access array elements using pointer syntax, and how does the compiler translate arr[i] internally? 1. What is a multidimensional array? How is a two-dimensional array structured in memory? 2. Explain the concept of an "array of arrays". How does this relate to the declaration int arr/ROWS//COLS;? 3. The name of a two-dimensional array without indices is a pointer constant. What does this pointer point to? What do the expressions *(A + i) and *(*(A + i) +j) mean for a two-dimensional array A? 4. Describe the different ways to access the element A/1/[2/ of a two-dimensional array
using pointers. 5. What is the rule for omitting the size of dimensions when initializing and when passing a multidimensional array to a function? Why is it allowed to omit only the first dimension? 6. Explain the principle of "row-major order" for storing two-dimensional arrays in memory.
How does this affect element access? 7. Why are nested loops the standard tool for processing multidimensional arrays?
Describe the typical pattern for iterating through a matrix. 1. How is a character string stored in memory in C++? What is the role of the null terminator (10), and why is it critical for C-style strings? 2. Why must the size of a char array declared to hold a string be at least one greater than the number of characters you intend to store? 3. The array name without an index is a pointer constant. What does the name of a char array point to? 4. What are the two main ways to initialize a C-style string? What is a common mistake when using the initializer list method, and what is its consequence? 5. Why is it necessary to add _CRT_SECURE_NO_WARNINGS to the preprocessor definitions in Visual Studio when working with many standard C library functions?
What is the alternative approach? 6. What is the key difference between stropy and strncpy? Why might strncpy be considered safer? 7. How does the stremp function determine if one string is "less than" another? Why can't you use the == operator to compare two C-style strings for content equality? 8. Describe the purpose and parameters of the strok function. How do you get all tokens from a string? 9. What do the functions strchr and strrchr do? How do they differ? 10. Explain what the strstr function returns and what it is commonly used for. 11. What is the purpose of the functions in the < cctype> header? Give three examples of such functions and their use. 12. What is the difference between tolower(c) and_tolower(c)? When should you use each? 1. What is a function in C++? Name the three core benefits of using functions in a program. 2. What is the difference between a function declaration (prototype) and a function definition? Provide examples. 3. What is a function signature? Which elements are part of the signature, and which are not? 4. What methods of passing parameters to a function do you know? Explain the difference between pass-by-value, pass-by-pointer, and pass-by-reference. 5. Why can't you pass an array to a function by value? What is the correct way to pass an array to a function? 6. What is variable scope? How is it related to functions? 7. How does a function return a value? What happens if a function with a non-void return type does not return a value on all control paths? 8. Can you use multiple return statements in a single function? Provide an example. 9. What is function overloading? What is it based on? 10. How is interaction between functions organized in a program? Provide an example program with several functions. 11. What are default parameters? How are they specified, and in what cases are they useful? 12. How can you prevent a function from modifying the data passed to it? What modifiers are used for this? 13. What is recursion? Provide an example of a recursive function. 14. What common errors occur when working with functions? How can they be avoided? 15. How do you use pointers to functions? Provide an example of declaring and calling a function through a pointer. 用中文解答
最新发布
11-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值