本文最后更新于 2024-10-18,文章内容距离上一次更新已经过去了很久啦,可能已经过时了,请谨慎参考喵。

title: C++学习笔记之二
top_img: false
tags:
  - C++
categories:
  - 编程语言
cover: false
abbrlink: a55fac5e
date: 2024-01-09 19:45:31
copyright:
comments:

存储类

存储类定义C++程序中变量/函数的范围(可见性)和生命周期。这些说明符放置在它们所修饰的类型之前。

static存储类

static 存储类指示编译器在程序的生命周期内保持局部变量的存在,而不需要在每次它进入和离开作用域时进行创建和销毁。因此,使用 static 修饰局部变量可以在函数调用之间保持局部变量的值。

static 修饰符也可以应用于全局变量。当 static 修饰全局变量时,会使变量的作用域限制在声明它的文件内。

在C++中,当 static 用在类数据成员上时,会导致仅有一个该成员的副本被类的所有对象共享。

举个例子:

#include <iostream>
void func(void);    // 函数声明 
static int count = 10;    // 全局变量
int main() {
    while(count--) {
        func();
    }
    return 0;
}
void func(void) {    // 函数定义
    static int i = 5;    // 局部静态变量
    i++;
    std::cout << "变量 i 为 " << i ;
    std::cout << " , 变量 count 为 " << count << std::endl;
}

执行结果:

那么假如我们去掉 static 修饰符呢:

#include <iostream>
void func(void);    // 函数声明 
int count = 10;    // 全局变量
int main() {
    while(count--) {
        func();
    }
    return 0;
}
void func(void) {    // 函数定义
    int i = 5;    // 局部静态变量
    i++;
    std::cout << "变量 i 为 " << i ;
    std::cout << " , 变量 count 为 " << count << std::endl;
}

执行结果:

只有全局变量还在正常运行,但局部静态变量的值每一轮下来都只会+1,第二轮的时候还是会恢复到初始值

extern存储类

extern 存储类用于提供一个全局变量的引用,全局变量对所有的程序文件都是可见的。当您使用 extern 时,对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。

当有多个文件且定义了一个可以在其他文件中使用的全局变量或函数时,可以在其他文件中使用 extern 来得到已定义的变量或函数的引用。可以这么理解,extern 是用来在另一个文件中声明一个全局变量或函数。

extern 修饰符通常用于当有两个或多个文件共享相同的全局变量或函数的时候,例如:

main.cpp:

#include <iostream>
#include "./test/support.cpp"
int count;
extern void write_extern();
int main() {
    count = 5;
    write_extern();
}

support.cpp:

#include <iostream>
extern int count;
void write_extern(void) {
    std::cout << "Count is " << count << std::endl;
}

执行结果:

在main.cpp文件中通过 extern 关键字引入了support.cpp文件中的 write_extern() 函数

在support.cpp文件通过 extern 关键字引入了main.cpp文件中的 count 变量

thread_local存储类

使用 thread_local 说明符声明的变量仅可在它在其上创建的线程上访问。 变量在创建线程时创建,并在销毁线程时销毁。 每个线程都有其自己的变量副本。

thread_local 说明符可以与 staticextern 合并。

可以将 thread_local 仅应用于数据声明和定义,thread_local 不能用于函数声明或定义。

示例:

thread_local int x;  // 命名空间下的全局变量
class X {
    static thread_local std::string s; // 类的static成员变量
};
static thread_local std::string X::s;  // X::s 是需要定义的

void foo() {
    thread_local std::vector<int> v;  // 本地变量
}

运算符

算术运算符

+ 、减 - 、乘 * 、除 / 就不说了,取模运算符 % 是取除法后的余数,自增 ++ 和自减 -- 指整数值+1或者-1,示例:

#include <iostream>
using namespace std;
int main() {
    int a = 21;
    int b = 10;
    int c;
    c = a + b;
    cout << "Line 1 - c 的值是 " << c << endl;
    c = a - b;
    cout << "Line 2 - c 的值是 " << c << endl;
    c = a * b;
    cout << "Line 3 - c 的值是 " << c << endl;
    c = a / b;
    cout << "Line 4 - c 的值是 " << c << endl;
    c = a % b;
    cout << "Line 5 - c 的值是 " << c << endl;
    int d = 10;    // 测试自增、自减
    c = d++;
    cout << "Line 6 - c 的值是 " << c << endl;
    d = 10;    // 重新赋值
    c = d--;
    cout << "Line 7 - c 的值是 " << c << endl;
    return 0;
}

执行结果:

关系运算符

  • == 两数值相等为真
  • != 两数值不相等为真
  • > 左大于右为真
  • < 左小于右为真
  • >= 左大于等于右为真
  • <= 左小于等于右为真

示例:

