声明
本系列来至中国大学慕课-阚道宏老师的C++系列课程的相关课堂笔记,阚道宏老师讲解细腻、精准,由浅入深,徐徐渐进,有兴趣的可以去中国大学慕课上查看阚道宏老师的相关课程。
本文仅用于学习交流,若有侵权,请联系我
一、程序中的变量
1.1 变量的基本概念
数据是程序处理的对象,程序中的数据包括原始数据、中间结果和最终结果等,数据要存放在内存中,才能被CPU读取和处理,处理后的结果也只能保存在内存中,C++使用变量来保存数据。
定义变量:就是为变量申请内存空间
访问变量:定义变量后,可以向所分配的内存单元写入数据或读出数据,这称为变量的访问。
程序执行时,程序中的变量就是内存中的某一个内存单元。程序结束,退出时,变量将释放其所占用的内存空间,以便给其他程序继续使用。简单的说程序中的变量就等于内存单元:变量=内存单元。
1.2 变量的定义
不同类型数据,有不同的数值范围,所需要的存储位数不一样,例如月份可以用整数表示,其数值范围为1-12,转化为二进制,其二进制位数最多为4位,内存是以字节为单位进行管理的,一个字节就是8位,我们可以申请字节来存储月份数据,不足8位的数据高位补零,而对于摄氏温度这样的数据,其数值可能为负数,也可能为实数,计算机存储负数需要使用有符号格式,即补码的格式,而存储实数需要阶码加尾数的格式,这些格式统称为存储格式。
定义变量时,要根据数据的取值范围,指定其存储位数和存储格式,为了让程序员在定义变量时,能方便的指定存储位数和存储格式,计算机高级语言引入了数据类型(存储位数、存储格式)的概念,在C++中所有变量都是有类型的,程序员在定义变量时,需指定其数据类型,也就是指定其存储位数和存储格式。
根据实际运用的需要,C++语言为数值计算,预定义了11种数据类型,它们被称为基本数类型,C++语言规定了每一种基本数据类型的存储位数、有没有符号位、是存储整数还是实数以及所存储的数值范围等等,
数据类型 | 说明 | 存储位数(字节数-位数) | 数值范围 | 运算用途 |
---|---|---|---|---|
char或signed char | 有符号字符型 | 1-8 | -128~127 | 算术运算|关系运算 |
unsigned char | 无符号字符型 | 1-8 | 0~255 | 算术运算|关系运算 |
short 或 signed short | 有符号短整型 | 2-16 | -32768~32767 | 算术运算|关系运算 |
unsigned short | 无符号短整型 | 2-16 | 0~65535 | 算术运算|关系运算 |
int 或 signed int | 有符号整型 | 4-32 | -2147483648~2147483647 | 算术运算|关系运算 |
long 或 signed long | 有符号长整型 | 4-32 | -2147483648~2147483647 | 算术运算|关系运算 |
unsigned 或 unsigned int | 无符号整型 | 4-32 | 0~4294967295 | 算术运算|关系运算 |
unsigned long | 无符号长整型 | 4-32 | 0~4294967295 | 算术运算|关系运算 |
float | 单精度浮点型 | 4-32 | 算术运算|关系运算 | |
double | 双精度浮点型 | 8-64 | 算术运算|关系运算 | |
long double | 长双精度浮点型 | 8-64 | 算术运算|关系运算 |
程序员因根据处理数据可能的取值范围来判断选择哪种数据类型,既要防止溢出保证精度,又要尽可能的少占用内存,不同类型的数据可以做不同的运算,比如在C++语言中,整数(负整数、0、正整数)类型既可以做算术运算(加减乘除),也可以做关系运算(比较大小),整数类型还可以进行位运算,实数(有理数和无理数的集合)类型不可以。因此在计算机语言中,数据类型的内涵,除了包括储存位数和存储格式之外,还隐含包括了该类型数据可以进行哪些运算。
1.3 C++中的词法元素
程序员在定义变量时,除了指定数据类型之外,还需要为变量命名,即指定变量名,C++语言的词法元素包括:关键字、标识符、常量、运算符、分隔符等。
1、关键字
关键字是C++语言预先定义好的,具有特定含义的单词,例如定义数据类型时使用的:int、float、unsigned等,
2、标识符
程序中所包含的一些实体,例如变量,它们需要程序员命名,由程序员定义的实体名称统称为标识符,对标识符的命名,需要符合3条命名规则:
- 以大写、小写、下划线开头
- 后续部分可以是大小字母、下划线、数字0-9
- 标识符不能是关键字、中文
另外,C++区分大小写字母。
3、语句
语句是一条完整的指令,c++中的语句应符合c++的语法规则,并以分号";"结束,分号是英文状态下的符号。
1)变量定义语句
C++语言变量定义语句语法:
先指定数据类型,在指定变量名,变量名的命名需符合C++语法格式;并以分号“;”结束。
数据类型 变量1, 变量2, ..., 变量名n;
语法说明:
- 数据类型:指定变量的储存位数和储存格式
- 变量名:符合标识符的命名规则
- 定义多个变量:可以在一条语句中定义多个具有相同数据类型的变量,变量之间用英文逗号“,”隔开
举例:定义2个变量 ctemp 和 ftemp
double ctemp;
// 计算机为double类型变量ctemp分配8个连续字节作为其内存单元,并以浮点数格式在该内存单元中存储数据
double ftemp;
//上述两条语句等价于
double ctemp, ftemp;
程序由计算机执行,当执行到程序中的定义变量语句时,计算机为所定义的变量分配内存空间,后续语句,将通过变量名来访问该内存单元,计算机只能识别机器语言,机器语言时通过内存地址来访问内存的,高级语言则通过变量名来访问内存,变量名便于程序员记忆和使用,高级语言需编译成机器语言才能被计算机执行,编译时,程序中的变量名被转换成了内存地址。
2)变量的访问
定义变量后,可以向变量所分配的内存单元写入或读出数据,对变量的读写操作,统称为对变量的访问。
-
写入
C++对变量的写入有3中方式
- 利用键盘输入:使用关键字cin+右移运算符的方式,cin >> ctemp;
- 赋值:使用赋值运算符等于号“=”,对变量进行赋值运算,ctemp = 36;
- 初始化:定义变量时为变量赋值,称为变量初始化,这个值称为变量的初始值或初值,int x = 10, y;
-
读出
C++从变量读出数据的操作有2种方式。
- 自动读取:当变量做为操作数参与运算时,计算机将自动读取其内存单元中存放的数据,ftemp = ctemp*1.8 + 32;
- 输出:使用输出语句,读出内存单元中存放的数据,并显示给用户,cout << ftemp;
只有定义后的变量才有内存空间,才能被访问,程序员在编写c++程序时,应遵循,先定义,后访问的原则;未经定义的变量不能访问。
二、程序中的常量
在程序运行的过程中,数值不会改变的量,称为常量,常用的常量有字面常量和符号常量,常量也有数据类型,在定义时需要指定数据类型,以确定计算机在存储该常量时的存储位数和存储格式。
2.1 常量的数据类型
C++采用两种方式指定常量的数据类型:
-
默认形式
C++ 中整数常量默认为int类型,储存位数为32位,4个字节,所有的实数常量默认为double类型,储存位数64位,8个字节。小数点是区分整数和实数的标志,比如10和10.0
-
后缀形式
后缀字母 数据类型 举例 在整数后面添加L或l 长整型long 10L,10l,-20L 在整数后面添加U或u 无符号格式unsigned 10U,10u,20UL,20LU 在实数后面添加F或f 单精度浮点型float 10.5f,10.5F,20F -
指定整型常量的数制
C++中的数值常量默认为10进制,某些情况下,程序员可能需要以8进制,16进制来表示整数常量
10进制:直接写入10进制数字,例如32就是32
8进制:以0开头的整数常量,例如032
16进制:以0x开头的整数常量,就是16进制,例如0x32
同样的数字,比如都是32,进制越大,数值越大。编写时,程序员可以使用8进制、10进制、16进制编写,编译成机器语言是,编译器会将不同进制的常量,都转换为2进制运行,计算机硬件只认2进制。
2.2 常量的类型
-
字面常量
数学表达式书写,ftemp = ctemp*1.8 +32;这个表达式中,1.8是实数常量,32是整数常量。
- 负数:-32,-1.8
- 实数:1.8、0.18e1、018E1
-
符号常量
可以将经常使用的常量定义为一个符号常量,然后在程序代码中使用符号常量代替具体的数值。实例:
#include
using namespace std; #define pie 3.14159 //定义一个符号常量pie表示圆周率的数值 int main() { double r; cout<<"请输入圆的半径:"; cin >> r; double s; s = r*r*pie; cout<<"圆的面积为:"<< s <
C语言语法:定义符号常量
在C语言中通过define关键字定义常量。
以”#“开头,结尾不能加”;“
-
符号常量名应符合命名规则,习惯上符号常量名用大写字母。
代码实列:
#define PI 3.14 //定义符号常量PI,来表示圆周率值3.14
c++语言:
可以通过const关键字来定义常变量,语法如下:
const PI 3.14; //末尾必须以分号;结束
三、算术运算
3.1 表达式
元素计算过程和元素计算内容的公司,称之为表达式。表达式由运算符、操作数、括号组成,C++语言中,在表达式后面加上分号”;“,就构成了表达式语句。表达式语句用于处理数据,是C++中最常用的语句。
1、运算符
运算符有优先级,优先级高的先算,同级运算符,按其结合性所规定的顺序来运算,例如是从左到右,或是从右到左,括号可以提高优先级,括号内的先算,多层括号时,先算里层括号。
大部分运算符需要2个操作数,例如两个数的加法。C++将需要两个操作数的运算符称作为双目运算符,某些运算符只需要一个操作数,称之为单目运算符。
双目运算符(一元运算符):2个操作数,例如加减法
单目运算符(二元运算符):1个操作数,例如求绝对值
C++语言根据功能和用途,将运算符划分为:
- 算术运算符
- 位运算符
- 关系运算符
- 逻辑运算符
算术运算符:加减乘除是最常用的算术运算符,分别用+(加)、-(减)、*(乘)、/(除)来表示。由算术运算符构成的表达式称为算术表达式。C++中加减乘除的运算规则和常识一样的,先乘除后加减,但是也存在一些区别。
优先级:运算符有不同的优先级,优先级高的先算,C++语言用1-15的数值来表示优先级的高低,1为最高,15为最低,乘除为3级,加减为4级。
结合性:同级运算符按其结合性所规定的顺序来运算,例如加减乘除的结合性是从左到右,与数学中一致,在C++语言有些运算符的结合性是从右到左。
操作数及其类似转换:算术表达式中,参与运算的操作数可以是常量,也可以是变量,C++中的操作数,除了数值还有数据类型,CPU只能对相同类型的两个操作数进行算术运算,所以计算机能够运算5+3。当运算两个不同类型的操作数时,例如:5.5+3,5.5是double类型,3是int类型,C++语言规定,当不同的两个操作数参与算术运算时,需先转换成相同类型,然后再运算,C++为类型转换提高了2种方法:
-
强制转换
语法:数据类型(操作数)或(数据类型)操作数,例子如下:
short(32); //指定32为有符号短整型(2字节) float(1.8); //指定1.8为单精度浮点型(4字节)
注:数据类型应与操作数的数值相符,否则将造成数值的改变。例如:
float(32); //将32转换为32.0,不存在数据丢失,可以接受。 int(1.8); //1.8将变为1,丢失小数部分。 short(32769); //变为-32767,溢出 unsigned short(-1); // 将-1变成65535,丢失了符号,且数值改变。
-
自动转换
程序员在源程序种编写算术表达式时,可以将数据类型的转换工作交给编译器来完成,C++编译器将源程序编译成目标程序时,如果发现,某个表达含有不同数据类型的操作数,就会自动进行转换,自动转换又称为隐含转换,自动转换的原则是从低类型向高类型转换。
char unsigned short unsigned int unsigned long unsigned float double char short long 低>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>高
例如:5.5+3,编译器将自动转换为5.5+(double)3,自动转换功能能够简化程序员的工作。
-
表达式结果
在C++语言中,任何数据都是有数据类型的,因此表达式的计算结果有值、也有数据类型,算术表达式计算结果的数据类型,等于其操作数的数据类型。如果参与运算的操作数类型不同,将自动转换,遵循低类型向高类型转换的规则,表达式的结果的数据类型和操作数转换的数据类型一致。
特别注意:两个整数相除,例如5/2,结果为2,因为5和2均为int类型,其结果也将保持int类型,丢掉小数部分,系统不会对表达式结果的数据类型进行自动转换。正确做法是将其中一个操作数变为double类型,比如5.0/2,结果将为2.5。
-
括号
C++在做运算时只许用小括号(),括号内的先行计算,多层括号时,先算最里层括号,C++对中括号[]和大括号{}赋予了新的含义,用在了其他场合,不能在算术表达式中使用。
-
其他算术运算符
取余运算符(%):例如,10%6 = 4,%只能对2个整数型操作数进行取余运算,运算结果也是整型。运算优先级为3,运算顺序从左至右,和乘除运算符相同。
-
自增运算符(++):如果要将某一个数值型变量X的值加1,就可以利用自增运算符,写作:X++,++是单目运算符,操作数必须是变量,不能是任何形式的常量,X++可以理解为一个表达式,表达式的值等于X增加1之前的值,数据类型和X的值保持一致。这是一种泛化运算符,有2层含义,第一层是将变量的值增加1,第二层是构成一个表达式,其可以作为操作数继续参与运算。++可以前置,可以后置,两种写法均能将变量的值增加1,但是作为表达和参与运算时两者有所区别。例子:
//假设 int x = 10; (x++)*2; //此时表达式:x++的值为10,运算结果为20 (++x)*2;//此时表达式:++x的值为11,运算结果为22
作为表达式时值不同:x++的值等于变量x+1之前的值,++x等于变量x+1后的值。
运算优先级和结合性不同:++后置时,例如x++,运算优先级为1级,结合性为从左至右;++前置时,例如++x,运算优先级为2级,结合性为从右至左。
自减运算符(--):作用是让变量减一,也有前置和后置,用法、优先级、结合性与自增运算符相对应。
四、位运算
计算机程序,可以用一个二进制位,来记录某种对象的开关状态,这种二进制位,被称为状态位。举例,假设用计算机程序来控制一组开关,用0表示关,用1表示开,那么一个字节就能表示8盏灯的开关状态。对二进制数值的状态进行设定,就能控制灯的开关状态。
C++提供了6种位运算符,可以用于状态位的设定和检测:
运算符 | 描述 | 运算优先级 | 结合性 | 实例:假设如果 A = 60,且 B = 13,现在以二进制格式表示,它们如下所示:A = 0011 1100,B = 0000 1101 |
---|---|---|---|---|
& | 位与运算符:双目运算符,参与运算的两个位都为1,结果为1,否则为0,也就是求交集。可以用来检测操作数种某一位的状态,或将其置为0。 | 8 | 从左向右 | (A & B) 将得到 12,即为 0000 1100 |
| | 位或运算符:双目运算符,参与运算的两个位,只要有一个为1,结果为1,否则为0,可以理解为求并集。利用位或运算,可以将一个操作数的某一位,置为1。 | 10 | 从左向右 | (A| B) 将得到 61,即为 0011 1101 |
^ | 异或运算符:双目运算符,参与运算的2个位,如果结果不同,结果为1,如果相同,结果就为0。可以利用异或运算,将一个操作数的位进行反置 | 9 | 从左向右 | (A ^ B) 将得到 49,即为 0011 0001 |
~ | 位反运算符:二进制补码运算符是一元运算符,具有"翻转"位效果,即0变成1,1变成0。 | 2 | 从右向左 | (~A ) 将得到 -61,即为 1100 0011,一个有符号二进制数的补码形式。 |
<< | 左移运算符:按二进制位,左操作数的值向左移动右操作数指定的位数。高位被移除,低位补零。 | 5 | 从左向右 | A << 2 将得到 240,即为 1111 0000 |
>> | 右移运算符:按二进制位,左操作数的值向右移动右操作数指定的位数。低位移除,无符号数,高位补零,有符号数,高位补符号位。 | 5 | 从左向右 | A >> 2 将得到 15,即为 0000 1111 |
C++语言无法直接书写2进制,通常是将2进制转换为16进制后进行位运算。16进制的书写方式:0x
所有位运算的操作数必须是整型:char 、short 、int 、long,包括有符号和无符号类型。
五、赋值运算
5.1 赋值运算符
赋值运算符“=”,用于修改变量的数值,即将新数值写入变量对应的内存单元,存储在该内存单元中的原数值将被擦除。赋值运算符,是将“=”右边的表达式结果或数值,赋值给左边的变量,C++语法规定,赋值运算符左边的必须是变量。
赋值运算本身也构成一个赋值表达式,赋值表达式的结果,他的数值等于左边变量赋值以后的数值,数据类型就是左边变量的数据类型,例如:(x=5)2 ,x=5的数据类型为int型,数值等于5,可以将这个表达式作为操作数继续参与运算。赋值运算符的优先级是14级,结合性是从右至左*。
例子:y=x=2+6; 与 y=(x=(2+6));等价,先计算加法,然后依次从右至左进行赋值。
5.2 泛化运算符
泛化运算符:=,++,--
C++语言中,像加(+)减(-)乘(*)除(/)这样的普通运算符,不会改变操作数的值,例如,x+5,只会读出x存放的值与5相加,不会改变变量x存放的值。
由泛化运算符所构成的表达式,运算时,不仅会得到运算结果,还会改变操作数的值,例如x++,合理运用泛化运算符,可以让语句更加简洁。例子:
a = 10; b = 10; c = 10;
a = b = c = 10; //以上两条语句等价
y = x; x = x + 1;
y = x++; //以上2条可以等价
x = x + 1; y = x;
y = ++x; //以上2条语句等价
5.3 复合赋值运算符
赋值运算符还可以与部分的算术运算符和位运算符组合形成复合赋值运算符。
x=x+5 | ··· | ··· | ··· | ··· | ··· | ··· | ··· | ··· | ··· |
---|---|---|---|---|---|---|---|---|---|
+= | -= | *= | /= | %= | &= | |= | ^= | <<= | >>= |
如果一个变量x与表达式进行某种运算,运算结果再赋值给变量x,那么这一条赋值语句就可以使用复合运算符,
复合运算符,总时先计算后面的表达式。复合运算符的优先级和结合性与赋值运算符“=”一样的,是14级、从右至左。
y *= x+2;
y = y*(x+2);//以上两条语句等价
5.4 变量初始化
定义变量时,为变量赋一个值,称之为变量初始化。C++语言可以像C语言一样通过赋值运算符为变量进行初始化,例如:int x = 10,y; 这条语句,将变量x,y的数据类型定义为int类型,并分配内存空间,其中x=10,意为像x所分配的内存单元写入数值10,y没有赋值,y的值可能是以前的程序遗留下的,是不确定的。
在面向对象程序设计中,初始化是通过构造函数来实现的,C++支持面向对象程序设计,可以使用面向对象的语法形式来初始化变量,例如:in(10),y;
int x=10,y; //c语言定义的变量初始化
int x(10),y; //C++面向对象变量初始化
5.5 常变量
初始化后,数值不能改变的变量称之为常变量,和C语言不同,C++使用关键字const定义。
语法要素:
- 定义方式:const 数据类型 变量名 = 初始值;
- 定义常变量时必须初始化
- 常变量定义后不能改变,不能被再次赋值
#define x 10 //C语言风格,定义x的常变量为10。
const int x=10; //C++语言风格
如果程序处理的某个数据是常量,在程序运行过程中不需要变动,可以定义为常变量,常变量可以提供程序可读性,便于程序修改等等
六、数据的输入输出
程序的功能是对数据进行处理,通常原始数据需要用户使用输入设备,输入到计算机,处理结果通过输出设备反馈给用户。
以前程序员在控制台上操作计算机,所运行的程序时命令行程序,控制台主要包括键盘和显示器,通过键盘下达指令,通过显示器查看处理结果。因此将键盘称为标准输入,将显示器称为标准输出,将命令行程序称为控制台程序,这些称呼沿用至今。
C++语言将数据从键盘输入到某个内存变量,或将某个内存变量中的数据输出到显示器,这个过程被看成是一个数据流动的过程。站在内存变量的角度,键盘是一种提供数据输入的数据源,显示器则是一种数据输出时的目的地,C++语言将提供输入数据的数据源称作输入数据流,而将输出数据时的目的地称作输出数据流。统称为输入/输出流。
- 键盘(cin)—输入数据流
- 显示器(cout)—输出数据流
输入/输出流t不是C++语言的主体,是其附属组成部分,使用cin/cout时需要导入一些外部程序。如下所示:
#include
using namespace std;
int main(){
int x,y; double z;
cin >>x>>y>>z;
cout << x << y*z << z << endl;
return 0;
}
标准输入语法:
cin >> 变量1 >> 变量2 >> ... ... >> 变量n;
- cin表示键盘输入,借用右移运算符“>>”表示数据从键盘流向后面的变量
- 可以输入多个变量数据,在输入时,用空格键space或tab键隔开,回车键enter结束。
- 键盘输入数据的类型应与变量的类型匹配
- 执行该语句时,计算机将暂停程序的执行,等待用户从键盘输入指定个数和类型的数据,然后将这些数据按位置次序赋值给相应的变量。
标准输出的语法:
cout << 表达式1 << 表达式2<< ... ... << 表达式n << endl;
- cout表示显示器,借用左移运算符“<<”表示数据从内存流向显示器。
- 单个常量或变量可认为是最简单的表达式
- 表达式“endl”表示换行显示
- 一条语句可以输出多个表达式结果
- 执行该语句时,计算机首先从右往左挨个计算表达式的结果,然后再从左往右依次输出显示各个表达式计算的结果,各个结果之间没有间隔,在没有endl表达式的情况下,输出结果也不会换行。
cout还可以在程序中输出提示信息,使得程序的运行更加人性化。提示信息为字符串常量,例如:“china”或“中国china”
七、引用与指针
再第一节中,讲解了关于变量定义和内存单元的关系,可以通过变量名访问内存单元,这是一种访问内存的方法,还可以通过引用和指针的方法对变量所在的内存单元进行访问。
访问变量内存单元的方法:变量名、指针、引用
7.1 引用
在c++中允许为已经定义的变量取一个别名,称为变量引用名,它是一个特殊变量称为引用变量,引用变量和其引用的变量公用一个内存单元,不会再单独分配内存空间。引用说明符和位与运算符都用&表示,&这个符号具有多意性,要根据实际情况进行区分,定义引用变量后,可以直接访问引用变量来访问引用的变量。
定义引用变量与定义普通变量方法类似,需要指定引用变量的字符串类型,需要和引用的变量类型一致;语法如下:
引用数据类型 &引用变量名 = 被引用变量名
引用类型:引用变量的数据类型,必须和被引用的变量一致;
引用说明符:用&来申明;
引用变量名:需符合标识符的命名规则;
被引用变量名:指定被引用的变量名,必须是已经定义的变量;
-
初始化:定义引用变量必须指定是哪个变量的引用,引用变量只能引用一个变量,定义后不能再引用其他变量。
int x =10; //定义引用变量 int &xa = x; // 称xa是x的一个引用 int x, int y; int &xa = x; //一条定义变量的语句即可包括普通变量,又包括引用变量
7.2 指针
指针访问:通过内存地址去访问内存单元的方式。
计算机独写内存操作的最小单位是字节,为每个字节指定一个整数编号,通常从0开始编号连续编号,这个整数编号就称为该字节的内存地址,程序执行时,计算机将系统中的空闲内存分配给程序中定义的变量,C++语言提供一个取地址运算符“&”来获取内存单元的地址,语法如下:
& 变量名
语法说明:
- 所取出的比哪里地址是程序执行时该变量所分配的内存地址,每次执行程序时,变量不一定会被分配到同一内存单元,这取决于本次执行时计算机哪些内存单元是空闲的。
- 一个变量可能占用多个字节,变量地址指的是变量所占内存单元第一个字节的地址,也称首地址。
- 取地址运算符是单目运算符,操作数必须是变量,其优先级为2级,结合性从右向左。
- C++语言中,“&”是一符多义的符号,位与运算符、引用说明符、取地址运算符,其含义要更具上下文的实用场景进行区分。
实列:
#include
using namespace std;
int main(){
int x = 10;
cout << "x的值=" << x << endl;
cout << "x的内存首地址为:" << &x; //显示变量x的内存首地址,结果是16进制。
return 0;
}
指针类型:
内存地址是一类特殊类型的数据,C++语言将地址类型称为指针类型,简称指针,指针类型存储位数为32位,4个字节,以无符号整数类型存储。
指向类型:
C++语言可以通过内存地址,来访问对应的内存单元,给定一个内存地址,如何去访问内存单元呢?内存单元中存放的数据类型并不确定,不同类型的数据占用的字节数不同,储存格式也不同,通过地址访问内存单元时,需要指定对应的是什么样的数据类型,这样才可以存储数据,这样的数据类型称为地址的指向类型,通过地址访问某个变量x的步骤需要3步:
定义一个专门保存地址的变量(假设为p),该变量称为指针变量
取出变量x的地址,将其赋值给指针变量p
-
通过指针变量p所保存的地址来访问变量x的内存单元。
指针变量:
指针变量是专门保存其它变量内存地址的变量,指针变量保存的是哪个变量的内存地址,就指向哪个变量。改变指针变量保存的内存地址,就叫改变指向。
一个指针变量只能指向同一数据类型的不同变量,这个数据类型,就是指针变量的指向类型。例如一个指向类型为int型的指针变量,只能指向int类型的变量,这种变量称为int型指针变量。
定义指针变量的语法:
*指向类型 指针变量名;
- 指向类型:指定了指针变量能够保存哪种类型变量的地址,或者说指定了指针变量能够指向哪种类型的变量;
- *p是指针变量说明符,定义变量语句种,变量名前加“*”,表示该变量为指针变量;
- 指针变量命名需复合标识符的命名规则
代码实例:
#include
using namespace std;
int main(){
int x, y;
int *p; //定义int型指针变量p
p = &x; // 取出x的内存地址,并赋值给指针变量p,即p指向变量x
/*上述2条语句等价于下面一条*/
int *p = &x; //定义一个int型指针变量p,初始化为指向x
p = &y; //取出y的内存地址赋值给指针变量p,则p修改了指向,指向y
return 0;}
7.3 变量的间接访问
变量的直接访问与间接访问:通过变量名是直接访问,通过指针和引用变量的方式是间接访问。
为了实现通过内存地址对变量的间接访问,C++语言提供了指针运算符,又被称为取内容运算符语法如下:
*指针变量名
- 按照指针所保存的地址访问指向的内存单元,可写入读出数据
- 间接访问之前,指针变量应当指向某个已经存在的变量,即指针变量必须先赋值,再间接访问,否则会出现错误。
- 指针运算符是单目运算符,其优先级为2级,结合性从右向左。
- C++语言种“*”是一个多义符,乘法运算符、指针变量说明符、指针运算符,在不同的场合具有不同含义,要更具上下文进行区分。
#include
using namespace std;
int main(){
int x; int *p = &x; ///定义一个int型指针变量p,初始化为指向x
*p = 10; //通过指针变量间接访问x,将x的值修改为10
cout << "x的值为:"<< x << ",指针变量p指向的值:" <<*p <
使用指针变量应对注意的问题:
-
指针变量应当先赋值,也就是说,指针变量必须先指向某一个同类型的变量,再间接访问。
int *p;cout<< *p; //错误
因为p没有指向任何变量。没有初始化的指针变量p中可能保存了一个随机地址值,这个时候通过*p来访问这个随机地址所对应的内存单元,将会出现不可预料的错误,一个多任务的操作系统,可以同时运行多个程序,每个程序只能访问自己所分配的内存单元,随意访问其他程序的内存单元,或访问一个不存在的内存单元这都属于严重错误,是被严格禁止的。
-
指针变量不能用除0以外整数来赋值。
int *p = 10; // 错误,原因同第一点, /*但是有一个例外:*/ int *p = 0; //这种写法正确,表示现在指针变量不指向任何一个变量。
-
指针变量的指向类型应当与所指向的变量类型一致。
double x = 105; int *p = &x; //上述写法错误。
C++语言另外提供了一种特殊的指向类型,void类型,该类型可以指向任意类型的变量,例如:
int x = 10; double y = 10.5; void *p; p = &x; cout << *((int *)p); //强制转换void指针变量p的指针变量类型为int类型,结果为10 p = &y; cout << *((double *)p); //强制转换void指针变量p的指针变量类型为double类型,结果为10.5
**void指针类型在间接访问变量时,必须先强制转换为目标变量的数据类型相对应的数据变量。
-
相同类型指针变量之间可以相互赋值
int x = 10, *pi = &x; double y = 10.5, *pd = &y; int*p1; p1= pi; //正确 p1 = pd; //错误,两者数据类型不一致 void *p2; p2 = pi; //正确 p2 = pd; //正确
-
可以定义指向常变量的指针,通过该指针变量不能修改常变量的值
const int x=10; x = 15; //错误 const int *p; //定义int型常变量指针p,前面必须加const P = &x; cout << *p; //正确,可以间接访问x的内存单元,读取常变量x的值 *p = 15; //错误,不能间接修改常变量的值,只能读取 int y=20; p = &y; // 指向常变量的指针,也可以指向普通变量。 cout<< *p; //正确,可以通过常变量指针间接读取普通变量的值 *p = 15; //错误,常变量指针无法间接修改普通变量的值。
定义指向常变量的指针时,必须用关键字const声明。指向常变量的指针,也可以指向普通变量,可以间接访问普通变量,但是同样不能修改所指向的普通变量。
-
可以定义指针类型的常变量,注意:与定义指向常变量的指针是两回事儿。
即指针常变量,定义时,在*和标识符之间加上const声明,定义时需初始化,确定其指向的某个变量,定义以后不能再改变其指向。
int x = 10, y=20; int *const p = &x; //定义时,在*和标识符之间加上const声明,定义指针类型的常变量p,定义时就必须初始化。