C++基础

1、关键字

菜鸟教程

2、数据类型

内置数据类型:算数类型和空类型

算数类型:整形和浮点型

2.1 整型

  1. 定义超出范围会溢出; | 数据类型 | 占用空间 | 取值范围 | | --- | --- | --- | | short(短整型) | 2 字节 | (-215~215-1) | | int(整型) | 4 字节 | (-231~231-1) | | long(长整型) | win 为 4,linux 为 4(32 位),8(64 位) | (-231~231-1) | | long long(长长整型) | 8 字节 | (-263~263-1) |
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
#include<iostream>
using namespace std;

int main(){
// 1.短整型
short a = 10;

// 2.整型
int b = 10;

// 3.长整型
long c = 10;

// 4.长长整型
long long d = 10;

cout << "num1=" << a << endl;
cout << "num2=" << b << endl;
cout << "num3=" << c << endl;
cout << "num4=" << d << endl;

system("pause");

return 0;
}

2.2 实型

  1. 用于表示小数;
  2. 单精度 float 和双精度 double;
  3. 小数默认为双精度,定义 float 时需要加 f,如float a =3.14f;,否则会截断;
  4. 科学计数法,float f2 = 3e2;//3*10^2 float f2 = 3e-2;//3*0.1^2

区别

数据类型 占用空间 有效数字类型
float 4 字节 7 位有效数字
double 8 字节 15-16 位有效数字
1
2
3
4
5
6
7
8
9
10
11
#include<iostream>
using namespace std;

int main() {

float a = 3.14f;
double b = 3.14;

system("pause");
return 0;
}

2.3 字符型

  1. 表示形式char ch = 'a';
  2. 单引号里面只能写一个字符;
  3. 只占用一个字节;
  4. 字符型进行存储时以 ASCII 存储;
1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
using namespace std;

int main() {
// 创建字符型变量
char ch = 'a';
cout << "字符为" << ch << endl;
cout << "字符所占内存为" << sizeof(ch) << endl;
cout << "字符ch对应的ASCII编码为" << (int)ch << endl;

system("pause");
return 0;
}

2.4 转义字符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
\a:蜂鸣,响铃
\b:回退:向后退一格
\f:换页
\n:换行,光标到下行行首
\r:回车,光标到本行行首
\t:水平制表
\v:垂直制表
\\:反斜杠
\':单引号
\":双引号
\?:问号
\ddd:三位八进制
\xhh:二位十六进制
\0:空字符(NULL),什么都不做

2.5 字符串型

  1. C 语言类型:char 变量名[] = "字符串值";
  2. C++类型:string 变量名 = "字符串值";
  3. iostream已经隐式地包含了 string 库,但最好还是显式一下,以免发生错误。头文件#include<string>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
#include<string>
using namespace std;

int main() {
// 1、C语言风格
char string1[] = "hello";
// 2、C++风格
string string2 = "world";

cout << "string1="<<string1 << endl;
cout << "string2="<<string2 << endl;

system("pause");
return 0;
}

2.6 布尔类型

  1. true 和 false;
  2. 只占用 1 字节;
  3. 实质上 true 为 1,false 为 0;
1
2
3
4
5
6
7
8
9
10
11
#include<iostream>
using namespace std;

int main() {
// bool
bool flag = true;
cout << "flag=" << flag << endl; //输出为flag=1

system("pause");
return 0;
}

2.7 输入

  1. cin 变量名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>
using namespace std;

int main() {
// 1、整型
int a = 10;
cout << "a=" << a << endl;
cin >> a;
cout << "a之后=" << a << endl;
// 2、字符串
string str = "helllo";
cout << "str1=" << str << endl;
cin >> str;
cout << "str2=" << str << endl;
system("pause");
return 0;
}

2.8 decltype 类型指示符

  1. 从表达式的类型推算出要定义变量的类型,而不使用该对象的初始化变量;
  2. decltype(f()) sum = 0;sum 的类型就是 f()函数返回值类型;

关于引用类型

1
2
3
4
5
6
7
8
9
#include<iostream>

int main()
{
const int ci = 0, & cj = ci;
decltype(ci) x = ci; //x类型是const int
decltype(cj) y = x; //y类型是const int&
// decltype(cj) z; //错误,z的类型为引用,必须初始化;
}