#include <iostream>
using namespace std;
int main() {
    int a = 21;
    int b = 10;
    int c ;
    if(a == b) {
        cout << "Line 1 - a 等于 b" << endl;
    } else {
        cout << "Line 1 - a 不等于 b" << endl;
    }
    if (a < b) {
        cout << "Line 2 - a 小于 b" << endl;
    } else {
        cout << "Line 2 - a 不小于 b" << endl;
    }
    if (a > b) {
        cout << "Line 3 - a 大于 b" << endl;
    } else {
        cout << "Line 3 - a 不大于 b" << endl;
    }
    // 改变 a 和 b 的值
    a = 5;
    b = 20;
    if (a <= b) {
        cout << "Line 4 - a 小于或等于 b" << endl;
    }
    if (b >= a) {
        cout << "Line 5 - b 大于或等于 a" << endl;
    }
    return 0;
}

执行结果:

逻辑运算符

  • && 逻辑运算: 运算符
  • || 逻辑运算: 运算符
  • ! 逻辑运算: 运算符

示例:

#include <iostream>
using namespace std;
int main() {
    int a = 5;
    int b = 10;
    int c = 15;
    if (a < b && a < c) {    // 两个条件都为真
        cout << "Line 1 - 条件为真"<< endl;
    }
    if (a < b || a > c) {    // 任意一个条件为真
        cout << "Line 2 - 条件为真"<< endl;
    }
    if (a > b) {
        cout << "Line 3 - 条件不为真"<< endl;
    }
    if ( !(a > b) ) {
        cout << "Line 4 - 条件为真"<< endl;
    }
    return 0;
}

执行结果:

位运算符

位运算符作用于位,并逐位执行操作,各个运算符的真值表如下:

pqp&qp|qp^q~p
000001
010111
111100
100110

假设A=60,B=13,现在以二进制格式表示:

A = 0011 1100
B = 0000 1101

(A & B) 的值为 0000 1100 ,即为12;

(A | B) 的值为 0011 1101 ,即为61;

(A ^ B) 的值为 0011 0001 ,即为49;

(~A) 的值为 1100 0011 ,即为-61,一个有符号二进制数的补码形式;

位移运算符:

  • << 二进制左移运算,将一个运算对象的各二进制位全部左移若干位,左边的二进制位丢弃,右边补0,A << 2 的值为 1111 0000 ,即为240;
  • >> 二进制右移运算符,将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃,A >> 2 的值为 0000 1111 ,即为15。

赋值运算符

符号举例等同于
=c = 2或c = a + b把2赋值给c或把a+b的值赋给c
+=c += ac = c + a
-=c -= ac = c -a
*=c *= ac = c * a
/=c /= ac = c / a
%=c %= ac = c % a
<<=c <<= 2c = c << 2
>>=c >>= 2c = c >> 2
&=c &= 2c = c & 2
^=c ^= 2c = c ^ 2

还有一个,不过因为转义,打不出来,只能截图了:

杂项运算符

sizeof() :返回变量的大小,例如:

#include <iostream>
using namespace std;
int main() {
    cout << "Size of char : " << sizeof(char) << endl;
    cout << "Size of int : " << sizeof(int) << endl;
    cout << "Size of short int : " << sizeof(short int) << endl;
    cout << "Size of float : " << sizeof(float) << endl;
    cout << "Size of double : " << sizeof(double) << endl;
    cout << "Size of wchar_t : " << sizeof(wchar_t) << endl;
    return 0;
}

执行结果:

Condition ? X : Y :如果 Condition 为真,则值为X,否则值为Y,等同于:

if (Condition) {
    var = X;
} else {
    var = Y;
}

反过来,下面这段代码也可以简写:

if (y < 10) { 
    var = 30;
} else {
    var = 40;
}
// 等同于:
var = (y < 10) ? 30 : 40;

需要注意的是,一般在实际开发中尽量避免使用三元运算符!

逗号运算符

顾名思义,就是英文逗号,使用逗号运算符是为了把几个表达式放在一起,整个逗号表达式的值为系列中最后一个表达式的值,从本质上讲,逗号的作用是将一系列运算按顺序执行,例如:

#include <iostream>
using namespace std;
int main() {
    int i, j;
    j = 10;
    i = (j++, j+100, 999+j);    // 示例逗号运算符
    cout << i;
    return 0;
}

执行结果:

成员运算符

. (点)运算符和 -> (箭头)运算符用于引用类、结构和共用体的成员,例如:

// 示例结构体
struct Employee {
	char first_name[16];
	int age;
} emp;
// 下面的代码把值 "zara" 赋给对象 emp 的 first_name 成员:
strcpy(emp.first_name, "zara");
// 如果 p_emp 是一个指针,指向类型为 Employee 的对象,则要把值 "zara" 赋给对象 emp 的 first_name 成员,需要编写如下代码:
strcpy(p_emp->first_name, "zara");

