【问题标题】:How to switch on a 4-character string in C?如何在 C 中打开 4 个字符的字符串?
【发布时间】:2011-11-11 15:47:02
【问题描述】:

我需要基于 4 个字符的字符串进行切换。我将字符串放在一个联合中,这样我至少可以将其称为 32 位整数。

union
{
    int32u  integer;
    char    string[4];
}software_version;

但现在我不知道在案例陈述中写什么。我需要某种宏来将 4 个字符的字符串文字转换为整数。例如

#define STRING_TO_INTEGER(s)    ?? What goes here ??
#define VERSION_2_3_7           STRING_TO_INTEGER("0237")
#define VERSION_2_4_1           STRING_TO_INTEGER("0241")

switch (array[i].software_version.integer)
{
    case VERSION_2_3_7:
        break;

    case VERSION_2_4_1:
        break;
}

有没有办法制作 STRING_TO_INTEGER() 宏。或者有没有更好的方法来处理这个开关?

【问题讨论】:

  • 您知道这不能跨字节序边界移植吗?最好简单地定义一个版本整数,该整数在程序启动时从数字版本递增或计算。
  • 我不认为你可以在编译时从字符串 -> 数字。
  • 你为什么坚持用宏将你的VERSION常量从字符串转换?你可以简单地......你知道......直接输入整数(例如#define VERSION_2_3_7 237)......
  • @SethCarnegie 可以使用sscanf 解析字符串中的整数。
  • 另请注意,如果您坚持使用版本的字符数组表示并且想要 4 个字符,则应将结构的字符串元素定义为 5 个字符,其中一个为 NULL。

标签: c string macros switch-statement


【解决方案1】:

便携示例代码:

#include <assert.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>

#define CHARS_TO_U32(c1, c2, c3, c4) (((uint32_t)(uint8_t)(c1) | \
    (uint32_t)(uint8_t)(c2) << 8 | (uint32_t)(uint8_t)(c3) << 16 | \
    (uint32_t)(uint8_t)(c4) << 24))

static inline uint32_t string_to_u32(const char *string)
{
    assert(strlen(string) >= 4);
    return CHARS_TO_U32(string[0], string[1], string[2], string[3]);
}

#define VERSION_2_3_7 CHARS_TO_U32('0', '2', '3', '7')
#define VERSION_2_4_1 CHARS_TO_U32('0', '2', '4', '1')

int main(int argc, char *argv[])
{
    assert(argc == 2);
    switch(string_to_u32(argv[1]))
    {
        case VERSION_2_3_7:
        case VERSION_2_4_1:
        puts("supported version");
        return 0;

        default:
        puts("unsupported version");
        return 1;
    }
}

代码仅假定存在整数类型uint8_tuint32_t,并且与char 类型的宽度和符号以及字节序无关。只要字符编码仅使用uint8_t 范围内的值,就不会发生冲突。

【讨论】:

    【解决方案2】:

    像这样打开四字符代码

    switch (fourcc) {
    case 'FROB':
    }
    

    注意区别:"XXXX" 是字符串,'XXXX' 是字符/整数文字。

    但是,我建议您使用单独的版本号,例如:

    struct Version {
        int major, minor, patch;
    };
    
    bool smaller (Version lhs, Version rhs) {
        if (lhs.major < rhs.major) return true;
        if (lhs.major > rhs.major) return false;
        if (lhs.minor < rhs.minor) return true;
        if (lhs.minor > rhs.minor) return false;
        if (lhs.patch < rhs.patch) return true;
        if (lhs.patch > rhs.patch) return false; // redundant, for readabiltiy
    
        return false; // equal
    }
    

    【讨论】:

    • 您误解了标准。如果值恰好是另一种类型的陷阱表示,则访问最后写入的成员之外的另一个成员只是 UB。 int32u(我想这与uint32_t 相同)没有陷阱表示。
    • 我不明白这对我的 switch 语句有何帮助。
    • @Rocketmagnet:只能开启整数类型,未定义开启字符串/字符数组。
    • @Jens:我会删除那个短语。感谢您的提示。
    • @phresnel 是的,我知道这一点,这从我的问题中显而易见。这就是为什么我想要一种自动将字符串转换为整数的方法。
    【解决方案3】:

    更新:

    #define VERSION_2_3_7 '0237'
    

    【讨论】:

    • 他需要从字符串到数字,而不是从数字到字符串。
    • 问题是关于相反的方向。
    • @SethCarnegie,见上评论
    • 人们,停止投票。这实际上是正确的答案。
    • @Rocketmagnet,这个答案有缺陷,不要那样使用它。包含多个字符的字符常量(如建议的'0237')是特定于编译器的。此外,如果您真的对个人定义感到满意,那么您没有理由使用这样的垃圾。只需将其定义为一个长的无符号十进制数,例如237UL
    【解决方案4】:
    int versionstring_to_int(char * str)
    {
    char temp [ 1+ sizeof software_version.string ]; /* typically 1+4 */
    
    if (!str || ! *str) return -1;
    memcpy (temp, str, sizeof temp -1);
    temp [sizeof temp -1] = 0;
    
    return atoi (temp );
    }
    

    编辑:

    #define STRING_TO_INTEGER(s)    versionstring_to_int(s)
    #define VERSION_2_3_7           237
    #define VERSION_2_4_1           241
    
    switch ( STRING_TO_INTEGER( array[i].software_version.string) )
    {
    case VERSION_2_3_7:
        break;
    
    case VERSION_2_4_1:
        break;
    }
    

    【讨论】:

    • 不,这不起作用,因为它不能用作 case 语句。
    • 可以,只需在案例中使用数字 ID。它们应该是文字,并由程序员仔细匹配到相应的代码块。当一个数字同样可读且不易出错时,为什么还要一个字符串作为大小写标签?
    • 如果字符串包含字母怎么办?例如。 “ABC1”-> 0x41424331
    • 嗯,你必须解析它,不是吗?在项目的中年危机中更改命名/编号方案似乎没有多大意义。像这样的方案应该是稳定的。
    • 重点是将字符串的 4x8 位重新解释为 32 位整数。不解析字符串。任何 4 字符的字符串,包括 "0_?Q" 都可以重新解释为整数。但是,只能将数字字符串解析为整数。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-18
    • 2017-07-13
    • 2021-08-29
    • 2010-09-10
    • 1970-01-01
    • 1970-01-01
    • 2021-12-09
    相关资源
    最近更新 更多