[英]Why printf() gives a runtime error?
为什么下面的代码会编译并执行,但不会在输出中打印任何内容,
int i = 0;
printf(i = 0);
但这会产生运行时错误,
int i = 0;
printf(i = 1);
int i = 0;
printf(i = 0);
printf
的第一个参数必须是指向格式字符串的char*
值。 你给了它一个int
。 那就是问题所在。 printf(i = 0)
和printf(i = 1)
之间的行为差异在很大程度上无关紧要; 两者都同样错误。 (有可能第一个传递空指针,并且printf
以某种方式检测并处理空指针,但这会分散对真正问题的注意力。)
如果您想打印i = 0
的值,这是正确的方法:
printf("%d\n", i = 0);
您在参数中有副作用( i = 0
是赋值,而不是比较),这是合法但风格不佳的。
如果您有所需的#include <stdio.h>
,那么您的编译器至少必须警告您有关类型不匹配的信息。
如果你没有#include <stdio.h>
,那么你的编译器几乎肯定会警告调用printf
没有声明。 (C89/C90 编译器并不严格要求对此发出警告,但任何体面的编译器都应该发出警告,而 C99 或更高版本的编译器必须。)
当您编译代码时,您的编译器可能会给您一个或多个警告。 您未能在问题中包含这些警告。 您也未能向我们展示完整的自包含程序,因此我们只能猜测您是否拥有所需的#include <stdio.h>
。 如果你的编译器没有警告你这个错误,你需要找出如何要求它进行更好的诊断(如果不知道你使用的是哪个编译器,我们就无能为力)。
printf
函数中的表达式i = 0
和i = 1
将分别计算为0
和1
(并且i
将被初始化为0
和1
)。 所以上面的printf
语句在他们的表达式评估之后将等价于
printf(0); // To be clear, the `0` here is not a integer constant expression.
和
printf(1);
分别。
0
和1
都将被视为printf
语句中的地址,它将尝试从这些地址中获取字符串。 但是, 0
和1
都是未分配的内存地址,访问它们将导致未定义的行为。
printf 等待一个格式字符串作为它的第一个参数。 由于字符串 (char*) 只不过是指针,因此您需要提供一个地址作为第一个参数。
地址和整数隐式相互转换后,
printf(i = 0)
是(从 printf 的角度来看)与以下相同:
printf( 0 )
这与
printf( NULL ) // no string given at all
如果提供0作为参数,printf的将正常处理这种情况,不要做所有事情,因为0是一个保留地址,这意味着nothing at all
。
但是printf( 1 )
不同: printf 现在在地址 1 搜索字符串,这不是您程序的有效地址,因此您的程序会引发分段错误。
[更新] 这样做的主要原因是您需要了解有关 char 数组、指针、赋值和 printf 本身如何工作的几个事实的组合:
指针可以隐式转换为 int ,反之亦然,因此,例如 int 值17
被转换为内存地址 0x00000011 (17),而无需进一步通知。 这是因为 C 非常接近硬件,并允许您使用内存地址进行计算。 事实上,内部 int(或更具体:整数的一种特殊类型)和指针是相同的,只是语法不同。 这在将代码从 32 位架构移植到 64 位架构时会导致很多问题,但这是另一个话题。
赋值不同于比较: i = 0
和i == 0
的含义完全不同。 当 i 包含值0
时, i == 0
返回true而i = 0
返回... 0
。 这是为什么? 例如,您可以连接以下变量分配:
a = b = 3;
首先执行b = 3
,再次返回3
,然后将其分配给a
。 这对于测试数组中是否有更多项目等情况非常有用:
while( (data = get_Item()) )
现在引出下一点:
NULL
与 0 相同(也与false
相同)。 事实上,整数和指针可以相互转换(或者更好:在物理上是同一个东西,见我上面的评论),有一个名为NULL
的保留值,这意味着: pointing nowhere
。 该值定义为虚拟地址0x00000000
(32 位)。 因此,如果您愿意,为需要指针的函数提供 0,您可以提供一个空指针。
char* 是一个指针。 与 char[] 相同(行为和分配在逻辑上略有不同,但在内部它们基本相同)。 printf 需要一个 char*,但您提供一个 int。 这不是问题,如上所述, int 将被解释为地址。 几乎每个(写得好的)函数都将指针作为参数,对这些参数进行完整性检查。 printf 不排除:如果它检测到一个空指针,它将处理该情况并静默返回。 然而,当提供不同的东西时,没有办法知道它是否真的不是一个有效的地址,所以 printf 需要完成它的工作,当试图用分段错误寻址 0x00000001 的内存时被内核中断。
这是很长的解释。
顺便说一句:这仅适用于 32 位指针(因此编译为 32 位二进制文件)。 对于 64 位机器上的 long int 和指针也是如此。 但是,这是编译器的问题,它如何转换您提供的表达式(通常,0 的 int 值被隐式转换为 0 的 long int 值,然后在分配时用作指针地址,反之亦然”没有显式转换就可以工作)。
printf 需要一个 const char * 来输入,而你给它一个 int
为什么下面的这段代码可以编译并执行,但没有在输出中打印任何内容?
printf(i = 0);
这个问题包含了一个错误的前提。 在任何现代 C 编译器上,这种明显错误的代码至少会产生一个错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.