指针解引用

  1. 解引用之后得到引用类型
1
2
3
int i = 42, * p = &i, & r = i;
decltype(r + 0) b;
//decltype(*p) c; //错误,*p解引用之后类型为引用,即int&

多层括号

  1. 双层括号永远是引用
1
2
3
int i = 42;
//decltype((i)) d; // 错误,加括号之后(i)为表达式,得到引用类型
decltype(i) d;

3、运算符

3.1 算数运算符

  1. 两个小数不能做取模运算; | 运算符 | 描述 | 实例 | | --- | --- | --- | | + | 把两个操作数相加 | A + B 将得到 30 | | - | 从第一个操作数中减去第二个操作数 | A - B 将得到 -10 | | _ | 把两个操作数相乘 | A _ B 将得到 200 | | / | 分子除以分母 | B / A 将得到 2 | | % | 取模运算符,整除后的余数 | B % A 将得到 0 | | ++ | 自增运算符 ,整数值增加 1 | A++ 将得到 11 | | -- | 自减运算符 ,整数值减少 1 | A-- 将得到 9 |

前置自增和后置自增

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;

int main()
{
int a = 21;
int c ;

// a 的值在赋值之前不会自增
c = a++;
cout << "Line 1 - Value of a++ is :" << c << endl ;

// 表达式计算之后,a 的值增加 1
cout << "Line 2 - Value of a is :" << a << endl ;

// a 的值在赋值之前自增
c = ++a;
cout << "Line 3 - Value of ++a is :" << c << endl ;
return 0;
}
1
2
3
Line 1 - Value of a++ is :21
Line 2 - Value of a is :22
Line 3 - Value of ++a is :23

3.2 赋值运算符

算符 描述 实例
= 简单的赋值运算符,把右边操作数的值赋给左边操作数 C = A + B 将把 A + B 的值赋给 C
+= 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 C += A 相当于 C = C + A
-= 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 C -= A 相当于 C = C - A
*= 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 C = A 相当于 C = C A
/= 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 C /= A 相当于 C = C / A
%= 求模且赋值运算符,求两个操作数的模赋值给左边操作数 C %= A 相当于 C = C % A
<<= 左移且赋值运算符 C <<= 2 等同于 C = C << 2
>>= 右移且赋值运算符 C >>= 2 等同于 C = C >> 2
&= 按位与且赋值运算符 C &= 2 等同于 C = C & 2
^= 按位异或且赋值运算符 C ^= 2 等同于 C = C ^ 2
|= 按位或且赋值运算符 C |= 2 等同于 C = C | 2
1
2
3
4
5
6
7
8
9
10
11
#include<iostream>
using namespace std;

int main() {
int a = 8;
a >>= 2; // 等于8/2^2
cout << "a = " << a << endl;

system("pause");
return 0;
}

3.3 比较运算符

  1. 输出时加括号;(a==b);
  2. 计算时可加可不加; | 运算符 | 描述 | 实例 | | --- | --- | --- | | == | 检查两个操作数的值是否相等,如果相等则条件为真。 | (A == B) 不为真。 | | != | 检查两个操作数的值是否相等,如果不相等则条件为真。 | (A != B) 为真。 | | > | 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 | (A > B) 不为真。 | | < | 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 | (A < B) 为真。 | | >= | 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 | (A >= B) 不为真。 | | <= | 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 | (A <= B) 为真。 |
1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
using namespace std;

int main() {
int a = 8;
int b = 10;
cout << (a==b) << endl;
bool c = a != b;
cout << c << endl;
system("pause");
return 0;
}
1
2
line 1:0
line 2:1

3.4 逻辑运算符

假设变量 A 的值为 1,变量 B 的值为 0,则:

运算符 描述 实例
&& 称为逻辑与运算符。如果两个操作数都 true,则条件为 true。 (A && B) 为 false。
|| 称为逻辑或运算符。如果两个操作数中有任意一个 true,则条件为 true。 (A || B) 为 true。
! 称为逻辑非运算符。用来逆转操作数的逻辑状态,如果条件为 true 则逻辑非运算符将使其为 false。 !(A && B) 为 true。

