比if-else if更好的解决方案?

时间:2017-10-18 08:27:01

标签: c embedded

我必须根据像83025(大于65535)这样的大数字的值来做一些事情。为此,我不能使用switch-case,因为它只使用最大值为255的整数参数。(或者至少这是我所知道的。但我仍然尝试过编译,但是switch-case不起作用好。)

所以我想我会在下面制作一个if-else if梯子,但看起来并不太优雅。

if      ((refnum == 32120) ||  
         (refnum == 32075))   {

else if  (refnum == 51036)    {

else if ((refnum == 61024) ||  
         (refnum == 61060))   {

else if ((refnum == 71030) ||  
         (refnum == 71048))   {

else if ((refnum == 72012) ||  
         (refnum == 72024) ||  
         (refnum == 72048))   {

else if ((refnum == 81025) ||  
         (refnum == 81050) ||  
         (refnum == 81100))   {

else if ((refnum == 82012) ||  
         (refnum == 82024) ||  
         (refnum == 82048) ||  
         (refnum == 82096))   {

else if ((refnum == 83050) ||  
         (refnum == 83100))   {

您能否确认这是正确的方法?或者你有更好的主意吗?

其他信息:

  • refnum是一个32位无符号整数

  • 大号来自字符串的中间,strtol将其转换为DWORD

  • 在每种情况下我必须做的事情是执行strcpy然后return某个值。

  • 代码嵌入并在16位微控制器上运行

6 个答案:

答案 0 :(得分:7)

  

必须根据像83025这样的大数字的值来做一些事情

然后确保所涉及的所有变量都使用uint32_t

  

为此,我不能使用switch-case,因为它只使用最大值为255的整数参数

这是一个误解,不确定你从哪里得到这个想法。 switch语句适用于所有整型常量表达式。 switch语句本身没有数字限制。

(实际上,switch语句的控制表达式被隐式提升为类型int,如果它恰好是一个较小的整数类型。)

  

所以我想我会在下面做一个if-else if ladder,但它看起来并不太优雅。你能否证实这是正确的做法?或者你有更好的主意吗?

if-else会生成与相应交换机完全相同的机器代码。这个开关可能会增加一点可读性,因此可能是一个更好的选择:

switch (refnum)
{
  case 32120: do_this(); break;
  case 61024: do_that(); break;
  ...
  default:    do_something();
}

替代:

我注意到这些是按排序顺序排列的整数值。如果有很多值或需要快速查找,您也可以用二进制搜索替换整个事物。这可能会提供更快的代码,但也会增加复杂性。最好使用C标准bsearch()

但是,如果您希望最终实现的是返回指向字符串的指针,那么此解决方案可能是理想的。然后,您可以将数字和字符串存储为键值对:

typedef struct
{
  uint32_t key;
  const char* str;
} thingie_t;

static const thingie_t things [] = 
{
  { 32120, "text string" },
  { 32075, "another text string" },
  ...
};
  

大数字来自字符串的中间,strtol将其转换为DWORD

为什么使用签名号码?数据似乎没有签名。什么是DWORD?这是Windows编程中的一些臭类型,在嵌入式系统中肯定应该避免。使用stdint.h中的类型,而不是一些丑陋的自制类型。

答案 1 :(得分:4)

  

我必须根据83025这个大数字的值来做一些事情(大概是65535)。为此,我不能使用switch-case,因为它只使用最大值为255的整数参数。(或者至少这是我所知道的。)

您的理解不正确:以下是C标准的措辞:

  

6.8.4.2 switch声明

     

switch语句的控制表达式应为整数类型。

     

[...]每个case标签的表达式应为整数常量表达式,同一case语句中的switch个常量表达式中没有两个具有相同的值转换后。 [...]

     

对控制表达式执行整数提升。每个case标签中的常量表达式将转换为控制表达式的提升类型。如果转换后的值与提升的控制表达式匹配,则控制将跳转到匹配的case标签后面的语句。 [...]

因此,对于案例值,65535一般没有限制,如果18446744073709551615表达式的类型为switch,则最大值至少为unsigned long long。如果您的switch表达式是unsigned int并且您的目标平台具有16位整数,则案例表达式的最大值将为65535,但是根据您要测试的值,{ {1}}的类型必须大于此类型。

  

然而我仍然尝试编译,但是切换案例效果不佳。

你没有发布有问题的代码......除非你的编译器很古老或严重破坏,否则问题不在你怀疑的地方,更可能是代码中的错误。

来自提供的额外信息

编辑,目标平台确实有16位refnum,但int必须大于refnum或{{1}容纳大于int的值,unsigned int65535。然后,编译器应接受大于long的{​​{1}}值。古代编译器可能不符合这一点......在这种情况下,您可能会遇到更多问题。

答案 2 :(得分:3)

在这种情况下,Switch语句可以为您完成工作。

switch (refnum) {
    case 0:
    case 1:
        //Do stuff when refnum is 0 or 1
        break;
    case 2:
        //Do stuff when refnum is 2
        break;
    case 36371:
    case 36372:
    case 36373:
    case 36374:
        // if (refnum == 36371 || refnum == 36372 || refnum == 36373 || refnum == 36374)
        break;
    default: break;
}

美丽是您可以在if语句中应用多个案例陈述(例如case 0case 1),其行为与or相同。

GCC扩展还允许您以这种方式编写交换机:

switch (refnum) {
     case 36371 ... 36374:
        //Do the job when refnum is >= 36371 && refnum <= 36374
        break;
}

但请记住!这并非在所有编译器中都可用。

答案 3 :(得分:1)

您似乎拥有非常严格的非标准编译器。

您可以为每个动作创建一个函数,然后生成一个refnum到函数指针的查找表并扫描它。例如:

int action1( unsigned long refnum ) ;
int action2( unsigned long refnum ) ;
int action3( unsigned long refnum ) ;
int action4( unsigned long refnum ) ;
int action5( unsigned long refnum ) ;
...
int action8( (unsigned long ) ;


int doRefnumAction( unsigned long refnum )
{
    typedef void(*refnumFn)(unsigned long ) ;

    static const struct 
    {
        unsigned long refnum,
        refnumFn refnum_action
    } refnum_lookup[] = { {32120, action1}, {32075, action1},
                          {51036, action2},
                          ...
                          {82012, action7}, {82024, action7}, {82048, action7}, {82096, , action7},
                          {83050, action8}, {83100, action8} } ;

    // Find refnum in lookup and call action...
    for( int i = 0 ; 
         i < sizeof(refnum_lookup) / sizeof(*refnum_lookup) ;
         i++ )
    {
        if( refnum == refnum_lookup[i].refnum )
        {
            return refnum_lookup[i].refnum_action( refnum ) ;
        }
    }
}

是否更好以任何方式if-else if解决方案可能是意见问题,代码空间术语可能更大,执行速度更慢但可以更容易维护。

答案 4 :(得分:0)

一旦我们了解了编译器和真实代码的详细信息,我们就可以找出您认为不能使用switch语句的原因。在那之前,每个人都在猜测。

多年来我使用了大量的C编译器,从未听说过用这种方式限制开关的编译器。这听起来像是20世纪90年代存在于8位微控制器中的伪C编译器 - 这不是你今天看到的。

如果您使用的是合适的编译器,并且启用了优化,则开关将是最快的方式。有人提到二进制搜索是一个更复杂但更快的解决方案 - 一个好的编译器会自动从switch语句生成跳转表或二进制搜索,以便为您提供最有效的代码。

答案 5 :(得分:-1)

您可以创建一个包含要检查的所有值的全局数组。并简单地循环这个数组。如果你可以创建一个struct数组并使用函数指针来设置不同的行为,那么如果你需要为每个其他人提供特定的行为。

以下是一个例子:

// For int variables
#include <stdint.h>
// For printf
#include <stdio.h>

#define VALUE_TO_GUESS 3
#define NB_VALUES 4
#define MY_VALUE_1 1
#define MY_VALUE_2 2
#define MY_VALUE_3 3
#define MY_VALUE_4 4

void behavior_1(void) {
  printf("Behavior 1 !\n");
}

void behavior_2(void) {
  printf("Behavior 2 !\n");
}

void behavior_3(void) {
  printf("Behavior 3 !\n");
}

void behavior_4(void) {
  printf("Behavior 4 !\n");
}

// Definition of the struct using a function pointer
typedef struct s_compare {
    uint32_t value_to_compare;
    void (*ptr)(void);
}t_compare;

// Setting up my struct
t_compare g_compare[] = {
    {MY_VALUE_1, behavior_1},
    {MY_VALUE_2, behavior_2},
    {MY_VALUE_3, behavior_3},
    {MY_VALUE_4, behavior_4}
};

int main(void) {

  for (int i = 0; i < NB_VALUES; i++) {
      /* If your current value match the value set in the current struct
      then call the function pointer associated, with these 2 lines i can 
      compare an infinite quantity of numbers  */
      if (g_compare[i].value_to_compare == VALUE_TO_GUESS) {
          g_compare[i].ptr();
      }
  }
}

如果您需要有关此问题的解释,请随时告诉我。 这需要一些设置,但它是一个功能强大的解决方案,比if / else / else if树更优雅的解决方案。