【问题标题】:Passing NULL to printf %s [duplicate]将 NULL 传递给 printf %s [重复]
【发布时间】:2016-04-27 06:43:50
【问题描述】:
#include<stdio.h>
#include<string.h>

int main() {
    char *ptr = NULL;

    printf("%s", ptr);//The output is null
    //  printf("%s\n", ptr); //addition of **\n** give segmentation fault 

    return 0;
}

第一个printf 输出:(null)。但是为什么第二个printf 的输出是:Segmentation fault (core dumped) 刚刚添加:\n

【问题讨论】:

  • 在法律上:这是未定义的行为,所以任何东西,特别是任何输出,都是可以接受的。在日常生活中:垃圾进,垃圾出。
  • @hacks 你为第二次 printf 取出 cmets 了吗?
  • @Rishi 你用过哪个编译器?
  • @AnilKumar 版本 3.4.2
  • @Rishi 我用过 gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2。

标签: c


【解决方案1】:

printf("%s", ptr);

这里printf 需要一个有效的指针,它指向以空值结尾的字符串,你不能将空值传递给它。如果这样做,您将触发未定义的行为,并且无法推理程序的输出。

PS。我找到了一个答案,其中可能包含有关您可能感兴趣的事物的更多详细信息。请参阅here。实际上,您的问题似乎与该问题重复。

【讨论】:

  • 这不是它背后的真正原因,看我的回答
  • @Ctx OP 不是有未定义的行为吗?这是我的回答中唯一陈述的内容。
  • 不,不是,因为段错误的原因不是这一行,而是下一行。如果程序中只有两行之一,则第二行会出现段错误,而第一行不会。
  • @Ctx:在这里阅读许多答案:stackoverflow.com/questions/12222447/…。如果存在 UB,Segfault 是可能发生的一件事
  • 好的,这可能是这种行为符合标准的原因,但这不是原因,为什么会发生这种情况! IE。 with newline 和 without newline 的区别
【解决方案2】:

一些printf 实现关注NULL 字符串 参数,但其中一些实现有“错误”,请参阅gcc printf optimization

不管怎样,C 标准说:

参数应该是一个指向数组初始元素的指针 字符类型

如果不是,那么它是一个未定义的行为。您面临的行为就是其中之一。

【讨论】:

    【解决方案3】:

    背后的技术原因是:

    如果你写这样的语句

    printf("%s\n", ptr);
    

    一些编译器(即带有优化的 gcc)会将其“优化”为:

    puts(ptr);
    

    puts() 的 glibc 实现不会在 NULL 指针上打印 (null)(就像 printf 那样),但很高兴会出现段错误。

    如果在没有激活优化的情况下调用 gcc,则不会进行此替换,printf("%s\n", NULL); 不会出现段错误。

    此行为符合标准,因为将 NULL 指针传递给 printf() 会调用“未定义行为”。

    【讨论】:

    • "在没有激活优化的情况下调用 gcc 时,不会进行此替换并且 printf("%s\n", NULL); 不会出现段错误。"这是错误的 - 这不也是 UB 吗?如果是,你不能说它是否会出现段错误。
    • @GiorgiMoniava 根据标准这是未定义的行为,但这当然并不意味着它完全确定了 glibc-printf 与 gcc 的行为方式。因此,虽然代码调用了 UB,但这并不意味着具体运行时环境中的具体二进制文件的行为是不确定的。
    • 您也可以查看我更新的答案中的链接,我认为它更多地指的是您想说的话
    • @GiorgiMoniava d'Oh。阅读该问题后,我们既可以删除我们的答案,也可以将此问题作为重复问题关闭……很高兴见到你;)
    • 是的,我已将此标记为重复。会议也不错。
    【解决方案4】:

    据我所知 printf 实际上使用了一个名为 write() 的系统调用,内核将使用它来写入 stdout 文件描述符。在这种情况下,\n 会导致指针移动到未知区域并尝试将数据从那里写入标准输出文件描述符会导致这种行为。

    从 SO 本身找到一个很好的参考

    C/C++ function definitions without assembly

    【讨论】:

    • write() 在以用户程序调用printf() 开始的处理管道的最后——即在最后——被调用。 write() 很可能不会从原始字符串(此处不存在)而是从 C 标准的缓冲 I/O 使用的缓冲区中读取它传输到设备的数据图书馆。 (可能情况与无缓冲 I/O 不同。不确定。)目前尚不清楚换行符(毕竟只是复制到输出的字符)应该如何改变任何内容。
    • 行缓冲会在缓冲区中出现新行时引发 write() 调用。这里缓冲区为空(NULL)。 write {man write(3)} 的语法如下ssize_t write(int fildes, const void *buf, size_t nbyte);因此,如果尝试从那里获取某些东西时 buf 为 NULL,则可能会导致段错误。如果思考过程错误,我希望得到纠正。跨度>
    • printf “causes”(这是正确的——在您的帖子中您写了“is a”,这是不正确的)write,是的。但是printf 首先将其生成的格式化字符复制到一个单独的缓冲区(您可以使用setbuf 设置的缓冲区)。 write使用该缓冲区 调用(而不是使用原始数据)。那个缓冲区总是没问题的。导致问题的原因是 printf 函数 取消引用空指针,因为它试图将数据复制到 C 标准库具有的内部缓冲区。对write 的调用从未到达。
    猜你喜欢
    • 2021-04-27
    • 1970-01-01
    • 2014-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-21
    • 1970-01-01
    相关资源
    最近更新 更多