3.5 按位赋值操作

  1. 按位与(&=)运算符,它是二进制的“与”操作,即两数中对应位都为 1 时为 1,其他情况都为 0。例如,3 & 5 的结果为 1。而按位与赋值操作就是将左操作数和右操作数进行按位与操作,然后将结果赋给左操作数,即 a &= b 等价于 a = a & b。
  2. 其次是按位或(|=)运算符,它是二进制的“或”操作,即两数中对应位有一个为 1 时为 1,其他情况都为 0。例如,3 | 5 的结果为 7。而按位或赋值操作就是将左操作数和右操作数进行按位或操作,然后将结果赋给左操作数,即 a |= b 等价于 a = a | b。
  3. 接下来是按位异或(^=)运算符,它是二进制的“异或”操作,即两数中对应位相同时为 0,不同时为 1。例如,3 ^ 5 的结果为 6。而按位异或赋值操作就是将左操作数和右操作数进行按位异或操作,然后将结果赋给左操作数,即 a ^= b 等价于 a = a ^ b。
  4. 第四个是左移位(<<=)运算符,它可以将一个数左移 n 位,相当于将其乘以 2 的 n 次方。例如,3 << 5 的结果为 96。而左移位赋值操作就是将左操作数左移右操作数位数个位置,然后将结果赋给左操作数,即 a <<= b 等价于 a = a << b。
  5. 第五个是右移位(>>=)运算符,它可以将一个数右移 n 位,相当于将其除以 2 的 n 次方。例如,16 >> 2 的结果为 4。而右移位赋值操作就是将左操作数右移右操作数位数个位置,然后将结果赋给左操作数,即 a >>= b 等价于 a = a >> b。
  6. 第六个是取反()运算符,它可以将一个数的二进制码按位取反,例如,3 的结果为-4。而按位与取反(&~)赋值运算符是 a &= ~b 的简写,即先按位取反 b,然后与 a 进行按位与操作,最后将结果赋给 a

4、程序流程结构

  1. 顺序结构、选择结构、循环结构

4.1 选择结构

  1. if
  2. if...else
  3. if...else if..else if...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>
using namespace std;

int main() {
int score = 0;
cout << "请输入分数" << endl;
cin >> score;
if (score > 600) {
cout << "及格了" << endl;
}

system("pause");
return 0;
}

顺序计数器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<iostream>

int main() {
int currVal = 0, val = 0;
if (std::cin >> currVal) {
int cnt = 1;
while (std::cin >> val) {
if (val == currVal)
++cnt;
else {
std::cout << currVal << " occurs " << cnt << " times" << std::endl;
currVal = val;
cnt = 1;
}
}
std::cout << currVal << " occurs " << cnt << " times" << std::endl;
}
return 0;
}

4.2 三目运算符

  1. 做简单的判断;
  2. a > b ? a : b ;a 比 b 大则为 a,否则为 b;

4.3 switch

  1. switch 选择结构;
  2. switch 只能判断整型或者字符型,不能是区间;
  3. switch 效率较高;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
using namespace std;

int main() {
int score = 0;
cout << "请输入分数" << endl;
cin >> score;
switch (score) {
case 10:
cout << "经典" << endl;
break;
case 9:
cout << "还行" << endl;
break;
default:
cout << "不行"<< endl;
break;
}

system("pause");
return 0;
}

4.4 while

  1. while(循环条件){循环结构}

while 语句

1
2
3
4
5
6
7
8
9
10
11
int main() {
int sum = 0, val = 1;
while (val <=10)
{
sum += val;
++val;
}
std::cout << "Sum of 1 to 10 inclusive is "
<< sum << std::endl;
return 0;
}

输出:um of 1 to 10 inclusive is 55

猜数字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
using namespace std;


int main() {
int num = 0;
int numRam = rand() % 100 + 1;// 生成1-100的随机数
cout << "请输入数字" << endl;
cin >> num;
while (num != numRam) {
if (num > numRam) {
cout << "大了" << endl;
cin >> num;
}
else {
cout << "小了" << endl;
cin >> num;
}
}
cout << "猜对了" << endl;
system("pause");
return 0;
}

4.5 do...while

  1. do{ 循环语句 } while { 循环条件 };
  2. 会先执行一次;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
using namespace std;

int main() {
int num = 100;
do {
int a = 0;
int b = 0;
int c = 0;
a = num / 100;
b = num / 10 % 10;
c = num % 10;
if (a*a*a + b*b*b + c*c*c == num) {
cout << "是水仙花数" << num << endl;
}
num++;
} while (num >= 100 && num < 1000);
system("pause");
return 0;
}

