指针
指针
什么是指针
指针代表一种数据类型,使用它可以定义指针变量,这种变量里存储是整数,这种整数代表了内存的编号,每个整数代表一个字节,使用指针变量可以访问对应的内存,具体访问由指针的类型决定
什么情况下使用指针
注意:从理论上讲指针可以访问任何位置的内存,但绝大部分的内存没有权限去访问,会产生段错误(非法访问),建议只有在合适的时候才使用指针
函数之间的共享局部变量:
由于全局变量浪费内存,还是有可能会造成命名冲突,故而所有全局变量不适合大量共享, 而函数传递默认是值传递,无法共享,所以指针是函数之间共享局部变量的最好的选择。
提高函数的传参效率:
而函数传递默认是值传递(内存拷贝),当变量的字节数较多时,传递的变量的地址效率更高,只需要4/8个字节。
但是这样就带了变量被修改的风险,可以使用const配合指针保存变量
配合堆内存使用:
堆内存无法取名(不能与标识符建立映射关系),因此必须与指针配合使用
如何使用指针
定义指针变量:类型* 指针变量_p;
-
由于指针变量的用法与普通变量用法不同,为了避免与普通变量混淆,指针变量一般以p结尾
-
指针变量不能连续定义
int* p1,p2; //p1是指针,p2是int类型变量
int p1,p2; //p1,p2都是指针
-
指针变量与普通变量一样默认值是随机的(野指针),为了安全起见一定要初始化 *p == NULL,如果不知道赋什么值,就赋值为NULL(空指针)。
-
指针的类型决定了访问内存时的字节数。
指针变量的赋值
用变量的地址赋值: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字节为单位移动
- 不能解引用
- 可以与任何类型的指针自动类型转换(c语言)
- 一边用作函数的参数、返回值
函数指针:
函数会被翻译成二进制指令存储到代码段,函数名就是函数的首地址。
可以定义特殊的指针变量存储函数的首地址,这样就可以把函数当做当前数据进行传递。
我们可以把存储函数地址的指针变量叫做函数指针
如何定义函数指针?
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);
与普通指针的区别:
-
数组名是常量,普通指针是变量
-
普通指针变量有自己的存储空间,用来存储内存编号,它与目标内存是指向关系
-
数组名没有自己的存储空间,他就代表着数组空间的首地址,它与数组内存是映射关系
-
对数组名取地址结果还是数组名的值。
int arr[5];
arr <=> &arr;
arr 类型 int
*&arr 类型 [5;]
next

浙公网安备 33010602011771号