【问题标题】:How to switch and case for string?如何切换字符串和大小写?
【发布时间】:2013-01-08 16:07:12
【问题描述】:

我有以下代码

#define SWITCH(S) char *_S = S; if (0)
#define CASE(S) } else if (strcmp(_S, S) == 0) {switch(1) { case 1
#define BREAK }
#define DEFAULT } else {switch(1) { case 1

int main()
{
    char buf[256];

    printf("\nString - Enter your string: ");
    scanf ("%s", buf);

    SWITCH (buf) {
        CASE ("abcdef"):
            printf ("B1!\n");
            BREAK;
        CASE ("ghijkl"):
            printf ("C1!\n");
            BREAK;
        DEFAULT:
            printf ("D1!\n");
            BREAK;
    }
}

如果我用gcc -E生成预处理器代码,我会得到以下代码

int main()
{
    char buf[256];

    printf("\nString - Enter your string: ");
    scanf ("%s", buf);

    char *_S = buf;
    if (0) {
    } else if (strcmp(_S, "abcdef") == 0) {switch(1) { case 1:
        printf ("B1!\n");
        };
    } else if (strcmp(_S, "ghijkl") == 0) {switch(1) { case 1:
        printf ("C1!\n");
        };
    } else {switch(1) { case 1:
        printf ("D1!\n");
        };
    }
}

但是对于一些在代码中间定义char *_S = buf;的gcc是不受欢迎的,可能会产生编译错误

如何在我的宏中解决这个问题?

请不要建议将char *_S 定义为全局(在main 之外)

【问题讨论】:

  • 您希望宏生成什么?为什么char *_S = buf; 会出错?你的意思是如果_S 已经定义了吗?正如 unwind 所建议的,最好的方法是摆脱宏。
  • 它必须在一个块的开头,不一定是一个函数。正如 unwind 的回答中指出的那样,通过引入 SWITCH_END{ 添加到 SWITCH 和另一个 }
  • 这些宏可能会在调试会话中的某个时候回来咬你。这就是我尽可能避免使用宏的原因。您看到的代码与调试器看到的代码不同。
  • 鉴于您的 SWITCH 控制结构当前具有的有趣限制(在每个块中只能使用一次;必须在每个案例上都有一个 BREAK 以防止失败,特别是因此没有连续案例共享代码;使用保留名称),我不认为再有一个有趣的限制会使它更难使用。在每次使用它的整个过程中添加一个额外的块,例如{ SWITCH(S) { ... } }
  • 天哪,这太可怕了:(

标签: c macros


【解决方案1】:

完全删除宏,并以“扩展”方式编写,将声明移至顶部。这些宏太可怕了。

如果做不到这一点,调整SWITCH 以引入一个新范围(第二个{)。这当然会迫使你不得不关闭两个范围,所以也许在最后添加一个SWITCH_END abomination 来封装它。随便。

【讨论】:

  • 我觉得你没有足够强烈地强调“可怕”,但我还是继续对你的答案投了赞成票。
【解决方案2】:

哦不!!!

这是我在this post 写的一个笑话

不使用它是非常非常可怕的,如果你想避免 if-else 你可以在不折磨编译器的情况下这样做,考虑使用一对字符串:

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

int main(void)
{
    char buf[256];
    const char *ap[] = {
        "abcdef", "B1!\n",
        "ghijkl", "C1!\n",
        NULL    , "D1!\n",
    }, **p = ap;

    printf("\nString - Enter your string: ");
    scanf ("%s", buf);
    while (*p) {
        if (strcmp(buf, *p) == 0) break;
        p += 2;
    }
    printf("%s", *(++p));
    return 0;
}

【讨论】:

    【解决方案3】:

    确保代码被编译为 C99 或更高版本;否则,您将需要使用不同的控制结构。

    #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
    
      SWITCH(buf)
      {
        ...
      }
    
    #else
    
      if (strcmp(buf, "abcdef") == 0)
      {
        ...
      }
      else if (strcmp (buf, "ghijkl") == 0)
      {
        ...
      }
      else
      {
        ...
      }
    
    #endif
    

    使用预处理器来“改变”或扩展 C 语法通常不是一个好主意(我有疤痕组织来证明); switch 没有在字符串表达式上定义是有原因的。

    如果你真的想在这种情况下使用switch,那么最好编写一个哈希函数,为每个字符串返回一个键,然后打开结果:

    #define ABCDEF ... // hash key generated for "abcdef"
    #define GHIJKL ... // hash key generated for "ghijkl"
    ...
    switch(hash(buf))
    {
      case ABCDEF :
         ...
         break;
    
      case GHIJKL :
         ...
         break;
    
      default:
         ...
         break;
    }
    

    【讨论】:

    • +1 用于散列!尽管可能值得一提的是,您必须检查碰撞。
    • 对于哈希:存在碰撞风险。
    • 好伤心。由于散列,我会投反对票,但我不想浪费积分这样做。不...等等...我有解决方案...将决策发送到远程决策服务器。在云端做出决定!云是要走的路!
    • @phonetagger:天哪,不要让对分数的关注阻止你。您认为这是一个糟糕的答案,请投反对票。就个人而言,我认为 OP 应该放弃对宏的厌恶,并将其写成一个简单的 if-else 链(或构建一个表查找,或其他东西)。但如果他们真的想要使用switch,那么他们应该使用开关,而不是冒充开关的宏。散列字符串是实现这一目标的一种方法。从未声称这是这样做的最佳方式。
    • “从未声称这是最好的方式。”好的,我给你那个。是的,如果要进行多个字符串比较,表查找会很好。有一些门槛,当然每个人都不同,这样的查找表比相应的 if-else 链更容易理解。
    猜你喜欢
    • 2013-02-01
    • 2018-12-24
    • 1970-01-01
    • 2015-11-21
    • 1970-01-01
    • 2016-07-14
    • 1970-01-01
    • 1970-01-01
    • 2010-12-29
    相关资源
    最近更新 更多