4.6 for 循环

  1. for( 起始表达式; 条件表达式;循环语句 ){ 循环语句 }
  2. 可以用来读取一组数据;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
using namespace std;

int main() {
int a = 0;
int b = 0;
for (int num = 1; num < 100; num++) {
a = num / 10;
b = num % 10;
if (a == 7 || b == 7 || num % 7 == 0) {
cout << "敲桌子" << num << endl;
}
}
system("pause");
return 0;
}

读取输入不定的输入数据

1
2
3
4
5
6
7
8
9
int main() {
int sum = 0, value = 0;
while (std::cin >> value)
{
sum += value;
}
std::cout << "Sum is: " << sum << std::endl;
return 0;
}

输入:1 2 3 4 5 6 8 da 输出:Sum is: 29

或者 ctrl+z,再按 Enter,表示输入结束

输入:1 2 3 4^z 输出:Sum is: 10

乘法口诀

1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
using namespace std;

int main() {
for (int i = 1; i < 10; i++) {
for (int j = 1; j <= i; j++) {
cout << j <<"*"<<i <<"=" <<i*j<<"\t";
}
cout << endl;
}
system("pause");
return 0;
}

4.7 break 和 continue

  1. break 跳出循环体;
  2. continue,执行到本行不往下执行,但是循环会继续,只是跳出本次执行块;

4.8 goto

  1. 需要一个表示符,标识符命名规范同变量;
  2. 标识符一般大写;
1
2
3
4
goto FLAG;

FLAG:
cout << "跳转执行到这" << endl;

5、数组

  1. 存放相同类型的数据;
  2. 索引从 0 开始;

5.1 一维数组

  1. 定义
  • 数据类型 数组名[ 数组长度 ];
  • 数据类型 数组名[ 数组长度 ] = { 值1 ,值2 ,值3...};
  • 数组类型 数组名[] = {值1 ,值2,值3...};
  1. 获取数组中的数据arr[i]
  2. 数组占居内存大小sizeof(arr)
  3. 元素个数sizeof(arr)/sizeof(arr[0])
  4. 直接打印 arr,输出为首地址;元素地址为&arr[0]

6、引用

6.1 基本介绍

  1. 引用是变量的别名;
  2. 本质就是一个指针常量;
  3. 应用必须初始化,初始化后不能改变;
  4. 可以使用引用进行地址传递;

基本使用

1
2
3
4
5
6
7
8
9
#include<iostream>
using namespace std;

int main() {
int a = 10;
int& b = a;
b = 20;
cout << a << endl << b << endl;
}

6.2 3 种交换函数

  1. 地址传递;
  2. 引用的好处就是不需要解引用,在作为函数实参后,需要用到值时,直接使用引用名,而指针需要加*。
  3. 每次使用引用实际上是解引用的操作。
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
#include<iostream>
using namespace std;

// 值传递
void f1(int a, int b) {
int temp = a;
a = b;
b = temp;
}

// 地址传递
void f2(int *a ,int*b) {
int temp = *a;
*a = *b;
*b = temp;
}

// 引用传递
void f3(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}


int main(){
int a = 10;
int b = 20;
//f1(a, b);
//f2(&a, &b);
f3(a, b);
cout << "a = " << a<<endl;
cout << "b = " << b;

}

6.3 引用函数函数返回值

  1. 不要返回局部变量的引用(较老的版本中);
  2. 函数的调用可以作为左值;
1
2
3
4
5
6
7
8
9
10
11
12
int& test02() {
static int a = 20;
return a; //返回引用地址
}

int main() {
int& res = test02();
cout << res << endl;
test02() = 1000;
cout << res << endl;

}

输出为

20

1000

6.4 常量引用

  1. 使用场景:用来修饰形参,防止误操作;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>
using namespace std;

void f1(const int &a) {
// 不能再修改
// a = 20;
cout << a << endl;
}

int main() {
// 编译器会修改为 int temp= 10;const int &a = temp;只不过这个临时变量我们不知道
const int& a = 10;
// 修改a会报错,a已为只读
// a = 20;
int b = 10;
f1(b);
}

7、结构体

7.1 结构体概念

  1. 用户自定义数据类型;

