C++基础与深度解析 | 语句 | 分支语句 | 循环语句 | 达夫设备

2024-05-15 06:44

本文主要是介绍C++基础与深度解析 | 语句 | 分支语句 | 循环语句 | 达夫设备,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

文章目录

  • 一、语句基础
  • 二、分支语句
    • 1.分支语句--if
    • 2.分支语句--switch
  • 三、循环语句
    • 1.循环语句--while
    • 2.循环语句--do-while
    • 3.循环语句--for
    • 4.循环语句--基于范围的for循环
    • 5.break / continue语句
    • 四、语句的综合应用--达夫设备

一、语句基础

语句的常见类别

  • 表达式语句:表达式后加分号,对表达式求值后丢弃,可能产生副作用
  • 空语句:仅包含一个分号的语句,可能与循环一起工作
  • 复合语句(语句体):由大括号组成,无需在结尾加分号(分号标识语句的结束),形成独立的域(语句域)。语句域可以更加精确地控制对象的生命周期,在复合语句结束后对象消亡。复合语句可以包含多条子语句。
  • if语句

顺序语句与非顺序语句

  • 顺序语句

    • 从语义上按照先后顺序执行

    • 实际的执行顺序可能产生变化(编译器优化、硬件乱序执行)

      编译器优化–在不改变语义的情况下,对执行顺序执行调整

    • 顺序语句执行时,通过编译器优化、硬件乱序执行,与硬件流水线紧密结合,执行效率较高

  • 非顺序语句

    • 非顺序语句可分为分支语句与循环语句
    • 在执行过程中引入跳转,从而产生复杂的变化
    • 分支预测错误可能导致执行性能降低

    最基本的非顺序语句: goto

    • 通过标签指定跳转到的位置
    • 具有若干限制
      • 不能跨函数跳转
      • 向前跳转时不能越过对象初始化语句
      • 向后跳转可能会导致对象销毁与重新初始化
    • goto 本质上对应了汇编语言中的跳转指令:如jne,但有如下缺陷
      • 缺乏结构性的含义
      • 容易造成逻辑混乱
      • 除特殊情况外,应避免使用
    #include <iostream>int main()
    {int x = 3;if(x) goto label;x = x + 1;label:return 0;
    }
    

    image-20240511005059360

二、分支语句

1.分支语句–if

  if语句的语法可参考if 语句。当需要在分支语句中引入复杂的逻辑,需要使用语句块表示复杂的分支逻辑。if语句基于条件的真假来执行不同的分支。

基本形式

if (condition) {// 当condition为真时执行的代码
}

if-else结构:二分支

if (condition) {// 当condition为真时执行的代码
} else {// 当condition为假时执行的代码
}

if-else if-else结构:三分支以及多分支

if (condition1) {// 当condition1为真时执行的代码
} else if (condition2) {// 当condition1不为真且condition2为真时执行的代码
} else {// 当所有条件都不为真时执行的代码
}

使用逻辑运算符

C++中的逻辑运算符包括:

  • &&(逻辑与)
  • ||(逻辑或)
  • !(逻辑非)
if (condition1 && condition2) {// 当condition1和condition2都为真时执行的代码
} else if (condition1 || condition2) {// 当condition1或condition2为真时执行的代码
} else {// 当condition1和condition2都不为真时执行的代码
}

示例

#include <iostream>int main() {int score = 75;//根据score的值,程序会输出对应的评价等级if (score >= 90) {std::cout << "优秀" << std::endl;} else if (score >= 80) {std::cout << "良好" << std::endl;} else if (score >= 70) {std::cout << "中等" << std::endl;} else if (score >= 60) {std::cout << "及格" << std::endl;} else {std::cout << "不及格" << std::endl;}return 0;
}

