社区首页 >问答首页 >使用printf的%s说明符打印NULL的行为是什么?

使用printf的%s说明符打印NULL的行为是什么?
EN

Stack Overflow用户
提问于 2012-07-20 20:02:39
回答 4查看 62.2K关注 0票数 70

遇到了一个有趣的面试问题:

代码语言:javascript
代码运行次数:0
复制
test 1:
printf("test %s\n", NULL);
printf("test %s\n", NULL);

prints:
test (null)
test (null)

test 2:
printf("%s\n", NULL);
printf("%s\n", NULL);
prints
Segmentation fault (core dumped)

虽然这在某些系统上运行得很好,但至少我的系统会抛出一个分段错误。对这种行为最好的解释是什么?以上代码为C。

下面是gcc的信息:

代码语言:javascript
代码运行次数:0
复制
deep@deep:~$ gcc --version
gcc (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-07-20 20:31:38

首先要做的事情是:printf为其%s参数期待一个有效(即非空)指针,因此传递空值是官方未定义的。它可以打印"(null)“或者删除硬盘上的所有文件--对于ANSI来说,这都是正确的行为(至少,哈比森和斯蒂尔是这么告诉我的)。

尽管如此,是的,这是非常愚蠢的行为。事实证明,当您执行这样一个简单的printf时,所发生的事情是:

代码语言:javascript
代码运行次数:0
复制
printf("%s\n", NULL);

gcc很聪明,可以把它解构成对puts的召唤。第一个printf,这个:

代码语言:javascript
代码运行次数:0
复制
printf("test %s\n", NULL);

已经够复杂的了,gcc反而会向真正的printf发出一个电话。

(请注意,gcc在编译时会对无效的printf参数发出警告。这是因为它很久以前就开发了解析*printf格式字符串的能力。)

您可以通过使用-save-temps选项编译,然后查看生成的.s文件来自己看到这一点。

当我编写第一个示例时,我得到了:

代码语言:javascript
代码运行次数:0
复制
movl    $.LC0, %eax
movl    $0, %esi
movq    %rax, %rdi
movl    $0, %eax
call    printf      ; <-- Actually calls printf!

(我补充了一些评论。)

但是第二个代码是这样产生的:

代码语言:javascript
代码运行次数:0
复制
movl    $0, %edi    ; Stores NULL in the puts argument list
call    puts        ; Calls puts

最重要的是它不打印下面的换行符。就好像这会导致一个节段故障,所以它不麻烦。(它

票数 80
EN

Stack Overflow用户

发布于 2012-07-20 20:23:46

就C语言而言,原因是您正在调用未定义的行为,任何事情都可能发生。

至于为什么会发生这种情况的机制,现代gcc将printf("%s\n", x)优化为puts(x),而puts在看到空指针时没有打印(null)的愚蠢代码,而printf的一般实现则有这种特例。由于gcc不能像这样优化(通常)非平凡的格式字符串,所以当格式字符串中有其他文本时,printf实际上会被调用。

票数 35
EN

Stack Overflow用户

发布于 2012-07-20 20:36:21

第7.1.4节( C99或C11)说:

§7.1.4图书馆职能的使用 除非在下面的详细描述中另有明确说明,否则以下每条语句都适用:如果函数的参数具有无效值(例如函数域外的值,或程序地址空间外的指针,或当对应的参数不符合const-限定时指向不可修改存储的指针)或具有可变参数的函数不期望的类型(升级后),则行为是未定义的。

由于printf()规范没有说明当您为%s说明符传递一个指向它的空指针时会发生什么,所以行为是显式的未定义的。(请注意,传递要由%p说明符打印的空指针不是未定义的行为。)

以下是fprintf()家庭行为的“章节和诗句”(C2011 --在C1999中是一个不同的节号):

§7.21.6.1 fprintf函数 如果没有s长度修饰符,则参数应该是字符类型数组的初始元素的指针。..。 如果存在l长度修饰符,则该参数应是指向wchar_t类型数组的初始元素的指针。 p相对应的论点应是无效的指针。指针的值以实现定义的方式转换为打印字符序列.

s转换说明符的规范排除了空指针有效的可能性,因为空指针没有指向适当类型数组的初始元素。p转换说明符的规范不要求空指针指向任何特定的位置,因此NULL是有效的。

许多实现在传递空指针时打印一个字符串(如(null) ),这是一个很好的选择,依赖它是危险的。不明确行为的好处在于,这种反应是允许的,但它并不是必需的。类似地,崩溃是允许的,但不是必需的(更遗憾的是,如果人们在宽恕系统上工作,然后移植到其他不太宽容的系统,人们就会被咬伤)。

票数 19
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/11589342

复制
相关文章
如何正确使用const(常量),define(宏)
前言 在开发中,也许我们会经常使用到宏定义,或者用const修饰一些数据类型,经常有开发者不知道怎么正确使用,导致项目中乱用宏定义与const修饰符。本篇主要介绍在开发中怎么正确使用const与define(宏定义) 当我们想定义全局共用的一些数据时,比如通知名字,动画时长等等,我们可以用宏、常量、变量: 宏: // 注意后面不需要带符号 #define ScottDidLoginSuccess @"登陆成功" 变量: // 注意后面一定要带符号 NSString *sc
Scott_Mr
2018/07/05
8860
如何正确使用const(常量),define(宏)
前言 在开发中,也许我们会经常使用到宏定义,或者用const修饰一些数据类型,经常有开发者不知道怎么正确使用,导致项目中乱用宏定义与const修饰符。本篇主要介绍在开发中怎么正确使用const与define(宏定义) 当我们想定义全局共用的一些数据时,比如通知名字,动画时长等等,我们可以用宏、常量、变量: 宏: // 注意后面不需要带符号 #define ScottDidLoginSuccess @"登陆成功" 变量: // 注意后面一定要带符号 NSString *sc
Scott_Mr
2018/05/16
2.3K0
C/C++中define定义的常量与const常量
常量是在程序中不能更改的量,在C/C++中有两种方式定义常量,一种是利用define宏定义的方式,一种是C++中新提出来的const型常变量,下面主要讨论它们之间的相关问题;
Masimaro
2018/08/31
1.7K0
iOS define 和 const常量区别?
●define是宏定义,程序在预处理阶段将用define定义的内容进行了替换。因此程序运行时,常量表中并没有用define定义的常量,系统不为它分配内存。const定义的常量,在程序运行时在常量表中,系统为它分配内存。
赵哥窟
2020/02/13
4460
PHP中定义常量的区别,define() 与 const[通俗易懂]
这两种方式的根本区别在于const会在代码编译时定义一个常量,而define则是在代码运行时才定义一个常量。这就使得const会有以下几个缺点:
全栈程序员站长
2022/07/11
1.1K0
c++中constexpr_define和const定义常量的区别
常量表达式是指值不会改变且在编译过程中就能够得到计算结果的表达式,能在编译时求值的表达式。
全栈程序员站长
2022/11/09
1K0
c++中constexpr_define和const定义常量的区别
JavaScript 学习-10.使用const声明常量
前言 const 用于声明一个或多个常量,声明时必须进行初始化,且初始化后值不可再修改。 const 声明常量 const定义常量与使用let 定义的变量相似: 二者都是块级作用域 都不能和它所在作用域内的其他变量或函数拥有相同的名称 两者还有以下两点区别: const声明的常量必须初始化,而let声明的变量不用 const 定义常量的值不能通过再赋值修改,也不能再次声明。而 let 定义的变量值可以修改。 块级作用域 const定义常量也有块级作用域 var a = 10; const x = 'worl
上海-悠悠
2022/05/20
9690
iOS学习——iOS 宏(define)与常量(const)的正确使用
  在iOS开发中,经常用到宏定义,或用const修饰一些数据类型,经常有开发者不知怎么正确使用,导致项目中乱用宏与const修饰。你能区分下面的吗?知道什么时候用吗?
mukekeheart
2019/09/29
1.8K0
iOS学习——iOS 宏(define)与常量(const)的正确使用
C++中Const常量机制分析
const为C/C++常用的修饰符,表示该变量是一个常量,不可被修改等含义。那么在实际使用中会存在如下疑问:
洛杉矶
2018/06/08
2.4K0
ES6 const声明常量以及特点
ES6(ECMAScript 2015)引入了const关键字,用于声明常量。在JavaScript中,常量是指其值在声明后不能被重新赋值的变量。const声明的常量具有以下特点:
堕落飞鸟
2023/05/22
5030
C++常量const建议收藏
  常量折叠表面上的效果和宏替换是一样的,只是“效果上是一样的”,而两者真正的区别在于,宏是字符常量,在预编译宏替换完成后,该宏名字会消失,所有对宏的引用已经全部被替换为它所对应的值,编译器当然没有必要维护这个符号,而常量折叠发生的情况是,对常量的引用情况全部替换为该常量的值,但是,常量名并不会消失,编译器会把它放入到符号表中,同时会为该变量分配空间,栈空间或全局空间。
全栈程序员站长
2022/07/14
3040
3分钟短文|PHP 定义常量,我该用define还是const?这下不迷茫了
我们今天说一下 PHP 编程中,定义一个常量所使用的两种方法。有哪些区别,以及哪种是最佳实践?
程序员小助手
2020/06/17
1K0
C++中const与C中的const使用对比
大家好晚上好,今天给大家分享的是,c++中的const的使用,在我们以前学习c语言的时候,我们已经接触了const的用法,那么在c++中,const的使用,又会有什么样的不同呢?接下来就开始我们的分享吧!每天进步一点点,日积月累你也是专家!
用户6280468
2022/03/21
6460
C++中const与C中的const使用对比
【C 语言】const 关键字用法 ( 常量指针 - const 在 * 左边 - 修饰数据类型 - 内存不变 | 指针常量 - const 在 * 右边 - 修饰变量 - 指针不变 )
const 关键字 在 C 和 C++ 中的表现不同 , 本篇博客主要介绍 C 语言中的 const 用法 ;
韩曙亮
2023/03/29
2.4K0
ES6语法中常量声明(const)的实现原理
ES6 const 特点: 临时性死区 在定义的时候完成初始化 不能重新定义 不能重新赋值 语义化标识,表示声明后不可更改的不变量 原理: ES5没有块级的概念,我们只能大概模拟一下const的定义。 我们将const 声明的变量绑定在全局对象上,借助 Object.defineProperty()劫持该对象,配置相关描述符实现const的特性。 关键字和数字不能作为对象属性 var const_customer = function(param, value) { // 目前是在浏览器端测试全局对
伯爵
2019/10/13
2.2K0
ES6语法中常量声明(const)的实现原理
const 指针 常量指针 指针常量 常量指针常量
指针常量在定义时必须初始化,且不允许修改,但其指向的地址的值可以修改,即p不可改写而*p可以改写。
叶茂林
2023/07/28
1410
const与#define的区别、优点
补充:预处理器根据以#开头的命令,修改原始的程序。比如我们常见的#include <stdio.h>命令告诉处理器读取系统头文件stdio.h的内容,并把它直接插入程序文本中。咱们的#define也是,仅仅是单纯的文本替换。
WindSun
2019/08/29
1.8K0
C++:18---const关键字(附常量指针、指针常量、常量指针常量)
一、const变量的一些基本特点 ①const修饰的变量不能被修改 const int a=10;a=20;//错误 ②因为const修饰的变量不能被修改,所以必须被初始化 int a=10;const int b=a; //正确const int c=10; //正确 ③const修饰的变量可以赋值给其他值 const int a=10;int b=a;//正确 ④可以有常量引用 int a=10;const int &b=a; 二、在其他文件中使用const常量(extern) const常量默
用户3479834
2021/02/03
1.4K0
点击加载更多

相似问题

使用#define声明常量有什么好处?

34

我应该使用#define,enum还是const?

14104

在C++中,使用#define或const来避免神奇的数字更好吗?

32

#define与const声明的优先级

10

C常量字符串声明出错

40
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文