7.2 结构体使用

  1. struct 结构体名称 { 结构体成员列表 }
  2. 实例化的三种方法
  • struct 结构体名 变量名
  • struct 结构体名 变量名 = {成员 1 值,成员 2 值...}
  • 定义结构体时顺便创建

创建结构体

1
2
3
4
5
struct Student {
string name;
int age;
int score;
}s3;

实例化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//1. struct 结构体名 变量名
struct Student s1;
s1.name = "张三";
s1.age = 18;
s1.score = 78.5;
cout << s1.name << "\t" << s1.age << "\t" << s1.score << endl;

//2. struct 结构体名 变量名 = {}
struct Student s2 = { "李四",20,80.0 };
cout << s2.name << "\t" << s2.age << "\t" << s2.score << endl;


//3. 创建结构体时创建变量s3
s3.name = "王五";
s3.age = 23;
s3.score = 90.0;
cout << s3.name << "\t" << s3.age << "\t" << s3.score << endl;

7.3 结构体数组

  1. struct Student stuArray[2] = {{"张三",18,60},{"李四",20,80}};
  2. 索引从第 0 个开始
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
#include<iostream>
using namespace std;

//1. 定义结构体
struct Student {
string name;
int age;
double score;
};


int main()
{
//2. 创建结构体数组
struct Student stuArray[3] = {
{"张三",18,20},
{"李四",18,20},
{"王五",18,20},
};

//3. 赋值
stuArray[2].name = "赵六";
stuArray[2].age = 20;
stuArray[2].score = 30;

//4. 遍历
for (int i = 0; i < 3; i++) {
cout << stuArray[i].name << "\t" << stuArray[i].age << "\t" << stuArray[i].score << endl;
}


}

7.4 结构体指针

  1. 指向结构体struct Student* p = &s;,其中struct可以省略;
  2. 访问结构体指针访问p->name;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<iostream>
using namespace std;

//1.定义结构体
struct Student {
string name;
int age;
double score;
};


int main() {
//2.创建结构体变量
struct Student s = {"张三",18,80};

//3.通过指针指向结构体变量
struct Student* p = &s;

//4.通过指针访问结构体变量
cout << p->name <<"\t" << p->age<<"\t" << p->score << endl;
}

7.5 结构体嵌套结构体

  1. 结构体 1 作为结构体 2 的成员变量;
  2. 结构体 1 应该定义在结构体 2 前面;
  3. 结构体里结构体赋值t.stu.score = 80;或者t.stu = { "张三",20,100 };
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
#include<iostream>
using namespace std;

// 定义学生的结构体
struct Student {
string name;
int age;
double score;
};

// 定义老师的结构体
struct Teacher {
int id;
string name;
int age;
//将另一个结构体作为本结构体的成员
struct Student stu;
};


int main() {
Teacher t;
t.id = 01;
t.name = "老师1";
t.age = 45;
t.stu.name = "学生1";
t.stu.age = 18;
t.stu.score = 80;
//或者
t.stu = { "张三",20,100 };

cout << t.stu.name << endl;
}

7.6 结构体做函数参数

  1. 将结构体作为参数传递到函数里;
  2. 值传递,不会修改原对象值;
  3. 地址传递,会改变原对象值;
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
#include<iostream>
using namespace std;

// 定义学生的结构体
struct Student {
string name;
int age;
double score;
};

// 值传递,不会修改原对象值
void printStudent(struct Student s) {
cout << "1.打印信息:" << s.name << "\t" << s.age << "\t" << s.score << endl;
};
// 地址传递,会改变原对象值
void printStudent(struct Student *p) {
cout << "2.打印信息:" << p->name << "\t" << p->age << "\t" << p->score << endl;
};

int main() {
struct Student s = {"张三",18,90};
printStudent(s);
printStudent(&s);

}

7.7 结构体 const

在某些时候我们需要将结构体变量传入到一个函数内,如果使用值传递就会占用大量的内存空间,因为每调用一次函数,计算机就会赋值出一个新的结构体变量,大小和原变量相同,所以应采用地址传递来节省空间,因为指针只占用 4 个字节。但是使用地址传递会带来一个隐患,即可以在函数体内修改原结构体变量的值,这对数据来说是不安全的,我们需要用 const 进行修饰,使结构体变量不可更改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
using namespace std;

