深入探究C语言中的常量指针与野指针概念及其应用

简介: 深入探究C语言中的常量指针与野指针概念及其应用



常量指针(Constant Pointers)

在C语言中,const关键字用于声明常量,而野指针则是一种危险的指针类型。下面将详细解释这两个概念及其应用。

常量指针是指指向常量的指针,它不能用来修改所指向的数据。这有助于保护数据不被意外修改,提高程序的安全性和可维护性。

1. 指向常量的指针

当你想阻止通过指针修改数据时,可以使用指向常量的指针。这种指针的类型是指向常量的指针类型,例如 int * const p;。这意味着你不能通过这个指针来修改它所指向的数据。

2. 常量指针

另一种形式是常量指针,即指针本身的值不能被修改,但可以通过该指针修改其指向的数据。这种指针的类型是指向非常量的常量指针类型,例如 const int *p;。这意味着你不能修改指针 p 所指向的数据的值。

以下是一个使用const声明常量的示例:

#include <stdio.h>
int main() 
{
  const int a = 10;//a具有了常属性(不能被修改了)
  //a本质上还是变量
  //在C++中,const修饰的变量就是常量
  //a = 20; 错误,a修改不了
  //int arr[a]; 错误  a不算常量值
    printf("%d\n", a);
    return 0;
}

在这个示例中,我们声明了一个名为a的整型常量,并将其初始化为10。然后我们使用printf函数输出a的值。注意,我们不能修改a的值,否则编译器会报错。

 

通过修改被const修饰的a的地址,修改a的值

int main()
{
  const int a = 10;
  //a = 20;//error
  int* p = &a;
  *p = 0;
  printf("a = %d\n", a);
  return 0;
//}

 

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 = %d\n", 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 = %d\n", 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 = %d\n", a);
  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 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,同时确保指针在其有效作用域内使用。

代码如下:

//未初始化指针,产生的野指针
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++编程中的一个常见问题,它们可能导致程序不稳定、数据损坏和内存泄漏等严重问题。因此,理解野指针的危害并采取适当的预防措施是非常重要的

学习指针的目的是使用指针解决问题,那什么问题,非指针不可呢?

两个数的交换

void swap(int* pa, int* pb)
{
  int tmp = *pa;
  *pa = *pb;
  *pb = tmp;
}
int main()
{
  int a = 10;
  int b = 20;
  printf("交换前:%d %d\n", a, b);
  swap(&a, &b);
  printf("交换前:%d %d\n", a, b);
  return 0;
}

模拟实现库函数strlen

模拟实现库函数strlen
int my_stelen(char* str)
{
  int count = 0;
  while (*str!='\0')
  {
    count++;
    str++;
  }
  return count;
}
int main()
{
  char arr[] = "abcdef";
  int len = my_stelen(arr);
  printf("%d ", len);
  return 0;
}

应用场景

  • 常量指针:当你想保护某些数据不被修改时,可以使用常量指针。例如,在函数参数中传递一个指向常量的指针,这样在函数内部就不能修改这个数据。
  • 野指针:避免使用未初始化的指针和已经释放的内存地址的指针,以减少程序出错的风险。确保在使用指针之前进行适当的初始化,并在不再需要时将其置为NULL。

希望对你有帮助~加油各位!!

目录
相关文章
|
15天前
|
传感器 物联网 大数据
C 指针在物联网的应用
在物联网(IoT)中,C 语言及其指针功能广泛应用于嵌入式系统。C 指针在内存管理、设备驱动、数据结构处理、传感器通信等方面发挥关键作用,如动态分配内存、直接访问硬件寄存器、传递复杂数据结构等,有效提升了资源受限环境下的性能和灵活性。通过函数指针和省电模式管理,还能实现事件驱动编程和节能目标,使 C 语言成为 IoT 开发的重要工具。
46 12
|
23天前
|
存储 C语言
【C语言基础】一篇文章搞懂指针的基本使用
本文介绍了指针的概念及其在编程中的应用。指针本质上是内存地址,通过指针变量存储并间接访问内存中的值。定义指针变量的基本格式为 `基类型 *指针变量名`。取地址操作符`&`用于获取变量地址,取值操作符`*`用于获取地址对应的数据。指针的应用场景包括传递变量地址以实现在函数间修改值,以及通过对指针进行偏移来访问数组元素等。此外,还介绍了如何使用`malloc`动态申请堆内存,并需手动释放。
|
26天前
|
存储 人工智能 C语言
C语言程序设计核心详解 第八章 指针超详细讲解_指针变量_二维数组指针_指向字符串指针
本文详细讲解了C语言中的指针,包括指针变量的定义与引用、指向数组及字符串的指针变量等。首先介绍了指针变量的基本概念和定义格式,随后通过多个示例展示了如何使用指针变量来操作普通变量、数组和字符串。文章还深入探讨了指向函数的指针变量以及指针数组的概念,并解释了空指针的意义和使用场景。通过丰富的代码示例和图形化展示,帮助读者更好地理解和掌握C语言中的指针知识。
|
1月前
|
存储 安全 C语言
C语言 二级指针应用场景
本文介绍了二级指针在 C 语言中的应用,
|
2月前
|
存储 编译器 C语言
【C语言篇】深入理解指针2
代码 const char* pstr = "hello world."; 特别容易让初学者以为是把字符串 hello world.放 到字符指针 pstr ⾥了,但是本质是把字符串 hello world. 首字符的地址放到了pstr中。
|
2月前
|
存储 程序员 编译器
【C语言篇】深入理解指针1
assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报错终⽌运⾏。这个宏常常被称为“断⾔”。
|
2月前
|
C语言
【C语言】指针速览
【C语言】指针速览
18 0
|
23天前
|
存储 Serverless C语言
【C语言基础考研向】11 gets函数与puts函数及str系列字符串操作函数
本文介绍了C语言中的`gets`和`puts`函数,`gets`用于从标准输入读取字符串直至换行符,并自动添加字符串结束标志`\0`。`puts`则用于向标准输出打印字符串并自动换行。此外,文章还详细讲解了`str`系列字符串操作函数,包括统计字符串长度的`strlen`、复制字符串的`strcpy`、比较字符串的`strcmp`以及拼接字符串的`strcat`。通过示例代码展示了这些函数的具体应用及注意事项。
|
26天前
|
存储 C语言
C语言程序设计核心详解 第十章:位运算和c语言文件操作详解_文件操作函数
本文详细介绍了C语言中的位运算和文件操作。位运算包括按位与、或、异或、取反、左移和右移等六种运算符及其复合赋值运算符,每种运算符的功能和应用场景都有具体说明。文件操作部分则涵盖了文件的概念、分类、文件类型指针、文件的打开与关闭、读写操作及当前读写位置的调整等内容,提供了丰富的示例帮助理解。通过对本文的学习,读者可以全面掌握C语言中的位运算和文件处理技术。
|
26天前
|
存储 C语言
C语言程序设计核心详解 第七章 函数和预编译命令
本章介绍C语言中的函数定义与使用,以及预编译命令。主要内容包括函数的定义格式、调用方式和示例分析。C程序结构分为`main()`单框架或多子函数框架。函数不能嵌套定义但可互相调用。变量具有类型、作用范围和存储类别三种属性,其中作用范围分为局部和全局。预编译命令包括文件包含和宏定义,宏定义分为无参和带参两种形式。此外,还介绍了变量的存储类别及其特点。通过实例详细解析了函数调用过程及宏定义的应用。