一、C++入门
1、注释
单行注释: //
多行注释: /* */
2、变量的定义
语法:数据类型 变量名 = 初始值;
3、常量的定义方式
3.1 #define 宏常量: #define 常量名 常量值
例: #define N 100
通常再文件上方定义,表示一个常量,不可修改
3.2 const修饰的变量: const 数据类型 常量名 = 常量值
例: const int n = 1000
通常在变量定义前加关键字const,修饰该变量为常量,不可修改
区别:(参考来源)
#define | const | |
---|---|---|
定义常量 | 不带类型 | 带类型 |
起作用阶段 | 预处理阶段,将代码中的标识符全部替换为常量 | 在整个程序的编译和运行期间都才存在 |
调试 | 不可调试 | 可调试 |
安全性检查 | 可能会导致边界效应 | 起修饰作用,常量有类型,有安全性检查 |
范围 | 可以表示数字、表达式以及函数 | 可以表示变量、参数、返回值、指针、引用,不能表示函数以及表达式 |
作用域 | 从#define开始到#undef结束 | 整个生命周期 |
4、关键字/标识符
二、数据类型
1、整型
数据类型 | 占用空间 | 取值范围 |
---|---|---|
short | 2字节 | -215~215-1 |
int | 4字节 | -231~231-1 |
long | windows为4字节,Linux为4字节(32位),8字节(64位) | -231~231-1 |
long long | 8字节 | -263~263-1 |
2、sizeof关键字(重点:学指针必备)
作用:利用sizeof关键字可以统计数据类型所占内存大小
语法:sizeof(数据类型/变量)
3、实型(浮点型)
数据类型 | 占用空间 | 有效数字范围 |
---|---|---|
float | 4字节 | 7位有效数字 |
double | 8字节 | 15~16位有效数字 |
数字超过六位的情况下,会显示6位有效数字(小数点前后一共加起来六位)
float f1 = 3.141548f; //若不加最后的f,小数默认为double类型,加了f然后转化为float
//输出为 3.14155
//科学计数法
float f2 = 3e2; //3 * 10 ^ 2
float f3 = 3e-2 //3 * 0.1 ^ 2
4、字符型
只占用一个字节大小
字符变量不是把字符本身放到内存中存储,而是将对应的ASCII编码放入到存储单元
语法: char ch = 'a'; //不能用双引号,且只能有一个字符
//字符型变量对应ASCII编码
//a ~ 97
//A ~ 65
(int)ch //输出97
5、字符串型
1.c风格字符串:char 变量名[] = "字符串值"
2.c++风格字符串:string 变量名= "字符串值" //头文件:#include<string>
6、布尔类型
只占用一个字节大小
true 真(本质为1)
false 假(本质为0)
7、转义字符
转义字符 | 含义 | ASCII码值(十进制) |
---|---|---|
\n | 换行,将当前位置移到下一行开头 | 010 |
\t | 水平制表(HT),跳到下一个TAB位置 | 009 |
\v | 垂直制表(VT) | 011 |
\\ | 代表一个反斜线字符“\” | 092 |
\’ | 代表一个单引号字符 | 039 |
\‘’ | 代表一个双引号字符 | 034 |
? | 代表一个问号 | 063 |
三、运算符
运算符类型 | 作用 | 运算符 |
---|---|---|
算术运算符 | 用于处理四则运算 | +、-、*、/、%、++、– |
赋值运算符 | 用于将表达式的值赋给变量 | =、+=、-=、*=、/=、%= |
比较运算符 | 用于表达式的比较,并返回一个真值或假值 | ==、!=、<、>、<=、>= |
逻辑运算符 | 用于根据表达式的值返回真值或假值 | !、&&、 |
前置递增:先让变量+1,然后进行表达式运算
后置递增:先进行表达式运算,后让变量+1
四、程序流程
顺序结构、选择结构、循环结构
1、顺序结构
代码就是顺序执行的。
2、选择结构
if、switch(表达式只能是整型或字符型)
3、循环结构
while(循环条件){循环语句}
do{循环语句}while(循环条件)
for(起始表达式;条件表达式;末尾循环体){循环语句;}
补充
#include<iostream>
#include<stdlib.h> //包含scrand()和rand()
//time系统时间头包含
#include<ctime>
using namespace std;
int main(){
//添加随机数种子 作用利用当前系统时间生成随机数,防止每次随机数都一样
srand((unsigned int)time(0));
int ans = rand();
cout << ans;
}
4、跳转语句
(1)break:用于跳出 循环结构
(2)continue:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环
(3)goto:可以无条件跳转语句(一般不用)
五、数组
数组名就是数组的首地址。
1、一维数组
定义方式:
- 数据类型 数组名[数组长度];
- 数据类型 数组名[数组长度] = {值1, 值2 …};
- 数据类型 数组名[ ] = {值1, 值2 …};
2、二维数组
定义方式:
- 数据类型 数组名[ 行数 ][ 列数 ];
- 数据类型 数组名[ 行数 ][ 列数 ] = {{数据1,数据2}},{{数据3,数据4}};
- 数据类型 数组名[ 行数 ][ 列数 ] = {{数据1,数据2,数据3,数据4}};
- 数据类型 数组名[ ][ 列数 ] = {{数据1,数据2,数据3,数据4}};
六、函数
1、概述
作用:将一段经常使用的代码封装起来,减少重复代码
2、函数的定义
语法: //形参的变化不会影响实参
返回值类型 函数名 (参数列表){
函数体语句;
return表达式;
}
3、函数声明
声明:提前告诉编译器函数的存在,可以利用函数的声明。
声明可以有多次,函数定义只能有一次。
int max(int a, int b);
4、函数的分文件编写
作用:让代码结构更加清晰
步骤(总体步骤是这样,但不同编译器操作有所不同):
(1)创建后缀名为.h的头文件
(2)创建后缀为.cpp的源文件
(3)在头文件中写函数的声明
(4)在源文件中写函数的定义
Dev-c++的分文件编写方式参考:参考链接
main.cpp
#include <iostream>
#include "swap.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv) {
int a = 10;
int b = 20;
swap(a,b);
return 0;
}
swap.h
#include<iostream>
using namespace std;
//函数的声明
void swap(int a, int b);
swap.cpp
#include "swap.h"
//函数的定义
void swap(int a ,int b){
int temp = a;
a = b;
b = temp;
cout << "a=" << a << endl;
cout << "b=" << b << endl;
}
七、指针
1、指针基本概念
作用:可以通过指针间接访问内存
2、指针变量的定义和使用
1、定义指针
int a = 10;
//指针定义的语法:数据类型 * 指针变量名;
int * p;
//让指针记录变量a的地址
p = &a;
//结果同,都为地址
cout << "a的地址为:" << &a << endl;
cout << "指针p为:" << p << endl;
2、使用指针
//可以通过解引用的方式来找到指针指向的内存
//指针前加 * 代表解引用,找到指针指向的内存中的数据
*p = 1000;
//结果都为1000
cout << "a=" << a <<endl;
cout << "*p=" << *p << endl;
3、指针所占内存空间
- 在32位操作系统下,指针占4个字节,不管什么数据类型
- 在64位操作系统下,指针占8个字节,不管什么数据类型
4、空指针和野指针
4.1 空指针
空指针:指针变量指向内存中编号为0的空间
int * p = NULL;
用途:初始化指针变量
注意:空指针指向的内存是不可访问的(0~255之间的内存编号是系统占用的,因此不可以访问)
4.2 野指针
野指针:指针变量指向非法的内存空间
//指针变量p指向内存地址编号为0x1100的空间
int * p = (int *)0x1100;
//访问野指针报错
cout << *p << endl;
4.3 结论
空指针和野指针都不是我们申请的空间,因此不要访问。
5、const修饰指针
int a = 10;
int b = 10;
int * p = &a;
const修饰指针有三种情况:
1.const修饰指针:--- 常量指针
const int * p = &a;
特点:指针的指向可以改,但指针指向的值不可以改
*p = 20; //错误
p = &b; //正确
2. const修饰常量:--- 指针常量
int * const p = &a;
特点:指针的指向不可以改,但指针指向的值可以改
*p = 20; //正确
p = &b; //错误
3.. const既修饰指针,又修饰常量
const int * const p = &a;
特点:指针的指向和指针值相等 值都不可以改
*p = 20; //错误
p = &b; //错误
6、指针和数组
作用:利用指针访问数组中的元素
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int * p;
p = arr; //arr就是数组的首地址
for(int i=0; i<10; i++){
cout << *p << endl;
p++;
}
7、指针和函数
作用:利用指针作为函数参数,可以修改实参的值
#include<iostream>
using namespace std;
//实现两个数字进行交换
void swap01(int a, int b){
int temp = a;
a = b;
b = a;
}
//数字交换
void swap02(int *p1, int *p2){
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
int main(){
int a = 10;
int b = 20;
//指针和函数
//1、值传递不改变实参
swap01(a,b);
//结果: a = 10, b = 20
//2、地址交换改变实参
swap02(&a, &b);
//结果: a = 20, b = 10
cout << "a=" << a <<endl;
cout << "b=" << b <<endl;
system("pause");
}
8、指针、数组、函数
案例描述:封装一个函数,利用冒泡排序,实现对数组的升序排列
#include<iostream>
using namespace std;
//冒泡排序 参数1:数组的首地址;参数2:数组长度
void bubbleSort(int * arr, int len){
for(int i = 0; i < len - 1; i++){
for(int j = 0; j < len - i - 1; j++){
if(arr[j] > arr[j + 1]){
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
//打印数组
void printArray(int * arr, int len){
for(int i = 0; i < len; i++)
cout << arr[i] << endl;
}
int main(){
//1、创建函数
int arr[10] = {4,3,6,9,8,7,1,2,5,10} ;
//数组长度
int len = sizeof(arr) / sizeof(arr[0]);
//2、创建函数,实现冒泡排序
bubbleSort(arr, len);
//3、打印排序后的数组
printArray(arr, len);
system("pause");
}
八、结构体
1、结构体概念
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型
2、结构体定义和使用
语法:struct 结构体名{ 结构体成员列表 }
通过结构体创建变量的方式有三种:
- struct 结构体名 变量名
- struct 结构体名 变量名 = {成员1值,成员2值…}
- 定义结构体时顺便创建变量
#include<iostream>
#include<string>
using namespace std;
//结构体定义
struct student{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
}stu3; //结构体变量创建方式3
int main(){
//结构体变量创建方式1
struct student stu1; //struct关键字可以省略
stu1.name = "张三";
stu1.age = 18;
stu1.score = 100;
cout << "姓名:" << stu1.name << " 年龄" << stu1.age << " 分数" << stu1.score << endl;
//姓名:张三 年龄18 分数100
//结构体变量创建方式2
struct student stu2 = {"李四",19,60};
cout << "姓名:" << stu2.name << " 年龄" << stu2.age << " 分数" << stu2.score << endl;
//姓名:李四 年龄19 分数60
stu3.name = "张三";
stu3.age = 16;
stu3.score = 90;
cout << "姓名:" << stu3.name << " 年龄" << stu3.age << " 分数" << stu3.score << endl;
//姓名:张三 年龄16 分数90
}
3、结构体数组
作用:将自定义的结构体放入到数组中方便维护
语法:struct 结构体名[元素个数] = { {}, {}, ... ,{} }
#include<iostream>
#include<string>
using namespace std;
//1.定义结构体
struct student{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
int main(){
//2.创建结构体数组
struct student stuArr[3] = {
{"张三", 18 ,100},
{"李四", 28 ,90},
{"王五", 98 ,80}
};
//3.给结构体数组中的元素赋值
stuArr[2].name = "赵六";
stuArr[2].age = 80;
stuArr[2].score = 60;
//4.遍历结构体数组
for(int i=0; i<3; i++){
cout << "姓名:" << stuArr[i].name << " 年龄:" << stuArr[i].age
<< " 分数:" << stuArr[i].score << endl;
}
/*
姓名:张三 年龄:18 分数:100
姓名:李四 年龄:28 分数:90
姓名:赵六 年龄:80 分数:60
*/
}
4、结构体指针
作用:通过指针访问结构体中的成员
利用操作符->
可以通过结构体指针访问结构体属性
#include<iostream>
#include<string>
using namespace std;
//结构体指针
struct student{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
int main(){
//1.创建学生结构体变量
struct student s = {"张三", 18 ,100};
//2.通过指针指向结构体变量
struct student * p = &s;
//3.通过指针访问结构体变量中的数据
//通过结构体指针,访问结构体中的属性,需要用' -> '
cout << "姓名:" << p->name << " 年龄:" << p->age
<< " 分数:" << p->score << endl;
//姓名:张三 年龄:18 分数:100
}
5、结构体嵌套结构体
作用:结构体中的成员可以是另一个结构体
例如:每个老师辅导一个学员,一个老师的结构体中,记录一个学生的结构体
#include<iostream>
#include<string>
using namespace std;
//结构体指针
struct student{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
struct teacher{
int id; //教师编号
string name; //教师姓名
int age; //年龄
struct student stu; //辅导的学生
};
int main(){
//结构体嵌套结构体
//创建老师
struct teacher t;
t.id = 1;
t.name = "老王";
t.age = 50;
t.stu.name = "张三";
t.stu.age = 20;
t.stu.score = 99;
}
6、结构体做函数参数
作用:将结构体作为参数向函数中传递
传递方式有两种:
- 值传递
- 地址传递
总结:不想修改主函数中的数据,用值传递;反之用地址传递
#include<iostream>
#include<string>
using namespace std;
//结构体指针
struct student{
string name; //姓名
int age; //年龄
int score; //分数
};
//打印学生信息函数
//1.值传递
void printStudent1(struct student s){
s.age = 100;
cout << "子函数1中打印:\n姓名:" << s.name << " 年龄:" << s.age
<< " 分数:" << s.score << endl;
}
/*
子函数1中打印:
姓名:张三 年龄:100 分数:85
*/
//2.地址传递
void printStudent2(struct student * p){
p->age = 66;
cout << "子函数2中打印:\n姓名:" << p->name << " 年龄:" << p->age
<< " 分数:" << p->score << endl;
}
/*
子函数2中打印:
姓名:张三 年龄:66 分数:85
*/
int main(){
//结构体作函数参数
//将学生传入到一个参数中,打印学生身上的所有信息
//创建结构体变量
struct student s;
s.name = "张三";
s.age = 20;
s.score = 85;
printStudent1(s);
printStudent2(&s);
cout << "main函数中打印:\n姓名:" << s.name << " 年龄:" << s.age
<< " 分数:" << s.score << endl;
/*
main函数中打印:
姓名:张三 年龄:66 分数:85
*/
}
7、结构体中const使用场景
作用:用const来防止误操作
#include<iostream>
#include<string>
using namespace std;
//结构体指针
struct student{
//成员列表
string name; //姓名
int age; //年龄
int score; //分数
};
//打印学生信息函数
void printStudent(const struct student * p){
//p->age = 100; 错误,加入const后,一旦有修改的操作就会报错,可以防止误操作
cout << "姓名:" << p->name << " 年龄" << p->age
<< " 分数" << p->score << endl;
}
int main(){
//创建结构体变量
struct student s = { "张三", 15, 70};
//通过函数打印结构体变量信息
printStudent(&s);
//姓名:张三 年龄:15 分数:70
}
九、文件
程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放。
通过文件可以将数据持久化
C++中对文件操作需要包含头文件 == < fstream >==
文件类型:
- 文本文件: 文件以文本的ASCII码形式存储在计算机中
- 二进制文件: 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
操作文件的三大类:
- ofstream:写操作
- ifstream:读操作
- fstream:读写操作
1、文本文件
1.1 写文件步骤如下:
- 包含头文件
#include<fstream>
- 创建流对象
ofstream ofs;
- 打开文件
ofs.open("文件路径",打开方式)
- 写数据
ofs << "写入的数据
- 关闭文件
ofs.close()
文件打开方式:
打开方式 | 解释 |
---|---|
ios::in | 为读文件而打开文件 |
ios::out | 为写文件而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 追加方式写文件 |
ios::trunc | 如果文件存在先删除,再创建 |
ios::binary | 二进制方式 |
注意: 文件打开方式可以配合使用,利用|
操作符
例如: 用二进制方式写文件 ios::binary | ios:: out
#include<iostream>
#include<fstream> //头文件包含
using namespace std;
//文本文件 写文件
int main(){
//1.包含头文件 fstream
//2.创建流对象
ofstream ofs;
//3.指定打开方式
ofs.open("test.txt", ios::out);
//4.写内容
ofs << "姓名:张三" << endl;
ofs << "性别:男" << endl;
ofs << "年龄:18" << endl;
//5.关闭文件
ofs.close();
system("pause");
return 0;
}
结果(位于相对路径下,在该cpp的父目录下):
1.2 读文件步骤如下:
- 包含头文件
#include<fstream>
- 创建流对象
ifstream ifs;
- 打开文件并判断文件是否打开成功
ifs.open("文件路径",打开方式);
- 读文件
四种获取方式() 见代码
- 关闭文件
ifs.close()
#include<iostream>
#include<fstream> //头文件包含
#include<string>
using namespace std;
//文本文件 读文件
void test01(){
//1.包含头文件 fstream
//2.创建流对象
ifstream ifs;
//3.打开文件 并且判断是否打开成功
ifs.open("test.txt", ios::in);
if ( !ifs.is_open() )
{
cout << "文件打开失败" << endl;
return ;
}
//4.读数据
//第一种方式:直接读取,以空格换行
char buf[1024] = { 0 };
while( ifs >> buf ){
cout << buf << endl;
}
//第二种读取方式:数组方法,逐行读取,可读取空格
//若在第n行读取数据时,该行数据的长度 > 临时存储数组的长度,则从改行开始读取失败
char buf[1024] = { 0 };
//第一个参数buf:代表读取的东西存放的位置
//第二个参数sizeof(buf):代表读取的最大字符
while( ifs.getline(buf, sizeof(buf)) ){
cout << buf << endl;
}
//第三种读取方式:字符串读取,逐行读取,可读取空格
string buf;
while (getline(ifs, buf))
{
cout << buf << endl;
}
//第四种:逐字符读取,可读取空格,但是效率较低(不推荐)
char c;
while ((c = ifs.get()) != EOF) //EOF end of file
{
cout << c;
}
//5.关闭文件
ifs.close();
}
int main(){
test01();
system("pause");
return 0;
}
输出:
个人总结:
方式一和二的区别在于是否可以读取空格。
方式二和三的区别在于读取某一行的长度是否有限。
所以,掌握方式一和三yyds
2、二进制文件
以二进制的方式对文件进行读取操作
打开方式要指定为ios::binary
2.1 写文件
二进制方式写文件主要利用流对象调用成员函数write
函数原型:ostream& write(const char * buffer, int len);
参数解释:字符串指针buffer指向内存中一段存储空间。len是读写的字节数。
写文件步骤:
- 包含头文件
#include<fstream>
- 创建输出流对象
ofstream ofs
- 打开文件
ofs.open("文件路径", 打开方式);
- 写数据
ofs.write()
- 关闭文件
ofs.close()
#include<iostream>
#include<fstream> //头文件包含
using namespace std;
class Person
{
public:
char m_Name[64]; //姓名
int m_Age; //年龄
};
//二进制文件 写文件
void test01(){
//1.包含头文件 fstream
//2.创建流对象
ofstream ofs("person.txt", ios::out | ios::binary);
//3.打开文件
//ofs.open("person.txt", ios::out | ios::binary);
//4.写文件
Person p = {"张三", 18};
ofs.write( (const char *)&p, sizeof(Person));
//5.关闭文件
ofs.close();
}
int main(){
test01();
system("pause");
return 0;
}
由于输出结果为二进制文件,显示会是乱码。
总结: 文件输出流对象可以通过write函数,以二进制方式写数据
2.1 读文件
二进制方式读文件主要利用流对象调用成员函数read
函数原型:istream& read(char *buffer, int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数。
读文件步骤:
- 包含头文件
#include<fstream>
- 创建流对象
ifstream ifs;
- 打开文件并判断文件是否打开成功
ifs.open("文件路径",打开方式);
- 读文件
ifs.read()
- 关闭文件
ifs.close()
#include<iostream>
#include<fstream> //头文件包含
using namespace std;
class Person
{
public:
char m_Name[64]; //姓名
int m_Age; //年龄
};
//二进制文件 读文件
void test01(){
//1.包含头文件 fstream
//2.创建流对象
ifstream ifs;
//3.打开文件 并且判断是否打开成功
ifs.open("person.txt", ios::in | ios::binary);
if( !ifs.is_open() ){
cout << "文件打开失败" << endl;
return ;
}
//4.写文件
Person p;
ifs.read( (char *)&p, sizeof(Person));
cout << "姓名:" << p.m_Name << " 年龄:" << p.m_Age << endl;
//5.关闭文件
ifs.close();
}
int main(){
test01();
system("pause");
return 0;
}
结果: