C|switch整型表达式与跳转表,直落以及作用域
lipiwang 2024-11-15 22:02 24 浏览 0 评论
Although it is possible to chain many if-else statements together, this is both difficult to read and inefficient. Consider the following program:
虽然可以将许多if-else语句链接在一起,但这既难以阅读,又效率低下。考虑以下程序:
#include <iostream>
void printDigitName(int x)
{
if (x == 1)
std::cout << "One";
else if (x == 2)
std::cout << "Two";
else if (x == 3)
std::cout << "Three";
else
std::cout << "Unknown";
}
int main()
{
printDigitName(2);
return 0;
}
While this example isn’t too complex, x is evaluated up to three times (which is inefficient), and the reader has to be sure that it is x being evaluated each time (not some other variable).
虽然这个例子并不太复杂,但x最多要计算三次(这是低效的),读者必须确保每次都是x(而不是其他变量)。
Because testing a variable or expression for equality against a set of different values is common, C++ provides an alternative conditional statement called a switch statement that is specialized for this purpose. Here is the same program as above using a switch:
因为根据一组不同的值测试变量或表达式的相等性是常见的,所以C++提供了一个称为switch语句的替代条件语句,该语句专门用于此目的。下面是使用开关的与上面相同的程序:
#include <iostream>
void printDigitName(int x)
{
switch (x)
{
case 1:
std::cout << "One";
return;
case 2:
std::cout << "Two";
return;
case 3:
std::cout << "Three";
return;
default:
std::cout << "Unknown";
return;
}
}
int main()
{
printDigitName(2);
return 0;
}
The first kind of label is the case label, which is declared using the case keyword and followed by a constant expression. The constant expression must either match the type of the condition or must be convertible to that type.
第一种标签是case标签,它是使用case关键字声明的,后跟一个常量表达式。常量表达式必须与条件的类型匹配,或者必须可转换为该类型。
The second kind of label is the default label (often called the default case), which is declared using the default keyword. If the conditional expression does not match any case label and a default label exists, execution begins at the first statement after the default label.
第二种标签是默认标签(通常称为默认大小写),它是使用default关键字声明的。如果条件表达式不匹配任何大小写标签,并且存在默认标签,则从默认标签后的第一条语句开始执行。
The idea behind a switch statement is simple: an expression (sometimes called the condition) is evaluated to produce a value. If the expression’s value is equal to the value after any of the case labels, the statements after the matching case label are executed. If no matching value can be found and a default label exists, the statements after the default label are executed instead.
switch语句背后的思想很简单:对表达式(有时称为条件)求值以生成值。如果表达式的值等于任何case标签后的值,则执行匹配case标签后的语句。如果找不到匹配值且存在默认标签,则执行默认标签后的语句。
Compared to the original if statement, the switch statement has the advantage of only evaluating the expression once (making it more efficient, a jump table is generated during compilation time, which can realize direct jump), and the switch statement also makes it clearer to the reader that it is the same expression being tested for equality in each case.
与原来的if语句相比,switch语句的优点是只对表达式求值一次(使其更有效,在编译期生成了一份跳转表,可以实现直接跳转),switch语句还使读者更清楚地知道,在每种情况下,它都是相同的表达式。
case常使用枚举值来枚举case:
void keyBoardAscii()
{
enum keys {HOME=151, UP, PGUP, LEFT=155,
RIGHT=157, END=159, DOWN, PGDN}; //定义枚举类型
int ch;
printf("\n敲击一个键,显示字母和ASCII码(ESC退出):\n");
do{
ch=getch();
if( ch == 0xe0 ) // 11100000 224
{
ch=getch();
ch+=80;
}
switch(ch)
{
case UP: // 改善了可读性,意义非凡
printf("Up %d\n\n",ch);
break;
case DOWN:
printf("Down %d\n\n",ch);
break;
case LEFT:
printf("Left %d\n\n",ch);
break;
case RIGHT:
printf("Right %d\n\n",ch);
break;
case HOME:
printf("Home %d\n\n",ch);
break;
case END:
printf("End %d\n\n",ch);
break;
case PGUP:
printf("PageUp %d\n\n",ch);
break;
case PGDN:
printf("PageDown %d\n\n",ch);
break;
case 8: // 按删除键清屏
case 163:
system("cls");
break;
default:
printf(" %c %d\n",ch,ch);
}
}while (ch != 27 ); //ESC 退出
}
1 Why does the switch type only allow for integral (or enumerated) types?
为什么开关类型只允许整数(或枚举)类型?
The answer is because switch statements are designed to be highly optimized. Historically, the most common way for compilers to implement switch statements is via Jump tables -- and jump tables only work with integral values.
答案是因为switch语句是经过高度优化设计的。从历史上看,编译器实现switch语句最常用的方法是通过跳转表,而跳转表只对整数值起作用。
For those of you already familiar with arrays, a jump table works much like an array, an integral value is used as the array index to “jump” directly to a result. This can be much more efficient than doing a bunch of sequential comparisons.
对于那些已经熟悉数组的人来说,跳转表的工作方式与数组非常相似,使用整数值作为数组索引直接“跳转”到结果。这比进行一系列连续比较要有效得多。
Of course, compilers don’t have to implement switches using jump tables, and sometimes they don’t. There is technically no reason that C++ couldn’t relax the restriction so that other types could be used as well, they just haven’t done so yet (as of C++20).
当然,编译器不必使用跳转表实现开关,有时也不必。从技术上讲,C++没有理由不放松限制,以便也可以使用其他类型,只是他们还没有这样做(截至C++20)。
2 Switch fallthrough(直落)
In the above examples, we used return statements to stop execution of the statements after our labels. However, this also exits the entire function.
在上面的示例中,我们使用return语句来停止标签后语句的执行。但是,这也会退出整个函数。
A break statement (declared using the break keyword) tells the compiler that we are done executing statements within the switch, and that execution should continue with the statement after the end of the switch block. This allows us to exit a switch statement without exiting the entire function.
break语句(使用break关键字声明)告诉编译器,我们已经完成了在switch内执行语句的工作,并且应该在switch块结束后继续执行该语句。这允许我们在不退出整个函数的情况下退出switch语句。
When a switch expression matches a case label or optional default label, execution begins at the first statement following the matching label. Execution will then continue sequentially until one of the following termination conditions happens:
当switch表达式与case标签或可选default标签匹配时,将从匹配标签后的第一条语句开始执行。然后继续执行,直到出现以下终止条件之一:
① The end of the switch block is reached.
到达switch块的末端。
② Another control flow statement (typically a break or return) causes the switch block or function to exit.
另一个控制流语句(通常为break或continue)导致switch块或函数退出。
③ Something else interrupts the normal flow of the program (e.g. the OS shuts the program down, the universe implodes, etc…)
其他一些东西会中断程序的正常运行(例如,操作系统关闭程序,宇宙内爆等等)
Note that the presence of another case label is not one of these terminating conditions -- thus, without a break or return, execution will overflow into subsequent cases.
请注意,另一个case标签的存在并不是这些终止条件之一,因此,如果没有中断或返回,执行将溢出到后续的case中。
This is probably not what we wanted! When execution flows from a statement underneath a label into statements underneath a subsequent label, this is called fallthrough.
这可能不是我们想要的!当执行从标签下的语句流到后续标签下的语句时,这称为fallthrough。(直落的语法机制符合C的效率优先的原则,只管跳转,不管跳出。)
Once the statements underneath a case or default label have started executing, they will overflow (fallthrough) into subsequent cases. Break or return statements are typically used to prevent this.
一旦case或default标签下面的语句开始执行,它们将溢出(起落)到后续的case中。Break或return语句通常用于防止出现这种情况。
bool IsLeapYear(int y) // 判断是否为闰年
{
return((y%4==0&&y%100!=0)||y%400==0);
}
int DaysOfMonth(int m) // 判断某月天数的函数
{
switch(m){
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:return 31;
case 4:
case 6:
case 9:
case 11:return 30;
case 2:if(IsLeapYear(year))
return 29;
else
return 28;
}
return 0;
}
switch常用于菜单选择:
int Menu()
{
#define CHCS 6
printf("\n ╔┉┉┉操作菜单(输入前面数字即运行对应功能┉┉╗\n");
printf(" ┋\t1___显示ASCII码 ┋\n");
printf(" ┋\t2___敲击一个键,显示字母和ASCII码 ┋\n");
printf(" ┋\t3___查询汉字GB2312 ┋\n");
printf(" ┋\t4___查询汉字Unicode ┋\n");
printf(" ┋\t5___输出GB2312(7445(6763+字母和符号682)┋\n");
printf(" ┋\t6___输出GBK字符(21886(包括繁体)) ┋\n");
printf(" ┋\t0___退出系统 ┋\n");
printf(" ╚┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉┉╝\n");
printf(" 请输入菜单序号(0~%d):",CHCS);
int sn;
for(;;)
{
scanf(" %d",&sn);
if(sn<0||sn>CHCS)
printf("\n\t输入错误,重选0-%d",CHCS);
else
break;
}
return sn;
}
int main()
{
int choice;
while(1){
choice = Menu();
system("cls");
switch(choice)
{
case 0:
exit(1);
break;
case 1:
printAscii();
break;
case 2:
keyBoardAscii();
break;
case 3:
cngb();
break;
case 4:
getUnicode();
break;
case 5:
printGB2312();
break;
default:
break;
}
}
return 0;
}
3 Switch case scoping(作用域)
With if statements, you can only have a single statement after the if-condition, and that statement is considered to be implicitly inside a block:
对于if语句,在if条件之后只能有一条语句,并且该语句被视为隐式位于块内:
if (x > 10)
std::cout << x << " is greater than 10\n"; // this line implicitly considered to be inside a block
However, with switch statements, the statements after labels are all scoped to the the switch block. No implicit blocks are created.
然而,对于switch语句,标签后的语句都限定在switch块的范围内。不会创建隐式块。
switch (1)
{
case 1: // does not create an implicit block
foo(); // this is part of the switch scope, not an implicit block to case 1
break; // this is part of the switch scope, not an implicit block to case 1
default:
std::cout << "default case\n";
break;
}
In the above example, the 2 statements between the case 1 and the default label are scoped as part of the switch block, not a block implicit to case 1.
在上面的示例中,case1和default标签之间的2条语句的作用域是switch块的一部分,而不是case1所隐含的块。
You can declare (but not initialize) variables inside the switch, both before and after the case labels:
您可以在case标签之前和之后在开关内声明(但不初始化)变量:
#include <stdio.h>
main()
{
switch (1)
{ // start a block, a scope, one or some case may be skip
int a; // okay: declaration is allowed before the case labels
//int b=5; // illegal: initialization is not allowed before the case labels
// because it be executed at runtime
case 1:
int y; // okay but bad practice: declaration is allowed within a case
y = 4; // okay: assignment is allowed
break;
case 2:
//int m = 4; // illegal: initialization is not allowed if subsequent cases exist
y = 5; // okay: y was declared above, so we can use it here too
break;
case 3:
{ // start a nested block, a nested scope
int z=4; // okay: initialization at a nested block
break;
}
case 4:
int i=4; // okay: initialization is allowed at final case
break;
}
getchar();
}
Although variable y was defined in case 1, it was used in case 2 as well. Because the statements under each case are not inside an implicit block, that means all statements inside the switch are part of the same scope. Thus, a variable defined in one case can be used in a later case, even if the case in which the variable is defined is never executed!
虽然变量y在case1中定义,但在case2中也使用了它。因为每种情况下的语句都不在隐式块内,这意味着switch内的所有语句都属于同一scope。因此,在一种情况下定义的变量可以在以后的情况下使用,即使定义变量的情况从未执行!
Put another way, defining a variable without an initializer is just telling the compiler that the variable is now in scope from that point on. This happens at compile time, and doesn’t require the definition to actually be executed at runtime.
换句话说,定义一个没有初始值设定项的变量只是告诉编译器,从那时起,该变量现在就在scope内。这发生在编译时,不需要在运行时实际执行定义。
However, initialization of variables does require execution at runtime. Initialization of variables is disallowed in any case that is not the last case (because the initializer could be jumped over, which would leave the variable uninitialized). Initialization is also disallowed before the first case, as those statements will never be executed, as there is no way for the switch to reach them.
然而,变量的初始化确实需要在运行时执行。在任何不是最后一个case的情况下,都不允许初始化变量(因为可以跳过初始值设定项,这会使变量未初始化)。在第一个case之前,也不允许初始化,因为这些语句永远不会执行,因为switch无法访问它们。
If a case needs to define and/or initialize a new variable, best practice is to use an explicit block underneath the case statement.
如果一个case需要定义和/或初始化一个新变量,最佳做法是在case语句下面使用显式的语句块。
ref:
https://www.learncpp.com/cpp-tutorial/switch-statement-basics/
https://www.learncpp.com/cpp-tutorial/switch-fallthrough-and-scoping/
-End-
相关推荐
- 前端入门——css 网格轨道详细介绍
-
上篇前端入门——cssGrid网格基础知识整体大概介绍了cssgrid的基本概念及使用方法,本文将介绍创建网格容器时会发生什么?以及在网格容器上使用行、列属性如何定位元素。在本文中,将介绍:...
- Islands Architecture(孤岛架构)在携程新版首页的实践
-
一、项目背景2022,携程PC版首页终于迎来了首次改版,完成了用户体验与技术栈的全面升级。作为与用户连接的重要入口,旧版PC首页已经陪伴携程走过了22年,承担着重要使命的同时,也遇到了很多问题:维护/...
- HTML中script标签中的那些属性
-
HTML中的<script>标签详解在HTML中,<script>标签用于包含或引用JavaScript代码,是前端开发中不可或缺的一部分。通过合理使用<scrip...
- CSS 中各种居中你真的玩明白了么
-
页面布局中最常见的需求就是元素或者文字居中了,但是根据场景的不同,居中也有简单到复杂各种不同的实现方式,本篇就带大家一起了解下,各种场景下,该如何使用CSS实现居中前言页面布局中最常见的需求就是元...
- CSS样式更改——列表、表格和轮廓
-
上篇文章主要介绍了CSS样式更改篇中的字体设置Font&边框Border设置,这篇文章分享列表、表格和轮廓,一起来看看吧。1.列表List1).列表的类型<ulstyle='list-...
- 一文吃透 CSS Flex 布局
-
原文链接:一文吃透CSSFlex布局教学游戏这里有两个小游戏,可用来练习flex布局。塔防游戏送小青蛙回家Flexbox概述Flexbox布局也叫Flex布局,弹性盒子布局。它决定了...
- css实现多行文本的展开收起
-
背景在我们写需求时可能会遇到类似于这样的多行文本展开与收起的场景:那么,如何通过纯css实现这样的效果呢?实现的难点(1)位于多行文本右下角的展开收起按钮。(2)展开和收起两种状态的切换。(3)文本...
- css 垂直居中的几种实现方式
-
前言设计是带有主观色彩的,同样网页设计中的css一样让人摸不头脑。网上列举的实现方式一大把,或许在这里你都看到过,但既然来到这里我希望这篇能让你看有所收获,毕竟这也是前端面试的基础。实现方式备注:...
- WordPress固定链接设置
-
WordPress设置里的最后一项就是固定链接设置,固定链接设置是决定WordPress文章及静态页面URL的重要步骤,从站点的SEO角度来讲也是。固定链接设置决定网站URL,当页面数少的时候,可以一...
- 面试发愁!吃透 20 道 CSS 核心题,大厂 Offer 轻松拿
-
前端小伙伴们,是不是一想到面试里的CSS布局题就发愁?写代码时布局总是对不齐,面试官追问兼容性就卡壳,想跳槽却总被“多列等高”“响应式布局”这些问题难住——别担心!从今天起,咱们每天拆解一...
- 3种CSS清除浮动的方法
-
今天这篇文章给大家介绍3种CSS清除浮动的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。首先,这里就不讲为什么我们要清楚浮动,反正不清除浮动事多多。下面我就讲3种常用清除浮动的...
- 2025 年 CSS 终于要支持强大的自定义函数了?
-
大家好,很高兴又见面了,我是"高级前端进阶",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!1.什么是CSS自定义属性CSS自...
- css3属性(transform)的一个css3动画小应用
-
闲言碎语不多讲,咱们说说css3的transform属性:先上效果:效果说明:当鼠标移到a标签的时候,从右上角滑出二维码。实现方法:HTML代码如下:需要说明的一点是,a链接的跳转需要用javasc...
- CSS基础知识(七)CSS背景
-
一、CSS背景属性1.背景颜色(background-color)属性值:transparent(透明的)或color(颜色)2.背景图片(background-image)属性值:none(没有)...
- CSS 水平居中方式二
-
<divid="parent"><!--定义子级元素--><divid="child">居中布局</div>...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- maven镜像 (69)
- undefined reference to (60)
- zip格式 (63)
- oracle over (62)
- date_format函数用法 (67)
- 在线代理服务器 (60)
- shell 字符串比较 (74)
- x509证书 (61)
- localhost (65)
- java.awt.headless (66)
- syn_sent (64)
- settings.xml (59)
- 弹出窗口 (56)
- applicationcontextaware (72)
- my.cnf (73)
- httpsession (62)
- pkcs7 (62)
- session cookie (63)
- java 生成uuid (58)
- could not initialize class (58)
- beanpropertyrowmapper (58)
- word空格下划线不显示 (73)
- jar文件 (60)
- jsp内置对象 (58)
- makefile编写规则 (58)