【问题标题】:Is there any better implementation for this switch case construct?这个开关盒构造有更好的实现吗?
【发布时间】:2015-09-09 09:26:05
【问题描述】:

我正在开发一个嵌入式系统,其中必须访问一个寄存器,然后递增以实现我正在寻找的结果,因为机器正在通知并配置为对我的访问和更改做出反应改变国旗。所以switch的参数必须保持原样,否则会改变嵌入式系统的行为。

但可能会出现我不想让任何案例被调用的情况。但我仍然需要访问并增加 switch 的参数。

(更深入我正在逐步将一系列模拟值转换为数字值转换。索引用于与当前转换保持同步并将其与相应的案例相关联以正确处理数字。可能会发生索引与当前转换不同步的状态,因此必须运行转换序列,而不会调用任何案例(以防止设置错误数据),直到序列完成并且可以执行重新同步)

我目前这样做的方式是这样的:

switch (RunIndex++)/*RunIndex may only be accessed one time per execution
of this construct and has to be incremented in the same step. thats given.*/
{
   if (RunIndexSyncedWithADCensured == false)
   {
     break;
   }
   case 0:
     Case0RelatedData = SomeOperationsForCase0(RawData);
     break;
   case 1:
     Case1RelatedData = SomeOperationsForCase1(RawData);
     break;
   case 2:
     Case2RelatedData = SomeOperationsForCase2(RawData);
     break;
   default:
     RunIndex = 0;
     break;
}

这个结构可以完成这项工作,但它看起来有点争议,考虑将它提交到生产代码中我感觉不太好。

那么有没有更好看的方法来实现同样的效果,而不需要额外的变量或赋值?

注意:

这也可能是相关的,这是由两部分组成的中断函数的第一部分。 第一部分处理必须发生的事情if() 转换完成。第二部分,还有什么要做的if()这个转换也结束了序列。因此,在不进入第二部分的情况下简单地从函数返回是没有选择的。并且目前没有循环结构,if(...)break; 可能会爆发。 (这也是我将 if 放在 switch 范围内的原因,因为它至少按标准是一种有效的突破方式。)

【问题讨论】:

  • 为什么在switch 中包含if 块?
  • @venki 防止,正如我在 OP 中所说,如果存在无法确保同步的状态,则会调用这些案例。
  • 你知道if 块永远不会执行吗?
  • 在 Santosh 指点我之后我注意到了...我正在修复 sn-p,1 刻。
  • 不要与bool 常量比较!这很难读。而是直接使用值作为条件(在这里,你必须否定):if ( !RunIndexSyncedWithADCensured )

标签: c embedded


【解决方案1】:

首先,switch() 中的if() 永远不会被执行。
考虑下面的代码 sn-p:

#include <stdio.h>

int main(int argc, char *argv[])
{
    int i = 2;

    switch(i) {
        if (i == 2) {
            printf("I M HERE\n");
        }
        case 1:
            printf("1\n");
        break;
        case 2:
            printf("2\n");
        break;
        default:
            printf("default\n");
        break;
    }
    return 0;
}

对于您的代码:您希望打印字符串 I M HERE。但事实并非如此。
上述代码 sn -p 的输出为:

2

No statements before case/default(switch constructs): is executed inside switch

现在回答

我不想让任何案例被调用。但我仍然需要访问并增加开关的参数

只需将if() 移到 switch() 之外。

if (RunIndexSyncedWithADCensured) {
    switch (RunIndex++) {
        case 0:
            Case0RelatedData = SomeOperationsForCase0(RawData);
            break;
            /* Other cases here */
        default:
           RunIndex = 0;
           break;
    }
} else
    RunIndex++;

【讨论】:

    【解决方案2】:

    为什么不先保存值,然后增加它并在开关中使用保存的值?顺便说一句,这还包括两个访问,第一个是从 RunIndex 读取值,第二个是递增它。

    int runIndex = (RunIndex++);
    if (RunIndexSyncedWithADCensured )
    {
        switch (runIndex)/*RunIndex may only be accessed one time per execution
        of this construct and has to be incremented in the same step. thats given.*/
        {
            case 0:
                Case0RelatedData = SomeOperationsForCase0(RawData);
                break;
            case 1:
                Case1RelatedData = SomeOperationsForCase1(RawData);
                break;
            case 2:
               Case2RelatedData = SomeOperationsForCase2(RawData);
               break;
            default:
               RunIndex = 0;
               break;
        }
    }
    

    【讨论】:

    • 在第一个语句中,它首先读取RunIndex的值并将其存储在runIndex中,然后递增。所以你不需要-1。我在此处的任何代码 sn-ps 中都没有发现任何效率问题。
    【解决方案3】:

    由于您使用相邻的索引号,您可以创建一个函数指针数组来替换开关。这就是优化器无论如何都会把开关变成的东西。因此,您得到的不是晦涩的开关,而是:

    if (RunIndexSyncedWithADCensured)
    {
      SomeOperationsForCase[RunIndex](RawData);
    }
    
    RunIndex++;
    if (RunIndex > MAX)
    {
      RunIndex = 0;
    }
    

    与 switch 语句设计完全无关:如果RunIndex 是敏感的易失变量,例如某些硬件寄存器,那么您不应该直接在任何形式的计算中使用它。复制一份:

    volatile int RunIndex; 
    ...
    
    int index = RunIndex; // read from RunIndex
    
    if (RunIndexSyncedWithADCensured)
    {
      SomeOperationsForCase[index](RawData);
    }
    
    index++;
    if (index > MAX)
    {
      index  = 0;
    }
    
    RunIndex = index; // write to RunIndex
    

    这是所有此类易失性变量的标准做法。

    【讨论】:

    • 但是在这段代码中,您从 RunIndex 多次读取并使用它,不是吗?它看起来很可读,那很好。但我猜那些函数调用会降低性能而不是 switch case 变体,不是吗?
    • @Zaibis 此代码等同于您的原始代码,除了 if 语句修复。正如我所说,优化器可能会将您的开关变成类似上面的东西,尤其是在开关很大的情况下。
    • @Zaibis 像往常一样,当性能出现时,如果没有一个非常具体的系统来讨论它是没有用的。您应该编写可读的代码,句号。如果您确实考虑性能,则必须考虑整数比较所花费的时间与函数调用所花费的时间,然后您还必须考虑上面的代码改进了分支预测的程度。此外,编译器可能会以某种方式进行内联,尽管在函数指针方面通常不会。
    • 我绝对同意可读代码和内联/函数指针的东西。但我不同意代码是相同的。在您的 sn-p 中,如果没有同步,我看到 RunIndex 的值至少被访问/读取 2 次,同步时被访问/读取 3 次。这对我的系统来说是致命的,因为控制器将在读取 RunIndex 后立即开始下一次转换。因此,您的代码只会丢弃和取消同步大量转换。 (记住我说的是嵌入式系统,而不是操作系统应用程序)
    • @Zaibis 你应该明确表示它是一个 volatile 变量。您必须将其读入临时变量。无论如何,这完全是另一个问题:如何在嵌入式系统中正确使用硬件寄存器。我会更新一个例子。
    猜你喜欢
    • 2014-04-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-25
    相关资源
    最近更新 更多