Why we can only constant expressions in c switch statements? 2. Why remaining cases executed when no Break used in switch?

huangapple go评论47阅读模式
英文:

Why we can only constant expressions in c switch statements? 2. Why remaining cases executed when no Break used in switch?

问题

在C中的Switch语句

  1. 为什么只能使用常量表达式
  2. 如果没有使用break,为什么会执行所有剩余的情况

" : "

英文:

In C Switch statements

  1. Why only constant expressions
  2. Why all remaining cases are executed after no break is used?

" : "

答案1

得分: 2

一个开关预计可以被翻译成通过跳转表进行范围检查的跳转。

对于类似于*的情况:

void sw(unsigned X){ 
    switch(X){ 
    case 0: case0(); 
    case 1: case1(); 
    case 2: case2(); 
    case 3: case3(); 
    case 4: case4(); 
    case 5: case5(); 
    } 
}

你可以期望生成类似于以下代码的代码(通过GNU labels-as-values C扩展表达的,它只是反映了基本的汇编能力):

void sw0(unsigned X){
    static const void *table[]={
        //存储标签地址
        &&case0,
        &&case1,
        &&case2,
        &&case3,
        &&case4,
        &&case5,
    };
    if(X<=5){
        goto *table[X];
        
        //具有标签的代码:
        case0: case0();
        case1: case1();
        case2: case2();
        case3: case3();
        case4: case4();
        case5: case5();
    }
}

(实际上,clang为这两个函数生成了完全相同的代码:https://gcc.godbolt.org/z/nfvz9sGxM)

  1. case标签需要是常量,因为它们将成为静态case表中的位置(可能经过一些调整)。无法基于动态位置定义静态表。

  2. 自动执行下一个语句/指令是自动的,因为这是C/汇编代码的特性(无论它是否具有标签):一个语句/指令在另一个之后执行,除非进行跳转。

你可能会注意到C标准将“case”称为case标签,这就是它们的本质。它们只是标签(即它们标记代码中的位置/地址),带有一些额外的限制/期望:它们只能在switch语句内部,并且它们的值(标签的地址)预计将存储在最近的封闭switch语句的跳转表中(如果确实将该特定switch编译为一个--还有其他选项)。


*示例摘自我的 Quora回答,解释了switch语句和case的C编译器是如何产生机器代码的,该回答还提到了除了直接跳转表之外,使用switch的其他代码生成选项。

英文:

A switch is expected to be translatable into a range-checked jump through a jump table.

For something like*:

void sw(unsigned X){ 
	switch(X){ 
	case 0: case0(); 
	case 1: case1(); 
	case 2: case2(); 
	case 3: case3(); 
	case 4: case4(); 
	case 5: case5(); 
	} 
}

You can expect codegen equivalent to (expressed via the GNU labels-as-values C extension¸ which just reflects a basic assembler capability):

void sw0(unsigned X){
	static const void *table[]={
        //store label addresses
		&amp;&amp;case0,
		&amp;&amp;case1,
		&amp;&amp;case2,
		&amp;&amp;case3,
		&amp;&amp;case4,
		&amp;&amp;case5,
	};
	if(X&lt;=5){
		goto *table[X];
        
        //the code with the labels:
		case0: case0();
		case1: case1();
		case2: case2();
		case3: case3();
		case4: case4();
		case5: case5();
	}
}

(Indeed, clang generates exactly the same code for the two functions: https://gcc.godbolt.org/z/nfvz9sGxM)

  1. The case labels need to be constants because they are to become positions in the static case table (possibly after some adjustments). It wouldn't be possible to define a static table based on dynamic positions.

  2. Fall-through is automatic because that's what you get with C/assembly code (whether it has labels or not): one statement/instruction is executed after another unless a jump is made.

You might notice that the C standard refers to "cases" as case labels and that's what they are. They're just labels (i.e., they mark a position/address in code) with some added restrictions/expectations:
they can only be inside switches and their value (the address of the label) is expected to be stored in the nearest enclosing switch's jump table (if that particular switch is indeed compiled into one--there are other options).


*Example taken from my Quora answer to How do C compilers produce machine code for switch statements and cases, which also mentions what other codegen you might get with switch's besides straightforward jumptables.

huangapple
  • 本文由 发表于 2023年2月10日 04:00:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/75403847.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定