注意事项

  • 条件表达式必须用圆括号()括起来。

  • 条件表达式的结果必须是布尔值(truefalse)。

  • 虽然C++允许省略圆括号,但为了提高可读性,建议总是使用圆括号。

  • 实现多重分支–在分支语句中,再继续使用if-else分支语句

  • else 会与最近的 if 匹配(如果没有使用大括号的话)

    #include <iostream>int main()
    {int grade = 65;if (grade > 60)if (grade > 80)std::cout << "Excellent\n";elsestd::cout << "Bad\n";
    }//输出结果为Bad,但想要的是什么都不输出
    

    使用大括号改变匹配规则后,不能与大括号外面的else语句形成匹配

    #include <iostream>int main()
    {int grade = 65;if (grade > 60) {if (grade > 80)std::cout << "Excellent\n";}elsestd::cout << "Bad\n";
    }
    //不输出任何东西
    
  • 使用大括号改变匹配规则

if V.S. constexpr if–C++17引入constexpr if

  • constexpr if的条件是常量表达式
  • constexpr是在编译期确定的,因此,在编译期就可以确定执行哪个分支,屏蔽掉其他分支(为编译器优化引入更多的可能性)
#include <iostream>int main()
{constexpr int grade = 65;//在编译期就可以优化成如下// if constexpr (grade > 70) {//     if (grade > 80)//         std::cout << "Excellent\n";// }// elsestd::cout << "Bad\n";
}

带初始化语句的 if:(从C++17开始)

  从C++17开始可以将初始化语句放入括号里面。如果希望变量只是为了if分支语句而引入的,将可以使用带初始化的if语句。

#include <iostream>int main()
{int x = 3;if(int y = x +3; y > 100){} else {}int y = 4;
}

2.分支语句–switch

  switch语句的语法可参考switch语句。

switch语句的基本语法:

switch (expression) {case constant-expression1:// 当expression的值与constant-expression1相等时执行的代码break;case constant-expression2:// 当expression的值与constant-expression2相等时执行的代码break;// ...default:// 如果expression的值与所有case都不匹配时执行的代码break;
}
  • expression:可以是任意表达式,但表达式的值必须具有整型或枚举类型,或者可隐式转换到整型或枚举类型的类类型。可以包含初始化的语句(C++17起)

    switch ( 初始化语句 (可选) 条件 ) 语句		
    等价于
    {
    初始化语句
    switch ( 条件 ) 语句
    }
    
  • constant-expression:是与case标签相关联的常量表达式,它必须与switch表达式的类型兼容。

  • break:用于终止switch语句中当前case的执行,防止代码继续执行到下一个case。如果省略break,程序将执行当前case之后的所有case,直到遇到一个breakswitch语句结束,这称为“fall through”。

  • case/default 标签

    • case 后面跟常量表达式 , 用于匹配 switch 中的条件,匹配时执行后续的代码

    • 可以使用 break 跳出当前的 switch 执行

    • default 用于定义缺省情况下的逻辑

    • 在 case/default 中定义对象要加大括号

      在标签下不能直接定义对象,会有编译错误。在标签下定义对象默认对象的作用域是整个switch语句,如果该标签后面还有下一个标签,则在执行下一个标签时就会跳过对象的初始化。举例如下:

      #include <iostream>int main() {int x = 1;switch (x){case 1:int x = 0; // 初始化std::cout << x << '\n';break;default:// 编译错误:跳到 default: 会在尚未初始化 'x' 的情况下进入它的作用域std::cout << "default\n";break;}
      }
      

      定义对象要加大括号,将对象的生命周期由switch语句变为由大括号定义的域

      #include <iostream>int main() {int x = 1;switch (1){case 1:{int x = 0;std::cout << x << '\n';break;} // 'x' 的作用域在此结束default:std::cout << "default\n"; // 无错误break;}
      }
      
  • C++17标准引入了[[fallthrough]];属性,允许程序员明确指出两个case之间的意图是连续的

     #include <iostream>int main()
    {const int i = 2;switch (i){case 1:std::cout << "1";case 2:              // 从这个 case 标号开始执行std::cout << "2";case 3:std::cout << "3";[[fallthrough]];  C++17 属性,用以关闭对直落的警告case 5:std::cout << "45";break;           // 语句的顺序执行到此终止case 6:std::cout << "6";default:break;}
    }
    

switch语句的简单示例

#include <iostream>int main() {int day = 4;switch (day) {case 1:std::cout << "Monday" << std::endl;break;case 2:std::cout << "Tuesday" << std::endl;break;case 3:std::cout << "Wednesday" << std::endl;break;case 4:std::cout << "Thursday" << std::endl;break;case 5:std::cout << "Friday" << std::endl;break;default:std::cout << "Invalid day" << std::endl;}return 0;
}

