【问题标题】:Why does printf function ignore the latter \0? [duplicate]为什么 printf 函数会忽略后者 \0? [复制]
【发布时间】:2019-06-30 13:13:40
【问题描述】:

我对 \0 的一些功能感到困惑。

我知道 \0 是一个空字符,它是一个表示形式是字符串的术语。

int j;

j = printf("abcdef\0abcdefg\0");

printf("%d", j);

return 0;

当我尝试打印 "abcdef\0abcdefg\0" 时,C 只会打印字符串 'abcdef' 和 '6' 而不是 'abcdef' 和 'abcdefg' 的总和为 13。为什么会发生这种情况?

【问题讨论】:

  • printf 的角度来看,您的字符串是“abcdef”。为什么你期望它必须在第一个空字符之后继续?
  • 字符串文字中可以包含任何字符(包括 \0),但库函数通过它们看到的第一个 \0 来确定字符串的结尾。
  • @bruno 我认为 printf 会认为里面有两个字符串。我一定是误会了什么。
  • @skkupharah printf 无法知道字符串是如何构建/来的,它不知道您使用的是文字字符串,也不知道您是如何定义它的。与任何管理字符串的函数一样,字符 0 表示结束,仅此而已
  • @skkupharah 字符串文字只是内存中的一系列任意字节,C 中没有字符串类型。 printf() 或任何其他字符串函数只能在内存中获取地址,并且必须“将其发送” " 从那里,找到的第一个 \0 被视为字符串的结尾并停在那里。

标签: c


【解决方案1】:

"abcdef\0abcdefg\0",字符串文字,实际上是 staticconst(出于所有目的)char 数组,因此它具有编译器维护的关联大小:

#include <stdio.h>
#define S "abcdef\0abcdefg\0" 
//^string literals implicitly add a(nother) hidden \0 at the end
int main()
{
    printf("%zu\n", sizeof(S)); //prints 16
}

但数组在 C 中被特殊对待,并将它们作为参数传递给函数或几乎任何运算符将它们转换为指向其第一个元素的指针。

指针没有关联的大小。

当您将char const* 传递给函数(例如printf)时,该函数只接收一个数字——第一个元素的地址。

printf 和 C 中的大多数字符串函数获取大小的方式是计算字符直到第一个 '\0'

如果您将指针传递给char 数组的第一个元素,该数组中包含显式嵌入的零,那么对于计数到第一个'\0' 的函数,该字符串实际上在第一个'\0' 处结束。

【讨论】:

  • 请定义“二等公民”。该术语的大多数定义将字符串包括为第二类,但不包括数组。
  • @WilliamPursell 谢谢。改变了措辞。我宁愿不涉及主观术语。
【解决方案2】:

C 中的字符串是一个字符序列,后跟一个 NUL 字符,即'\0'。没有单独的“长度”字段。

当字符串以文字形式出现时,例如"hello",实际存储在内存中的是:

'h', 'e', 'l', 'l', 'o', '\0'

所以您可以看到,如果您的字符串本身包含'\0',就任何 C 标准库函数而言,这就是字符串的结尾。

一旦printf 在您的字符串中看到第一个'\0',它就会停止打印并返回,因为这是格式字符串的结尾。 printf 无法知道'\0' 之后还有另一个字符串。也许有——或者在那之后内存中可能只是随机的其他程序数据。它无法区分。

如果你想实际打印'\0' 字符,那么你需要有一些其他的方法来跟踪字符串的“真实”长度,并使用一个接受该长度作为参数的函数。或者,您可以在格式化过程中添加 '\0' 字符,方法是在格式字符串中指定 %c 并将 0 作为字符值传递。

【讨论】:

    【解决方案3】:

    这里

    j = printf("abcdef\0abcdefg\0"); /* printf stops printing once \0 encounters hence it prints abcdef */
    

    printf() 从字符串文字"abcdef\0abcdefg\0" 的基地址开始打印,即从a 开始,直到遇到第一个\0 字符。所以它打印abcdef

     -----------------------------------------------------------------
    | a | b | c | d | e | f | \0 | a | b | c | d | e | f | f | g | \0 |
     -----------------------------------------------------------------
    0x100 0x101 ...............|                                0x100 - assume this as base address of the string literal      
      |                        |
    starts printing      when printf sees
    from 0x100 memory    first \0
    location             it stops the printing & returns.
    

    然后printf() 返回可打印字符数,即6

    printf("%d", j); /* prints 6 */
    

    来自printf的手册页

    返回值

    成功返回后,这些函数返回数量 打印的字符(不包括用于结束输出的空字节 到 字符串)。

    【讨论】:

    • 所以澄清一下,printf 函数接受 \0 作为结束读取的信号。我说对了吗?
    • 是的。没错。
    • @skkupharah 很多函数都是空字符结束字符串,还好是编译器自动添加的,你不需要写“abcdef\0”,写“abcdef”就行了跨度>
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-06-16
    • 1970-01-01
    • 2021-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多