初阶C++笔记第一篇:C++基础语法
虽然以下大多数知识点都在C语言中学过,但还是有一些知识点和C语言不同,比如:代码格式、头文件、关键字、输入输出、字符串类型等...
1. 初识C++
1.1 第一个C++程序
编写C++分为4个步骤:
- 创建项目
- 创建文件
- 编写代码
- 运行程序
C++的第一条程序:
#include<iostream>
using namespace std;int main()
{cout << "hello world" << endl;//输出语句system("pause");//按任意键才继续执行return 0;
}
运行程序:
cout是由I/O流库定义的对象
在C++编程语言中,cout 是一个用于在计算机屏幕上显示信息的对象。它是 iostream 类型的对象,属于C++的输入输出流库。C++的输出是通过“流”(stream)的方式实现的,流运算符的定义等信息存放在C++的输入输出流库中。因此,如果在程序中使用 cout 和流运算符,就必须使用预处理命令将头文件 iostream 包含到本文件中,即 #include <iostream>。
C++中endl的作用
在C++中,endl是一个操纵符(manipulator),用于在输出流中插入换行符并刷新输出缓冲区。它的作用不仅仅是换行,还包括将缓冲区的内容立即显示出来,也叫做缓冲流冲刷。
说白了endl就是作为输出语句结尾需要将剩余的需要输出的值输出并在最后加个换行符' \n '。
1.2 注释
作用:在代码中加一些说明和解释,方便自己或其他程序员阅读代码
两种格式:
1.单行注释:/ / 描述信息
通常放在一行代码的上方,或者一条语句的末尾,对该行代码说明
2.多行注释:/* 描述信息 */
通常放在一段代码的上方,对该段代码做整体说明
提示:编译器在编译代码时,会忽略注释的内容
1.3 变量
作用:给一段指定的内存空间起名,方便操作这段内存
语法:数据类型 变量名 = 初始值;
示例:
#include<iostream>
using namespace std;int main()
{int a = 10;//创建变量cout <<"a = "<<a<< endl;//输出变量system("pause");//按任意键才继续执行return 0;
}
1.4 常量
作用:用于记录程序中不可更改的数据
C++定义常量两种方式:
1. #define 宏常量:#define 常量名 常量值
通常在文件上方定义,表示一个常量
2. const修饰的变量:const 数据类型 常量名 = 常量值
通常在变量定义前加关键字const,修饰该变量为常量,不可修改。
示例:
#include<iostream>
using namespace std;
//1、宏常量
#define day 7
int main()
{cout << "一周里总共有" << day << "天" << endl;//day = 8; 对常量进行赋值会报错//2、const修饰常变量const int month = 12;cout << "一年里总共有" << month << "月" << endl;//month = 13; 对const修饰的常变量赋值会报错system("pause");//按任意键才继续执行return 0;
}
运行程序:
1.5 关键字
作用:关键字是C++中预先保留的单词(标识符)
- 在定义变量或者常量的时候,不要用关键字(不要撞名)
asm | do | if | return | typedef |
auto | duoble | inline | short | typeid |
bool | dynamic_cast | int | signed | typename |
break | else | long | sizeof | union |
case | enum | mutable | static | unsigned |
catch | explicit | namespace | static_cast | using |
char | export | new | struct | virtual |
class | extern | operator | switch | void |
const | false | private | template | volatile |
const_cast | float | protected | this | wchar_t |
continue | for | public | throw | while |
default | friend | register | true | |
delete | goto | reinterpret_cast | try |
统计:C++总共有63个关键字
提示:在给变量或者常量起名称时,不要用C++的关键字,否则会产生歧义。
1.6 标识符命名规则
作用:C++规定给标识符(变量、常量)命名时,有一套自己的规则
- 标识符不能是关键字
- 标识符只能由字母、数字、下划线组成
- 第一个字符必须为字母或下划线
- 标识符中字母区分大小写
建议:给标识符命名时,争取做到见名知意的效果,方便自己和他人的阅读
2. 数据类型
C++规定在创建一个变量或者常量时,必须要指定出相应的数据类型,否则无法给变量分配内存
2.1 整型
作用:整型变量表示的是整型类型的数据
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) |
2.2 sizeof关键字
作用:利用sizeof关键字可以统计数据类型所占内存大小
语法:sizeof(数据类型 / 变量);
示例:
#include<iostream>
using namespace std;int main()
{cout << "short 类型所占空间为:" << sizeof(short) << endl;cout << "int 类型所占空间为:" << sizeof(int) << endl;cout << "long 类型所占空间为:" << sizeof(long) << endl;cout << "long long 类型所占空间为:" << sizeof(long long) << endl;system("pause");//按任意键才继续执行return 0;
}
运行代码:
2.3 实型(浮点型)
作用:用于表示小数
浮点型变量分为两种:
- 单精度float
- 双精度double
两者的区别在于表示的有效数字范围不同。
数据类型 | 占用空间 | 有效数字范围 |
float | 4字节 | 7位有效数字 |
double | 8字节 | 15 ~ 16位有效数字 |
示例:
#include<iostream>
using namespace std;int main()
{float f1 = 3.1415926f;double d1 = 3.1415926;cout << f1 << endl;cout << d1 << endl;//科学计数法表示小数//E后面如果是正数就代表10,如果是负数就代表0.1float f2 = 3E2;//3 * 10 ^ 2float f3 = 3E-2;//3 * 0.1 ^ 2cout << "f2= " << f2 << endl;cout << "f3= " << f3 << endl;system("pause");//按任意键才继续执行return 0;
}
运行代码:
我们只需要知道有这种科学计数法的格式,知道有这个表达式,以后见到不会懵。
为什么float变量后面需要额外加一个f ? 为什么最后double变量和float变量输出小数点后面的有效数字默认都是5个?
- 因为所有小数默认为double类型,所以给float类型赋值小数时后面加一个 f 将小数转换成float类型
- 因为输出最多只能表示6位有效数字,如果要看全部的有效数字需要额外的配置,我们现在还接触不到
2.4 字符型
作用:字符型变量用于显示单个字符
语法:char ch = 'a';
注意1:在现实字符型变量时,用单引号将字符括起来,不要用双引号
注意2:单引号内只能有一个字符,不可以是字符串
- C和C++中字符型变量只占用1个字节
- 字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放入到存储单元
示例:
#include<iostream>
using namespace std;int main()
{//1.创建字符char ch = 'a';cout << ch << endl;//2.字符和字符变量的大小cout<< "char字符型变量所占内存大小: " << sizeof(ch) << endl;//3.打印字符的ASCII码值//将字符变量ch的值'a'强制类型转换成int类型便可以得到ASCII码值cout << "字符" << ch << "的ASCII码值为: " << (int)ch << endl;system("pause");//按任意键才继续执行return 0;
}
代码运行:
ASCII码表:
ASCII码大致由以下两部分组成:
- ASCII非打印控制字符(控制字符):ASCII表上的数字 0-31 分配给了控制字符,用于控制像打印机等一些外围设备。
- ASCII打印字符(可显示字符):数字 32-126 分配给了能在 键盘上找到的字符,当查看或打印文档是就会出现。
可显示字符
可显示字符编号范围是32-126(0x20-0x7E),共95个字符。
控制字符
ASCII控制字符的编号范围是0-31和127(0x00-0x1F和0x7F),共33个字符。
2.5 转义字符
作用:用于表示一些不能显示出来的ASCII字符
现阶段我们常用的转义字符有: \n \\ \t
转义字符列表:
- \?:在书写连续多个问号时使用,防止他们被解析成三字母词,在新的编译器上无法验证
- 三字母词就是??)--转换-->],??(--转换-->[
- \':用于表示字符常量'
- \":用于表示字符串内部的双引号
- \\:用于表示反斜杠,防止字符被解释为转义字符
- \a:警报(蜂鸣)
- \b:退格键,光标回退一个字符,但不删除字符
- \f:换页符,光标移到下一页,在现代系统上,这已经反应不出来了,行为改成类似于\v
- \n:换行符
- \r:回车符,光标移到同一行的开头
- \t:制表符,光标移到下一个水平制表位,通常是下一个8的倍数
- \v:垂直分隔符,光标移到下一个垂直制表位,通常是下一行的同一列。
- 下面两种转义可以理解为:字符的8进制或16进制的形式
- \ddd:ddd表示1—3个八进制的数字。 如:\130 表示字符X
- \xdd:dd表示2个十六禁止的数字。 如:\x30 表示字符0
示例:
#include<iostream>
using namespace std;int main()
{//换行符'\n'cout << "hello world\n";//反斜杠'\\'cout << "\\" << endl;//水平制表符'\t'cout << "aaaa\thelloworld" << endl;cout << "aa\thelloworld" << endl;cout << "aaaaaa\thelloworld" << endl;system("pause");//按任意键才继续执行return 0;
}
运行结果:
cout << "hello world\n";
因为endl的作用也是换行,所以一般情况下后面没有endl也没有什么问题,如果没有endl就不会换行,下一次使用cout输出不会在当前语句的下一行输出,而是在当前语句的末尾输出。
2.6 字符串类型
作用:用于表示一串字符
两种风格
1. C风格字符串:char 变量名[ ] = "字符串值";
示例:
#include<iostream>
using namespace std;int main()
{char str[] = "hello world";cout << str << endl;system("pause");//按任意键才继续执行return 0;
}
运行代码:
注意:C风格的字符串要用双引号括起来
2. C++风格字符串:string 变量名 = "字符串值";
示例:
#include<iostream>
#include<string>
using namespace std;int main()
{string str = "hello world";cout << str << endl;system("pause");//按任意键才继续执行return 0;
}
注意:C++风格字符串需要加入头文件 include<string>
#include<iostream>
#include<string>
using namespace std;int main()
{char str1[20];str1 = "hello world";//报错string str2;str2 = "hello world";//不会报错cout << str2 << endl;system("pause");//按任意键才继续执行return 0;
}
还要注意如果用char[ ]的方式开辟一块存储字符串的变量也就是字符数组str1,当给str1直接赋值一串字符串时会报错,因为这里的str1是首元素地址,是数组,所以给一个地址再赋值一个地址就会报错。
而char*是一个指针,string是一个类,内部封装了char * ,管理这个字符串,是一个char *型的容器
总结:
- char[ ]类型是一个字符数组类型,所以数组名str1就是首元素地址。
- string本质上是一个类封装了char*,也就是相当于char*,可以直接赋值"hello world"常量字符串,因为是将这个常量字符串的首字符地址赋值给了string变量,所以str2相当于一个字符指针变量。
注意:string不能完全等同于char*,因为string本身就是一个类,就是封装了char*使用这个类型存储地址,但是不能直接给string这个类赋值NULL,因为它不完全是一个char*,这个类只能存储字符串。
2.7 布尔类型 bool
作用:布尔数据类型代表真或假的值
bool类型只有两个值:
- true --- 真(本质是1)
- false ---假(本质是0)
注意:理论上在C++里布尔0可以表示假false,其他非0的值都能表示真true
bool类型占1个字节大小
示例:
#include<iostream>
#include<string>
using namespace std;int main()
{bool flag = true;cout << flag << endl;//1flag = false;cout << flag << endl;//0cout << "size of bool= " << sizeof(bool) << endl;//bool类型大小为1system("pause");//按任意键才继续执行return 0;
}
运行代码:
2.8 数据的输入
作用:用于从键盘上获取数据
关键字:cin
语法:cin >> 变量
示例:
#include<iostream>
#include<string>
using namespace std;int main()
{//整型输入int a = 0;cout << "请给整型变量a赋值:>";cin >> a;cout << "整型变量a = " << a <<endl;//字符型输入char c = 'a';cout << "请给字符变量c赋值:>";cin >> c;cout << "字符变量c = " << c << endl;//浮点型输入double d = 0.0;cout << "请给浮点变量d赋值:>";cin >> d;cout << "浮点变量d = " << d << endl;//字符串类型输入string str = "hello";cout << "请给字符串类型变量str赋值:>";cin >> str;cout << "字符串类型变量str = " << str << endl;//布尔类型输入bool flag = true;cout << "请给布尔类型变量flag赋值";cin >> flag;cout << "布尔类型变量flag = " << flag << endl;system("pause");//按任意键才继续执行return 0;
}
代码运行:
3. 运算符
作用:用于执行代码的运算
本章我们主要讲解一下几类运算符:
运算符类型 | 作用 |
算术运算符 | 用于处理四则运算 |
赋值运算符 | 用于将表达式的值赋给变量 |
比较运算符 | 用于表达式的比较,并返回一个真值或假值 |
逻辑运算符 | 用于根据表达式的值返回真值或假值 |
3.1 算术运算符
作用:用于处理四则运算
算术运算符包括:+、-、*、/、%、前置++、后置++、前置--、后置--、正号+、负号-
3.2 赋值运算符
作用:用于将表达式的值赋值给变量
赋值运算符包括:=、+=、-=、*=、/=、%=
3.3 比较运算符
作用:用于表达式比较,并返回一个真值或假值
比较运算符包括:>、<、>=、<=、==、!=
3.4 逻辑运算符
作用用于根据表达式的值返回真值或假值
逻辑运算符有以下符号:
运算符 | 术语 | 示例 | 结果 |
! | 非 | !a | 如果a为假,则!a为真;如果a为真,则!a为假 |
&& | 与 | a && b | 如果a和b都为真,则结果为真,否则为假 |
| | | 或 | a | | b | 如果a和b有一个为真,则结果为真,二者都为假时,结果为假 |
4. 程序流程结构
C/C++支持最基本的三种程序运行结构:顺序结构、选择结构、循环结构
- 顺序结构:程序按顺序执行,不发生跳转
- 选择结构:依据条件是否满足,有选择的执行相应功能
- 循环结构:依据条件是否满足,循环多次执行某段代码
4.1 选择结构
4.1.1 if语句
作用:执行满足条件的语句
if语句的三种形式
- 单行格式if语句
- 多行格式if语句
- 多条件的if语句
1. 单行格式if语句:if(条件){条件满足执行的语句}
2. 多行格式if语句:if(条件){条件满足执行的语句}else{条件不满足执行的语句}
3. 多条件的if语句:if(条件1){条件1满足执行的语句}else if(条件2){条件2满足执行的语句}... else{都不满足执行的语句}
嵌套if语句:在if语句中,可以嵌套使用if语句,达到更精准的条件判断
案例需求:
- 提示用户输入一个高考考试分数,根据分数做如下判断
- 分数如果大于600分视为考上一本,大于500分考上二本,大于400分考上三本,其余视为未考上本科;
- 在一本分数中,如果大于700分,考入北大,大于650分,考入清华,大于600考入人大。
示例:
#include<iostream>
#include<string>
using namespace std;int main()
{int score = 0;cout << "请输入分数:" << endl;cin >> score;if (score > 600) {cout << "恭喜你,考入了一本" << endl;if (score > 700) {cout << "考上了北京大学" << endl;}else if (score > 650) {cout << "考上了清华大学" << endl;}else {cout << "考上了人民大学" << endl;}}else if(score > 500) {cout << "恭喜你,考入了二本" << endl;}else if (score > 400) {cout << "恭喜你,考入了三本" << endl;}else {cout << "为考入本科,还请再接再厉" << endl;}system("pause");//按任意键才继续执行return 0;
}
4.1.2 三目运算符
作用:通过三目运算符实现简单的判断
语法: 表达式1 ? 表达式2 : 表达式3
解释:
如果表达式1的值为真,执行表达式2,并返回表达式2的结果;
如果表达式1的值为假,执行表达式3,并返回表达式3的结果;
示例:
#include<iostream>
#include<string>
using namespace std;int main()
{int a = 10;int b = 20;int c = 0;c = a > b ? a : b;cout << "c = " << c << endl;//C++中三目运算符返回的是变量,可以继续赋值(a > b ? a : b) = 100;cout << "a = " << a << endl;cout << "b = " << b << endl;
}
注:三目运算符返回的是变量,可以对其进行赋值
4.1.3 switch语句
作用:执行多条件分支语句
语法:
switch(整型或整型表达式)
{case 结果1(常量或常量表达式): 执行语句; break;case 结果2(常量或常量表达式): 执行语句; break;...default: 执行语句; break;
}
注意1:switch语句中表达式类型只能是整型或者字符型
注意2:case里面如果没有break,那么程序会一直向下执行
注意3:与if语句比,对于多条件判断时,switch的结构清晰,执行效率高,缺点是switch不可以判断区间
4.2 循环结构
4.2.1 while循环语句
作用:满足循环条件,执行循环语句
语法: while(循环条件){ 循环语句 }
解释:只要循环条件的结果为真,就执行循环语句
4.2.2 do while循环语句
作用:满足循环条件,先执行循环语句
语法: do{ 循环语句 }while(循环条件);
注意:与while的区别在于do while会先执行一次循环语句,再判断循环条件
4.2.3 for循环语句
作用:满足循环条件,执行循环语句
语法:for(起始表达式;条件表达式;末尾循环体) { 循环语句;}
详解:
注意:for循环中的表达式,要用分号进行分隔
总结:while,do while,for都是开发中常用的循环语句,for循环结构比较清晰,比较常用
4.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;
}
4.3 跳转语句
4.3.1 break语句
作用:用于跳出选择结构或者循环结构
break使用的时机:
- 出现在switch条件语句中,作用是终止case并跳出switch
- 出现在循环语句中,作用是跳出当前的循环
- 出现在嵌套循环中,跳出最近的内层循环语句
4.3.2 continue语句
作用:在循环语句中,跳过本次循环中余下尚未执行的语句,继续执行下一次循环
continue和break的区别:
- 执行continue时只会跳过当前循环后面的代码,直接执行下一次循环语句
- 执行break时将直接跳出循环,不再继续执行循环语句
4.3.3 goto语句
作用:可以无条件跳转语句
语法: goto 标记;
解释:如果标记的名称存在,执行到goto语句时,会跳转到标记的位置
goto again;//创建一个标记
again://标记
//遇到goto again;这条语句就会跳转到again:标记处
goto语句,也被很多人调侃为飞雷神语句,为什么要叫飞雷神语句?可以将 goto again; 看作水门,而again:就是飞雷神。again不是goto语法,而是标记名称,标记名称可以根据自己需求起名。
goto语句有害论(禁术goto):
但是我们一般是不推荐使用goto语句的,因为goto语句多了就不断地跳来跳去,反而会影响到代码的逻辑结构,不太方便别人去阅读我们的代码。我们只需要知道有goto语句这个知识点就行,以便看到别人的代码有goto语句知道这是什么意思,但是不推荐大家随意使用。
5. 数组
5.1 概述
所谓数组,就是一个集合,里面存放了相同类型的数据元素
特点1:数组中的每个数据元素都是相同的数据类型
特点2:数组是由连续的内存位置组成
5.2 一维数组
5.2.1 一维数组定义方式
一维数组定义的三种方式:
- 数据类型 数组名[ 数组长度 ] ;
- 数据类型 数组名[ 数组长度 ] = { 值1,值2 . . .} ;
- 数据类型 数组名[ ] = { 值1,值2 . . .} ;
示例:
//第一种
int arr[10];
//暂时不用数组,就先定义好一个明确大小的数组//第二种
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
//开辟数组时明确大小并给这个数组初始化//第三种
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
//没有明确大小系统会自动开辟一块10个元素的空间存储数组初始化的元素
5.2.2 一维数组数组名
一维数组名称的用途:
- 可以统计整个数组在内存中的长度
- 可以获取数组在内存中的首地址
int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int sz = sizeof(arr)/sizeof(arr[0]);//结果为10
sizeof是计算类型或变量所占内存空间的大小,以字节为单位,sizeof(arr)则是计算数组的整个大小,10个元素每个元素都是int 4个字节的大小,所以sizeof(arr)就是40个字节大小。然后再除以一个元素的大小得到的就是元素个数。
注:数组名是首元素的地址
5.3 二维数组
二维数组就是在一维数组上多加了一个维度
可以将二维数组看作存储多个一维数组,二维数组的每个元素都是一个一维数组。
5.3.1 二维数组定义方式
二维数组定义的四种方式:
- 数组类型 数组名[ 行数 ][ 列数 ] ;
- 数组类型 数组名[ 行数 ][ 列数 ] = { {数据1,数据2} ,{数据3,数据4} };
- 数组类型 数组名[ 行数 ][ 列数 ] = { {数据1,数据2,数据3,数据4 };
- 数组类型 数组名[ ][ 列数 ] = { {数据1,数据2,数据3,数据4 };
建议:以上4种定义方式,利用第二种更加直观,提高代码的可读性
注意:初始化二维数组时,行数可以省略,但是列数不能省略
5.3.2 二维数组数组名
- 查看二维数组所占内存空间
- 获取二维数组首地址
和一位数组数组名相同,sizeof(数组名)是数组整体所占内存空间大小,数组名本身就是首元素地址。
注:二维数组的每个元素都是一个元素个数为列数的一维数组
二维数组数组名==第一个元素一维数组的地址==第一个元素一维数组的首元素地址
6. 函数
6.1 概述
作用:将一段经常使用的代码封装起来,减少重复代码
一个较大的程序,一般分为若干个程序块,每个模块实现特定的功能
6.2 函数的定义
函数的定义一般主要有5个步骤:
1、返回值类型
2、函数名
3、参数列表
4、函数体语句
5、return表达式
语法:
返回值类型 函数名(参数列表)
{函数体语句return表达式
}
- 返回值类型:一个函数可以返回一个值,在函数定义中
- 函数名:给函数起个名称
- 参数列表:使用该函数时,传入的数据
- 函数体语句:花括号内的代码,函数内需要执行的语句
- return表达式:和返回值类型挂钩,函数执行完后,返回相应的数据
6.3 函数的调用
创建好函数需要调用函数
函数名(参数);
上面就是调用函数的语法,如果有返回值前面可以创建一个整型变量来接收函数的返回值
注:函数调用时给函数传递的是实际参数,简称实参。而函数内部的参数列表并没有真实的参数,只是形式上的,所以简称形参。当函数为了接收函数调用传递过来的实参时就会创建空间来存储,这被称为形参的实例化。
6.4 传值调用和传址调用
概念:调用函数传参时有两中形式,分别是是传数值或传地址
传值调用:将值直接传递给形参,对形参作出改变不会影响到实参。
传址调用:将地址传递给形参,通过地址可以对实参做出改变。
6.5 函数的常见样式
常见的函数样式有4种
- 无参无返
- 有参无返
- 无参有返
- 有参有返
6.6 函数的声明
作用:告诉编译器函数名称及如何调用函数。函数的实际主题可以单独定义。
语法:
返回类型 函数名(参数列表);
与函数定义很像,只不过函数定义有函数体,而函数声明没有函数体所以函数声明后面要加上一个分号。
- 函数的声明可以多次,但是函数的定义只能有一次
6.7 函数的分文件编写
作用:让代码结构更加清晰
函数分文件编写一般有4个步骤
- 创建后缀名为.h的头文件
- 创建后缀名为.cpp的源文件
- 在头文件中写函数的声明
- 在源文件中写函数的定义
swap.h头文件
//swap.h文件
#include<iostream>
using namespace std;//实现两个数字交换的函数声明
void Swap(int* x,int* y);
swap.cpp源文件
//swap.cpp文件
//包含自定义头文件,需要证明这个文件是与swap.h文件关联的
#include "swap.h" //函数定义
void Swap(int* x,int* y)
{int a = *x;*x = *y;*y = a;
}
总结:swap.cpp也需要包含头文件说明这两个文件的函数声明和定义是关联的,其他文件想要调用swap.cpp函数,只需要包含头文件swap.h就行,因为swap.h和swap.c是关联的。
7. 指针
7.1 指针的基本概念
指针的作用:可以通过指针间接访问内存
- 内存编号是从0开始记录的,一般用十六进制表示
- 可以利用指针变量保存地址
7.2 指针的定义和使用
指针变量定义语法: 类型 * 变量名
示例:
#include<iostream>
using namespace std;int main()
{int a = 10;int* pa = &a;cout << "a = " << a << endl;cout << "pa = " << *pa << endl;*pa = 20;cout << "a = " << a << endl;system("pause");return 0;
}
运行代码:
7.3 指针所占内存
提问:指针变量也是需要开辟空间存储地址的,那这个空间也就是指针(地址)到底有多大呢?
答案是:4/8个字节,指针的大小是由编译器决定的,因为CPU是通过地址总线来传输地址信号,在32位操作系统下地址总线是32根,指针(地址)的大小是4个字节。在64位操作系统下地址总线是64根,指针(地址)的大小是8个字节。
7.4 空指针和野指针
空指针:指针变量指向内存中编号为0的空间,只要给指针变量赋值空指针NULL我们就不可以解引用,因为0~255之间的编号内存空间是系统占用的。
用途:初始化指针变量
注意:空指针指向的内存是不可以访问的
//NULL==0==空指针
int* p = NULL;
如果初始化指针变量不给地址就赋值一个空指针NULL,就是为了防止我们使用到野指针。
野指针:就是指向了为分配的内存空间,就是野指针,对未分配的内存空间解引用会报错。
7.5 const修饰指针
const修饰指针有三种情况:
- const修饰指针 ---常量指针
- const修饰常量 ---指针常量
- const即修饰指针,又修饰常量
1. const修饰指针 ---常量指针
int a = 10;
const int *pa = NULL;
pa = &a;//正确
*pa = 20;//错误
因为这里修饰的就是*pa是常量,所以*pa不能修改所指向的内存空间。
2. const修饰常量 ---指针常量
int a = 10;
int b = 20;
int *const pa = &a;
*pa = 20;//正确
pa = &b;//错误
const修饰pa本身就是pa不可以再指向其他地址。
3. const即修饰指针,又修饰常量
int a = 10;
int b = 20;
cosnt int* const pa = &a;
*pa = 20;//错误
pa = &b;//错误
既不想让*pa修改所指向的内存空间,又不想让pa指向其他空间就可以让两边都修饰const
8. 结构体
8.1 结构体基本概念
结构体属于用户自定义的数据类型,允许用户存储不同的数据类型。
8.2 结构体定义和使用
语法: struct 结构体名 { 结构体成员列表 };
通过结构体创建变量的方式有三种:
- struct 结构体名 变量名
- struct 结构体名 变量名 = {成员1值,成员2值...}
- 定义结构体时顺便创建变量
示例:
//第三种创建变量方式
struct stu
{int a;int b;char c;
}s3;//第一种方式
struct stu s1;
//第二种方式
struct stu s2 = {10,20,'a'};
其实在第一种和第二种的方式创建变量时,是可以把前面的struct给省略掉的,例如:
//第三种创建变量方式
struct stu
{int a;int b;char c;
}s3;//第一种方式
stu s1;
//第二种方式
stu s2 = {10,20,'a'};
第三种不可以。
结构体的使用:
//结构体访问成员
变量名.成员//结构体指针访问成员
变量名->成员
总结1:定义结构体时的关键字是struct,不可省略
总结2:创建结构体变量时,关键字struct可以省略
总结3:结构体变量利用操作符 "." 访问成员
8.3 结构体数组
作用:将自定义的结构体放入到数组中方便维护
语法: struct 结构体名 数组名[元素个数] = { {} , {} , . . . {}}
示例:
#include<iostream>
using namespace std;struct student
{char name[12];int age;int score;
};
int main()
{int n = 3;struct student* stu = (struct student*)malloc(sizeof(struct student) * n);for (int i = 0; i < n; i++){cin >> stu[i].name >> stu[i].age >> stu[i].score;}for (int i = 0; i < n; i++){cout << "名字:" << stu[i].name << " 年龄:" << stu[i].age << " 分数:" << stu[i].score << endl;}system("pause");return 0;
}
代码输出:
8.4 结构体指针
作用:通过指针访问结构体中的成员
利用操作符 -> 可以通过结构体指针访问结构体成员
8.5 结构体嵌套结构体
在结构体中可以定义另一个结构体作为成员,用于解决实际问题。
例如:需要一个结构体来存储学生信息,然后再需要一个结构体存储教师信息,教师信息需要嵌套存储学生的结构体来表示这是自己的学生。
示例:
//定义一个存储学生信息的结构体
struct student
{//名字char name[12];//年龄int age;//性别char ginder[5];
};
//定义一个存储教师信息的结构体
struct teacher
{//名字char name[12];//年龄int age;//教职编号int id;//管理学生struct student stu[50];//结构体嵌套
}
8.6 结构体做函数参数
作用:将结构体作为参数向函数中传递
传递方式有两种:
- 值传递
- 地址传递
还是老样子,如果需要改变结构体里的成员就传递地址,如果不需要改变成员就传值。
例如:将结构体传给函数打印函数的成员就传值。将结构体传给函数并修改结构体内的成员就传址
9. string功能解析
知识点重点:string字符串类型追加和两个string字符串判断
#include<iostream>
using namespace std;int main()
{string str1 = "hello ";string str2 = "world";//C++中的字符串类型变量是可以用下面这种方式追加字符串和字符的str1 += str2;cout << "str1 = " << str1 << endl;system("pause");return 0;
}
总结:只有string类型的变量可以直接以+=的方式追加字符或字符串
string字符串类型和char[ ]字符数组的区别:
1. 定义容量:
string:string类会自己增加容量,是动态,所以我们一开始创建是不需要专门指定大小
char[ ]:char[ ]一开始需要在[ ]内部指定大小
2. 字符串追加:
string:string追加字符串或字符不用任何库函数,只需要string类型变量+=(字符或字符串)
char[ ]:char[ ]字符数组不同,它需要strcat函数将一个字符数组的字符串追加到另一个字符串的后面
3. 字符串比较:
string:string也是可以不用自己调用库函数直接和其他字符串进行比较,直接判断string字符串==其他字符串
char[ ]:char[ ]是需要调用库函数strcmp来和其他字符串进行比较
总结:因为string是类,封装了这功能,所以我们是不需要自己调用库函数的,因为string里封装了这些功能。