// 定义学生的结构体
struct Student {
string name;
int age;
double score;
};

// 加上const修饰,就可以避免在地址传递时,函数体内修改原变量的属性
void printStu(const Student* p) {

//p->age = 20;//错误,不能修改
cout << p->name << "\t" << p->age << "\t" << p->score << endl;

}

int main() {
//创建结构体变量
Student s = { "张三",18,90 };
printStu(&s);
}

7.8 结构体案例 1

  1. 注意字符串字面量和字符相加,使用+=比较好;
  2. 随机数种子;
  3. 判断数据类型typeid(i).name();
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include<iostream>
using namespace std;
#include<string>
#include<ctime>

// 定义学生的结构体
struct Student {
string name;
int score;
};


// 老师的结构体
struct Teacher {
string name;
Student stuArray[5];
};


// 给老师和学生赋值函数
void allocateSpace(struct Teacher teaArray[], int len) {
string nameSpace = "ABCDE";
for (int i = 0; i < len; i++) {
string tname = "Teacher_" ;
tname += nameSpace[i];
teaArray[i].name = tname;
// 学生赋值
for (int j= 0; j < 5; j++) {
string sname = "Student_" ;
sname += +nameSpace[j];
int sscore = rand() % 61 + 40;//40~100
teaArray[i].stuArray[j].name = sname;
teaArray[i].stuArray[j].score = sscore;
}
}
}

void printFor(struct Teacher teaArray[], int len) {
for (int i = 0; i < len; i++) {
cout << teaArray[i].name << endl;
for (int j = 0; j < 5; j++) {
cout << "\t" << teaArray[i].stuArray[j].name << "\t" << teaArray[i].stuArray[j].score << endl;
}
}
}

int main() {

// 随机数种子
srand((unsigned int)time(NULL));

// 创建3名老师
struct Teacher teaArray[3];

// 给老师的学生赋值
int len = sizeof(teaArray) / sizeof(teaArray[0]);
allocateSpace(teaArray, len); //数组本身就是地址传递
printFor(teaArray, len);

}

7.9 结构体案例 2

  1. 冒泡排序;
  2. 临时变量 temp,类型为 hero;
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
43
44
45
46
47
48
49
50
51
52
53
54
#include<iostream>
#include<string>
using namespace std;

// 设计结构体
struct hero {
string name;
int age;
string sex;
};

// 冒泡排序函数
void rink(struct hero heroArray[],int len) {
for (int i = 0; i < len-1; i++) {
for (int j = 0; j < len - i - 1; j++) {
if (heroArray[j].age > heroArray[j+1].age) {
struct hero temp = heroArray[j];
heroArray[j] = heroArray[j+1];
heroArray[j+1] = temp;
}
}
}
}

// 打印输出
void printHero(struct hero heroArray[], int len) {
for (int i = 0; i < len; i++) {
cout << heroArray[i].name << "\t" << heroArray[i].age << "\t" << heroArray[i].sex << endl;
}

}


int main() {

// 创建英雄
struct hero heroArray[5] = {
{"刘备",23,"男"},
{"关羽",22,"男"},
{"张飞",20,"男"},
{"赵云",21,"男"},
{"貂蝉",19,"男"},
};

// 数组排序
int len = sizeof(heroArray) / sizeof(heroArray[0]);
rink(heroArray, len);

// 打印输出
printHero(heroArray, len);

system("pause");
return 0;
}

7.10 简单链表

链表:需要的时候就分配一块空间,内存是动态变化的。而数组长度是固定不变的。

实现

  1. 定义一个超女结构体,含有编号、姓名、下一个超女的指针;
1
2
3
4
5
6
struct SuperGril
{// 定义编号、姓名和下一个超女的指针
int no;
string name;
struct SuperGril* next;
};
  1. 初始化头号超女 header、尾部超女 tail 、临时超女 temp;SuperGril* header = nullptr, * tail = nullptr, * temp = nullptr;

  2. 使用临时节点创建超女,并给 header 和 tail 赋值

1
2
3
4
5
6
7
8
9
10
11
// 分配第一个节点,并赋值
temp = new SuperGril({ 1,"西施",nullptr });
header = tail = temp;

temp = new SuperGril({ 2,"冰冰",nullptr });
tail->next = temp; // 上一个节点的next指向新节点
tail = temp; // 尾部为新节点

