指针

指针

什么是指针

指针代表一种数据类型,使用它可以定义指针变量,这种变量里存储是整数,这种整数代表了内存的编号,每个整数代表一个字节,使用指针变量可以访问对应的内存,具体访问由指针的类型决定

什么情况下使用指针

注意:从理论上讲指针可以访问任何位置的内存,但绝大部分的内存没有权限去访问,会产生段错误(非法访问),建议只有在合适的时候才使用指针

函数之间的共享局部变量:

​ 由于全局变量浪费内存,还是有可能会造成命名冲突,故而所有全局变量不适合大量共享, 而函数传递默认是值传递,无法共享,所以指针是函数之间共享局部变量的最好的选择。

提高函数的传参效率:

而函数传递默认是值传递(内存拷贝),当变量的字节数较多时,传递的变量的地址效率更高,只需要4/8个字节。

但是这样就带了变量被修改的风险,可以使用const配合指针保存变量

配合堆内存使用:

​ 堆内存无法取名(不能与标识符建立映射关系),因此必须与指针配合使用

如何使用指针

​ 定义指针变量:类型* 指针变量_p;

  1. 由于指针变量的用法与普通变量用法不同,为了避免与普通变量混淆,指针变量一般以p结尾

  2. 指针变量不能连续定义

    int* p1,p2; //p1是指针,p2是int类型变量

    int p1,p2; //p1,p2都是指针

  3. 指针变量与普通变量一样默认值是随机的(野指针),为了安全起见一定要初始化 *p == NULL,如果不知道赋什么值,就赋值为NULL(空指针)。

  4. 指针的类型决定了访问内存时的字节数。

指针变量的赋值

​ 用变量的地址赋值:p= #

​ 用堆内存地址赋值:p=malloc(sizeof(*p));

指针变量的解引用:

​ 通过指针变量中存储编号去访问内存,具体访问多少字节由指针变量的类型决定。

该过程可能产生段错误,但要从指针变量赋值的步骤寻找原因

如何锁定段错误位置

先输入 ulimit -c unlimited(配置过了就不用了)

后输入 gcc -g name.c(所需要调试的文件名)

后输入 ./a.out(中文路径下无法生成core文件)

后输入 gdb a.out

后输入 run

如此一来便可以锁定错误位置

指针的运算:

​ 指针变量存储的是整数,理论上整形数据能使用的运算符、指针变量都可以使用,但只有以下运算有意义:

​ 指针 + n < = > 指针+(n*字节数)

​ 指针 - n < = > 指针 - (n*字节数)

​ 指针 - 指针 < = > (指针-指针)/字节数

使用指针要注意的问题:
空指针:

指针变量的值为NULL的指针被称为空指针,这种指针不能解引用,否则会产生段错误。

​ 大多数系统的NULL 的是0地址,可以把指针变量当做逻辑值使用,但是不建议这样。

为了避免空指针导致的段错误,当使用来历不明的指针时应判断是否空指针

if(NULL == p)(极个别系统是(!p))
{
 
}
野指针:

指针变量的值是随机的、不确定的,这种指针被称为野指针,无法判断这个指针是否是野指针,且野指针不一样立即出错,也不一定会出错,与空指针相比危害更大。

无法判断一个指针变量是否是野指针,所以为了避免野指针导致的Bug,我们应该避免产生野指针

1、定义指针变量时一定要初始化。

2、指向变量所指向的内存被释放,销毁后,指针要及时赋值为NULL。

3、函数不返回局部变量的地址。

指针与const的配合:
const int* p;  保护目标,不被目标修改,可以被其他修改
int const *p;  与上相同
int* const p;  保护指针变量p不被修改
const int* const p;既保护指针p也保护*p不被修改
int const* const p;同上

指针数组与数组指针:

指针数组:

​ 由指针变量组成的数据,他的身份是数组,成员是指针变量

​ int* arr[10];

数组指针:

​ 他的身体是指针,专门用来储存数组的地址

​ int (*arr)[5];

​ 类型 (*变量名)[n]

​ 注意:数组指针是以数组字节宽度为移动单位

  1 #include <stdio.h>
  2 
  3 int main(int argc,const char* argv[])
  4 {
  5     int arr[] = {1,2,3,4,5};
  6     //int (arrp)[5] = &arr;
  7     int *p = (int*)(&arr+1)-1;
  8     //本来是指向首地址,后加一是将其指向到到5的后面,-1又    将指向的地址一到了5
  9     printf("%d\n",*p);
 10 }

void指针:(万能指针)
  1. 以1字节为单位移动
  2. 不能解引用
  3. 可以与任何类型的指针自动类型转换(c语言)
  4. 一边用作函数的参数、返回值
函数指针:

​ 函数会被翻译成二进制指令存储到代码段,函数名就是函数的首地址。

​ 可以定义特殊的指针变量存储函数的首地址,这样就可以把函数当做当前数据进行传递。

​ 我们可以把存储函数地址的指针变量叫做函数指针

如何定义函数指针?

​ 1.复制函数声明语句

​ 2.给函数名加上小括号,并在函数名前加*,在函数名的末尾加上p

​ 3.给函数名重新取函数指针变量名

int (*compar)(const void *, const void *)

当我们把函数作为数据传递时,被调用的函数可以通过函数指针调用我们以参数形式提供的函数,这种模式叫做回调模式,比如:qsort 、bsearch

​ 问题:如何设计出通用的排序算法?

void sort(void* arr,size_t len,size_t size,[])
{
    
    
}

由于函数的指针的类型比较长,一般会选择使用typedef进行类型重定义

void func(int,int);
typedef void (*FP)(int,int);
即FP即为重定义的函数指针
二级指针:

​ 一级指针里面存储普通变量的地址,二级指针里面存储的是一级指针变量的地址。

当跨函数共享指针变量时,需要使用二级指针

定义二级指针:

类型** 变量名_pp;

二级指针的赋值:

int num;

int* p =&num;

int** pp = &p;

二级指针的解引用:

*pp <=>p;

**pp<=> *p;

指针与数组名的关系:

数组名就是数组空间的首地址,是一个常量地址,可以把他看做常指针。数组名可以使用解引用的语法访问成员,指针也可以使用[]解引用,所以:

​ arr[n] <=> *(arr+n);

与普通指针的区别:

  1. 数组名是常量,普通指针是变量

  2. 普通指针变量有自己的存储空间,用来存储内存编号,它与目标内存是指向关系

  3. 数组名没有自己的存储空间,他就代表着数组空间的首地址,它与数组内存是映射关系

  4. 对数组名取地址结果还是数组名的值。

    int arr[5];

    arr <=> &arr;

    arr 类型 int

    *&arr 类型 [5;]

next

posted @ 2021-07-10 17:12  de06  阅读(166)  评论(0)    收藏  举报