【问题标题】:What if a null character is present in the middle of a string?如果字符串中间出现空字符怎么办?
【发布时间】:2016-05-01 14:52:42
【问题描述】:

我知道字符串的结尾用空字符表示,但我无法理解以下代码的输出。

#include <stdio.h>
#include <string.h>

int
main(void)
{
    char s[] = "Hello\0Hi";
    printf("%d %d", strlen(s), sizeof(s));
}

输出:5 9

如果strlen() 在 o 的末尾检测到字符串的结尾,那么为什么 sizeof() 不做同样的事情呢?即使它不做同样的事情,不是 '\0' 一个 空字符(即只有一个字符),那么答案不应该是 8 吗?

【问题讨论】:

  • main() -> int main(void) 请。
  • @PW 显然在 c 标准中他们不使用 NUL,尽管我见过很多 ascii 表。在 c 标准中,单词 null 使用小写。

标签: c null sizeof strlen


【解决方案1】:

sizeof 运算符不会给出字符串的长度,而是给出其操作数类型的大小。由于在您的代码中操作数是一个数组,sizeof 会为您提供数组的大小,包括两个 null 字符。

如果是这样

const char *string = "This is a large text\0This is another string";
printf("%zu %zu\n", strlen(string), sizeof(string));

结果会非常不同,因为string 是指针而不是数组。

注意:使用size_t"%zu" 说明符,这是strlen() 返回的内容,是sizeof 给出的值的类型。

【讨论】:

  • 很好的答案,但是由于size_tsizeof返回类型,添加了关于%zu操作数的解释;
【解决方案2】:

strlen() 不关心字符串的实际 大小。它寻找一个空字节并在它看到第一个空字节时停止。

但是sizeof() 操作员知道总大小。它不关心您在字符串文字中的字节数。您不妨在字符串中包含所有空字节,sizeof() 仍会给出正确的数组大小(在这种情况下,strlen() 将返回0)。

它们没有可比性;他们做不同的事情。

【讨论】:

  • 实际上,“strlen”确实关心字符串的实际大小,因为字符串大小是由“nul终止”定义的。 sizeof 根本不关心字符串,只关心类型。
  • @Daniel because the string size is defined by being "nul terminated" -- 不,(空终止)既不是字符串 size 也不是 strlen() 返回的 size字符串。出于某种原因,它被称为“字符串长度”而不是“字符串大小”。 strlen(),顾名思义,不知道也不关心大小。它从给定的地址开始并查找第一个空字节。我支持答案,请不要投反对票,因为不清楚。
  • 啊,对不起,“字符串大小”没有定义,strlen关心字符串的长度,sizeof关心字符串的个数一种类型的字节!没有“字符串的大小”。
【解决方案3】:

如果 strlen() 在 o 的末尾检测到字符串的结尾,那么 sizeof() 为什么不做同样的事情呢?

strlen 仅适用于字符串(字符数组),而sizeof 适用于所有数据类型。 sizeof 计算任何给定数据类型的确切内存空间;而strlen 提供字符串的长度(不包括NULL 终止符\0)。所以在正常情况下,典型的字符数组s是这样的:

char s[] = "Hello";
strlen( s ) + 1  = sizeof( s ); // +1 for the \0

在您的情况下,它有所不同,因为您在字符数组s 的中间有一个 NULL 终止符:

char s[] = "Hello\0Hi";

这里,strlen 将检测到第一个 \0 并给出长度为 5。但是,sizeof 将计算 足以容纳字符数组的空格总数,包括两个 @987654333 @,这就是为什么它给出 9 作为第二个输出。