temp = new SuperGril({ 3,"丽英",nullptr });
tail->next = temp; // 上一个节点的next指向新节点
tail = temp; // 尾部为新节点
  1. 遍历链表
1
2
3
4
5
6
7
// 遍历链表
temp = header;
while (temp!=nullptr)
{
cout << "编号" << temp->no << "\t姓名" <<temp->name << "\t下一个节点" << temp->next << endl;
temp = temp->next; // 向后查找
}
  1. 释放链表内存
1
2
3
4
5
6
7
// 释放链表内存
while (header!=nullptr)
{
temp = header;
header = header->next;
delete temp;
}

全部代码

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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include<iostream>
#include<string>
using std::cout;
using std::string; using std::endl;

int main()
{
struct SuperGril
{// 定义编号、姓名和下一个超女的指针
int no;
string name;
struct SuperGril* next;
};

SuperGril* header = nullptr, * tail = nullptr, * temp = nullptr;

// 分配第一个节点,并赋值
temp = new SuperGril({ 1,"西施",nullptr });
header = tail = temp;

temp = new SuperGril({ 2,"冰冰",nullptr });
tail->next = temp; // 上一个节点的next指向新节点
tail = temp; // 尾部为新节点

temp = new SuperGril({ 3,"丽英",nullptr });
tail->next = temp; // 上一个节点的next指向新节点
tail = temp; // 尾部为新节点

// 传统赋值
//temp->no = 1;
//temp->name = "西施";
//temp->next = nullptr;

// c++11推荐
//*(temp) = { 1,"西施",nullptr };

// 遍历链表
temp = header;
while (temp!=nullptr)
{
cout << "编号" << temp->no << "\t姓名" <<temp->name << "\t下一个节点" << temp->next << endl;
temp = temp->next; // 向后查找
}

// 释放链表内存
while (header!=nullptr)
{
temp = header;
header = header->next;
delete temp;
}

printf("释放完毕\n");

system("pause");
return 0;
}

8、共同体

  1. 共同体占用的内存大小是成员占用内存最大的大小;(内存对齐)
  2. 全部成员使用一块内存;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
using std::string; using std::cout; using std::endl;

int main()
{
union MyUnion
{
int a;
double b;
char c[21];
};
MyUnion data;

// 占用大小
cout << "内存占用大小=" << sizeof(data) << endl; //24

cout << "内存地址data.a=" << (void*) &data.a << endl;
cout << "内存地址data.b=" << (void*) &data.b << endl;
cout << "内存地址data.c=" << (void*) &data.c << endl;

system("pause");
return 0;
}
image-20221023115211935

9、枚举

创建常量的一种方式

创建常量的方法:

  1. 宏常量,用预处理指令#define 创建
  2. 用 const 关键字创建;
  3. 枚举

注意事项:

  1. 枚举创建的变量取值只能在枚举范围之内;
  2. 枚举的作用域与变量的作用域相同;
  3. 可以设置枚举量的值
  4. 整型强制转为枚举量:Colors cc = Colors(1);
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
#include<iostream>
using std::string; using std::cout; using std::endl;

int main()
{
enum Colors
{
red=0,yellow,blue,other
};

// 创建一个枚举变量,只能为Colors中的一个
Colors color = red;

switch (color) // color是整数,可以用来switch
{
case red: // red是整型常量,可用来case
cout << "红色" << endl;
break;
case yellow:
cout << "黄色" << endl;
break;
case blue:
cout << "蓝色" << endl;
break;
case other:
cout << "红色" << endl;
break;
default:
cout << "未知" << endl;
break;
}

system("pause");
return 0;
}

10、指针

1
2
3
4
5
6
7
8
9
10
double dval;
double* pd = &dval;
double* pd2 = pd;
*pd2 = 2.0;
cout << "dval是"<<dval << endl;
cout << "dval的地址是"<<&dval << endl;
cout << "*pd是"<<*pd << endl;
cout << "pd的地址是"<<&pd << endl;
cout << "pd本身是"<<pd << endl;
cout << "pd2是"<<*pd2 << endl;

dval 是 2 dval 的地址是 0000004D63EFF808 pd 是 2 pd 的地址是 0000004D63EFF828 pd 本身是 0000004D63EFF808 pd2 是 2

11、函数