switch与if比较

  • 分支描述能力较弱

  • 在一些情况下编译期能引入更好的优化(运行期节省时间)

    从数据结构算法来讲,if接近于线性,而switch可利用跳表或二分查找

三、循环语句

1.循环语句–while

  while语句的语法可查看while语句。while循环是一种基本的循环结构,它允许代码在给定的布尔条件为true时重复执行。

while循环的基本语法

while (condition) {// 循环体:只要条件为真,就执行这里的代码
}
  • condition是任何能转换为bool的表达式,或带花括号或等号初始化式的单个变量的声明。

    如果条件是T t = x 这样的声明,那么被声明的变量仅在循环体内处于作用域中,而且在每次重复中销毁并重新创建

    #include <iostream>int main()
    {// 带单语句的 while 循环int i = 0;while (i < 10)i++;std::cout << i << '\n';// 带复合语句的 while 循环int j = 2;while (j < 9){std::cout << j << ' ';j += 2;}std::cout << '\n';// 带声明条件的 while 循环char cstr[] = "Hello";int k = 0;while (char c = cstr[k++])std::cout << c;std::cout << '\n';
    }
    

    运行结果为:

    10
    2 4 6 8 
    Hello
    
  • 语句:任何语句,是循环体

  • 处理逻辑:

    1. 判断条件是否满足(是否为true),如果不满足则跳出循环
    2. 如果条件满足则执行循环体
    3. 执行完循环体后转向步骤 1
  • 注意:在 while 的条件部分不包含额外的初始化内容

下面是一个使用while循环的示例,该示例展示了如何使用while循环来计算从1到10的整数之和:

#include <iostream>int main() {int sum = 0;int i = 1;while (i <= 10) {sum += i;  // 将i加到sum上i++;       // 增加i的值}std::cout << "The sum of numbers from 1 to 10 is: " << sum << std::endl;return 0;
}

2.循环语句–do-while

  do-while循环语句的语法可查看do-while循环。C++中的do-while语句是一种后测试循环,这意味着循环体内的代码至少会执行一次,之后才会判断循环条件。如果条件为真,则再次执行循环体,这个过程会一直重复,直到条件为假。

do-while循环的基本语法如下

do {// 循环体:这段代码至少执行一次
} while (condition);
  • condition是任意能转换成bool的表达式。如果condition为true,则循环体再次执行。这个过程会一直重复,直到condition为false。

  • 处理逻辑

    1. 执行循环体
    2. 判断条件是否满足,如果不满足则跳出循环
    3. 如果条件满足则转向步骤 1
  • do-while循环常用于至少需要执行一次的场合,例如用户输入验证、至少一次的尝试等。

  • 注意结尾处要有分号,表示一条语句的结束

    do-while循环的一个特点是循环体后面的while部分有一个分号;,这与while循环不同,while循环的条件后面不应该有分号。

下面是一个使用do-while循环的示例,该示例展示了如何使用do-while循环来提示用户输入一个正数:

#include <iostream>int main() {int number;do {std::cout << "Please enter a positive number: ";std::cin >> number;if (number <= 0) {std::cout << "The number is not positive. Try again." << std::endl;}} while (number <= 0);  // 循环继续,直到输入的number大于0std::cout << "Thank you for entering a positive number: " << number << std::endl;return 0;
}

3.循环语句–for

  for循环语句的语法可查看for循环。C++中的for循环是一种基本的迭代结构,它允许代码在给定的条件下重复执行。for循环通常用于当你知道循环需要执行的确切次数时。

for循环的基本语法