指针运算符

& 是一元运算符,返回操作数的内存地址。例如,如果 var 是一个整型变量,则 &var 是它的地址。该运算符与其他一元运算符具有相同的优先级,在运算时它是从右向左顺序进行的。可以把 & 运算符读作"取地址运算符",这意味着,&var 读作"var变量的地址"。

第二个运算符是间接寻址运算符 *,它是 & 运算符的补充。* 是一元运算符,返回操作数所指定地址的变量的值。示例:

#include <iostream>
using namespace std;
int main() {
    int var;
    int *ptr;
    int val;
    var = 3000;
    // 获取 var 的地址
    ptr = &var;
    // 获取 ptr 的值
    val = *ptr;
    cout << "Value of var :" << var << endl;
    cout << "Value of ptr :" << ptr << endl;
    cout << "Value of val :" << val << endl;
    return 0;
}

执行结果:

C++循环

while循环

举例:

#include <iostream>
using namespace std;
int main() {
    int a = 10;
    while (a < 20) {
        cout << "a 的值:" << a << endl;
        a++;
    }
    return 0;
}

如果条件为真,则执行循环中的语句,执行结果:

for循环

for循环的标准格式:

for (init; condition; increment) {
   statement(s);
}

init 会首先被执行,且只会执行一次。这一步可以声明并初始化任何循环控制变量,也可以留空,只要有一个分号出现即可;

接下来,会判断 condition,如果为真,则执行循环主体;

在执行完for循环主体后,控制流会跳回上面的 increment 语句,该语句可以更新循环控制变量,可以留空,只要在条件后有一个分号出现即可;

条件再次被判断,如果为真,则执行循环,这个过程会不断重复(循环主体,然后增加步值(increment 语句),再然后重新判断条件),在条件变为假时,for 循环终止。示例:

#include <iostream>
using namespace std;
int main() {
    for (int a = 10; a < 20; a = a + 1) {
        cout << "a 的值:" << a << endl;
    }
    return 0;
}

执行结果:

下面是一个范围循环的例子:

#include <iostream>
using namespace std;
int main() {
    int my_array[5] = {1, 2, 3, 4, 5};
    for (int& x : my_array) {    // 或者int &x : my_array
        x *= 2;
        cout << x << endl;
    }
}

执行结果:

可以看到,循环只遍历了整个数组每个元素后就结束了

注:&在这里不是取址符,是引用

do while循环

for和while循环是在循环开始前测试循环条件,而do while 循环是在循环的尾部检查它的条件,所以 do while循环会确保至少执行一次循环,语法如下:

do {
    statement(s);
} while ( condition );

举个栗子:

#include <iostream>
int main() {
    int a = 10;
    do {
        std::cout << "a 的值:" << a << std::endl;
        a = a + 1;
    } while (a < 20);
    return 0;
}

执行结果:

循环嵌套

顾名思义,就是套娃,示例:

#include <iostream>
int main() {
    int i, j;
    for (i = 2; i < 20; i++) {    // 2-20的质数
        for (j = 2; j <= (i / j); j++) {
            if(!(i % j)) {
                break; // 如果找到,则不是质数
            }
        }
        if(j > (i / j)) {
            std::cout << i << " 是质数" << std::endl;
        }
    }
    return 0;
}

执行结果:

循环控制语句

break语句:

break语句有以下两种用法:

  • 当break语句出现在一个循环内时,循环会立即终止,且程序流将继续执行紧接着循环的下一条语句;
  • 可用于终止switch语句中的一个case

举例:

#include <iostream>
int main() {
    int a = 10;
    do {
        std::cout << "a 的值:" << a << std::endl;
        a++;
        if(a > 15) {
            break;
        }
    } while(a < 20);
    return 0;
}

执行结果:

可以看到循环并没有循环到19,而是到15就结束了,因为执行到了break语句,至于为什么输出的a值最后是15而不是16,是因为a执行了a++之后就出发了break语句跳出了循环,所以循环内的打印a值的语句并没有执行,所以a最终的值是15

continue语句:

continue会跳过当前循环中的代码,强迫开始下一次循环。对于for循环,continue语句会导致重新执行条件测试和循环增量部分。对于while和do while循环,continue语句会导致程序控制回到条件测试上

用法:

示例:

#include <iostream>
int main()
{
    int a = 10;
    do
    {
        if(a == 15)
        {
            a++;
            continue;
        }
        std::cout << "a 的值:" << a << std::endl;
        a++;
    } while( a < 20 );
    return 0;
}

执行结果:

可以看到当a等于15时进入了if语句,执行a++之后遇到continue语句,此时a=16,并直接执行了打印a的值,所以执行结果里没有a=15