系统学习C++
方便自己日后复习,错误的地方希望积极指正
参考视频:黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难
1 第一个C程序-HelloWorld
编写一个C++程序分为四个步骤:
(1)创建项目
(2)创建文件
(3)编写代码
(4)运行程序
#include <iostream>
using namespace std;
/*
main 是一个程序的入口
每个程序有且只有一个
*/
int main()
{
cout << "hello world" << endl; // 输出Hello world
system("pause");
return 0;
}
2 程序的注释
编译器不会执行注释的内容
作用:代码添加说明,方便阅读
两种格式:
(1)单行注释://
通常放在语句的上方,或者一条语句的结尾
(2)多行注释:/**/
通常放在一段代码的上方
3 变量
变量存在的意义:方便我们管理内存空间
作用:给一段指定的内存空间起名,方便操作这段内存
语法:数据类型 变量名 = 初始值;
#include <iostream>
using namespace std;
int main()
{
int a = 10;
cout << "a = " << a << endl;
system("pause");
return 0;
}
4 常量
作用:用于记录程序中不可更改的数据
定义常量的方式:
(1)#define
宏常量:#define 常量名 常量值
通常在文件上方定义,表示一个常量
(2)const
修饰变量:const 数据类型 常量名 = 常量值
通常在变量定义前加关键字const,修改变量为常量,不可修改
#include <iostream>
using namespace std;
#define Day 7
int main()
{
cout << Day << endl;
const int month = 12;
system("pause");
return 0;
}
5 C++常用编程关键字
作用:C++预先保留的单词(标识符)
在定义变量或者常量时,不要用关键字,否则产生歧义
#include <iostream>
using namespace std;
int main()
{
int a = 10;
// int int = 10; 错误,第二个int是关键字
system("pause");
return 0;
}
6 标识符命名规则
作用:C++规定给标识符(变量、常量)命名时,有一套自己的规则
- 标识符不能是关键字
- 标识符只能由字母、数字、下划线组成
- 第一个字符必须为字母或者下划线
- 标识符中字母区分大小写
给变量起名的时候,最好能够做到见名知意。
7 数据类型
7.1 整型
C++规定在创建一个变量或者常量时,必须指定相应的数据类型,否则无法给变量分配内存。
数据类型存在意义:给变量分配合适的内存空间
#include <iostream>
using namespace std;
int main()
{
short num1 = 10; // (-32768- 32767)
int num2 = 10;
long num3 = 10;
long long num4 = 10;
cout << "num1 = " << sizeof(num1) << endl; // 2
cout << "num2 = " << sizeof(num2) << endl; // 4
cout << "num3 = " << sizeof(num3) << endl; // 4
cout << "num4 = " << sizeof(num4) << endl; // 8
system("pause");
return 0;
}
7.1.1 整数的书写
整数默认是十进制,一个表示十进制的数字不需要任何特殊的格式
(1)二进制
二进制由 0 和 1 两个数字组成,书写必须以0b或0B开头
(2)八进制
(3)十六进制
Tip:在C++中,不要在十进制数前面加0,会被编译器当成八进制,还有,不要随便删掉别人程序中整数前面的0,它不是多余的
#include <iostream>
using namespace std;
int main()
{
// 十进制
int a = 100;
cout << a << endl; // 100
// 二进制
int b = 0b101; // 5
int c = 0B1; // 1
cout << b << " " << c << endl;
// 八进制(设备的权限)
int d = 015; // 13
int e = -0101; // -65
cout << d << " " << e << endl;
// 十六进制(内存的地址)0 ~ 9 A ~ F a ~ f
int m = 0X2A; // 42
int n = -0XA0; // -160
cout << m << " " << n << endl;
return 0;
}
7.2 sizeof关键字
作用:利用sizeof关键字可以统计数据类型所占内存大小
语法:sizeof(数据类型 或者 变量)
#include <iostream>
using namespace std;
// short < int <= long <= longlong
int main()
{
short num1 = 10;
cout << sizeof(num1) << ' ' << sizeof(short);
system("pause");
return 0;
}
7.3 实型(浮点型)
作用:用于表示小数
(1)单精度 float
(2)双精度 double
区别:表示的有效数字范围不同
#include <iostream>
using namespace std;
// 默认情况下,输出一个小数,会显示6位有效数字
int main()
{
float f1 = 3.1415926f; // 默认double,在后面加f 让double - > float 3.14159
cout << f1 << endl;
double d1 = 3.1415926;
cout << d1 << endl;
// float double占用的内存空间
cout << sizeof(float) << ' ' << sizeof(double) << endl;// float:4 double:8
// 科学计数法
float f2 = 3e2; // 3 * 10^2 = 300
cout << f2 << endl;
float f3 = 3e-2; // 3 * 0.1^2 = 0.03
cout << f3 << endl;
system("pause");
return 0;
}
7.4 字符型
作用:字符型变量用于显示单个字符
语法:char ch = 'a';
Tip:
(1) 在显示字符型变量,用单引号,不能用双引号
(2) 单引号内只能有一个字符,不能是字符串
C/C++字符型变量只占1个字节
字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCIl编码放入到存储单元
#include <iostream>
using namespace std;
int main()
{
// 字符型变量创建
char ch = 'A';
cout << ch << endl;
// 字符型变量所占内存
cout << sizeof(char) << endl;
// 字符型变量常见错误
/* char ch2 = "s";
* char ch2 = 'ads';
*/
// 字符型变量对应的ASCII编码 a-97 A-65
cout << (int)ch << endl;
system("pause");
return 0;
}
7.5 转义字符
作用:用于表示一些不能显示出来的ASCII字符
现阶段常用的转义字符:\n \\ \t
#include <iostream>
using namespace std;
int main()
{
// 换行符
cout << "hello world" << '\n';
// 反斜杠
cout << "\\" << endl;
// 水平制表符 整齐输出数据
cout << "aaa\twad" << endl; // aaa(5个空格)wad 8个
cout << "aaaaa\twad" << endl;
cout << "aaaa\twad" << endl;
system("pause");
return 0;
}
7.6 字符串型
作用:用于表示一串字符
两种风格:
- C风格字符串:
char 变量名[] = "字符串值";
- C++风格字符串:
string 变量名 = "字符串值";
C++风格字符串,需要包含头文件
<string>
#include <iostream>
#include <string>
using namespace std;
int main()
{
cout << "hello world" << endl;
// char 字符串名[]
// =后面用双引号包含起来字符串
// C风格字符串
char str1[] = "hello world";
cout << str1 << endl;
// C++风格字符串,需要包含头文件<string>
string str2 = "hello world";
cout << str2 << endl;
system("pause");
return 0;
}
7.7 布尔类型 bool
作用:布尔类型代表真或假的值
bool类型只有两个值:
- true (本质是1)
- false (本质是0)
bool类型占1个字节大小
#include <iostream>
using namespace std;
int main()
{
// 创建bool类型
bool flag = true;
cout << flag << endl; // 1
flag = false;
cout << flag << endl; // 0
// 查看bool类型所占内存空间
cout << sizeof(bool) << endl; // 1
system("pause");
return 0;
}
7.8 数据的输入
作用:用于从键盘获取数据
关键字:cin
语法:cin >> 变量
#include <iostream>
#include <string>
using namespace std;
int main()
{
int a = 0;
// 整型
cout << "Please input a: " << endl;
cin >> a;
cout << "a = " << a << endl;
// 浮点型
float f = 3.14f;
cout << "please input f: " << endl;
cin >> f;
cout << "f = " << f << endl;
// 字符型
char ch = 'a';
cout << "Please input ch: " << endl;
cin >> ch;
cout << "ch = " << ch << endl;
// 字符串型
string str = "hello";
cout << "Please input str: " << endl;
cin >> str;
cout << "str = " << str << endl;
// 布尔类型
bool flag = false;
cout << "Please input flag: " << flag << endl;
cin >> flag; // 布尔类型只要非0都代表真
cout << "flag = " << flag << endl;
system("pause");
return 0;
}
7.9 typedef
创建数据类型的别名:
(1)为名称复杂的类型创建别名,方便书写和记忆
(2)创建与平台无关的数据类型,提高程序兼容性
typedef 原数据类型名 别名;
C++11还可以用using关键字创建数据类型的别名
using 别名=原数据类型名;
#include <iostream>
using namespace std;
int main()
{
typedef short int16_t;
typedef int int32_t;
typedef long long int64_t;
}
8 运算符
作用:用于执行代码的运算
8.1 算术运算符
两个小数是不可以做取模运算的
两个小数可以相除
8.2 赋值运算符
#include <iostream>
using namespace std;
int main()
{
// =
int a = 10;
a = 100;
cout << a << endl;
// +=
a = 10;
a += 2;
cout << a << endl;
// -=
a = 10;
a -= 2;
cout << a << endl;
// *=
a = 10;
a *= 2;
cout << a << endl;
// /=
a = 10;
a /= 2;
cout << a << endl;
// %=
a = 10;
a %= 2;
cout << a << endl;
system("pause");
return 0;
}
8.3 比较运算符
#include <iostream>
using namespace std;
int main()
{
// ==
int a = 10;
int b = 20;
cout << (a == b) << endl;
// !=
cout << (a != b) << endl;
// >
cout << (a > b) << endl;
// <
cout << (a < b) << endl;
// >=
cout << (a >= b) << endl;
// <=
cout << (a <= b) << endl;
system("pause");
return 0;
}
8.4 逻辑运算符
! && ||
#include <iostream>
using namespace std;
int main()
{
// 非!
// 在C++中, 除了0 都为真
int a = 10;
cout << !a << endl; // 0
cout << !!a << endl;// 1
// 与&&
// &&: 两个条件都为真,结果才为真
int b = 10;
int c = 10;
int d = 0;
int e = 0;
cout << (b && c) << endl;// 1
cout << (b && d) << endl;// 0
cout << (d && e) << endl;// 0
// 或||
// ||: 有一个真就是真,两个假就是假
cout << (b || c) << endl; // 1
cout << (b || d) << endl; // 1
cout << (d || e) << endl; // 0
system("pause");
return 0;
}
9 程序流程结构
9.1 选择结构
9.1.1 if 语句
作用:执行满足条件的语句
#include <iostream>
using namespace std;
int main()
{
// 单行if
int score = 0;
cout << "Please input score: " << endl;
cin >> score;
cout << "score = " << score << endl;
if (score >= 650) // if后面不要加;
{
cout << "OK!!!!" << endl;
}
// 多行if
if (score >= 650)
{
cout << "ok !!!!" << endl;
}
else
{
cout << "No !!!!" << endl;
}
// 多条件if
if (score >= 650)
{
cout << "ok" << endl;
}
else if (score <= 250)
{
cout << "yes" << endl;
}
else
{
cout << "No" << endl;
}
// 嵌套if
if (score >= 650)
{
if (score >= 700)
{
cout << "Beida" << endl;
}
}
system("pause");
return 0;
}
案例:三只小猪称体重
有三只小猪ABC,请分别输入三只小猪的体重,并且判断哪只小猪最重
#include <iostream>
using namespace std;
int main()
{
int A, B, C;
cin >> A >> B >> C;
if (A > B)
{
if (A > C)
{
cout << "A最重" << endl;
}
else
{
cout << "C最重" << endl;
}
}
else
{
if (B > C)
{
cout << "B最重" << endl;
}
else
{
cout << "C最重" << endl;
}
}
system("pause");
return 0;
}
9.1.2 三目运算符
语法:表达式1 ?表达式2 :表达式3
#include <iostream>
using namespace std;
int main()
{
int a = 10;
int b = 20;
int c = 0;
c = (a > b ? a : b);
cout << c << endl;
// 在C++中三目运算符返回的是变量,可以继续赋值
(a < b ? a : b) = 100;
cout << a << ' ' << b << endl;
system("pause");
return 0;
}
9.1.3 switch语句
#include <iostream>
using namespace std;
int main()
{
// switch语句
int score = 0;
cin >> score;
cout << score << endl;
switch (score)
{
case 10:
cout << "Classical" << endl;
break;// 退出当前分支
case 9:
cout << "Classical" << endl;
break;
case 8:
cout << "Very Good" << endl;
break;
default:
cout << "common" << endl;
break;
}
system("pause");
return 0;
}
if 和 switch 区别
switch缺点:判断只能是整型或者字符型,不可以是一个区间
switch优点:结构清晰,执行效率高
9.2 循环结构
9.2.1 while
#include <iostream>
using namespace std;
int main()
{
// while
int num = 0;
while (num < 10)// 0 ~ 9 写循环避免死循环
{
cout << num << endl;
num++;
}
system("pause");
return 0;
}
案例:猜数字
#include <iostream>
// time系统时间头文件包含
#include <ctime>
using namespace std;
int main()
{
// 添加随机数种子 利用当前系统生成随机数,防止每次随机数都一样
srand((unsigned int)time(NULL));
// 系统生成随机数
int num = rand()%100 + 1;// 0 ~ 99 -> 1 ~100
// 玩家进行猜测
int val = 0;
while (1)
{
cin >> val;
// 判断玩家的猜测 猜对,退出 猜错,提示猜的结果(过大,过小)
if (val > num)
{
cout << "caice guoda" << endl;
}
else if (val < num)
{
cout << "guoxiao" << endl;
}
else
{
cout << "right" << endl;
break;
}
}
system("pause");
return 0;
}
9.2.2 do…while
#include <iostream>
using namespace std;
int main()
{
int num = 0;
do {
cout << num << endl;
num++;
}while (num < 10);
system("pause");
return 0;
}
do…while和while循环区别在于do…while会先执行一次循环语句
案例:水仙花数
#include <iostream>
using namespace std;
int main()
{
// 100 ~ 999
int num = 100;
do {
int a = 0;
int b = 0;
int c = 0;
a = num % 10; // 个位
b = num / 10 % 10;// 十位
c = num / 100; // 百位
if (a * a * a + b * b * b + c * c * c == num) // 如果是水仙花,才打印
{
cout << num << endl;
}
num++;
}while (num < 1000);
system("pause");
return 0;
}
9.2.3 for循环语句
#include <iostream>
using namespace std;
int main()
{
// for (int i = 0; i < 10; i++)
// {
// cout << i << endl;
// }
int i = 0;
for ( ; ; )
{
if (i >= 10)
{
break;
}
i++;
cout << i << endl;
}
system("pause");
return 0;
}
#include <iostream>
using namespace std;
int main()
{
for (int i = 1; i <= 100; i++)
{
if (i % 7 == 0 || i % 10 == 7 || i / 10 == 7)
{
cout << "lalala" << endl;
}
else
{
cout << i << endl;
}
}
system("pause");
return 0;
}
9.2.4 嵌套循环
#include <iostream>
using namespace std;
int main()
{
for (int i = 0; i < 10; i++)// 外层循环
{
for (int j = 0; j < 10; j++)// 内层循环
{
cout << "* ";
}
cout << endl;
}
system("pause");
return 0;
}
案例:乘法口诀表
#include <iostream>
using namespace std;
int main()
{
for (int i = 1; i <=9; i++)
{
for (int j = 1; j <= i; j++)
{
cout << j << " * " << i << " = " << j * i << " ";
}
cout << endl;
}
system("pause");
return 0;
}
9.3 break语句
作用:用于跳出选择结构或者循环结构
使用:
- switch条件语句中,终止case并跳出switch
- 出现在循环语句中,作用是跳出当前的循环语句
- 出现在嵌套循环中,跳出最近的内层循环语句
#include <iostream>
using namespace std;
int main()
{
// switch
int select = 0;
cin >> select;
switch (select)
{
case 1:
cout << "common" << endl;
break;
case 2:
cout << "middle" << endl;
break;
case 3:
cout << "hard" << endl;
break;
default:
break;
}
// 循环语句
for (int i = 0; i < 10; i++)
{
if (i == 5)
{
break;
}
cout << i << endl;
}
// 嵌套循环语句
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
if (j == 5)
{
break; // 退出内层循环
}
cout << " * ";
}
cout << endl;
}
system("pause");
return 0;
}
9.4 continue语句
作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环
#include <iostream>
using namespace std;
int main()
{
// continue
for (int i = 0; i <= 100; i++)
{
if (i % 2 == 0)
{
continue; // 可以筛选条件,执行到此不再往下执行,执行下一次循环
}
cout << i << endl;
}
system("pause");
return 0;
}
9.5 goto语句
作用:无条件跳转语句
语法:goto 标记
#include <iostream>
using namespace std;
int main()
{
// goto语句
cout << "1" << endl;
cout << "2" << endl;
goto FLAG;
cout << "3" << endl;
cout << "4" << endl;
FLAG:
cout << "5" << endl;
system("pause");
return 0;
}
10 数组
10.1 一维数组定义方式
特点:
(1)数组中每个数据元素都是相同的数据类型
(2)数组是由连续的内存位置组成的
#include <iostream>
using namespace std;
int main()
{
// 数组定义
int arr1[5];
arr1[0] = 1;
arr1[1] = 2;
int arr2[5] = {10, 20, 30, 40, 50};
for (int i = 0; i < 5; i++)
{
cout << arr2[i] << endl;
}
int arr3[] = {1, 2, 3, 4, 5}; // 定义数组的时候,必须有初始长度
system("pause");
return 0;
}
Tip:
(1) 数组名的命名规范与变量名命名规范一致,不要和变量重名
(2) 数组中下标是从0开始索引
10.2 一维数组 - 数组名
一维数组名称的用途:
1、可以统计整个数组在内存中的长度 sizeof(arr)
2、可以获取数组在内存中的首地址 cout << arr << endl;
#include <iostream>
using namespace std;
int main()
{
// 可以统计整个数组在内存中的长度
int arr[10] = {1, 2, 4,5, 6,7, 8, 9, 10, 11};
cout << sizeof(arr) << endl; // 40 = 4 * 10
cout << sizeof(arr[0]) << endl; // 4
cout << sizeof(arr) / sizeof(arr[0]) << endl; // 10
// 可以获取数组在内存中的首地址
cout << arr << endl;
cout << &arr[0] << endl;
system("pause");
return 0;
}
数组名是一个常量,不可以进行赋值操作
五只小猪称体重
#include <iostream>
using namespace std;
int main()
{
// 五只小猪称体重
int arr[5] = {300, 350, 200, 400, 250};
int max = 0;
for (int i = 0; i < 5; i++)
{
if(arr[i] > max) max = arr[i];
}
cout << max << endl;
system("pause");
return 0;
}
数组元素逆置
#include <iostream>
using namespace std;
int main()
{
int arr[5] = {1, 3, 2, 5, 4};
for (int i = 0; i < 5; i++)
{
cout << arr[i] << ' ';
}
cout << endl;
int start = 0;
int end = sizeof(arr) / sizeof(arr[0]) - 1;
while (start < end)
{
int temp = arr[start];
arr[start] = arr[end];
arr[end] = temp;
start++;
end--;
}
for (int i = 0; i < 5; i++)
{
cout << arr[i] << ' ';
}
cout << endl;
system("pause");
return 0;
}
冒泡排序
#include <iostream>
using namespace std;
int main()
{
int arr[9] = {4, 2, 8, 0, 5, 7, 1, 3, 9};
for (int i = 0; i < 9; i++)
{
cout << arr[i] << ' ';
}
cout << endl;
for (int i = 0; i < 9 - 1; i++)
{
for (int j = 0; j < 9 - i - 1; j++)
{
if (arr[j] > arr[j + 1])
{
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
for (int i = 0; i < 9; i++)
{
cout << arr[i] << ' ';
}
cout << endl;
system("pause");
return 0;
}
10.3 二维数组定义方式
#include <iostream>
using namespace std;
int main()
{
// 二维数组定义方式
// No.1
int arr1[2][3];
arr1[0][0] = 1;
arr1[0][1] = 2;
arr1[0][2] = 3;
arr1[1][0] = 4;
arr1[1][1] = 5;
arr1[1][2] = 6;
// No.2
int arr2[2][3] =
{
{1, 2, 3},
{4, 5, 6}
};
// No.3
int arr3[2][3] = {1, 2, 3, 4, 5, 6};
// No.4
int arr4[][3] = {1,2,3,4,5,6,7,8,9};
for (int i = 0; i < 2; i++)
{
for (int j = 0; j < 3; j++)
{
cout << arr1[i][j] << ' ';
}
}
system("pause");
return 0;
}
10.4 二维数组名
(1)查看二维数组所占内存空间
(2)获取二维数组首地址
#include <iostream>
using namespace std;
int main()
{
double arr[2][3] =
{
{1, 2, 3},
{4, 5, 6}
};
cout << sizeof(arr) << endl;
cout << sizeof(arr[0]) << endl; // 第一行占用内存
cout << sizeof(arr[0][0]) << endl; // 第一个元素占用内存
cout << sizeof(arr) / sizeof(arr[0]) << endl;
cout << sizeof(arr[0]) / sizeof(arr[0][0]) << endl;
cout << arr << endl;
cout << arr[0] << endl;
cout << &arr[0][0] << endl;
system("pause");
return 0;
}
案例:
#include <iostream>
using namespace std;
int main()
{
/*
* 0 300
* 1 240
* 2 210
* */
int score[3][3] =
{
{100, 100, 100},
{90, 50, 100},
{60, 70, 80}
};
for (int i = 0; i < 3; i++)
{
int sum = 0;
for (int j = 0; j < 3; j++)
{
sum += score[i][j];
}
cout << i << ' ' << sum << endl;
}
system("pause");
return 0;
}
11 函数
函数的声明和定义可以书写在一起,也可以分开,如果书写在一起,一般放在main函数的上面,如果分开,一般在main函数的上面声明,在main函数的下面定义
C++中定义变量的场景主要有五种:
1)在全部函数外面定义的是全局变量。
2)在头文件中定义的是全局变量。
3)在函数和语句块内部定义的是局部变量。
4)函数的参数是该函数的局部变量。
5)函数内部用static修饰的是静态局部变量。
当程序想要使用全局变量的时候应该先考虑使用static(考虑到数据安全性)
11.1 函数分文件编写
*.h:声名全局变量,函数声名,数据结构和类的声名
*.cpp:函数的定义、类的定义
11.2 在VS中调试程序
11.3 递归函数
递归函数:运行时调用自己的函数
Tip:递归函数中一定要有递归终止的条件,否则就是死递归
#include <iostream>
using namespace std;
int f(int x)
{
if (x == 0) return 0; // 递归截止条件
return x + f(x - 1);
}
int main()
{
cout << "f(3)= " << f(3) << endl;
system("pause");
return 0;
}
12 指针
C++用运算符&获取变量在内存中的起始地址
指针变量简称指针,它是一种特殊的变量,专用于存放变量在内存中的起始地址
不管是整型、浮点型、字符型,还是其它的数据类型的变量,它的地址都是一个十六进制数。我们用整型指针存放整数型变量的地址;用字符型指针存放字符型变量的地址;用浮点型指针存放浮点型变量的地址,用自定义数据类型指针存放自定义数据类型变量的地址。
指针=&变量名
Tip:
(1) 对指针的赋值操作被称为“指向某变量”,被指向的变量数据类型称为“基类型”
(2) 指针的数据类型与基类型不符,编译会出现警告。但是,可以强制转换它们的类型
12.1 使用指针
指针用于函数的参数
值传递
:函数的形参是普通变量
传地址
:可以在函数中修改实参的值;减少内存拷贝,提升性能
12.2 用const修饰指针
(1)常量指针
语法:const 数据类型 *变量名
不能通过解引用的方法修改内存地址中的值(用原始的变量名是可以修改的)
1.一般用于修饰函数的形参,表示不希望在函数里修改内存地址中的值
2.如果用于形参,虽然指向的对象可以改变,但这么做没有任何意义
3.如果形参的值不需要改变,建议加上const修饰,程序可读性更好
(2)指针常量
语法:数据类型 *const 变量名
指向的变量(对象)不可改变
1.定义的同时必须初始化
2.可以通过解引用的方法修改内存地址中的值
3.C++把指针常量做了一些特别的处理,叫引用
(3)常指针常量
语法:const 数据类型 *const 变量名;
指向的变量(对象)不可改变,不能通过解引用的方法修改内存地址中的值
13 void 关键字
用途:
(1)函数返回值用void
(2)函数的参数填void,表示函数不需要参数
(3)形参用void*, 表示接受任意数据类型的指针
Tip:
1.不能用void声名变量,它不能代表一个真实的变量,但是void* 可以
2.不能对void* 指针直接解引用(需要转换成其他类型的指针)
3.把其他类型的指针赋值给void*指针不需要转换,反之要转换
#include <iostream>
using namespace std;
void func(string name, void* p)
{
cout << name << " " << p << endl;
cout << name << " " << *(char*)p << endl;
}
int main()
{
int a = 80;
char b = 'X';
cout << &a << endl;
cout << &b << endl;
func("a", &a);
func("b", &b);
return 0;
}
13 C++内存模型
内存主要分成四个区:栈、堆、数据段、代码段
栈和堆的主要区别:
(1)管理方式不同:栈是系统自动管理的,出作用域会被自动释放;堆需要手动释放,若程序中不释放,程序结束由操作系统回收
(2)空间大小不同:堆内存大小受限于物理内存空间;而栈小的可怜,一般只有8M(可以修改系统参数)
(3)分配方式不同
(4)分配效率不同
(5)是否产生碎片
(6)增长方向不同
14 动态分配内存new和delete
栈区的效率很高,但是空间很小;如果需要处理大量的数据,就必须使用堆区的内存
#include <iostream>
using namespace std;
int main()
{
// 1 声名一个指针
int *p;
// 2 用new运算符向系统申请一块内存,让指针指向这块内存
p = new int(5);
// int *p = new int(5);
// 3 通过对指针解引用的方法,像使用变量一样使用这块内存
cout << "*p = " << *p << endl;
*p = 8;
cout << "*p = " << *p << endl;
// 4 如果这块内存不用了,用delete运算符释放它
delete p;
}
Tip:
(1)动态分配出来的内存没有变量名,只能通过操作指针来操作内存中的数据
(2)如果动态内存分配的内存不用了,必须用delete释放它,否则有可能用尽系统的内存
(3)动态分配的内存生命周期与程序相同,程序退出时,如果没有释放,系统自动回收
(4)就算指针的作用域失效,指向的内存也不会释放
(5)用指针跟踪已分配的内存,不能跟丢
15 二级指针
指针:用于存放普通变量的地址
二级指针:存放指针变量的地址
语法:数据类型** 指针名;
1.传递地址
2.存放动态分配的内存的地址
#include <iostream>
using namespace std;
int main()
{
int a = 8;
cout << "a = " << a << " " << &a << endl;
int* p = &a;
cout << "p = " << p << " " << &p << " " << *p << endl;
int** h = &p;
cout << "h = " << h << " " << &h << " " << *h << endl;
}
16 空指针
C/C++,用0或NULL都可以表示空指针。
声名指针后,在赋值之前,让他指向空,表示没有指向任何地址。
(1)后果:
1.对空指针解引用,程序会崩溃
2.如果对空指针使用delete运算符,系统忽略该操作,不会出现异常。所以,内存被释放后,也该把指针指向空
3.在函数中,应该判断形参是否为空指针的代码,目的是保证程序的健壮性
(2)C++11的nullptr
用0和NULL表示空指针会产生歧义,c++11建议用nullptr,(void*)0
注意:在Linux平台下,如果使用nullptr,编译需要加-std=c++11参数
#include <iostream>
using namespace std;
void func(int* no, string* str)
{
if ((no == 0) || (str == 0)) return;
cout << *no << " " << *str << endl;
}
int main()
{
int* bh = 0;
string* message = 0;
func(bh, message);
delete bh;
delete message;
return 0;
}
17 野指针
野指针:指针指向的不是一个有效合法的地址
访问野指针,可能会造成程序的崩溃
出现野指针的情况:
(1)指针未初始化
(2)指针指向了动态分配的内存,内存被释放后,指针不会置空,但是,指向的地址失效
(3)指针指向的变量已经超越变量的作用域,让指针指向了函数的局部变量,或者把函数的局部变量的地址作为返回值赋值给指针
#include <iostream>
using namespace std;
int* func()
{
int a = 2;
cout << a << ' ' << &a << endl;
return &a;
}
int main()
{
// int *p = new int(5);
// cout << p << ' ' << *p << endl;
// delete p;
// cout << p << ' ' << *p << endl;
int* p = func();
cout << p << ' ' << *p << endl;
return 0;
}
规避方法:
1.指针在定义的时候,如果没地方指,就初始化为nullptr
2.动态分配的内存被释放后,将其置为nullptr
3.函数不要返回局部变量的地址
野指针的危害比空指针大很多,访问野指针,可能会造成程序的崩溃。程序的表现是不稳定,增加了调试程序的难度