for (initialization; condition; increment/decrement) {// 循环体:只要条件为真,就执行这里的代码
}
  • 初始化语句initialization:循环开始前的初始化步骤,通常用于声明和初始化循环控制变量。

  • 条件condition:在每次循环迭代开始前判断的布尔表达式。如果条件为真,则执行循环体。如果条件为假,则循环结束。

  • 迭代表达式increment/decrement:循环体执行完毕后执行的更新步骤,通常用于更新循环控制变量。这一步可以是增加(如i++)或减少(如i--)循环变量。

  • 处理逻辑

    1. 初始化语句会被首先执行(初始化语句声明与定义的变量的作用域是在for循环中)
    2. 条件部分会被执行,执行结果如果为 false ,则终止循环
    3. 否则执行循环体
    4. 循环体执行完后会对迭代表达式进行求值,之后转向 2
  • 在初始化语句中声明多个名字,只要它们可以使用相同的声明说明符序列(相同的基础类型)

    在变量生命与定义时,语法上允许声明多个变量,但是不建议这么做,因为容易引起歧义。如:int* p, q;,p是int指针,q是int型。

    #include <iostream>int main()
    {//初始化语句可以声明多个名字,只要它们可以使用相同的声明说明符序列for (int i = 0, *p = &i; i < 9; i += 2){std::cout << i << ':' << *p << ' ';}
    }
    
  • 初始化语句、条件、迭代表达式可以为空,可以省略初始化、条件或更新步骤中的任何一个

    #include <iostream>int main()
    {for (; i < 10; ) {// 循环体i++;  // 循环控制变量的更新在循环体内部进行}for ( ; ; );
    }
    

下面是一个使用for循环的示例,该示例展示了如何使用for循环来计算从1到10的整数之和:

#include <iostream>int main() {int sum = 0;for (int i = 1; i <= 10; i++) {sum += i;  // 将i加到sum上}std::cout << "The sum of numbers from 1 to 10 is: " << sum << std::endl;return 0;
}

for更多示例

#include <iostream>
#include <vector>int main()
{std::cout << "1) 典型的以单语句作为循环体的循环:\n";for (int i = 0; i < 10; ++i)std::cout << i << ' ';std::cout << "\n\n" "2) 初始化语句可以声明多个名字,\n""只要它们可以使用相同的声明说明符序列:\n";for (int i = 0, *p = &i; i < 9; i += 2)std::cout << i << ':' << *p << ' ';std::cout << "\n\n" "3) (循环)条件可以是声明:\n";char cstr[] = "Hello";for (int n = 0; char c = cstr[n]; ++n)std::cout << c;std::cout << "\n\n" "4) 初始化语句可以使用 auto 类型说明符:\n";std::vector<int> v = {3, 1, 4, 1, 5, 9};for (auto iter = v.begin(); iter != v.end(); ++iter)std::cout << *iter << ' ';//方便理解for循环的执行逻辑std::cout << "\n\n" "5) 初始化语句可以是表达式:\n";int n = 0;for (std::cout << "循环开始\n";std::cout << "循环测试\n";		//<<运算符的结果为std::cout,隐式转换为truestd::cout << "迭代 " << ++n << '\n'){if (n > 1)break;}std::cout << "\n" "6) 每次迭代时均会调用循环体中创建的对象的构造函数和析构函数:\n";struct S{S(int x, int y) { std::cout << "S::S(" << x << ", " << y << "); "; }~S() { std::cout << "S::~S()\n"; }};for (int i{0}, j{5}; i < j; ++i, --j)S s{i, j};std::cout << "\n" "7) 初始化语句可以使用结构化绑定:\n";long arr[]{1, 3, 7};for (auto [i, j, k] = arr; i + j < k; ++i)std::cout << i + j << ' ';std::cout << '\n';
}

运行结果:

1) 典型的以单语句作为循环体的循环:
0 1 2 3 4 5 6 7 8 9 2) 初始化语句可以声明多个名字,
只要它们可以使用相同的声明说明符序列:
0:0 2:2 4:4 6:6 8:8 3) (循环)条件可以是声明:
Hello4) 初始化语句可以使用 auto 类型说明符:
3 1 4 1 5 9 5) 初始化语句可以是表达式:
循环开始
循环测试
迭代 1
循环测试
迭代 2
循环测试6) 每次迭代时均会调用循环体中创建的对象的构造函数和析构函数:
S::S(0, 5); S::~S()
S::S(1, 4); S::~S()
S::S(2, 3); S::~S()7) 初始化语句可以使用结构化绑定:
4 5 6

4.循环语句–基于范围的for循环

  基于范围的for循环的语法可查看基于范围的for循环。for循环的一个变体是范围基for循环(C++11及以后版本引入),它允许你直接迭代容器(如数组、向量、字符串等)中的元素,而无需显式管理索引变量。

范围基for循环的语法

for (const auto& element : container) {// 循环体:对container中的每个元素执行操作
}
  • container可以是任何支持迭代的容器

  • element是容器中当前迭代到的元素

  • 本质:语法糖,编译器会自动转换为 for 循环的调用方式

    image-20240512182551654

  • 转换形式的衍化: C++11 / C++17 / C++20(编译器对范围表达式的推导)

    image-20240512183521741

  • 使用常量左值引用读元素;使用 万能引用修改元素

    常量左值引用读元素

    #include <iostream>
    #include <vector>int main()
    {std::vector<std::string> strs{"h","e","l","l","o"};for (const auto& str : strs) //不加const为非常量左值引用,可以修改元素,但并不安全std::cout << str << "\n";
    }
    

    万能引用修改元素

    #include <iostream>
    #include <vector>int main()
    {std::vector<std::string> strs{"h","e","l","l","o"};//万能引用修改元素for (auto&& str : strs)str += "-";for (const auto& str : strs)std::cout << str << "\n";
    }
    

5.break / continue语句

  在C++中,breakcontinue是控制循环流程的两个关键字,它们在循环结构(如forwhiledo-while循环)中起到关键作用。

break

  break关键字用于立即终止其所在的循环体。当break被执行时,它会跳出最内层的循环,并继续执行循环后面的代码。这适用于任何类型的循环结构。

for (int i = 0; i < 10; ++i) {if (i == 5) {break; // 当i等于5时,立即退出循环}std::cout << i << " ";
}
// 输出: 0 1 2 3 4

continue

  continue关键字则用于跳过当前循环的剩余部分,并立即开始下一次循环迭代。当continue被执行时,它会跳过循环体中剩余的代码,并根据循环条件继续执行下一次迭代。

for (int i = 0; i < 10; ++i) {if (i % 2 == 0) {continue; // 跳过偶数,只打印奇数}std::cout << i << " ";
}
// 输出: 1 3 5 7 9
  • breakcontinue都只影响最内层的循环。如果你在嵌套循环中使用它们,它们只控制最内层的循环结构。
  • 在使用breakcontinue时,应当小心谨慎,因为过度使用或不当使用可能会使代码逻辑变得难以理解和维护。
  • 在使用switch语句时,break也用于终止switch中的一个case,防止代码继续执行到下一个case(除非下一个case被明确地指定)。

四、语句的综合应用–达夫设备

  达夫设备(Duff’s Device)是一种在C/C++编程中提高循环效率的技巧,它利用了C语言中switch语句的“fal through”特性来实现循环展开,从而减少循环控制的开销。这种方法最早由Tom Duff在1983年提出,目的是为了优化循环,尤其是在循环体内执行的操作非常快速时,循环的测试条件会占用相当一部分时间。

  达夫设备的核心思想是减少循环迭代次数,通过预先计算循环的迭代次数并将循环体的一部分操作放入switch语句的case分支中,从而减少循环控制的开销。当循环次数不能被展开的固定次数整除时,达夫设备使用switch来处理剩余的迭代次数。

  • 达夫设备使用循环展开提升系统性能
  • 处理无法整除的情形
    • 额外增加一个循环语句
    • 将 switch 与循环结合

以下是达夫设备的一个典型示例,该示例展示了如何将数据从from数组复制到to数组:

register short *to, *from;
register count; //count不能整除
{register n = (count + 7) / 8;  /* 假设count > 0 */switch (count % 8) {case 0: do { *to = *from++;case 7:      *to = *from++;case 6:      *to = *from++;case 5:      *to = *from++;case 4:      *to = *from++;case 3:      *to = *from++;case 2:      *to = *from++;case 1:      *to = *from++;} while (--n > 0);}
}

在这个例子中,count表示需要复制的元素数量。循环被展开为8次迭代,因为这样可以减少循环控制的开销。switch语句根据count除以8的余数选择从哪个case开始执行,而case分支中的代码会“跌落”到下一个case,直到遇到do-while循环的结束条件。
  达夫设备虽然可以提高效率,但它牺牲了代码的可读性和可维护性。现代编译器通常能够自动进行循环展开优化,因此达夫设备在现代编程中较少使用。然而,了解这种技术对于理解编译器优化和程序性能优化仍然有其价值。

这篇关于C++基础与深度解析 | 语句 | 分支语句 | 循环语句 | 达夫设备的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/991094

相关文章

使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)

《使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)》在现代软件开发中,处理JSON数据是一项非常常见的任务,无论是从API接口获取数据,还是将数据存储为JSON格式,解析... 目录1. 背景介绍1.1 jsON简介1.2 实际案例2. 准备工作2.1 环境搭建2.1.1 添加

