GNU/Linux(64位)+ GCC_11.2.0

数据类型

4 种基本数据类型
数据类型 中文名称 所开辟的存储空间的大小(Byte)
int 整型 4
float 单精度浮点型 4
double 双精度浮点型 8
char 字符型 1
4 种限定符(施加于基本数据类型)

shortlongsignedunsigned

signed 和 unsigned 用于限定所有整型和字符型(包括被 short 或 long 限定了的整型)

给数据类型起别名,使用 typedef

image-20220509221931771

sizeof 根据数据类型获取数据元素所占内存空间的字节数

1
2
3
4
sizeof( ElemType );

// 举个例子
printf("%d\n", sizeof(int)); // 4

变量

可将变量理解为存储数据的容器

变量的命名规则
  1. 字母或下划线开头,可包含数字
  2. 字母严格区分大小写
  3. 以下划线开头的变量名是有可能和系统定义的变量名冲突的
  4. 拒绝使用关键字和保留字
变量的声明 & 初始化
1
2
int foo;  // 声明变量
foo = 1; // 初始化变量

变量的声明和初始化可合二为一

1
int foo2 = 2;

只声明不初始化的变量其所在内存是脏的

常量

一旦初始化,则其值无法被改变的量

字符常量

字符 + 单引号 <=> 字符常量

字符常量参与运算时,实际上是其对应的 ASCII 码参与运算

1
printf("%d\n", 1 + '0'); // 49
字符串常量

请参考这里

运算符与表达式

算术运算符

+-*/%

++--

除法运算 & 取模运算(Python3 vs C)

赋值运算符

+=-=*=/=%=

=

关系运算符

==!=>>=<<=

逻辑运算符

&&||!

位运算符

&、或|、异或^、取反~

「左移<<、右移>>」=> 保持符号位不变 !

如果你想看二进制,可以使用 C++ 的 bitset

eg:

1
2
short int a = 6;
cout << bitset<sizeof(a) * 8>(a) << endl;
三目运算符

expression1 ? res1 : res2

类型转换

在同一表达式中出现不同类型变量时,该表达式中的变量们会根据某种规则,统一转换成某种类型

进而得出更精确或更能描述实际情况的结果

这就是类型转换

自动的类型转换

数据位短的类型会向数据位长的类型转换(可使用 C++ 的 typeid()验证)

image-20220509085908279

相比于 int,unsigned int 的数据位多一个

image-20220509090104459

char 和 short int 统一向 int 转换

1
2
3
4
short int a = 1;
char b = 'x';

printf("%d\n", sizeof(a + b)); // 4

有可能损失数据精度

1
2
3
4
5
6
7
float a = 100.5f;
int b = a;
printf("%d\n", b); // 100, 损失了数据精度

float c = 100;
int d = c;
printf("%d\n", d); // 100, 没损失数据精度

控制

条件判断
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (表达式1) {
...
} else if (表达式2) {
...
} else {
...
}

---

switch (表达式) {
case 常量表达式1: ...; break;
case 常量表达式2: ...; break;
case 常量表达式3: ...; break;
default: ...;
}

一个良好的代码风格是:永远都加上花括号,即使只有一行代码。

循环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 当循环次数不确定,较适合用 while 循环
while (表达式) {

if () {
continue;
} else {
break;
}

}

---

// 当确定循环次数,较适合用 for 循环
for (表达式1; 表达式2; 表达式3) {
...
}

// 等价于
表达式1;
while (表达式2) {
...
表达式3;
}

// 因此如下是一种死循环
for (;;) {
...
}

---

// 先斩后奏型循环
do {

if () {
continue;
} else {
break;
}

} while (表达式);

函数

For developer:用于代码的封装和复用,参数就是一种抽象

For user:APIs,让小白和巨佬专注于业务逻辑,而无需关注函数内部的代码实现

1
2
3
4
返回值类型 函数名 (参数列表) {
...
return 返回值;
}

返回值类型默认为整型

image-20220508223746456

对于无参函数,建议将参数列表写为 void

作用域

除全局作用域外,一个花括号就能形成一个作用域

image-20220508232147708

image-20220508232342233

image-20220508233126388

结论:

外层作用域无法访问内层作用域

内层作用域可以访问外层作用域

生存期

1
2
3
4
5
6
7
8
9
int foo() {
int a = 2;
}

int main() {
int a = 1;
foo();
printf("%d\n", a); // 1
}

静态变量

相当于拥有局部作用域的全局生存期变量

image-20220508231315863

指针

指针与 const

https://liupj.top/2021/11/18/pointer&const/

指向函数的指针
1
2
3
4
5
6
7
8
9
10
11
int add(int a, int b) {
return a + b;
}

int main() {
int (*ptr)(int a, int b);

ptr = add;

printf("%d\n", ptr(1, 2)); // 3
}

递归

1
2
3
4
// 直接递归(形式上是自调用)+ 单递归入口
void r() {
r();
}
1
2
3
4
5
6
7
8
9
10
11
// 直接递归(形式上是自调用)+ 多递归入口

int i = 0;

void r() {
if (i < 2) {
r();
r();
i++;
}
}

代码优化思路:将全局变量 i 改为静态变量,放到 r() 的函数体中,以获得更好的封装性和可读性

image-20220509212113718

本文不介绍间接递归

数组

image-20220509214046922

数组的声明与初始化
1
2
int arr[4] = { 1, 2, 3, 4 };
// 数组容量可省略不写,由元素个数决定数组容量
1
2
3
4
5
6
7
8
9
10
11
int arr[4] = { 0 };
for (int i = 0; i < 4; i ++) { printf("%d", arr[i]); } // 0000
printf("\n");

int arr2[4];
for (int i = 0; i < 4; i ++) { printf("%d", arr2[i]); } // 乱七八糟
printf("\n");

int arr3[4] = { 1 };
for (int i = 0; i < 4; i ++) { printf("%d", arr3[i]); } // 1000
printf("\n");
数组与指针

https://liupj.top/2022/04/05/array&pointer/

结构体

数组中元素的数据类型相同,结构体中元素的数据类型可以不同

1
2
3
struct {
只能有成员变量
}

参考文档

c reference

c++ reference