什么是指针:
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*)# *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。
野指针比空指针的危害更大:
野指针产生的错误具有隐藏性、潜伏性、随机性,所以野指针比空指针危害更大。