本笔记主要记录本人不太熟悉的一些知识点,并不是健全的知识体系,仅供本人日后复习。
一.常量
1.命名习惯:通常用大写字母表示
2.作用:用于记录程序中不可更改的数据
3.两种定义方式:
(1)#define 宏常量:
#define 常量名 常量值
// 定义体重60kg,并可不可修改
#define WEIGHT 60
注:宏常量的定义通常放在文件上方
(2)const 修饰的常量:
const 数据类型 常量名 = 常量值
// 定义体重60kg,并且不可修改
const int WEIGHT = 60;
二.数据类型
C++规定,在创建一个变量或常量时,必须要指定相应的数据类型,否则无法给变量分配内存。
由此我们可以知道指明数据类型的作用就是便于给变量或者常量分配内存。
(一) 整形
数据类型 | 占用空间 | 取值范围 |
---|---|---|
short | 2字节 | (-2^15 ~ 2^15 -1) |
int | 4字节 | -2^31 ~ 2^31-1 |
long | Windows为4字节,Linux为4字节(32位),8字节(64位) | (-2^31 ~ 2^31-1 |
long long | 8字节 | -2^63 ~ 2^63-1 |
占用空间大小:short < int <= long <= long long
(二) sizeof关键字
1.作用:利用sizeof关键字可以统计数据类型所占内存的大小
2.语法:sizeof(数据类型)或者 sizeof(变量)
int main(){
cout << "int 类型所占内存空间为: " << sizeof(int) << endl;
int a = 100;
cout << "变量 a 所占内存空间为: " << sizeof(a) << endl;
system("pause");
return 0;
}
(三) 实型(浮点型)
数据类型 | 占用空间 | 有效数字范围 |
---|---|---|
float | 4字节 | 7位有效数字 |
double | 8字节 | 15~16位有效数字 |
(四) 字符型
1.作用:字符型变量用于显示单个字符
2.语法: char 变量名 = 字符
char ch = 'a';
3.注意事项:
(1) 在显示单个字符时,要用单引号把字符括起来,不要用双引号
(2)单引号内只能由一个字符,不可以是字符串
4.补充:
(1)C和C++中字符变量只占用一个字节
(2)字符变量并不是把字符本身放到内存中存储。而是把对应的ASCII编码放到存储单元
(五) 字符串型
1.作用:用于表示一串字符
2.两种风格
(1)C风格字符串:char 变量名[ ] = “字符串值”
char str1[] = "Hello world";
cout << str1 << end;
(2)C++风格字符串:string 变量名 = “字符串值”
string str = "hello world";
cout << str << endl;
3.注意事项
(1)不管是哪种风格,字符串的值都要用双引号括起来。
(2) C++风格字符串,需要在头文件加上
#include <string>
(六) 布尔类型bool
1.占用内存:1字节
2.只有两个值:
· true – 真(本质是1)
· false --假(本质是0)
3.代码示例
int main()
{
bool flag = true;
cout << flag << endl; // 输出为1
flag = false;
cout << flag << endl; // 输出为0
cout << "size of bool = " << sizeof(bool) << endl; //输出为1
system("pause");
return 0;
}
三.运算
(一) 算术运算
1.两个整数相除依然为整数
// 示例代码
int a1 = 10;
int b1 = 3;
cout << a1 / b1 << endl; //输出为3
2.在除法和取模运算中,除数都不能为0,否则会报错。
int a2 = 10;
int b2 = 0;
cout << a2 / b2 << endl; //报错
cout << a2 % b2 << endl; //报错
3.两个小数不能取模,只有整型变量才可以进行取模运算。
double d1 = 3.14;
double d2 = 1.1;
cout << d1 % d2 << endl; //报错
4.前置递增先对变量进行++,再计算表达式;后置递增相反
//前置递增先对变量进行++,再计算表达式
int a2 = 10;
int b2 = ++a2 * 10;
cout << b2 << endl; //输出110
//后置递增先计算表达式,后对变量进行++
int a3 = 10;
int b3 = a3++ * 10;
cout << b3 << endl; //输出100
5.cpp中没有乘方运算符,计算乘方可以调用cmath头文件里面的pow函数。
#include <cmath>
#include <iostream>
using namespace std;
int main()
{
cout << "2 ^ 3 = " << pow(2, 3) << endl; // 2 ^ 3 = 8
}
(二) 比较运算
C和C++ 语言的比较运算中, “真”用数字“1”来表示, “假”用数字“0”来表示。
int a = 10;
int b = 20;
cout << (a == b) << endl; // 0
cout << (a != b) << endl; // 1
cout << (a > b) << endl; // 0
cout << (a < b) << endl; // 1
cout << (a >= b) << endl; // 0
cout << (a <= b) << endl; // 1
四.程序流程控制
(一) 选择结构
1.if语句
(1)语法:if(判断条件){ 执行语句 }
(2)注意:判读条件后面没有分号
2.三目运算符
(1)作用:
(2)语法:表达式1 ? 表达式2 : 表达式3
(3)解释:
如果表达式1正确,执行表达式2,并返回表达式2的结果;
如果表达式1不正确,执行表达式3,并返回表达式3的结果;
(4)与if语句对比:三目运算符简单但是不容易嵌套
3.switch语句
(1)注意事项:
- switch语句中表达式类型只能是整型或者字符型
- case里面如果没有break,那么程序会一直向下执行。
- default只有在case匹配失败的时候才会执行
(2)与if语句对比
对于多条件判断,switch语句结构清晰,执行效率高,但是switch不能判断区间
(二) 循环结构
1.while语句
(1)语法:while(判断条件) { 循环语句 }
注意事项:在执行while循环语句时,程序必须提供跳出循环的出口,否则会出现死循环。
2.do…while循环语句
(1)语法:do{ 循环语句 } while (循环条件)
(2)与while的区别在于:do…while会先执行一次循环语句,再判断循环条件
3.for 循环语句
(1)语法:for(起始表达式;条件表达式;末尾循环体) { 循环语句; }
(2)注意:
- for循环中的表达式,要用分号进行分隔
- while , do…while, for都是开发中常用的循环语句,for循环结构比较清晰,比较常用
(三) 跳转语句
1.break语句
(1)作用:跳出选择结构或者循环结构
- 出现在switch中,作用是终止case并跳出switch
- 出现在循环结构语句中,作用是跳出当前的循环语句
- 出现在嵌套循环中,跳出最近的内层循环语句
2.continue语句
(1)作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环
(2)注意:continue并没使整个循环中值,而break会跳出循环
3.goto语句
(1)作用:可以无条件跳转语句
(2)语法:goto 标记
如果标记存在,执行到goto语句时,会跳转到标记的位置。
// 示例代码,最终只会输出:1 5
cout << "1" << endl;
goto FLAG;
cout << "2" << endl;
cout << "3" << endl;
cout << "4" << endl;
FLAG:
cout << "5" << endl;
(3)注意:在程序中不建议使用goto语句,以免造成程序流程混乱。
五.数组
(一) 数组的特点
- 数组中的每个数据元素都是相同的数据类型
- 数组是由连续的内存位置组成的
(二) 一维数组数
1.三种定义方式
(1) 数据类型 数组名[ 数组长度 ];
(2) 数据类型 数组名[ 数组长度 ] = { 值1,值2 …};
(3)数据类型 数组名[ ] = { 值1,值2 …};
注:对于第二种,{}内不足预定义的数组长度个数据时,剩余数据用0补全,例子如下:
int arr[10] = {1, 2};
cout << arr[3] << endl; // 输出为0
2.一维数组数组名
- 可以统计整个数组在内存中的长度
//获取整个数组占用内存空间大小
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
cout << "整个数组所占内存空间为: " << sizeof(arr) << endl;
cout << "每个元素所占内存空间为: " << sizeof(arr[0]) << endl;
cout << "数组的元素个数为: " << sizeof(arr) / sizeof(arr[0]) << endl;
注意:当把数组名当成参数传入函数时,sizeof(arr)=4。原因如下:
当数组名传入参数时,此时数组名是作为指针传入的,因为数组不能作为形参传入函数。在32位操作系统中,指针大小就是4。如果想要在函数中使用数组大小,必须在外部计算好,然后再传入函数。
- 可以获取数组在内存中的首地址
//可以通过数组名获取到数组首地址
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
cout << "数组首地址为: " << (int)arr << endl;
cout << "数组中第一个元素地址为: " << (int)&arr[0] << endl;
cout << "数组中第二个元素地址为: " << (int)&arr[1] << endl;
- 数组名是常量,其值为数组内存的首地址,故不可以进行赋值操作
- 数组内存首地址,即数组第一个元素地址
(三) 冒泡排序
1.作用: 最常用的排序算法,对数组内元素进行排序。
2.步骤:
- 比较相邻的元素,如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素做同样的工作,执行完毕后,找到第一个最大值
- 重复以上步骤,每次比较次数减一,直到不需要比较
//示例: 将数组 { 4,2,8,0,5,7,1,3,9 } 进行升序排序
int array[9] = { 4,2,8,0,5,7,1,3,9 };
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9 - 1 - i; j++)
{
if (arr[j] > arr[j+1])
{
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
(四) 二维数组
1.二维数组的定义
- 数据类型 数组名[ 行数 ][ 列数 ];
- 数据类型 数组名[ 行数 ][ 列数 ] = { {数据1,数据2 } ,{数据3,数据4 } };
- 数据类型 数组名[ 行数 ][ 列数 ] = { 数据1,数据2,数据3,数据4};
- 数据类型 数组名[ ][ 列数 ] = { 数据1,数据2,数据3,数据4};
注意:对于二维及以上的数组,当定义的时候如果要省略一个维度,只能省略第一个维度,否则会报错。
2.二维数组数组名
- 二维数组名就是这个数组的首地址
- 查看二维数组所占内存空间
- 计算某一行的内存大小
- 计算行数和列数
int 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;
六.函数
(一) 概述
1.作用
- 将一段经常使用的代码封装起来,减少重复代码
- 一个较大的程序,一般分为若干个程序块,每个模块实现特定的功能
2.定义
返回值类型 函数名 (参数列表)
{
函数体语句
return表达式
}
注意:当返回值类型为void时,可以没有return表达式。当返回值类型为其它非void类型时,必须有对应类型的返回值,否则会报错。
(二) 三种传递参数的方式
- 传值参数:实际参数的值被复制到由函数开辟的内存空间中,成为形参的初始值。完成参数值传递之后,函数体中的语句对形参的访问、修改都是在这个标识对象上操作的,与实际参数无关。
void swap(int num1, int num2)
{
cout << "交换前:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
int temp = num1;
num1 = num2;
num2 = temp;
cout << "交换后:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
}
int main()
{
int a = 10;
int b = 20;
swap(a, b);
cout << "main中的 a = " << a << endl;
cout << "main中的 b = " << b << endl;
system("pause");
return 0;
}
总结: 值传递时,不改变实参的值
- 指针参数:指针传递本质上和值传递差不多,实际上是把实际的指针参数传递给函数中创建的形参指针。不同的地方在于,指针传递的情况时,可以通过在函数中通过形参指针的间址访问对实际变量做出更改,这是值传递无法做到的。
void swap(int* num1, int* num2)
{
cout << "交换前:" << endl;
cout << "num1 = " << *num1 << endl;
cout << "num2 = " << *num2 << endl;
int temp = *num1;
*num1 = *num2;
*num2 = temp;
cout << "交换后:" << endl;
cout << "num1 = " << *num1 << endl;
cout << "num2 = " << *num2 << endl;
}
int main()
{
int a = 10;
int b = 20;
swap(&a, &b);
cout << "main中的 a = " << a << endl;
cout << "main中的 b = " << b << endl;
system("pause");
return 0;
}
总结: 传入指针参数,可以改变实参的值
- 引用参数:与以上两者不同的是函数调用时,形式参数不需要开辟新的存储空间,形式参数名作为引用(别名)绑定与实际参数表示的对象上。执行函数体时,对形参的操作就是对实参对象操作。
void swap(int& num1, int& num2)
{
cout << "交换前:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
int temp = num1;
num1 = num2;
num2 = temp;
cout << "交换后:" << endl;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
}
int main()
{
int a = 10;
int b = 20;
swap(a, b);
cout << "main中的 a = " << a << endl;
cout << "main中的 b = " << b << endl;
system("pause");
return 0;
}
(三) 函数的声明
- 作用: 告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义
- 函数的声明可以有多次,但是函数的定义只能有一次
(四) 函数的4种常见样式
- 无参无返
- 有参无返
- 无参有返
- 有参有返
(五) 函数的分文件编写
- 作用:让代码结构更加清晰
- 函数分文件编写一般有4个步骤
(1) 创建后缀名为.h的头文件
(2)创建后缀名为.cpp的源文件
(3)在头文件中写函数的声明
(4) 在源文件中写函数的定义
七.指针
(一) 基本概念
- 指针的作用:可以通过指针间接访问内存
- 内存编号是从0开始记录的,一般用十六进制数字表示
- 可以利用指针变量保存地址
- 所有指针类型在32位下是4个字节
- 所有指针类型在64位下是8个字节
- VS左上角配置管理器:X86(32位),X64(64位)
(二) 指针的定义和使用
- 定义:数据类型 * 变量名
- 普通变量和指针变量的区别:
a. 普通变量存放的是数据,指针变量存放的是地址
b. 指针变量可以通过“ * ” 操作符,操作指针变量指向的内存空间,这个过程称为解引用。 - 可以通过“ & ”符号,获取变量的地址。
(三) 空指针和野指针
- 空指针:指针变量指向内存中编号为0的空间。
用途: 初始化指针变量 - 野指针:指针变量指向非法的内存空间。
- 注意:空指针和野指针都不是我们申请的空间,不能访问,否则会报错。
(四) const修饰指针
三种情况:
- const 修饰常量: 常量指针
int a = 10;
int b = 10;
//const修饰的是常量,指针指向可以改,指针指向的值不可以更改
//换言之,int * p1指向的是const int,所以指向的值不可以改变
const int * p1 = &a;
p1 = &b; //正确
//*p1 = 100; 报错
- const 修饰指针: 指针常量
int a = 10;
int b = 10;
//const修饰的是指针,指针指向不可以改,指针指向的值可以更改
//换言之,const *p1指向int,所以p1的指向(保存的地址)不可以改变,但指向的值可以改变
int* const p1 = &a;
// p1 = &b; 报错
*p1 = 100; // 正确
- const 即修饰指针,又修饰常量
int a = 10;
int b = 10;
//const修饰的是常量,指针指向不可以改,指针指向的值也不可以更改
//换言之,const * p1指向的是const int,所以p1的指向(保存的地址)不可改,p1指向的值i也不可以改变
const int* const p1 = &a;
// p1 = &b; 报错
//*p1 = 100; 报错
//示例
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p = arr;
for (int i = 0; i < 10; i ++)
{
cout << *(p + i) << endl;
/* 也可以用下面这个
cout << *p << endl;
p++;
*/
}
- 二维数组
//示例
int arr[][4] = {1, 2, 3, 4, 5, 6, 7, 8};
for (int i = 0; i < 2; i ++)
for(int j = 0; j < 4; j++)
{
cout << *(*(arr + i) + j) << endl;
}
八.结构体
(一) 基本概念
- 结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
(二) 结构体定义和使用
- 定义:struct 结构体名 { 结构体成员列表 };
- 创建变量的三种方式
a. struct 结构体名 变量名
b. struct 结构体名 变量名 = { 成员1值 , 成员2值…};
c. 定义结构体时顺便创建变量
//结构体定义
struct student {
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
}stu3; //结构体变量创建方式c
int main() {
//结构体变量创建方式a
struct student stu1; //struct 关键字可以省略
stu1.name = "张三";
stu1.age = 18;
stu1.score = 100;
//结构体变量创建方式b
struct student stu2 = { "李四",19,60 };
//定义stu3
stu3.name = "王五";
stu3.age = 18;
stu3.score = 80;
return 0;
}
(三) 结构体数组
- 作用:将自定义的结构体放入到数组中方便维护
- 语法: struct 结构体名 数组名[元素个数] = {{}, {}, … {} }
//结构体定义
struct student {
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
}
int main() {
//结构体数组
struct student arr[3]= { {"张三",18,80 }, {"李四",19,60 }, {"王五",20,70 } };
for (int i = 0; i < 3; i++)
{
cout << "姓名:" << arr[i].name << " 年龄:" << arr[i].age << " 分数:" << arr[i].score << endl;
}
system("pause");
return 0; }
(四) 结构体指针
- 作用:通过指针访问结构体中的成员
- 利用操作符 ->可以通过结构体指针访问结构体属性
struct student
{
string name;
int age;
int score;
};
int main()
{
student stu = {"Aven", 19, 99};
student *p = &stu;
p->score = 80;
cout << "姓名: " << p->name << "年龄" << p->age << "分数:" << p->score << endl;
return 0;
}
(五) 结构体嵌套结构体
- 作用: 结构体中的成员可以是另一个结构体
(六) 结构体做函数参数
- 作用: 将结构体作为参数向函数中传递
- 传递方式有两种:
a. 值传递
b. 地址传递 - 示例
//学生结构体定义
struct student
{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
//值传递
void printStudent(student stu )
{
stu.age = 28;
cout << "子函数中 姓名:" << stu.name << " 年龄: " << stu.age << " 分数:" << stu.score << endl;
}
//地址传递
void printStudent2(student *stu)
{
stu->age = 28;
cout << "子函数中 姓名:" << stu->name << " 年龄: " << stu->age << " 分数:" << stu- >score << endl;
}
(七) 结构体中 const使用场景
- 作用:用const来防止失误操作
- 示例
//学生结构体定义
struct student
{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
//const使用场景
void printStudent(const student *stu) //加const防止函数体中的误操作
{
//stu->age = 100; //操作失败,因为加了const修饰
cout << "姓名:" << stu->name << " 年龄:" << stu->age << " 分数:" << stu->score << endl;
}
int main()
{
student stu = { "张三",18,100 };
printStudent(&stu);
system("pause");
return 0;
}