在C语言中,指针是一个非常核心和强大的概念。
指针是一个变量,它存储了另一个变量的内存地址。
通过指针,程序能够直接操作内存中的数据,这为编程提供了极大的灵活性。
1、指针的声明
(1)类型+*+变量名
int *p;
double *d;
char *c;
string *str;
class red;
read* r;
(2)有相同的2个指针吗
// 定义
int a;
int b;
int *p1;
int *p2;
// 初始化整数
a = 10;
b = 10;
// 初始化指针
p1=&a;
p2=&a;
如上图,定义了int类型的变量a和b,那么a和b是相同的变量吗?
很简单他们是2个变量,a和b都有自己的地址,地址内存储的整数值都未初始化;
指针p1和p2是相同的指针吗?
他俩是不同的指针,p1和p2,他俩都有独属于自己的地址,地址内存储的值未初始化
再次思考,初始化整数执行后,a=b=10;a和b是相同的变量吗?
依然不是相同的变量,只是他们拥有相同值,a和b各自地址内都存储了相同的整数10;因为修改a并不影响b的值。即后续如果a=20,他不影响b=10。他俩不是同一个变量。
再次思考,初始化指针后,p1和p2是相同的指针吗?
依然不是相同的变量,只是他们拥有相同的指向,都指向a的地址;因为修改p1的指向并不影响p2的指向。即后续如果p1=&b,他不影响p2。他俩不是同一个变量。
结论:声明几个指针,就有几个指针,即便指向相同,他们也不是同一个指针。方便理解当指针做函数参数时,不混淆。
2、指针初始化
(1)使用其它变量的地址初始化
使用变量n的地址来初始化指针p
int n=10;
int*p = &n;
(2)数组名初始化指针
数组名代表数字首元素的地址
int arr[10];
int*p = arr;
(3)动态分配内存初始化
使用 malloc
函数 或 new可以在堆上分配内存,并返回一个指向这块内存的指针。
class Test;
Test* p;
p = new Test();
(4)使用空初始化
class Test;
Test*p;
p = NULL;
p = nullptr;
3、地址操作符号&
在一个变量前+&可以取他的地址。
int n =10;
int *p;
// 打印整数变量n的地址
cout<<&n;
// 打印指针变量P的地址
cout<<&p;
4、间接引用操作符*
在指针前+*可以取他指向的地址里的值
int n = 10;
int*p = &n;
// 输出指针p指向的地址里的值,10
cout<<*p;
(1)char*类型指针
*p和p是等价的都输出 字符 a
char c = 'a';
char* p = &c;
cout << *p << endl;
cout << p << endl;
(2) int*类型的 直接输出p会输出n的地址,等价于&n
int n = 10;
int*p = &n;
// 输出指针p指向的地址里的值,10
cout<<*p;
// 2者相同
cout<<p;
cout<<&n;
5、指针的算数计算
指针可以进行++、--、等运算
int arr[10];
int *p = arr;
int *p1 = p++;
int *p2 = P++;
6、指针的安全性
由于指针操作的低级特性,不正确的使用可能导致程序错误,如访问无效的内存地址
例如:使用未初始化的指针、使用指向已经被释放的指针等。
7、函数参数
指针可以作为函数参数
判断2次打印结果是否相同。
int n1=10;
int n2=20;
void func(int*p)
{
p=&n2;
}
main()
{
int* p = &n1;
cout<<*p<<endl;
func(p);
cout<<*p<<endl;
}
打印:10 10。参考指针声明里的解释
8、指针日常开发中的一些注意事项
(1)初始化指针:
确保所有指针在使用前都被初始化。未初始化的指针可能包含任意内存地址,使用它们可能导致未定义行为。
(2)使用 `NULL` 指针:
在声明指针后,将其设置为 `NULL`,直到你为其分配了有效的内存地址。
(3) 检查指针有效性:
在通过指针访问内存之前,检查它是否指向一个有效的内存区域。
(4)避免越界访问:
访问数组时,确保不会超出数组的边界,这可能会导致数组越界错误。
(5)使用 `sizeof` 操作符:
使用 `sizeof` 来确定数据类型的大小,避免硬编码值,这有助于防止错误的计算。
(6)谨慎使用 `malloc` 和 `free`:
使用 `malloc` 分配内存后,确保使用 `free` 释放内存,避免内存泄漏。同时,检查 `malloc` 返回的指针是否为 `NULL`。
(7)避免重复释放内存:
一旦内存被释放,就不应该再次释放它,这会导致未定义行为。
(8)使用指针数组时要小心:
当使用指针数组存储多个指针时,确保每个指针都被正确初始化。
(9)注意指针的生命周期:
动态分配的内存应该在不再需要时及时释放,避免内存泄漏。
(10)使用引用传递时要小心:
当函数通过指针参数修改变量时,确保调用者了解这种影响。
(11)避免使用悬空指针:
一旦内存被释放,相应的指针应立即设置为 `NULL`,避免成为悬空指针。
(12)类型安全:
在将数据类型转换为指针类型时,确保这种转换是安全的。
(13)理解指针和数组的关系:
数组名是指向数组首元素的指针,对数组名的操作实际上是对指针的操作。
指针声明时要初始化为NULL,使用前先判断是否为NULL。