c++指针常量和常量指针的区别

指针一定要从地址的角度理解,就是保存地址的变量。32bit系统不管什么数据类型都是占 4 字节,64bit系统就是占 8 字节

指针常量:指向不能动,但是指向的内容可以动

指针常量存的其实是一个地址。指针常量的定义方法:

int i = 1;
int *const p = &i;  // p 是指针常量

指针常量也可以叫地址常量,说明指针常量里面存的是地址, 且是不可改变的,也就是指向不变。但是地址里面的值是可以改的

//64bit 系统上运行的结果
#include <iostream>
using namespace std;

// 指针常量, int *const p。指针常量占8个字节(64 bit系统),所以指针常量就是个地址(也可叫地址常量)
int main() {
	int i = 1;
	int a = 2;
	cout << &i << endl;  // 变量 i 的地址
	int *const p = &i;    // 定义一个指针常量 p,指针常量是8个字节的地址,且地址不可改变
	i = 2;    // 指针常量地址所指向的值可以改变

	cout << "*p = " << *p << endl;  // 改变地址指向的值

//	p = &a;               // 指针常量p指向固定的地址,更改指针常量地址会报错
	//p++;   // 更改指针常量 p 里的地址会报错,即指向不能动
	*p = 5;      // 不能改变 p 里面所存的地址,但是可以通过 *p 直接对地址里存的值进行更改,即指向的内容可动

	cout << p << endl;   //
	cout << sizeof(int *const) << endl;   // 指针常量占 8 字节(64 bit),32 bit 占 4 字节
	cout << sizeof(const int) << endl;    // 常量指针占 4 字节(64bit and 32 bit)
	cout << sizeof(p) << endl;   // p 是指针常量,占 8 字节(64bit)
	cout << sizeof(*p) << endl;  // *p 是整型变量,占 4 字节(64 bit and 32 bit)
}

>>>
0x78fe1c
*p = 2
0x78fe1c
8
4
8
4

指针常量示意图(红色代表不可更改):
在这里插入图片描述

常量指针:指向可以动,但是指向的内容不能动

指针可以认为是保存地址的一个变量(地址变量)。常量指针的定义如下:

int i = 1; 
const int *p = &i;  // 定义常量指针 p

常量指针 p 里面也是存地址的,

#include <iostream>
using namespace std;

