常量指针(Constant Pointers)
在C语言中,const
关键字用于声明常量,而野指针则是一种危险的指针类型。下面将详细解释这两个概念及其应用。
常量指针是指指向常量的指针,它不能用来修改所指向的数据。这有助于保护数据不被意外修改,提高程序的安全性和可维护性。
1. 指向常量的指针
当你想阻止通过指针修改数据时,可以使用指向常量的指针。这种指针的类型是指向常量的指针类型,例如 int * const p;
。这意味着你不能通过这个指针来修改它所指向的数据。
2. 常量指针
另一种形式是常量指针,即指针本身的值不能被修改,但可以通过该指针修改其指向的数据。这种指针的类型是指向非常量的常量指针类型,例如 const int *p;
。这意味着你不能修改指针 p
所指向的数据的值。
代码语言:javascript复制以下是一个使用
const
声明常量的示例:
#include <stdio.h>
int main()
{
const int a = 10;//a具有了常属性(不能被修改了)
//a本质上还是变量
//在C 中,const修饰的变量就是常量
//a = 20; 错误,a修改不了
//int arr[a]; 错误 a不算常量值
printf("%dn", a);
return 0;
}
在这个示例中,我们声明了一个名为a
的整型常量,并将其初始化为10。然后我们使用printf
函数输出a
的值。注意,我们不能修改a
的值,否则编译器会报错。
代码语言:javascript复制通过修改被const修饰的a的地址,修改a的值
int main()
{
const int a = 10;
//a = 20;//error
int* p = &a;
*p = 0;
printf("a = %dn", a);
return 0;
//}
代码语言:javascript复制const位置的不同
//const 修饰指针变量的时候,放在*的右边
//const 限制的是指针变量本身,指针变量不能再指向其他变量了
//但是可以通过指针变量,修改指针变量指向的内容
int main()
{
int a = 10;
int b = 20;
int* const p = &a;
//一但指向a就不能指向b了
//p = &b;//error
*p = 100;//修改*p却可以
printf("a = %dn", a);
return 0;
}
//const 修饰指针变量的时候,放在*的左边
//限制的是指针指向的内容,不能修改指针指向的内容
//但是可以修改指针变量本身的值(修改指针变量的指向)
int main()
{
int a = 10;
int b = 20;
int const* p = &a;
//一但指向a就不能指向b了
p = &b;//OK
//*p = 100;//error
printf("a = %dn", a);
return 0;
}
//int const* const p = &a
//*两边都加上const 就都改不了了
int main()
{
int a = 10;
int b = 20;
int const* const p = &a;
//一但指向a就不能指向b了
//p = &b;//error
//*p = 100;//error
printf("a = %dn", a);
return 0;
}
指针的应用
代码语言:javascript复制利用指针打印数组的几种方式
//用指针打印数组
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i )
{
printf("%d ", *p);
p ;
}
return 0;
}
//另一种方法
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < sz; i )
{
printf("%d ", *(p i));
}
return 0;
}
//利用指针的关系运算打印数组
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;//等于 &arr[0];
int sz = sizeof(arr) / sizeof(arr[0]);
while (p < arr sz)
{
printf("%d ", *p);
p ;
}
return 0;
}
//指针-指针的前提是,两个指针指向同一个空间
int main()
{
int arr[10] = { 0 };
printf("%zd ", &arr[9] - &arr[0]);//9
return 0;
}
野指针(Wild Pointers)的产生
野指针通常产生于**未初始化的指针、指针越界访问以及指向已释放内存的指针**。具体如下:
1. 未初始化的指针:定义指针变量时,如果没有进行初始化,那么该指针的值是随机的,可能指向任意的内存地址。这种情况下,如果尝试通过这个指针去读取或写入数据,可能会导致程序崩溃或其他不可预期的行为。
2. 指针越界访问:当指针超出了它所指向的数据结构(如数组)的边界时,就会发生越界访问。例如,一个指向大小为10的数组的指针,如果尝试访问数组的第12个元素,就会造成越界。
3. 指向已释放内存的指针:当一块内存被释放后,原有的指针如果继续指向这块内存,而没有置空或者重新赋值,这个指针就变成了所谓的“悬挂指针”或“野指针”。
为了避免野指针的产生和影响,应当总是在声明指针时对其进行初始化,并在释放指针指向的内存后立即将指针置为NULL,同时确保指针在其有效作用域内使用。
代码如下:
代码语言:javascript复制//未初始化指针,产生的野指针
int main()
{
int* p;//p是一个局部变量,不初始化的默认存的是随机值
*p = 20;
//报错 printf("%d n", p);
return 0;
}
//数组越界,产生的野指针
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = &arr[0];
int sz = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i <= sz; i )
{
printf("%d ", *p);
p ;
}
return 0;
}
//指针指向的空间释放,产生的野指针
int test()
{
int a = 10;
return &a;
}
int main()
{
int* p = test();
printf("%d n", *p);
return 0;
}
野指针(Wild Pointers)的危害
野指针的危害主要体现在以下几个方面:
1. 触发段错误:当野指针指向一个不可访问的内存地址时,尝试对其进行解引用操作可能会导致段错误,这是因为程序试图访问一个非法的内存区域。
2. 数据损坏:如果野指针指向了一个正在被其他部分的程序使用的内存空间,并且通过这个野指针修改了该内存空间的内容,那么可能会导致数据损坏,甚至程序崩溃。
3. 内存泄漏:在某些情况下,野指针可能导致内存泄漏。例如,如果一个野指针指向了一块已经分配但未被释放的内存,而这块内存又在其他地方被重复分配,就会造成内存泄漏。
4. 调试困难:野指针的存在可能会使得程序的调试变得非常困难,因为它们可能在程序的任何地方引发错误,而且这些错误可能不会立即显现,增加了查找和修复问题的难度。
如何避免
为了避免野指针带来的危害,可以采取以下措施:
1. 初始化指针:在声明指针变量时,应当对其进行初始化,避免其成为一个野指针。
2. 及时置空:当一个指针不再使用时,或者它所指向的内存空间已经被释放时,应该将其置为NULL,以防止其成为野指针。
3. 谨慎解引用:在使用指针前,应当确保它指向的是一个有效的内存地址,避免对无效地址进行解引用操作。
4. 使用智能指针:在一些支持智能指针的编程语言中,可以使用智能指针来自动管理内存,减少野指针的产生。
总的来说,野指针是C/C 编程中的一个常见问题,它们可能导致程序不稳定、数据损坏和内存泄漏等严重问题。因此,理解野指针的危害并采取适当的预防措施是非常重要的。
代码语言:javascript复制学习指针的目的是使用指针解决问题,那什么问题,非指针不可呢?
两个数的交换
void swap(int* pa, int* pb)
{
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前:%d %dn", a, b);
swap(&a, &b);
printf("交换前:%d %dn", a, b);
return 0;
}
代码语言:javascript复制模拟实现库函数strlen
模拟实现库函数strlen
int my_stelen(char* str)
{
int count = 0;
while (*str!='