【问题标题】:scanf("%d", char*) - char-as-int format string?scanf("%d", char*) - char-as-int 格式字符串?
【发布时间】:2010-06-14 10:14:27
【问题描述】:

char-as-number 的格式字符串修饰符是什么?

我想使用 sscanf 将一个不超过 255(实际上少得多)的数字读入无符号字符类型变量。

使用典型

 char source[] = "x32";
 char separator;
 unsigned char dest;
 int len;

 len = sscanf(source,"%c%d",&separator,&dest);
 // validate and proceed...

我收到了预期的警告:sscanf 的参数 4 是 char* 类型,预期为 int*。

据我了解,char 没有修饰符(例如 %sd 简称,%lld 表示 64 位长)

  • 危险吗? (溢出只是溢出(翻转)变量还是会写入分配的空间之外?)
  • 有没有比分配一个临时 int 变量更漂亮的方法来实现这一点?
  • ...或者您会建议完全不同的方法吗?

【问题讨论】:

  • 我不太清楚,为什么在以字符形式阅读时使用%d
  • @Lucas:在内存受限的环境中读取少量数字。使用 int 会很浪费。

标签: c csv format string-formatting


【解决方案1】:

您可以在 glibc 的 scanf() 下使用 %hhd,MSVC 似乎不支持直接将整数存储为 char(有关支持的转换的更多信息,请参阅 MSDN scanf Width Specification

【讨论】:

  • 根据 int 的大小(不一定是 4 个字节),%hdd 可能不是 1 个字节。
  • 由于我对编译器环境和硬件(嵌入式产品的闭源固件)有 100% 的控制权,这似乎是一个完美的解决方案。
  • @Dpp:它是 %hhd,无论 sizeof(int) 如何,它都可以工作。请参阅链接文档:“下一个指针是指向有符号字符或无符号字符的指针”。
【解决方案2】:

使用它很危险。由于存在从 unsigned char* 到 int* 的隐式转换,如果数字大于 0xFF,它将使用堆栈中变量旁边的字节(最多 3 个)并破坏它们的值。

%hhd 的问题在于,根据 int 的大小(不一定是 4 个字节),它可能不是 1 个字节。

sscanf 似乎不支持将数字存储为 char,我建议您改用 int。虽然如果你想要 char 翻转,你可以在之后将 int 转换为 char,例如:

int dest;
int len;

len = sscanf(source,"%c%d",&separator,&dest);
dest = (unsigned char)dest;

【讨论】:

  • 你错了,%hhd中的hh修饰符表示下一个指针将是指向signed charunsigned char的指针。
【解决方案3】:

我想读一个数字从来没有 超过 255(实际上要少得多) 进入一个无符号字符类型变量 使用 sscanf。

在大多数情况下,使用 char 表示整数可以节省一点点。

这通常取决于架构和编译器,但大多数现代 CPU 并不擅长处理与寄存器大小不同的数据类型。 (值得注意的例外是 64 位架构上的 32 位 int。)

在此处添加对非 CPU 字对齐内存访问的惩罚(不要问我为什么 CPU 会这样做)char 的使用应仅限于真正需要 char 或内存消耗是一个问题的情况。

【讨论】:

  • 嵌入式项目,一个同时具有 32 位和 8 位指令集 (ARM) 的处理器,大小是一个问题。
【解决方案4】:

这是不可能的。

sscanf 在读取整数时永远不会写入单个字节。

如果将指向单个分配字节的指针作为指向 int 的指针传递,则会超出范围。由于默认对齐方式,这可能没问题,但您不应该依赖它。

创建一个临时的。这样您还可以运行时检查它。

【讨论】:

    【解决方案5】:

    可能最简单的方法是将数字简单地加载到一个临时整数中,并且仅当它在所需的边界内时才存储它。 (你可能需要unsigned char result = tempInt & 0xFF; 之类的东西)

    【讨论】:

    • 转换为 (unsigned char) 更安全,因为在某些架构上 char 可以有 7 位。 (定义 CHAR_BIT (limits.h) 通常会给出这个数字)
    【解决方案6】:

    这很危险。 Scanf 将在字符大小的变量上写入整数大小的值。在您的示例中(很可能),除非您重新组织堆栈变量,否则不会发生任何事情(scanf 在尝试写入整数大小的“dest”时会部分覆盖 len,但随后它会返回正确的“len”并用“正确”值覆盖它)。

    相反,做“正确的事”而不是依赖编译器情绪:

     char source[] = "x32";
     char separator;
     unsigned char dest;
     int temp;
     int len;
    
     len = sscanf(source,"%c%d",&separator,&temp);
     // validate and proceed...
    
     if (temp>=YOUR_MIN_VALUE && temp<=YOUR_MAX_VALUE) {
       dest = (unsigned char)temp;
     } else {
       // validation failed
     }
    

    【讨论】:

    • 其实是反过来的,它会覆盖'char separator'和'char source[]'的一部分。顺便说一句,后者应该更好地定义'const char source []'。
    • @Dpp: 是的,你是对的(忘记堆栈是如何工作的:o)另一个建议确实也是正确的,但应该针对 OP(我不想改变太多) .
    猜你喜欢
    • 2021-10-17
    • 2012-04-22
    • 2016-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-26
    相关资源
    最近更新 更多