// 常量指针, const int *p。指针常量占 8 个字节(64 bit系统),所以存的也是地址
int main() {
	int i = 1;
	int a = 2;
	cout << &i << endl;  // 变量 i 的地址
	const int *p = &i;    // 定义一个常量指针 p,常量指针 p 是 8 个字节的地址
	
	cout << "*p = " << *p << endl;  // 地址指向的值
	cout << p << endl;   // 常量指针的地址

//	*p = 5;    // 企图通过常量指针来更改 i 对应的值,会报错
	i = 3;     // 虽然不能通过常量指针来更改 i 对应的值,但是可以改变 i 本身

	cout << "*p = " << *p << endl;  // 改变地址指向的值

	p++;   // 改变常量指针里的地址值
	cout << p << endl;   // 常量指针的地址 
	
	cout << sizeof(const int*) << endl;  // 常量指针占 8 字节(64 bit),32 bit 占 4 字节
	cout << sizeof(int *const) << endl;  // 指针常量占 8 字节(64 bit),32 bit 占 4 字节
>>>
0x78fe1c
*p = 1
0x78fe1c
*p = 3
0x78fe20
8

常量指针的示意图:
在这里插入图片描述

数组名是一个常量指针

对于整形数组来说,因为数组名是一个固定的地址(数组的首地址),所以就是指向不能动,但是指向的内容可移动,即可以通过指针常量改变数组内容。
(1)int类型数组的例子:

#include <iostream>
using namespace std;

int main() {
	int a[3] = {1, 2, 3};  // 定义数组并初始化
	//int a[3];   // 定义数组
	//a = {1, 2, 3}; // 定义数组后,如果直接用 = 赋值会出错,因为数组名是常量地址(常量指针),不能被赋值,c中只有变量才能被赋值
	//a[0] = 1;   // 只声明了数组之后,就只能通过这种方式初始化数组
	//a[1] = 2;
	//a[2] = 3;
	cout << a << endl;
	cout << &a[0] << endl;
	cout << "a[0] = " << a[0] << endl;
	*a = 5;  // 通过数组名(常量指针)改变值
	cout << "a[0] = " << a[0] << endl;
	cout << sizeof(a) << endl;
}
>>>
0x78fe14
0x78fe14
a[0] = 1
a[0] = 5
12

(2)char类型的数组例子
初始化:char类型数组初始化除了可以和int类型初始化一样以外,还可以用strcpy()函数来初始化

#include <iostream>
using namespace std;

int main() {
	char a[5];  //
	char b[] = "yang";  // 这是一个字符串,等价于 char b[] = {'y','a','n','g','\0'}; 效果一样
	//char b[] = {'y', 'a', 'n', 'g', '\0'};
	strcpy(a, b);  // 用 strcpy() 初始化字符数组,a的长度最好大于等于b的长度,不然会出现警告, 注意strcpy()没有返回值
	// strcpy(a, "yang"); // 用 strcpy() 初始化字符数组

	cout << "a = " << a << endl;  // 字符串名字并不是常量指针,输出 yang
	cout << "a[0] = " << a[0] << endl;
	cout << "&a = " << &a << endl;  // 字符串名字并不是常量指针,输出字符串的首地址
	cout << "&a[0] = " << &a[0] << endl; // 并没有输出字符串的首地址,输出yang
	cout << "&a[1] = " << &a[0] << endl;  // 输出 ang
	cout << sizeof(a) << endl;
}
>>>
a = yang
a[0] = y
&a = 0x78fe16
&a[0] = yang
&a[1] = ang
5

并没有字符数组这个概念,字符数组就是字符串,在c++编译器内,char b[] = {'y', 'a', 'n', 'g', '\0'};char b[] = {'y', 'a', 'n', 'g'};会被默认为字符串"yang",系统会自动增加'\0'。字符数组的名字并不是首字母的地址,而是整个字符串。。

数组和字符串的关系:字符串可以看成是char型的数组。

字符串就是数组后面加了一个\0结尾字符,所以字符串的性质和数组性质差不多,只是字符串长度比数组+1。字符串本身就是数组,数组本身就是指针,所以字符串也是指针,但是字符并不是指针。。但是字符串名字不是一个常量指针,是一个字符串类型。char型数组赋值可以用strcpy()函数,
string定义字符串,其实是用了容器,本质上的字符串就是个char型的数组,在c++里,char型数组会被认为是字符串。

char b[] = "yang";  // 这是一个字符串,等价于 char b[] = {'y','a','n','g','\0'}; 效果一样
char b[] = {'y', 'a', 'n', 'g', '\0'};
// 这两个定义字符串的方法是一样的

字符串的一些函数:
strcpy(s1, s2); 复制字符串 s2 到字符串 s1。
strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。连接字符串也可以用 + 号,即两个字符串可以相加。
strlen(s1);返回字符串 s1 的长度。
strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回值小于 0;如果 s1>s2 则返回值大于 0。
strchr(s1, ch); 返回一个指针,指向字符串 s1 中**字符 ch 第一次出现的位置。
strstr(s1, s2); 返回一个指针,指向字符串 s1 中
字符串 s2 **第一次出现的位置。

c++指针数组和数组指针

(1)c++指针数组:它是一个数组,数组里面存储的是一些指向 int 或 char 或其他数据类型的指针。指针数组的声明如下: int *ptr[MAX][]优先级比*高,所以ptr[]先结合成一个数组,int *只不过是修饰ptr[MAX]数组的,所以这是指针数组);ptr是一个数组名, 在这里ptr中的每个元素,都是一个指向 int 值的指针。在一个32位操作系统中,一个指针变量占的内存大小固定为4字节,指针数组的内存大小看数组的大小。
(2)c++数组指针:首先它是一个指针,指向一个数组的入口地址,在一个32位操作系统中,它只占4字节。数组指针的声明如下: int (*ptr)[10];这是一个指针,ptr是指针变量名,指向一个有10个元素的整形数组的入口(这个数组是匿名的),使用时类似函数指针,先给该指针赋一个数组的入口地址,才能找到该数组。

指针函数和函数指针

(1)指针函数:返回指针的函数,其本质是一个函数,而该函数的返回值是一个指针。声明如下:int *fun(int x,int y);这个函数就是一个指针函数。其返回值是一个 int 类型的指针,返回值是一个地址指针函数可以定义为 void 类型的

