C语言进阶-指针

什么是指针:

1、指针(pointer)是一种数据类型,使用它可以定义指针变量,简称指针。

2、指针变量中存储的是内存的地址,是一种无符号的整数。

3、通过指针变量中记录的内存地址,我们可以读取内存中所存储的数据,也也可以向,内存中写入数据。

4、一般使用%p以十六进制格式显示内存地址。

如何使用指针:

定义指针变量:

类型* 指针变量名;

1、指针变量中只记录了内存中某字节的地址编号,我们把它当作一个内存块的首地址,当使用指针变量访问内存时具体访问多少个字节,由指针变量的类型决定。

char* p;    // 能访问1字节 
short* p;   // 能访问2字节
int* p;     // 能访问4字节

2、普通变量与指针变量的用法不同,为了避免混用,所以指针变量一般以p结尾。

3、指针变量不能连续定义,一个*只能定义出一个指针变量。

int num1,num2,num3;
int* p1,p2,p3;      // p1是指针变量,p2、p3是普通的int类型变量
int *p1,*p2,*p3;    // p1、p2、p3都是指针变量
​
typedef int* intp;
intp p1,p2,p3;      // p1、p2、p3都是指针变量

4、指针变量与普通变量一样,默认值是随机的(野指针),为了安全尽量给指针变量初始化,如果不知道该赋什么值,可以先初始化为NULL(空指针)。

给指针变量赋值:

指针变量 = 内存地址。

所谓的给指针变量赋值,就是给指针变量存储一个内存地址,如果该内存地址是非法的,当使用指针变量访问内存是就会出现段错误。

int* p = malloc(4); // 把堆内存的地址赋值给指针变量
int* p = #      // &计算出变量的内存地址(单目运算符)
注意:num变量的类型必须与p的类型相同
指针变量解引用:

*指针变量

指针变量赋值就是引用一块内存,解引用就是根据指针变量存储的内存地址,去访问内存,具体访问多少个字节由指针变量的类型决定。

如果指针变量中存储的是非法内存地址,该动作会出现段错误,要从指针变量赋值的步骤去解决。

int num = 1234;
int* p = #
​
*p <=> num;
printf("%d",*p);
*p = 2345;
printf("%d\n",num);
int main()
{
    const int num = 1234;
    int* p = (int*)&num;
    *p = 6666;
    printf("%d\n",num);
}

为什么要使用指针:

1、函数之间共享变量

函数之间的命名空间是互相独立,并且以赋值方式传参的,所以传参无法解决共享变量的问题

全局变量虽然可以在函数之间共享,但过多使用全局变量可以会造成命名冲突和内存浪费。

当函数一个需要返回两个以上的参数时,就需要共享变量了。

2、使用指针变量可以节约函数的传参效率

函数之以赋值方式传参的,也就是内存的拷贝,把一个变量的内存内容拷贝给别一个变量,当变量的字节数比较大时(大于4字节),传参效率就很低,而传递变量的地址,只需要拷贝4字节内存。

#include <stdio.h>
                                                                                
void func(long double f)
{
​
}
int main()
{
    long double f = 3.14;
    for(int i=0; i<1000000000; i++)
    {   
        func(f);
        f++;
    }   
}
/*
real    0m5.527s
user    0m5.523s
sys     0m0.004s
*/
​
void func(long double* f)
{
​
}
int main()
{
    long double f = 3.14;
    for(int i=0; i<1000000000; i++)
    {   
        func(&f);
        f++;
    }   
}
/*
real    0m2.553s
user    0m2.553s
sys     0m0.000s
*/

3、使用堆内存时必须与指针变量配合

堆内存无法取名字,标准库、操作系统提供内存分配接口的返回值都内存地址,所以必须使用变量配合才能使用堆内存。

void *malloc(size_t size);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);

注意:由于使用指针变量具有一定的危险,所以除了以情况,不要轻易使用指针。

使用指针要注意的问题:

空指针:

指针变量中如果存储的是NULL,那么它就是空指针,因此操作系统规定程序不能访问该内存,只要访问就会产生段错误,同时也是返回值是指针类型的函数执行错误的标志。

如何避免空指针产生的段错误?

对来历不明的指针进行解引用前,要先判断是否是空指针。

1、当指针变量接收了函数的返回值,判断是否是空指针,既能避免访问空指针产生的段错误,也能知道该函数执行是否失败、出错。

2、如果设计的函数参数是指针变量,那么调用者传递实参就可能是空指针,对指针变量解引用前要先判断。

if(NULL == p) // 正确写法
{
​
}
​
if(p == NULL) // 错误写法,当少写一个等号时,就变成了给p赋值为空指针
{
​
}
​
if(!p)  //大多数系统的NULL是0,但有少数系统的NULL是1 
{
    
}
野指针:

指针变量中存储的地址,无法确定是否是合法的内存地址,这种指针变量被称为野指针。

对野指针解引用的后果:

1、一切正常,指针变量恰好存储的是空闲的内存地址,概率不高。

2、段错误,存储的是非法的内存地址。

3、脏数据,存储的是其它变量的内存地址。

如何避免野指针产生的错误:

野指针无法判断出来,但所有的野指针都是人为制造出来的,所以要想避免野指针产生的错误,只能不制造野指针。

如何不制造野指针:

1、定义指针变量时一定要初始化,要么赋值一个合法的内存地址,要么初始化NULL。

2、不返回局部变量、块变量的地址,当函数执行完毕后,局部变量和块变量就被销毁。

3、与堆内存配合的指针变量,当堆内存被释放、销毁,该指针变量要及时的赋值为NULL。

野指针比空指针的危害更大:

野指针产生的错误具有隐藏性、潜伏性、随机性,所以野指针比空指针危害更大。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值