11.1 函数默认值

  1. 如果一个形参位置已经有默认值,则后面的形参都得有默认值;
  2. 如果函数声明中形参已经有默认值,在函数定义时就不能给默认值,否则会报错;
1
2
3
4
5
6
7
8
9
10
#include<iostream>
using namespace std;
int f1(int a, int b = 20, int c = 30)
{
return a + b + c;
}
int main() {
f1(10); //可以只传递一个,因为在函数中b、c已经给过值
f1(10,60); //如果给b传了值,则用传入的
}

声明和定义

1
2
3
4
5
6
7
8
9
#include<iostream>
using namespace std;
int f2(int a = 10, int b = 10);
int f2(int a = 10,int b = 10)
{
}
int main() {
f2();
}

报错:f2 重定义默认参数

11.2 函数占位参数

  1. c++函数形参列表可以有站位参数,但调用函数时必须补齐该位置;
  2. 语法:数据类型int 函数名f(数据类型int)
  3. 占位参数也可以有默认参数;
1
2
3
4
5
6
7
8
void f(int)
{
cout << "你好" << endl;
}
int main()
{
f(10);
}

默认参数

1
2
3
4
5
6
7
8
void f(int = 10)
{
cout << "你好" << endl;
}
int main()
{
f();//这里就可以不用传
}

11.3 函数的重载

  • 同一个作用域内;
  • 函数名相同;
  • 函数参数类型不同或者个数不同或者顺序不同
  1. 函数名可以相同,提高复用性;
  2. 返回值不可以作为重载的条件;void f()和int f()
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
#include<iostream>
using namespace std;
void f()
{
cout << "f()调用了!" << endl;
}
void f(int a)
{
cout << "f(int a)调用了!" << endl;
}
void f(int a,int b)
{
cout << "f(int a,int b)调用了!" << endl;
}
void f(double b)
{
cout << "f(double b)调用了!" << endl;
}
int main()
{
f();
f(10);
f(10.0);
f(10,20);
}

11.4 函数重载注意事项

  1. 引用作为重载的条件;
1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
using namespace std;
void f(int &a) {
cout << "f(int &a)调用了!" << endl;
}
void f(const int &a) {
cout << "f(const int &a)调用了!" << endl;
}
int main() {
int a = 10;
f(a); // f(int &a)调用了!
f(10);// f(const int &a)调用了!
}

函数重载遇见默认参数

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
using namespace std;
void f(int &a,int &b = 10) {
cout << "f(int &a ,int &b = 10)调用了!" << endl;
}
void f(int &a) {
cout << "f(const int &a)调用了!" << endl;
}
int main() {
int a = 10;
f(a); // 错误
}

11.5 内联函数

函数声明之后定义,在主函数中被多次调用,原始写法:这样的写法会使程序在函数之间来回跳转,消耗一定的执行速度代价。使用内联函数的方法可以将 show 函数放在 main 函数里,避免来回跳转;但会占用一定的内存;使用较小的函数体

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<iostream>
using std::cout; using std::endl; using std::string;
void show(const short num, const string message);
int main()
{
show(1, "我是啥啥鸟");
show(2, "我是啥啥鸟");
show(3, "我是啥啥鸟");
system("pause");
return 0;
}
void show(const short num, const string message)
{
cout << "亲爱的" << num << "号," << message << endl;
}

内联函数:相当于把函数嵌入进去

  1. 内联函数不能递归
  2. 函数体太大的函数,编译器不会将其作为内联函数
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
inline void show(const short num, const string message);
int main()
{
show(1, "我是啥啥鸟");
//{
// short num = 1;
// string message = "我是啥啥鸟";
// cout << "亲爱的" << num << "号," << message << endl;
//}
show(2, "我是啥啥鸟");
//{
// short num = 1;
// string message = "我是啥啥鸟";
// cout << "亲爱的" << num << "号," << message << endl;
//}
show(3, "我是啥啥鸟");
//{
// short num = 1;
// string message = "我是啥啥鸟";
// cout << "亲爱的" << num << "号," << message << endl;
//}
system("pause");
return 0;
}
inline void show(const short num, const string message)
{
cout << "亲爱的" << num << "号," << message << endl;
}

C++基础
http://example.com/2024/03/17/001 C++基础/
作者
DB
发布于
2024年3月17日
许可协议