#include <iostream>
using namespace std;

int p = 0;   // 只能输出全局变量的地址
int *add(int a, int b) {
	p = a + b;
	int *m = &p;  // 在调用指针函数时, 需要一个同类型的指针来接收其函数的返回值
	return m;
}

int main() {
	int a = 2;
	int b = 4;
	int *p = add(a, b);
	cout << *p << endl;
	return 0;
}

(2)函数指针:声明如下:int (*fun)(int x,int y);,其中函数指针 (*fun) 本质是一个指针变量,该指针指向某个函数的入口,即函数的地址。总结来说,函数指针就是指向函数的指针,或者说保存函数入口地址的指针。声明如下:int (*fun)(int x,int y); 函数指针是需要把一个函数的地址赋值给它。函数指针指向的函数必须有返回值,否则它将指向一个空地址,这是指针使用时候所不允许的情况

#include <iostream>
using namespace std;

int add(int x, int y) {
	return x + y;
}

int sub(int x, int y) {
	return x - y;
}

int (*fun)(int x, int y);   // 声明函数指针,两个函数只需要声明一次即可

int main(int argc, char *argv[]) {   
	//第一种写法
	fun = &add; // 把add函数的地址赋给fun,也就是将函数的入口给fun。取址运算符 & 不是必须的,因为一个函数标识符(function name)就表示了它的地址
	cout << "(*fun)(1, 2) = " << (*fun)(1, 2) << endl;

	//第二种写法
	fun = &sub;  // 把sub函数的地址赋给fun,也就是将sub函数的入口给fun
	cout << "fun(1, 2) = " << (*fun)(1, 2) << endl;

	return 0;
}

main(int argc, char *argv[])中第一个参数,int型的argc,为整型,用来统计程序运行时发送给main函数的命令行参数的个数,第二个参数,char*型的argv[],为字符串数组,用来存放指向的字符串参数的指针数组,每一个元素指向一个参数。各成员含义如下: argv[0]指向程序运行的全路径名, argv[1]指向在DOS命令行中执行程序名后的第一个字符串, argv[2]指向执行程序名后的第二个字符串, argv[3]指向执行程序名后的第三个字符串, argv[argc]为NULL .

c++引用

(1)引用其实就是给变量起了个别名,实质上是个指针常量。在语法上引用没有独立空间,和被引用的实体公用以块空间。其实,引用是有空间的,因为它是用指针来实现的。

#include <iostream>
using namespace std;

int main() { // 引用有指针常量的性质,既可以通过引用b来更改a的值,也可以通过a本身来更改
	int a = 10;
	int &b = a;
	a = 20;
	b = 30;   // 
	cout << a << endl;
	cout << b << endl;
	cout << &a << endl;  //
	cout << &b << endl;  // 与 a 公用一块地址

	int &c = a;   // 一个变量可以有多个引用
	c = 60;
	cout << c << endl;  // 多个引用一个更改,其他的都更改
	cout << a << endl;  // 语法上没有独立空间,和引用的实体共用一块空间。
	cout << b << endl;
	return 0;
}
>>>
30
30
0x78fe1c
0x78fe1c
60
60
60

如果变量时const类型,则引用类型也要是const类型。如下:

#include <iostream>
using namespace std;

int main() { // 引用有指针常量的性质,既可以通过引用b来更改a的值,也可以通过a本身来更改
	const int a = 10;
	const int &b = a;

(2)引用的作用

  • 引用做函数的参数:传引用可以提高效率。传引用可以减少深拷贝
    引用做参数不需要拷贝,因为形参是实参的别名。可以提高效率。
int swap(&a, &b);  // 调用时相当于执行 &a = c, &b = d,即形参是实参的别名
swap(c, d);  // 
  • 引用做返回值:减少拷贝,提高效率
    在这里插入图片描述
    这个程序 count() 函数输出的 n 需要拷贝给临时变量,然后临时变量在拷贝给 ret, 需要拷贝两次。
    在这里插入图片描述
    在这里插入图片描述
    引用做 Count() 函数的返回值只需要一次拷贝。
    在这里插入图片描述
    (3)引用和指针的区别:引用的本质是个指针常量,但是和一般的指针有一些不一样。
    在这里插入图片描述
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值