定义
strlen
和 sizeof
是 C 语言中两个用于确定字符串或数据类型大小的常用操作符,但它们的工作方式和目的有所不同。下面是对这两个操作符的详细解释:
strlen
strlen
是一个库函数,定义在 <string.h>
头文件中。它用于计算以空字符(\0
)结尾的字符串的长度。
函数原型:
size_t strlen(const char *str);
参数:
str
:指向以空字符结尾的字符串的指针。
返回值:
- 返回字符串中字符的个数,不包括末尾的空字符。
示例:
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "Hello";
printf("Length of str: %zu\n", strlen(str)); // 输出:Length of str: 5
return 0;
}
注意事项:
strlen
只能用于以空字符结尾的字符串。如果传递给strlen
的指针没有指向一个以空字符结尾的字符串,strlen
会继续读取内存,直到遇到空字符为止,这可能导致未定义行为(包括程序崩溃)。strlen
的计算开销较大,因为它需要遍历整个字符串来查找空字符。因此,如果字符串的长度是已知的或者不会改变,使用strlen
可能不是最高效的方法。
sizeof
sizeof
是一个编译器内置的操作符,用于确定变量或类型所占用的内存大小(以字节为单位)。
语法:
size_t sizeof(object);
size_t sizeof(type);
参数:
object
:可以是变量、数组、结构体等。type
:可以是数据类型(如int
,float
,char
等)。
返回值:
- 返回对象或类型所占用的内存大小(以字节为单位)。
示例:
#include <stdio.h>
int main() {
char c = 'A';
int i = 42;
double d = 3.14;
char str[] = "Hello";
printf("Size of char: %zu bytes\n", sizeof(c)); // 输出:Size of char: 1 byte
printf("Size of int: %zu bytes\n", sizeof(i)); // 输出可能是:Size of int: 4 bytes(取决于平台)
printf("Size of double: %zu bytes\n", sizeof(d)); // 输出可能是:Size of double: 8 bytes(取决于平台)
printf("Size of str: %zu bytes\n", sizeof(str)); // 输出:Size of str: 6 bytes(包括末尾的空字符)
printf("Size of pointer: %zu bytes\n", sizeof(&c)); // 输出指针的大小,通常是 4 或 8 字节(取决于平台)
return 0;
}
注意事项:
sizeof
在编译时就能确定结果,因此它是一个常量表达式。sizeof
计算的是对象或类型在内存中的大小,而不是它存储的值的数量。例如,对于数组str
,sizeof(str)
返回的是整个数组(包括末尾的空字符)所占用的字节数,而不是字符串的长度。sizeof
可以用于任何数据类型和对象,包括基本类型、结构体、联合、数组和指针等。
总结来说,strlen
用于计算字符串的长度(不包括末尾的空字符),而 sizeof
用于确定变量或类型在内存中的大小(以字节为单位)。在处理字符串时,如果你想知道字符串包含多少个字符,应该使用 strlen
;如果你想知道存储字符串的数组或变量需要多少内存,应该使用 sizeof
。
详细示例及结论
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main()
{
char name[24]="haha ";
int iLen = strlen(name);
printf("strlen name has " " = %d\n",iLen);
iLen = sizeof(name);
printf("sizeof name has " " = %d\n",iLen);
// strlen name has = 5
// sizeof name has = 24
//结论 空格占用一个字符
char name1[24]="haha";
iLen = strlen(name1);
printf("strlen name = %d\n",iLen);
iLen = sizeof(name1);
printf("sizeof name = %d\n",iLen);
// strlen name = 4
// sizeof name = 24
char name2[24]="haha\n";
iLen = strlen(name2);
printf("strlen name has \\n = %d\n",iLen);
iLen = sizeof(name2);
printf("sizeof name has \\n = %d\n",iLen);
// strlen name has \n = 5
// sizeof name has \n = 24
//结论 换行符占用一个字符
char name3[24]="haha\0";
iLen = strlen(name3);
printf("strlen name has \\0 = %d\n",iLen);
iLen = sizeof(name3);
printf("sizeof name has \\0 = %d\n",iLen);
// strlen name has \0 = 4
// sizeof name has \0 = 24
//结论 结束符不占用字符
char name4[24]="";
iLen = strlen(name4);
printf("strlen name has no member = %d\n",iLen);
iLen = sizeof(name4);
printf("sizeof name has no member = %d\n",iLen);
// strlen name has no member = 0
// sizeof name has no member = 24
char name5[24]="haha\\n";
iLen = strlen(name5);
printf("strlen name has \\n = %d\n",iLen);
iLen = sizeof(name5);
printf("sizeof name has \\n = %d\n",iLen);
// strlen name has \n = 6
// sizeof name has \n = 24
return 0;
}
编译运行结果
常见问题
每一个字符串默认自带什么字符
在C语言中,每一个字符串默认自带一个结束字符,即空字符(null character),用 \0
表示。这个空字符用于标识字符串的结束位置。因此,当你定义一个字符串时,编译器会自动在字符串的末尾添加一个 \0
字符。例如,当你写下以下代码:
char str[] = "Hello";
实际上,在内存中,str
数组的内容会是:
H e l l o \0
这里,\0
就是字符串的结束符,它告诉 strlen
和其他字符串处理函数在哪里停止读取字符串。这个特性使得C语言的字符串函数能够正确地处理变长字符串,而不需要提前知道字符串的确切长度。
stren 字符串长度 不包含结束符吧
是的,strlen
函数计算字符串的长度时,是不包含末尾的空字符(null character,即 \0
)的。strlen
函数返回的是从字符串开始到第一个空字符之前的字符数,即实际存储的可见字符的数量。因此,如果你有一个字符串 "Hello"
,strlen
函数会返回 5
,因为字符串包含五个字符 'H'
, 'e'
, 'l'
, 'l'
, 'o'
,而不包括末尾的 \0
。
这个特性是设计 strlen
函数时有意为之的,因为空字符在C语言中被用作字符串的结束标志,而不是字符串内容的一部分。因此,在计算字符串长度时,空字符不被计入长度内。这样,strlen
和其他字符串处理函数就能够正确地处理字符串,直到遇到结束标志为止。
字符串拷贝 目的字符串的长度是不是要比strlen计算的拷贝的字符串长度加1
是的,当进行字符串拷贝操作时,目的字符串(即目标缓冲区)的长度应该至少要比源字符串的长度(通过 strlen
计算得出)多 1。这是因为 strlen
函数返回的是源字符串中字符的数量,不包括末尾的空字符(null terminator, \0
)。在拷贝字符串时,你需要确保目的字符串有足够的空间来容纳源字符串的所有字符,以及额外的空字符作为结束符。
例如,如果你有一个源字符串 "Hello"
,它的长度是 5(通过 strlen
计算得出)。当你拷贝这个字符串到另一个缓冲区时,你需要一个至少长度为 6 的缓冲区来确保能够存储所有的字符以及末尾的空字符。
char src[] = "Hello";
char dest[6]; // 长度至少为 strlen(src) + 1
strcpy(dest, src); // 拷贝字符串,包括末尾的空字符
在上面的代码中,dest
数组的长度是 6,这足够存储源字符串 "Hello"
的 5 个字符以及一个空字符。使用 strcpy
函数进行拷贝时,它会将源字符串的所有字符(包括末尾的空字符)复制到目的字符串中。
注意,如果目的字符串的长度小于源字符串的长度加一,就会发生缓冲区溢出(buffer overflow),这是一种常见的安全漏洞,可能导致程序崩溃或更严重的安全问题。因此,在进行字符串拷贝操作时,务必确保目的字符串有足够的空间。
不加1 会有什么影响 举例说明
如果在进行字符串拷贝操作时,目的字符串(即目标缓冲区)的长度没有比源字符串的长度多 1,即没有为末尾的空字符(null terminator, \0
)预留空间,那么将会发生缓冲区溢出(buffer overflow)。这会导致一系列的问题,包括但不限于:
-
数据损坏:拷贝操作会覆盖目的字符串缓冲区末尾之后的内存区域,可能损坏该区域存储的其他重要数据。
-
程序崩溃:如果溢出的数据破坏了程序的执行状态(比如函数返回地址),程序可能会立即崩溃。
-
安全漏洞:攻击者可以利用缓冲区溢出漏洞执行恶意代码,获取系统的敏感信息或控制权限。
下面是一个具体的例子来说明不加 1 会产生的影响:
#include <stdio.h>
#include <string.h>
int main() {
char src[] = "Hello";
char dest[5]; // 长度没有比 strlen(src) + 1 大
strcpy(dest, src); // 拷贝字符串,但没有足够的空间容纳空字符
printf("dest: %s\n", dest); // 尝试打印 dest,但可能会崩溃或输出错误内容
return 0;
}
在这个例子中,dest
数组的长度是 5,与 src
字符串的可见字符数相等,但没有额外的空间来存储末尾的空字符。当使用 strcpy
函数进行拷贝时,它会尝试将 src
中的所有字符(包括末尾的空字符)复制到 dest
中。由于 dest
没有足够的空间,最后一个空字符会被写入 dest
数组之后的内存区域,这可能会导致未定义的行为。
在实际执行时,程序可能会崩溃,或者如果 dest
数组后面恰好有其他变量的内存,那么这些变量的值可能会被意外地修改。此外,如果 dest
数组后面是程序的栈空间,并且溢出的数据覆盖了函数的返回地址,那么程序可能会跳转到错误的地址执行,导致更严重的后果。
为了避免这种情况,总是要确保目的字符串的长度至少比源字符串的长度多 1,以便有足够的空间来存储空字符。在实践中,使用像 strncpy
这样的安全字符串拷贝函数也是一个好的做法,因为它们允许你指定一个最大拷贝长度,从而防止缓冲区溢出。