【问题标题】:Is there any crucial difference between C Compilers?C编译器之间有什么关键区别吗?
【发布时间】:2021-06-29 04:28:09
【问题描述】:

我是 C 编程的新手,我在函数 printf 的参数中使用以下占位符规范时发现了这种奇怪的行为。在使用在线 C 编译器 (OnlineGDB) 和带有 gcc 终端编译的 VSCode 时,我得到了两个不同的结果。 那是sn-p:

#include <stdio.h>

int main()
{
    printf("%lld", 4);
    return 0;
}

正如 Wiki 所说,“ll 长度字段占位符是为整数类型制作的,并导致 printf 期待一个 long long-sized 整数参数”。 在在线编译器中我得到了这个:

main.c:13:16:警告:格式“%lld”需要“long long int”类型的参数,但参数 2 的类型为“int”[-Wformat= ]
4

所以它在警告后打印 4

VSCode中的结果完全不同????:

15621861207441412

没有警告,但这个数字是从哪里来的?

为什么在如此“简单”的情况下会有如此不同的行为?值 4 不也是一个 ll 整数吗?我的猜测是第二个期望更大的值,因此它在该间隔内向我发送了一个相当随机的值。另外,我必须为大学考试学习C,你能推荐我在编译示例项目时应该使用哪个编译器吗? 非常感谢您的宝贵时间。

【问题讨论】:

  • 警告是正确的。 4int 文字。如您所说,要拥有long long 文字,您需要编写4LL。另外,您在 VSCode、MSVC 中使用的是哪个编译器?您是否尝试过启用所有警告?我没有使用过 MSVC,但如果你这样做,它应该会发出警告。您的在线编译器可能正在使用 GCC 或 clang。
  • 编译器没有义务警告可变参数函数的参数。从本质上讲,编译器不知道它们的使用,但在printf 的情况下,一个有用的编译器会分析格式字符串并检查参数是否正确。 MSVC 对 printf()fprintf()sprintf() 执行此操作,但不是它自己的 cprintf() 函数。注意4默认是int类型,试试4LL
  • printf() 函数信任您已将符合格式规范的数据放入堆栈。如果格式需要 8 个字节,那么无论 为什么 它们在堆栈上,都会使用 8 个字节。在您的一个环境中,其他 4 个字节恰好是 0。
  • @mediocrevegetable1 详细信息:C 将4 指定为常量,而不是literal。 C 指定了 2 个文字:stringcompound。与常量不同,这些文字可以获取它们的地址。关于警告的要点。

标签: c gcc compilation format-string


【解决方案1】:

请注意,printf("%lld", 4);undefined behavior,我完全相信您的代码应该会产生恶魔。不要编写行为未定义的代码 - 只编写您知道其行为方式的代码。

但是这个数字是从哪里来的?

假设您的平台是小端,int 有 4 个字节,long long 有 8 个字节(字节有 8 位)。所以...%lld 读取 64 位。但是4int,所以只需要 32 位具有值4,而其他 32 位......没有被触及,剩下的(编译器做出这个决定,编译器也可以做出决定将它们归零)。为了形象化,4 值的寄存器/堆栈在调用printf 后可能如下所示:

[0x04][0x00][0x00][0x00][0x??][0x??][0x??][0x??]
                        ^^^^^^^^^^^^^^^^^^^^^^^^  - 32-bits uninitialized
^^^^^^^^^^^^^^^^^^^^^^^                           - 32-bits filled with 4
 compiler initializes only first 4 bytes, does not care about more
            

未初始化的值只是一些先前操作的剩余部分(先前的程序?C 启动例程?)。值15621861207441412 等于0x0037800000000004 - 你可能会注意到低8 字节是0x00000004,而0x00378000printf 拾取的一些剩余的未初始化字节。

【讨论】:

  • 非常感谢您的澄清。我需要深入研究 C 的特性,以更好地理解它发生了什么以及为什么。关于这种语言的好手册有什么建议吗?我的教授建议的书似乎有点混乱......
  • 好吧,C 不是汇编。在 C 中,作为一种语言printf("%lld", 4); 是无效的,毫无意义。有this,对于一个初学者 C和机器码的交互,我想我可以推荐Arduino和他们的一些介绍。
猜你喜欢
  • 2011-01-26
  • 1970-01-01
  • 2016-02-11
  • 1970-01-01
  • 1970-01-01
  • 2011-05-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多