云计算百科
云计算领域专业知识百科平台

指针(2)

指针(2)

  • 前言
  • const 关键字
    • 修饰指针变量
      • *号前
      • *号后
  • 野指针
    • 野指针的定义
    • 成因
      • 未初始化
      • 越界访问
      • 访问已释放的空间
    • 预防
      • NULL
      • 不要越界访问
      • 注意空间释放
  • 指针调用
    • 传值调用
      • 代码
    • 传址调用
      • 代码

前言

本文接着指针(1)继续分享C语言指针的相关知识,上文链接: 指针(1)。

const 关键字

const 中文注释: 常量的;

在C语言中,const 关键字用于声明一个变量为常量,这意味着一旦变量被赋予了值,它就不能被修改。

修饰指针变量

而当const修饰指针变量时,同样具备值不能修改的特性,但是在指针变量中又因为位置的不同而具有不同的效果。

*号前

const int * a;
int const * a;

上述的两种写法的效果是一致的且都保持了const在*号前的特点。

当const 在* 前修饰变量时,*p的值是不可修改的。

我们可以理解成这个指针变量指向的空间内的值是不可修改的。 不使用const修饰的情况: 不使用const修饰的情况 在*号前修饰的情况下是无法对 *a1进行修改,即对a1所指向的地址内存储的值不可修改。 在*号前修饰 但是请注意,a1所指向的地址内存储的值不能修改,并不代表a1所指向的地址不可修改!!! 在这里插入图片描述

*号后

而这种情况,则是将指针变量的所指向的地址修饰为不可修改,即地址不可修改,但是地址内容可以修改。

int a = 10;
int b = 5;
//使用const修饰 在*号后
int* const a1 = &a;
*a1 = 66;
printf("%d", *a1);

结果为66 ,当我们尝试修改a1地址时候会发现…

int a = 10;
int b = 5;
//使用const修饰 在*号后
int* const a1 = &a;
//修改a1所指向的地址
a1 = &b;//报错
printf("%d", *a1);

总结: 当const在 * 号前修饰指针变量时,指针指向地址内存储的数据不可修改,但是地址可修改; 当const在 * 号后修饰指针变量时,指针所指向的地址不可修改,但地址内存储的数据可修改; 当前后都有const,地址和内容都不可修改。

野指针

野指针的定义

野指针(Dangling Pointer)是指向无效内存或者已被释放的内存的指针。指向一个 “随机” 的内存位置,这个位置可能包含任何数据,甚至可能是另一个正在运行的程序的数据。使用野指针可能导致程序崩溃、数据损坏或安全漏洞。

成因

主要有以下三种成因:未初始化 、 越界访问、访问已释放的空间。

未初始化

int* a;

当我们定义了一个指针变量但是没有初始化,编译器会随机给它一个地址,这个地址谁也不知道会访问到哪里。

越界访问

int arr[5]={1,2,3,4,5};
int * p = arr[10];

可以看到数组只有5个元素,当使用指针变量去访问到第10个元素的时候就会造成越界访问,可能会进入到其他程序的空间导致程序崩溃。 但是因为位置的可控,也使得我们能通过指针的手段进入到其他程序的内存中进行访问、修改、删除等操作。

访问已释放的空间

int Add(int x, int y)
{
int z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 2;
printf("%d",Add(a,b));
int* p = &z;
return 0;
}

在函数的章节,我们知道了形参和实参的概念,函数内的变量的生命周期就是函数调用开始和结束,当函数结束后原来的空间就会被释放。 当我们去指针去访问的已经被释放的空间就会形成野指针。

预防

那么我们该怎么在开发过程中避免野指针呢?

NULL

对于未初始化的情况,我们在定义一个指针变量的时候就要初始化,如果暂时不知道指向哪个地址的时候,用NULL代替。

int* p = NULL;

在使用变量前,将地址补充。

不要越界访问

在使用指针变量的时候注意不要越界访问。

注意空间释放

注意空间的释放,可以使用static关键字避免空间被释放。

static int z;

int Add(int x, int y)
{
z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 2;
printf("%d",Add(a,b));
int* p = &z; //不会报错
return 0;
}

指针调用

在编程语言中,特别是在C和C++中,函数参数的传递方式主要有两种:传值调用(Call by Value)和引用调用(Call by Reference)。这两种传递方式在函数执行时对实参(actual argument)和形参(formal parameter)的影响上有本质的区别。

传值调用

传值调用是最常见的参数传递方式。在这种方式中,实参的值被复制给形参。在函数体内,形参是实参的一个副本,因此对形参的任何操作都不会影响到实参。这意味着,如果函数内部改变了形参的值,实参的值是不会变的。例如:

代码

int Add(int x,int y)
{
return x+y;
}
int main()
{
int a = 1;
int b = 2;
int c = Add(a,b);
return 0;
}

传址调用

传址调用是另一种参数传递方式,它允许被调用函数能够修改调用函数中的变量。在这种方式中,实参的地址被传递给形参,这样形参就成为了实参的一个别名。因此,对形参的任何操作都会直接反映在实参上。传址调用在C中通过指针实现。例如:

代码

int Swap(int*x, int* y)
{

int temp = *x;
*x = *y;
*y = temp;
return z;
}
int main()
{
int a = 10;
int b = 2;
//交换a和b的数值
Swap(&a, &b);
printf("a = %d\\nb = %d", a, b);

return 0;
}

赞(0)
未经允许不得转载:网硕互联帮助中心 » 指针(2)
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!