指针
指针是C语言的灵魂。通过指针,可以轻松地访问内存中的数据,对数据进行读写
概念: 指针是变量,用来存放内存地址,地址一般用16进制的数字表示,用来唯一标识一块内存,我们可以通过&
获取变量的地址
指针变量的定义语法:数据类型* 变量名;
指针变量与普通变量的区别: 普通变量存放数据,指针变量存放地址;指针变量可以通过*
操作符操作指针变量指向的那块空间,这个过程称为解引用
指针的大小: 指针在32位平台是4个字节,在64位平台是8个字节(与指针的类型无关)
int i = 10;
char c = 'y';
int* pi = &i;
char* pc = &c;
printf("%d\n", sizeof(pi)); //32位平台输出4,64位平台输出8
printf("%d\n", sizeof(pc)); //32位平台输出4,64位平台输出8
指针的类型: 指针的类型决定了指针进行+/-
操作的结果,也决定了对指针解引用时可以操作几个字节
int i = 10;
char c = 'a';
int* pi = &i;
char* pc = &c;
printf("%p\n", pi);
printf("%p\n", pi + 1);
printf("\n");
printf("%p\n", pc);
printf("%p\n", pc + 1);
运行以上代码,结果如下:
可以看到,int*
类型的指针+1,地址数值+4;char*
类型的指针+1,地址数值+1。同时,对int*
类型的指针进行解引用操作可以访问4个字节的数据,对char*
类型的指针进行解引用操作只能访问1个字节的内存
空指针: 空指针是指向内存编号为0的空间的指针,空指针指向的内存不可以访问,主要作用是初始化变量
// 定义一个double类型的指针变量,指向内存地址为0的空间
double* pd = NULL;
// 对空指针进行解引用操作进行访问,赋给double类型的变量
// 此条语句执行出错,内存编号0 ~255为系统占用内存,不允许用户访问
double d = *pd;
野指针: 指向非法内存空间的指针
const与指针:
- const修饰指针,指针的指向可以修改,但指向的值不能修改,称为常量指针;语法:
const 数据类型* 变量名
- const修饰常量:指针的指向不能修改,但指向的值可以修改,称为指针常量;语法:
数据类型* const 变量名
// 定义两个变量m和n,并为它们赋初始值
int m = 10;
int n = 20;
// const修饰指针,指针的指向可以修改,但指向的值不能修改
const int* pm = &m;
pm = &n; // 正确,指向可以修改
//*pm = 30; // 报错,指向空间里的值(*pm)不可修改
// const修饰常量:指针的指向不能修改,但指向的值可以修改
int* const pn = &n;
//pn = &m; // 报错,指向(pn)不可修改
*pn = 40; // 正确,指向空间里的值(*pn)可以修改
指针和函数: 利用指针作函数的参数,可以修改实参的值;若不想修改实参,也可以用值传递的方式
#include<stdio.h>
void swap1(int a, int b)
{
int tmp = a;
a = b;
b = tmp;
}
void swap2(int* pa, int* pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
int a = 10;
int b = 20;
swap1(a, b); // 值传递交换a和b
printf("a=%d b=%d\n", a, b); // a与b并未发生交换
swap2(&a, &b); // 地址传递交换a和b
printf("a=%d b=%d\n", a, b); // a与b发生了交换
return 0;
}
引用
引用是 给变量取的别名 ,不是新定义一个变量,引用类型必须和引用实体类型相同;引用和它引用的变量共用同一块空间;语法:类型& 引用名 = 引用实体;
int a = 10; // 定义一个int类型的变量a
int& ra = a; // 使用引用给a起一个别名ra
printf("a=%d \nra=%d\n\n", a, ra); // a与ra的值相同
printf("a的地址:%p \nra的地址:%p\n", &a, &ra); // a与ra的地址相同
引用的特性:
- 引用必须在定义时就初始化
- 一个实体可以有多个引用
- 一个引用只能对应一个实体,不能更改
int a = 10;
int b = 20;
//int& ra; // 报错,需要初始化
int& ra1 = a;
int& ra2 = a;
ra1 = b; // 这是赋值操作,并不是把ra1修改为了b的引用
引用做函数参数: 使用引用传参,可以达到和地址传参一样的效果,但引用传参的语法更加清楚简单
// 使用引用传参的函数可以完成对值的交换
void my_swap(int& a, int& b)
{
int tmp = a;
a = b;
b = tmp;
}
int main()
{
int a = 10;
int b = 20;
my_swap(a, b);
cout << a << " " << b << endl; // 输出的a和b的值发生了交换
return 0;
}
引用做函数返回值: 引用可做函数参数的返回值,但引用做函数参数返回值时,所返回的变量的生命周期必须比函数更长,不能返回局部变量
// 引用做函数返回值
int& test_ref1(int& a)
{
a += 1;
return a;
}
// 函数返回局部变量的引用
int& test_ref2(int a, int b)
{
int k = a + b;
return k;
}
int main()
{
int a = 10;
int b = 20;
test_ref1(a);
cout << a << endl;
int& k = test_ref2(1, 2); // 把1+2的值存入k中
test_ref2(3, 4);
cout << k << endl; // 输出的k并不是1+2的值,而是上一行中调用的3+4的值
my_swap(a, b);
cout << a << " " << b << endl;
return 0;
}
指针和引用的区别和联系
引用虽然是给变量取的别名,和所引用的变量共用同一快空间,但引用在C++内部实现是一个指针常量(指向不可改变,指向的值可以改变)
指针和引用的不同点:
指针 | 引用 |
---|---|
指针声明时不是必须要初始化 | 引用在声明时就要初始化 |
指针有NULL指针 | 没有NULL引用 |
指针可以随时修改指向,使其指向另一个同类型的实体 | 引用一旦引用某个实体,就不能进行修改 |
指针++的结果是向后偏移一个数据类型的大小 | 引用++是将引用实体+1 |
修改指针指向的值时,需要进行解引用操作 | 修改引用实体时,不需要解引用,可以直接修改引用达到修改引用实体的效果 |
指针的大小与类型无关,只与平台有关 | 引用的大小是实体的大小 |
指针有多级指针 | 没有多级引用 |
操作指针可能会引发危险 | 引用的使用相对安全 |