关于&的本质
&本质是运算符,是一个取地址符。
以下是有关取地址符的一些运算
#include<stdio.h>
int main(void)
{
int i=0;
int p;
//p=(int)&i;//这一步是强制类型转换,将地址转换成整型,编译器64位架构这样会出现精度丢失报错,无法运行 ;32位架构下可以运行。
printf("0x%x\n",&i);//取地址需要明确变量
printf("%p\n",&i);//变量分配在堆栈里,地址从高到低分配,先写的分配到高位地址。
printf("%p\n",&p);
printf("%d\n",sizeof(int));//64位架构int4字节,地址8字节;32位架构int4字节,地址也一样。
printf("%d\n",sizeof(&i));
int a[10];
printf("%p\n",&a);
printf("%p\n",a);
printf("%p\n",&a[0]);
printf("%p\n",&a[1]);
}
以下是运行结果图,我的编译环境是64位架构,从数组的运行结果可知与指针有联系,数组其实是指针变量。
什么是指针
1.指针就是保存地址的变量,用*来定义。
2.*是单目运算符,可以访问指针变量的值对应的地址所对应的变量。
指针的定义和取地址如下
int i=2;
int *p;
p=&i;//现在p里面保存的就是变量i的地址,或者直接初始化int *p=i也是一样。
*p=26;//这里通过地址访问i变量的值,并进行修改,可以说*p和i是等价的。
int ai[]={1,2,3,4,5,6,7,8,9,10};
int *q=ai;//取指针地址不用加&,因为数组变量是特殊的指针
q[1]=5;//[]运算符可以对数组做也可以对指针做,和ai[1]等价
3.指针的运算符&和*是相互反作用的
这里就直接放图片说明
指针的应用场景
- 交换两个变量的值。
用指针交换两个变量的值的好处是,可以在自定义的函数内改变外部函数变量的值,可以将结果带出函数,这就是指针与函数的结合,例如交换两个变量的值。
- 要用函数返回多个值
用return只能返回一个值,所以当需要返回多个值时某些值需要通过指针返回。
- 通过返回运算状态,而值通过函数返回
参考下图的例子
如图是一个做除法的函数,因为分母不能为0,所以当分母为0时我们可以返回0来表示程序出错而不执行判断语句。
指针的常见错误
- 定义指针变量,还未指向任何变量,就开始使用
int i=2;
int *p;
*p=12;//此时指针未指向变量,内部的指向未知,可能会向不正确的地方写入数据导致程序崩溃
指针与数组
1.传入函数的数组成为了指针。
例如int型数组,用sizeof()函数在main函数里面得到的数组元素个数乘int的字节数,而在自定义函数里得到的是数组的地址,例子如下图
由于传入的数组是指针,所以在函数内部改变数组元素的值可以带到函数外,相当于函数的参数a[]和指针是一个东西,所以把a[]改成*a是等价的,一样可以编译成功。
2.所以数组变量是特殊的指针
int a[10];
int *p=a;//此处就不需要取地址符&
- a==&a[0],数组变量的地址和首个单元的地址是一样的。
- []运算符可以对数组做也可以对指针做
p[0]<==>a[0],p是指针变量,a是数组变量
若a是int变量,p[0]会把a当成数组长度为一的数组来运算,但a[0]这种运算就是错误的
- *运算符可以对指针做也可以对数组做,例如a[10],*a访问的就是数组的第一个单元,因为a的首地址等于&a[0]。
- 数组变量是const指针,所以不能被赋值
比如另一个数组等于另外一个数组是不可取的
- 指针与const
这里就直接用视频的截图来说明
主要的标志在于*和const谁前谁后
- 可以把非const转换成const
void f(const int* x);//这里就保证不能在函数内部通过指针改变变量的值,可以应用于某些场景
- const数组
const int a[]={1,2,3,4}//表示每个数组单元都不能被改变,就必须通过初始化赋值。
指针运算
- 指针做加法
int ai[]={1,2,3,4,5,6,7,8,9,10};//指针单元的地址规律为每个单元连续加一个单位类型
int *q=ai;
printf("q=%p\n",q);
printf("q+1=%p\n",q+1);//q+1得到的地址为增加一个int型的单位,也就是下一元素的地址
printf("*(q+1)=%d\n",*(q+1));//指针+1可以指向下一数组元素
*p++的运算为地址加一,因为++的优先级高于 *,这常用于对数组的连续操作
- 指针相减
//指针相减运算
int ai[]={1,2,3,4,5,6,7,8,9,10};
int *q1=&ai[5];
int *q=ai;
printf("q1=%p\n",q1);
printf("q1-q=%d\n",q1-q);//相减的结果为地址的差除以sizeof的单位,输出结果相当于之间的元素个数
- 指针可以进行比较,数组中的单元地址肯定是线性增加的
- 注意指针的类型,不同类型的指针不能相互赋值
- 0地址
动态内存分配
malloc函数
用来向电脑申请储存空间,大小以字节为单位
- 解决变量不能作为数组长度的问题
int number;
int* a;
int i;
printf("输入数量:");
scanf("%d",&number);
a=(int*)malloc(number*sizeof(int)); //malloc的返回类型不是int所以要类型转换一下
for(i=0;i<number;i++){
scanf("%d",&a[i]);//a可以完全当成一个数组来用
}
free(a);//用来释放申请的地址
- 调用完malloc函数要使用free函数释放申请的空间
系统的空间是有限的,如果程序一直申请而不释放空间,则空间用完时会申请失败返回0,但是只能还原空间即返回首地址,改变地址释放空间就会出错,例如上面返回free(a++)程序就会报错,没有调用malloc函数free也会报错
void *p;
int cnt=0;
while((p=malloc(100*1024*1024))){
cnt++;
}
printf("分配了%d00MB空间",cnt);//最后得到的是系统能够申请的空间,不同电脑是不一样的
特例:free(NULL)是不会出错,因为free判断为NULL不会进行操作,如果上面的程序运算错误这样可以防止程序崩溃,所以指针未初始化赋值为NULL是个好习惯
int *p=NULL;
free(p);//这样程序就不会因为返回错误的p而崩溃