繁体   English   中英

为什么 printf() 会出现运行时错误?

[英]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 = 0i = 1将分别计算为01 (并且i将被初始化为01 )。 所以上面的printf语句在他们的表达式评估之后将等价于

printf(0);  // To be clear, the `0` here is not a integer constant expression.

printf(1);

分别。

01都将被视为printf语句中的地址,它将尝试从这些地址中获取字符串。 但是, 01都是未分配的内存地址,访问它们将导致未定义的行为。

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 = 0i == 0的含义完全不同。 当 i 包含值0时, i == 0返回truei = 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM