C基础知识补漏与巩固(二):控制流

本系列的目的

一方面,为考研的理论知识做准备,博主决定重新过一遍语法知识;另一方面,在多技术栈各种知识的学习过程中,深刻认识到C/C++基础的重要性,于是决定撰写本系列。
本系列特征:

  • 相对冷门知识
  • 相对基础知识
  • 编程中容易犯的错

本系列基于:

  • 海贼宝藏(https://www.haizeix.com/)课程
  • 《C陷阱与缺陷》(C Traps and pitfalls)
  • 个人OJ刷题总结

条件表达式

条件表达式的1和0不仅代表布尔值,也是可以用于运算的真实值

(小技巧:a += (a == b)可代替分支)

    a += (a == b);//如果a == b,a += 1,否则 a += 0
    printf("%d\n", a); 
非0即为真值

用1表达真,用0表达假,但实际上程序中的仍何值只要非0都会被判定为真,包括负数等。

短路原则

对于逻辑表达式,若前面的表达式可确定整体的值,则不再计算后面的表达式
技巧:代替分支

    a < b && printf("Yes\n");
    !(a < b) && printf("No\n");

分支结构

初学者容易犯的错误

(表达式)不可连续判断
if (0 < x <= 60)
	cout << "yes!" << endl;

当x = 50, 程序实际的执行是0 < x先判定,即得到1值,在1 < 60 判断。

switch-case中,进入case后将一直执行直至break或结束

所以有:

swtich(a){
	case 1:
	case 2:cout << "yes" << endl;
	case 3:cout << "No" << endl;
}

无break时,例如从1进入,将依次执行2、3中的yes和no输出(如果有default也会执行)

当然,我们也可以利用这种特性:

swtich(a){
	case 1:
	case 2:cout << "yes" << endl;break;
	case 3:
	case 4:cout << "No" << endl;break;
}

如果我们此时的需求是1和2时有相同的输出,3和4时有相同的输出,那么这种特性利用可以使程序清晰而方便。

cpu的分支预测

当cpu以流水线形式执行时,分支语句可能会导致这样的问题:下一条语句的执行需要读取上一条语句的结果值,但此时往往上一条语句还未运行完成,于是cpu预测由此诞生。
Linux有宏如下:

#define likely(x) _builtin_expect(!!(x), 1)
#define unlikely(x) _builtin_expect(!!(x), 0)
//likely表示x经常成立
//unlikely表示宏不经常成立
  • 两次!的意义:逻辑值的归1化(即将真值都归1)
  • 该宏即协助cpu做分支预测

应用实例如下:(选择leetcode-9:回文数)

class Solution {
public:
    bool isPalindrome(int x) {
  //  if (__builtin_expect(!!(x < 0), 0)) return false;
        if (x < 0) return false;
        long long y = x;
        long long z = 0;
        while (x){
            z = z * 10 + x % 10;
            x /= 10;
        }
    return z == y;
    }
};

在这里插入图片描述

class Solution {
public:
    bool isPalindrome(int x) {
    if (__builtin_expect(!!(x < 0), 0)) return false;
        // if (x < 0) return false;
        long long y = x;
        long long z = 0;
        while (x){
            z = z * 10 + x % 10;
            x /= 10;
        }
    return z == y;
    }
};

在这里插入图片描述

此处借用leetcode 的ai复杂度分析,优化前为log10(N),优化后为log2(N)
(由于优化有限,有时候该题跑出来的实际速度可能相差不大乃至相同,但是根据复杂度分析是能看出确实变快了)

循环结构

技巧:不断读入用户输入
    int n;
    while (scanf("%d", &n) != EOF){
        printf("2 * x = %d\n", 2 * n);
    } 

EOF:

  • linux/MacOs:Ctrl/Control + D
  • Windows:Ctrl + Z
  • 循环的充分利用
    for (int i = 0, f1 = 1, f2 = 1; i < 20; i++, f2 += f1, f1 = f2 - f1){
        printf("%d\n", f1);
    }

并不提倡这种做法,但可以据此充分理解for的语法
但不允许这样初始化:
for (int i = 0, int f1 = 1, f2 = 1; i < 20; i++, f2 += f1, f1 = f2 - f1)即连续使用int,因为for仅允许使用一个声明,而int i ,f1被视为一个声明,两次使用int则视为两个声明

被封印的goto语句

由于goto语法功能的可代替性,工程上也不常用(可能会破坏代码的结构)

  • goto被用于将控制无条件转移到所想要的位置。
  • 若跳过的代码中有变量定义,不影响编译和使用相关变量,但相关初始化不会生效。
  • 基本使用示范:
#include<stdio.h>
int main(){
    goto lab_1;
    printf("Hello World!\n");
    lab_1:
        printf("Hello Hang Zhou!\n");
//----------------------------------------//
    goto lab_2;
    int a, b;
    scanf("%d%d", &a, &b);
lab_2:
    printf("%d\n", a * b);

    return 0;

}
  • goto模拟if-else:
#include<stdio.h>
int main(){
    int n;
    scanf("%d", &n);
    n % 2 == 0 && ({goto if_stmt; 0;});//小括将大括号变为一个有返回值的表达式,且返回值等于大括号中最后一条表达式的值
    !(n % 2 == 0) && ({goto else_stmt; 0;});//大括号的意义是将goto和后面一条语句合成为一个复合语句
    if_stmt:
        printf("%d is even\n", n);
        goto if_end;
    else_stmt:
        printf("%d  is odd\n", n);
    if_end:
    return 0;
}
  • goto模拟while:
#include<stdio.h>
int main(){
    int n, i = 1;
    scanf("%d", &n);
judge:
    if (i <= n) goto stmt;
    else goto while_end;
stmt:
    printf("%d ", i);
    i++;
    goto judge;
while_end:
    printf("\n");
    return 0; 
}
  • goto模拟for:
#include<stdio.h>
int main(){
    int n;
    scanf("%d", &n);
    int i  = 1;
for_1:
    goto for_2;
for_2:
    if (i <= n)
        goto for_4;
    else 
        goto  for_end;
for_3:
    i++;
    goto for_2;
for_4:
    if (i % 3 == 0) goto for_3;//不能使用continue,只能在循环中使用
    else {
        printf("%d ", i);
        goto for_3;
    }
for_end:
    printf("\n");
    return 0;

}

C中“一条语句”的理解

共五种类型:

  • 复合语:{staement};——由大括号构成的语句
  • 表达式语句:expression;——包含变量各种运算的式子
  • 选择(分支)语句:if_else和switch_case
  • 循环语句:while/do_while/for
  • 跳转语句:break/continue/return/goto
    第六种:空语句——仅由;组成,其实也称为一条语句

if,while等控制的都是后面的一条语句,所以if后可以非大括号的形式直接接上一个for循环,for后也可以不需要大括号接上if_else

if(1){
	cout << "1" << endl;
	cout << "2" << endl;
}
//-----------------------------
for (int i = 0; i < 10; i++);{//初学者容易犯的错,此时for后的一条语句实际是空语句而非复合语句
	cout << "1" << endl;
	cout << "2" << endl;
	cout << "3" << endl;
}
//------------------------------
int n = 5;
while(n--)
	if (1){
		cout << "1" << endl;
		cout << "2" << endl;
}//此时的if整体也是一条语句,可以不需要大括号
//------------------------------
//循环嵌套打印一个三维矩阵
for(int i = 1; i <= 3; i++){
	for (int j = 1; j <= 3; j++)
		printf("(%d, %d) ",i, j);
	printf("\n");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值