【问题标题】:ASCII value = 0 and '\0'ASCII 值 = 0 和 '\0'
【发布时间】:2018-11-19 03:31:38
【问题描述】:

我已阅读this post。 但是当我尝试时:

  printf("before null %c after null\n", 0);  // (ASCII=0) != '\0' ??

而不是得到:

before null 

我明白了:

before null   after null

所以我的问题是: ASCII 值 0 真的等于 '\0' 吗?

【问题讨论】:

  • 是的,但是 printf 正在逐段打印你的字符串,所以它不会在中间 \0 结束。它打印"before null ",然后是%c,然后是" after null"
  • 是的,您可以随时评论自己的问题和/或自己的答案。甚至是对您自己问题的回答。
  • 感谢您的回答。但是我试过printf("before null \0 after null\n");,输出是:before null 这两者有什么区别?
  • @iamsuperwen 是 printf 评估“%c”并决定如何处理相应的值 (0)。 NUL 对于字符串是带外(类似于 SQL 占位符如何防止 SQL 注入)。与printf("before null %c after null \0 after silly null in string\n", 0);比较。
  • 同样,printf("%cn", '\\') 输出 \n(两个字符),而不是换行符。

标签: c ascii null-character


【解决方案1】:

原因是printf 不是实际上将"Hello %s", "World" 替换为"Hello World" 然后将它们全部打印出来。 相反,它会在每个字符solo中连续打印"Hello " 然后"World",按顺序

如果您曾经尝试使用putchar() 打印单个null 字符,您会注意到它打印的是一个空格,这就是printf 也基于它打印一个空格的原因。

请注意,它会在其他系统(例如 Linux)上打印 nothing

printf 实际工作原理的示例代码。


const char * x;
// while the current char != '\0'
while (*format)
{
    // if the current char == '%'
    if (*format == '%')
    {
        // increment the pointer so we can point to the next char and skip printing '%'
        switch (*(++format)) // then switch that next char (specifier).
        {
        case 'c':
            putchar(va_arg(args, char)); // if the argument is null, then it's putchar(0);
            break;
        case 's':
            // regular operation of printing a string argument.
            x = va_arg(args, const char*);
            while (*x) putchar(*x++);
            break;
        }
        // skips the format specifier so we don't print it (e.g 's', 'c'..)
        *format++;
    }
    // else: isn't a format specfier.
    else
       // print the current char (pointer) of the original string
        putchar(*format++); // increments it for the next operation.
}
va_end(args);

所以回到你的问题,它会打印每个字符,当涉及到参数0 null 时,putchar() 将根据你的系统放置一个空格或什么都不放。

你可以说printf参数并没有与终止它的原始字符串真的有任何关系,它们彼此不认识。
就像当你 printf("Hello %s, from SO!", "World");, "World" 实际上以 \0 结尾时一样,但它只会终止自己,而不是另一个 , from SO!

是的0'\0'。他们是同一个角色。

【讨论】:

  • 如果putchar(0) 放了一个空格字符,你的系统就坏了。它输出一个 NUL 字符(在 ASCII 系统上)。这如何影响终端或应用程序是另一回事。
  • 我的系统没有损坏。它会在 Windows 系统上打印一个空格,而在 Linux 上什么也不会。根据 OP 代码结果,我试着回答他,因为他使用的是 Windows 系统。
  • 然后就是 Windows 终端将 NUL 转换 为空格。如果您将输出重定向到一个文件并使用十六进制编辑器查看它,您会发现一个 NUL,而不是一个空格。
【解决方案2】:
当给定值0'\0' 时,

printf 不会在%c 格式说明符 的那个字符位置终止 printf 格式字符串。相反,nul 的终端输出通常是占位符(例如 space 等)

但是你可以在字符串中插入一个nul,然后使用%s输出字符串格式说明符,实际上十进制实际上是0等效 ASCII character '\0' 的 ASCII 值,并将在 nul-character 处终止字符串(参见:www.ASCIItable.com),例如

#include <stdio.h>

#define FMT "before null %c after null\n"

int main (void) {

    char buf[sizeof FMT * 2];

    puts (FMT);

    sprintf (buf, FMT, 0);
    printf ("using 0 : '%s'", buf);
    putchar ('\n');

    sprintf (buf, FMT, '\0');
    printf ("using \\0: '%s'", buf);
    putchar ('\n');

    return 0;
}

使用/输出示例

$ ./bin/str_printf_null
before null %c after null

using 0 : 'before null '
using \0: 'before null '

【讨论】:

  • "printf 不会使用 %c 格式说明符和值 0 或 '\0' 将 nul 输出到标准输出 - 似乎对我来说...?
  • 嗯,这可能表明操作系统如何处理带有%cnul-character 的输出是实现定义的。我在 Linux 上测试过。我还将在windoze上进行测试,看看他们是否实现相同。感谢您的信息。
  • 嗯 - 我从你的链接中得到“Zoinks!你走错路了。”。我还在 Win7 上进行了测试,得到了相同的输出。我如何查看您的代码?
  • 哦,是的,是的……我们说的是同一件事,但不同。我不是说%c 不会输出字节,我说的是它不会在该字符位置终止printf format-string 的效果。它只是在输出中为nul 使用占位符。
  • @Attie - 我希望以更清晰的方式更新以反映实际行为。
【解决方案3】:

ASCII 值 0 是否真的等于 \0

是的


字符串在内存中的存储方式和printf() 等函数处理方式的差异很重要。

"before null %c after null\n"
"before null \0 after null\n"

两者都存储在内存中,末尾有一个隐含的\0 终止符。第二个在中间有一个明确的\0 字符的事实改变了事情。

printf() 将扫描字符串直到 "the end",打印组件...在 C 中 "the end" 通常意味着直到第一个 @ 987654327@ / nul 字符。

对于第一个变体,printf() 将字符复制到输出,直到它到达 %c 指令,此时它会查看给函数的参数......它可能会发现你给了 @987654330 @,或者它可能会发现您提供了'+' - 无论哪种方式,它都会将其复制到输出中。然后它会继续将字符复制到输出,寻找字符串的“结尾”。

使用第二个变体,printf() 将开始将字符复制到输出,找到“结尾”(由 \0 表示),然后停止。

如果您要使用snprintf(),那么结果/输出将包含以下内容:(同样,带有隐式\0 终止)

"before null \0 after null\n"
"before null "

如果您随后打印这两个,它们将看起来相同,但内存内容会不同。

但是,printf() 的输出是终端(或文件)...\0 会发生什么取决于您的终端仿真器...它可能根本不显示,它可能显示为空格,或者它可能有一个有趣的盒子符号......

需要注意的重要一点是,这发生在运行时——而不是编译时。

【讨论】:

  • 是的,看起来就是这样。谢谢你们的cmets。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多