【问题标题】:A funny thing with sprintfsprintf 的一个有趣的事情
【发布时间】:2013-06-08 13:09:03
【问题描述】:

我对 sprintf 感到很困惑,以至于不同平台的一个有趣问题。 代码:

int main () 
{
    char sql[1024];
    uint32_t app_id = 32;
    uint64_t task_id = 64;
    sprintf(sql, "%u, %u", task_id, app_id);
    printf ("%s\n", sql);
    return 0;
}

控制台中的结果(MSVC2010 调试/发布):64, 0

但控制台中的代码相同(CentOS64 gcc4.4.6):64, 32

任何人都会帮助我,tks!

-------------已更新--------------

谢谢各位。我看过这篇文章:sprintf for unsigned _int64

实际上,PRIu64"inttypes.h" 中定义为:I64uwindows 不支持。所以我可以这样写:

sprintf(sql, "%I64u, %I32u", task_id, app_id);

【问题讨论】:

  • %u 不是 uint64_t 的正确格式说明符。请改用PRIu64
  • 在 windows 上,uint64_t 是 unsigned long long;如果您希望它全面正常工作,您需要坚持@simonc 的建议
  • #include <inttypes.h>sprintf(sql, "%"PRIu64",%"PRIu32, task_id, app_id); 可以解决问题
  • 你添加的链接真的很好。

标签: c linux windows printf


【解决方案1】:

格式字符串%lu 在 64 位变量为 long long 的 32 位机器上不起作用。

使用 %llu 代替 64 位:

#include <stdio.h>
#include <stdlib.h>
#include <linux/types.h>
#include <stdint.h>

int main () 
{
    char sql[1024];
    uint32_t app_id = 32;
    uint64_t task_id = 64;
    sprintf(sql, "%llu, %u", task_id, app_id);
    printf ("%s\n", sql);
    return 0;
}

【讨论】:

  • 错了。首先,ll 代表long long,而您可能只想使用long。此外,这与实际打印输出无关:64, 0
  • uint64 在 Linux 上的工作时间很长。但是,这不会在 Windows 上解决它。一个正常的 unsigned int 是 u,long 是 lu,long long 是 llu
  • 在我的 32 位 Linux 机器上,如果我只使用 %lu,gcc 4.7 会报错。原因是 64 位 unsigned int 是 long long。
  • @AndrejsCainikovs 如果使用时间长,现实世界的许多平台将无法运行。 long 在所有平台上都不是 64 位的。在所有当前支持 long long 的平台上,long long 都是 64 位(尽管在平台上实现了 > 64 位的 long long 并没有什么问题,但目前还没有)
【解决方案2】:

sprintf()中对task_id使用%llu格式字符串,如下:

sprintf(sql, "%llu, %u", task_id, app_id);
//             ^
//            for: long long unsigned int

编辑:正如@Simonc 建议更好地使用:&lt;inttypes.h&gt; 中定义的PRIu32PRIu64 宏(因为你有Linux 标签)喜欢:

sprintf(sql, "%"PRIu64", %"PRIu32"", task_id, app_id);
//               ^           ^
//       for:   uint64_t    uint32_t  

【讨论】:

  • 这与实际打印输出有什么关系:64, 0?
  • @GrijeshChauhan 我不认为PRId32 是必需的。我认为"%"PRIu64", %u" 的格式字符串应该没问题。另外,请注意,您需要用于无符号整数的宏,因此需要PRIu64(同样,如果您想为uint32_t 使用等效的宏)。
  • @simonc 是的,它不需要inttypes.h 中的宏。定义为#define PRId32 "ld" 感谢您的编辑:)。
  • 在 32 位机器上 task_id 是 long long unsigned int。尝试使用 gcc 的 -Wextra 选项编译它!
  • @GrijeshChauhan,不,PRId32 之后不需要额外的 "",请使用 cppgcc -E 进行检查
【解决方案3】:

只是补充其他人所说的:

给予

sprintf(sql, "%u, %u", app_id, task_id);

而不是

sprintf(sql, "%u, %u", task_id, app_id);

输出32, 64!!

不用担心!原因如下: task_id 在推入app_id(作为sprintf 的参数)之前被推入堆栈(首先是高4 字节,然后是低4 字节)。但是当sprintf 接受参数时,它会从堆栈中弹出 4 个字节 + 4 个字节,因为格式中指定了两个 %u。所以它将task_id的低4个字节打印为unsigned int

sprintf(sql, "%u, %u", task_id, app_id); 给出时也会发生同样的情况。

app_id 首先被推送,task_id 然后被推送。但是当 sprintf 读取时,它会弹出两个 4 字节,64(task_id 的低 4 字节)和 00task_id 的高 4 字节并打印 64, 00

【讨论】:

    【解决方案4】:

    正如已经指出的那样,uint64_t 的正确格式说明符是 PRIu64不是 lu,因为不能保证 uint64_tlong)。 p>

    所以,你需要使用它:

    #define __STDC_FORMAT_MACROS
    #include <inttypes.h>
    
    char sql[1024];
    uint32_t app_id = 32;
    uint64_t task_id = 64;
    sprintf(sql, "%u, %" PRIu64, app_id, task_id);
    printf ("%s\n", sql);
    

    【讨论】:

    • 不,app_id(最后一个参数)是 uint_32
    • 我认为您的格式说明符是错误的。 task_iduint64_t
    • 是否需要#define __STDC_FORMAT_MACROS?在 gcc 4.7.2 上没有它也可以正常编译
    • @AlterMann 我认为是为了在使用 g++ 编译时提供支持
    【解决方案5】:

    格式说明符错误。 printf 函数不知道传递给它的参数是什么样的,它只能从格式说明符中推断出来。因此,当您向它传递一个 64 位整数并告诉函数您只传递了一个长整数(32 位)时,它只需要传入的 64 位值的一半。根据体系结构,可能会打印或不打印正确的结果。原因是数字是如何存储在内存中的,以及 printf 函数在读取 long 值时看到的内容。

    如果 long 值恰好具有相同的位数,或者该值存储在内存中的方式是预期结果恰好在正确的位置,则可以打印正确的值,但当然,代码仍然是错误的,尽管它似乎可以工作。

    【讨论】:

      【解决方案6】:

      试试:

      sprintf(sql, "%lu, %u", task_id, app_id);
      

      【讨论】:

        猜你喜欢
        • 2010-10-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多