深入理解C++ 空类大小

《深入理解C++空类大小》本文主要介绍了C++空类大小,规定空类大小为1字节,主要是为了保证对象的唯一性和可区分性,满足数组元素地址连续的要求,下面就来了解一下... 目录1. 保证对象的唯一性和可区分性2. 满足数组元素地址连续的要求3. 与C++的对象模型和内存管理机制相适配查看类对象内存在C++中,规

Node.js 中 http 模块的深度剖析与实战应用小结

《Node.js中http模块的深度剖析与实战应用小结》本文详细介绍了Node.js中的http模块,从创建HTTP服务器、处理请求与响应,到获取请求参数,每个环节都通过代码示例进行解析,旨在帮... 目录Node.js 中 http 模块的深度剖析与实战应用一、引言二、创建 HTTP 服务器:基石搭建(一

在C#中合并和解析相对路径方式

《在C#中合并和解析相对路径方式》Path类提供了几个用于操作文件路径的静态方法,其中包括Combine方法和GetFullPath方法,Combine方法将两个路径合并在一起,但不会解析包含相对元素... 目录C#合并和解析相对路径System.IO.Path类幸运的是总结C#合并和解析相对路径对于 C

JAVA中while循环的使用与注意事项

《JAVA中while循环的使用与注意事项》:本文主要介绍while循环在编程中的应用,包括其基本结构、语句示例、适用场景以及注意事项,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录while循环1. 什么是while循环2. while循环的语句3.while循环的适用场景以及优势4. 注意

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

Java解析JSON的六种方案

《Java解析JSON的六种方案》这篇文章介绍了6种JSON解析方案,包括Jackson、Gson、FastJSON、JsonPath、、手动解析,分别阐述了它们的功能特点、代码示例、高级功能、优缺点... 目录前言1. 使用 Jackson:业界标配功能特点代码示例高级功能优缺点2. 使用 Gson:轻量

Java如何接收并解析HL7协议数据

《Java如何接收并解析HL7协议数据》文章主要介绍了HL7协议及其在医疗行业中的应用,详细描述了如何配置环境、接收和解析数据,以及与前端进行交互的实现方法,文章还分享了使用7Edit工具进行调试的经... 目录一、前言二、正文1、环境配置2、数据接收:HL7Monitor3、数据解析:HL7Busines

python解析HTML并提取span标签中的文本

《python解析HTML并提取span标签中的文本》在网页开发和数据抓取过程中,我们经常需要从HTML页面中提取信息,尤其是span元素中的文本,span标签是一个行内元素,通常用于包装一小段文本或... 目录一、安装相关依赖二、html 页面结构三、使用 BeautifulSoup javascript

Python中的异步:async 和 await以及操作中的事件循环、回调和异常

《Python中的异步:async和await以及操作中的事件循环、回调和异常》在现代编程中,异步操作在处理I/O密集型任务时,可以显著提高程序的性能和响应速度,Python提供了asyn... 目录引言什么是异步操作?python 中的异步编程基础async 和 await 关键字asyncio 模块理论