【讨论】:

    【解决方案4】:

    strlen() 计算字符串的长度。这是通过返回'\0' 字符之前(不包括)之前的字符数量来完成的。 (请参阅下面的手册页。)

    sizeof() 返回给定变量(或数据类型)的字节数。请注意,您的示例 "Hello\0Hi" 有 9 个字符。但是您似乎不明白问题中字符 9 的来源。让我先解释一下给定的字符串。您的示例字符串是:

    "Hello\0Hi"
    

    这可以写成如下数组:

    ['H', 'e', 'l', 'l', 'o', '\0', 'H', 'i', '\0']
    

    注意最后一个 '\0' 字符。当使用字符串引号时,编译器以'\0' 字符结束字符串。这意味着"" 也是['\0'],因此有 1 个元素。

    注意 sizeof() 确实返回数组中的元素数。它返回字节数。 char 是 1 个字节,因此 sizeof() 确实返回元素的数量。但是,如果您使用任何其他数据类型,例如,如果您要在 [1, 2, 3, 4] 上调用 sizeof(),它将返回 16。因为 int 是 4 个字节并且数组有 4 个元素。

    注意,将数组作为参数传递只会传递指针。如果您将s 传递给另一个函数并调用sizeof(),它将返回指针的大小,与sizeof(void *) 相同。这是一个独立于数组的固定长度。

    STRLEN(3)                BSD Library Functions Manual                STRLEN(3)
    
    NAME
         strlen, strnlen -- find length of string
    
    LIBRARY
         Standard C Library (libc, -lc)
    
    SYNOPSIS
         #include <string.h>
    
         size_t
         strlen(const char *s);
    
         size_t
         strnlen(const char *s, size_t maxlen);
    
    DESCRIPTION
         The strlen() function computes the length of the string s.  The strnlen()
         function attempts to compute the length of s, but never scans beyond the
         first maxlen bytes of s.
    
    RETURN VALUES
         The strlen() function returns the number of characters that precede the
         terminating NUL character.  The strnlen() function returns either the
         same result as strlen() or maxlen, whichever is smaller.
    
    SEE ALSO
         string(3), wcslen(3), wcswidth(3)
    
    STANDARDS
         The strlen() function conforms to ISO/IEC 9899:1990 (``ISO C90'').
         The strnlen() function conforms to IEEE Std 1003.1-2008 (``POSIX.1'').
    
    BSD                            February 28, 2009                           BSD
    

    【讨论】:

    • “注意 sizeof() 不会返回数组的大小” - 嗯,确实如此。您的意思似乎是“sizeof 没有给出数组中元素的数量”,但在这种特殊情况下它确实如此,因为元素的大小为 1。
    • 好点我将“大小”更改为“元素数量”,但我的回答已经表明在这种特殊情况下它是正确的。所以你应该在下一次中途评论之前阅读整个答案;-)
    【解决方案5】:

    正如名称literal 本身所暗示的字符串文字是用双引号括起来的字符序列。此字符序列隐式附加一个终止零。

    所以任何用双引号括起来的字符都是字符串文字的一部分。

    当字符串字面量用于初始化字符数组时,它的所有字符(包括终止零)都用作字符数组相应元素的初始化器。

    每个字符串文字依次具有字符数组的类型。

    例如,C 中的这个字符串文字 "Hello\0Hi" 的类型为 char[9]:引号中包含 8 个字符加上隐式终止零。

    所以在内存中这个字符串字面量是这样存储的

    { 'H', 'e', 'l', 'l', 'o', '\0', 'H', 'i', '\0' }
    

    运算符sizeof 返回对象占用的字节数。所以对于上面的字符串字面量,sizeof 运算符将返回值9——它是字面量在内存中占用的字节数。

    如果你写了"Hello\0Hi",那么编译器本身可能不会只是从文字中删除这部分Hi。它必须将它与用引号括起来的文字的其他字符一起存储在内存中。

    sizeof 运算符返回 C 中任何对象的字节大小,而不仅仅是字符数组。

    一般来说,字符数组可以存储任何原始数据,例如从二进制文件中读取的一些二进制数据。在这种情况下,用户和程序不会像字符串一样考虑此数据,因此结果的处理方式与字符串不同。

    标准 C 函数strlen 是专门为字符数组编写的,用于查找字符数组中存储字符串的长度。它不知道数组中存储了哪些数据以及它们是如何写入其中的。它所做的只是搜索字符数组中的第一个零字符并返回字符数组中零字符之前的字符数。

    您可以在一个字符数组中顺序存储多个字符串。例如

    char s[12];
    
    strcpy( s, "Hello" );
    strcpy( s + sizeof( "Hello" ), "World" );
    
    puts( s ); // outputs "Hello"
    puts( s + sizeof( "Hello" ) ); // outputs "World"
    

    如果你要像这样定义一个二维数组

    char t[2][6] = { "Hello", "World" };
    

    那么它在内存中的存储方式与上面的一维数组相同。所以你可以写

    char *s = ( char * )t;
    
    puts( s ); // outputs "Hello"
    puts( s + sizeof( "Hello" ) ); // outputs "World"
    

    另一个例子。标准 C 函数strtok 可以将存储在字符数组中的一个字符串拆分为多个字符串,用零字节替换用户指定的分隔符。结果字符数组将包含多个字符串。

    例如

    char s[] = "Hello World";
    
    printf( "%zu\n", sizeof( s ) ); // outputs 12
    
    strtok( s, " " );
    
    puts( s ); // outputs "Hello"
    puts( s + sizeof( "Hello" ) ); // outputs "World"
    
    printf( "%zu\n", sizeof( s ) ); // outputs 12
    

    最后一个 printf 语句将输出相同的值,等于 12,因为数组占用相同的字节数。只需将分配给数组的内存中的一个字节从' ' 更改为'\0'

    【讨论】:

    • 注意:回复:“由于名称文字本身意味着字符串文字......”,文字不必是字符串。 (int []){2, 4} 是文字(复合)而不是字符串。
    • @chux 没有人说文字只是字符串文字。
    • 阅读“作为名称文字本身意味着字符串文字......”表明 literal 意味着 string literal
    • @chux 您应该阅读字面量的含义。我希望这会对你有所帮助。
    【解决方案6】:

    C 中的字符数组和指向字符数组的指针不是一回事。虽然您可以打印地址并获得相同的值。 C 中的数组由以下内容组成。

    1. 数组大小
    2. 它的地址/指针
    3. 元素的同质类型

    指针仅由以下组成:

    1. 地址
    2. 类型信息

      char s[] = "Hello\0Hi"; printf("%d %d", strlen(s), sizeof(s));

    这里你正在使用 sizeof() 计算数组的大小(它是 s 变量),它是 9。

    但是如果你把这个字符数组当作字符串,而不是 array(string now) 会丢失它的大小信息并变成一个指向字符的指针。当您尝试使用%s 打印字符数组时,也会发生同样的事情。

    所以strlen()%s 将字符数组视为字符串,它只使用其地址信息。你可以猜到,strlen() 不断增加指针来计算长度直到第一个空字符。当它遇到一个空字符时,你会得到一个到那个点的长度。

    所以 strlen() 给你 5 并且不计算 null 字符。

    所以sizeof() 运算符只告诉它操作数的大小。如果你给它一个数组变量,它会利用数组大小​​信息并告诉大小而不管空字符的位置。

    但是如果你给sizeof()pointer to array of characters,那么它会找到没有大小信息的指针,并打印指针的大小,在 64 位系统上通常是 64 位/8 字节,在 32 位系统上通常是 32 位/4 字节。

    如果您使用像 "Hello" 这样的双引号来初始化字符数组,比 C 添加一个空字符,否则它不会在 {'H','e','l','l','o'} 的情况下添加一个空字符。

    使用 gcc 编译器。希望对理解有所帮助。

    【讨论】:

    • 所有对象(包括数组和指针)都有地址和类型信息。这些东西都不需要存储。在数组的情况下,类型信息的一部分是数组中有多少元素。
    猜你喜欢
    • 2012-02-21
    • 2021-12-08
    • 2023-03-09
    • 1970-01-01
    • 2014-07-17
    • 2012-07-31
    • 2012-04-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多