【问题标题】:C int datatype and its variationsC int 数据类型及其变体
【发布时间】:2011-07-31 02:21:45
【问题描述】:

您好,今天我在 C99 标准中尝试 C 语言时,又遇到了一个我无法理解的问题,需要专家的帮助。

守则:

    #include <stdio.h>

    int main(void)
   {
    int Fnum = 256; /* The First number to be printed out */

    printf("The number %d in long long specifier is %lld\n" , Fnum , Fnum);

    return 0;
   }

问题:

1.)当我尝试运行此代码时,此代码提示我一条警告消息。

2.)但奇怪的是,当我尝试将说明符 %lld 更改为 %hd%ld 时, 执行期间未显示警告消息,控制台上打印的值是正确的数字 256 ,即使我尝试使用,一切似乎也很正常 %u%hu%lu。简而言之,只有当我使用 long long 说明符的变体时才会出现警告消息和错误的数字打印。

3.)为什么会发生这种情况??我以为long long的内存大小足以容纳值256,但是为什么不能用来打印出适当的值??

警告信息:(对于上述源代码)

C:\Users\Sam\Documents\Pelles C Projects\Test1\Test.c(7): warning #2234: Argument 3 to 'printf' does not match the format string; expected 'long long int' but found 'int'.

感谢您花时间阅读我的问题。上帝保佑。

【问题讨论】:

    标签: c variables types int


    【解决方案1】:

    您将Fnum 变量传递给printf,它的类型为int,但它期待long long。这与long long 能否保持256关系不大,只是你选择的变量输入int

    如果你只想打印 256,你可以得到一个输入到unsigned long long 的常量,如下所示:

    printf("The number %d in long long specifier is %lld\n" ,256 , 256ULL);
    

    或演员:

    printf("The number %d in long long specifier is %lld\n" , Fnum , (long long int)Fnum);
    

    【讨论】:

      【解决方案2】:

      这里发生了三件事。

      1. printf 接受可变数量的参数。这意味着编译器不知道参数(除了格式字符串)应该是什么类型。因此它无法将它们转换为适当的类型。
      2. 然而,由于历史原因,小于int 的整数类型在传入变量参数列表时会“提升”为int
      3. 您似乎在使用 Windows。在 Windows 上,intlong 的大小相同,即使指针是 64 位宽(这是微软故意违反 C89 的 - 他们实际上强制在 C99 中更改标准以使其“正常” )。

      这一切的结果是:编译器不允许将你的int 转换为long long,因为你在参数列表中使用了%lld。 (允许警告您忘记演员表,因为警告超出了标准行为。)因此,使用%lld,您的程序将无法运行。但是,如果您使用任何 other 大小说明符,printf 最终会寻找与 int 相同大小的参数并且它可以工作。

      【讨论】:

      • intlong 在 64 位机器上的大小相同并不违反 C89,它不保证整数与指针类型的相对大小或任何整数类型的能力保存指针值,反之亦然。我同意 MS 在 64 位机器上制作 long 32 位是疯狂的,但这纯粹是(不)明智的实现选择问题,而不是一致性问题。 MS 还有很多其他领域公然违反标准,但这不是其中之一。
      • 违反 C89 的行为可能并不明显,但我向您保证它确实存在。 C89 要求所有:ptrdiff_t 可以表示任意两个指针之间的差异(只要它们指向同一个数组); size_t 可以表示任意对象的大小; sizeof(size_t) &lt;= sizeof(long);和sizeof(ptrdiff_t) &lt;= sizeof(long)。如果你有扁平指针——Win64 就是这样——那么保持所有这些断言的唯一方法是sizeof(void*) &lt;= sizeof(long)。实际上,Win64 size_t 不能表示任何对象的大小比 long 不能保存指针 IMO 更大的问题。
      • 可悲的是,MS 将更改撞到 C99(具体来说,sizeof(size_t) 不必小于 sizeof(long))然后 size_t 保留为 32 位在 Win64 中。所以我们被 C99 中的 intmax_t 之类的废话困住了,但是想要它提供的许可证的公司并没有费心使用它。他们应该直接拒绝遵守 C99,退出标准流程,然后至少标准不会有这个包袱。
      • 这不是违规;它只是意味着实现不支持创建任何大于 2GB 的对象(包括通过malloc)。当然,如果编译器确实允许您创建更大的对象,则 that 是违规的。请记住,尽管ptrdiff_t 无法容纳那么大的差异,但许多 32 位实现通过允许分配略大于 2GB 来犯此违规行为。
      • @Zack: 不管size_t 的大小如何,intmax_t 都非常重要,除非您需要不存在大于long long 的扩展整数类型...
      【解决方案3】:

      在处理可变参数函数时,调用者和被调用者需要某种方式来约定可变参数的类型。在 printf 的情况下,这是通过格式字符串完成的。 GCC 足够聪明,可以读取格式字符串本身并确定 printf 是否会以与实际提供的方式相同的方式解释参数。

      在某些情况下,您可以使用稍有不同类型的论点。例如,如果你传递一个short,那么它会被隐式转换为一个int。而当 sizeof(int) == sizeof(long int) 那么也没有区别。但是 sizeof(int) != sizeof(long long int) 所以在这种情况下参数无法匹配格式字符串。

      【讨论】:

        【解决方案4】:

        这是由于 varargs 在 C 中的工作方式。与普通函数不同,printf() 可以接受任意数量的参数。程序员可以通过提供正确的格式字符串来告诉printf() 会发生什么。

        在内部,printf() 使用格式说明符来访问与输入参数对应的原始内存。如果您指定%lld,它将尝试访问64 位内存块(在Windows 上)并将找到的内容解释为long long int。但是,您只提供了一个 32 位参数,因此结果将是未定义的(它会将您的 32 位 int 与堆栈中接下来出现的任何随机垃圾结合起来)。

        【讨论】:

          猜你喜欢
          • 2016-10-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-13
          • 2016-01-18
          • 2013-04-02
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多