目录
一、变量的内存地址
- 取地址运算符&:取得变量的地址(操作数必须是变量)
- %p表示输出变量的地址值。
- 地址值是一个用十六进制的无符号整数表示,其字长一般与主机字长相同。
- 变量的地址:变量的内存中所占储存空间的首地址
- 变量的值:变量在储存空间中存放的数据
- 不能对没有地址的东西取地址(&(a+b),&(a++),&(++a))
- &a=&a[0]
- 指针变量=&普通变量
- *指针变量=普通变量的值
-
指针使用准则:
- 指针必须指向一块由意义的内存。
- 清楚指针指向的对象是什么。
- 不要使用未初始化的指针变量。
(指针变量没有初始化-野指针-意味着指针变量的值是一个随机值,强取的话将其当成地址不知道会指向哪,容易寄)
二、指针变量的定义与初始化
- 指针:一种存放地址的特殊变量(指针即是地址)
- 定义形式:类型关键字*指针名
- 初始化
-
-
Int i;
-
Int*p=&i;
-
-
-
int a;
-
int *p;(*可以靠近int也可以靠近p)
-
P=&a;(p是指针所指变量的地址)
-
int *p指针变量(*p是地址里面的内容)
-
int*pa=NULL;(输出时pa=00000000)
*与&互为逆运算
指针变量只能指向同一类型的变量
不能直接把数赋给指针(0除外)
指针运算符:*用来访问指针变量指向的变量的值(可以做左值/右值)
4.Void*万能指针
所有指针类型都可以赋值给万能指针
-
#include<stdio.h>
-
Int main (void)
-
{
-
Void*p=NULL;//(零指针)
-
Int =10;
-
P=(void*)&a;
-
*(int*)p=100;
-
return 0;
-
}
-
三、按值调用与模拟按引用调用
- 按值调用:将函数调用语句中的实参
函数形参值的改变未能影响实参的改变是因为传给函数形参的值只是函数调用语句中实参值的副本。所以无法在被被调函数中改变其调用语句的实参值
- 模拟按引用调用:通过向被调函数传递某个变量的地址值可以在被调函数中改变主调函数中这个变量的值。
- 按值调用好比把你电脑的文件夹的某个文件复制一份给别人,别人想怎么改都不会自己保存的文件有影响,而模拟按引用调用好比将开机密码交给别人然后允许别人访问修改你的文件一样,那么自己电脑里的文件就无法保持原样了。
- 指针的使用场景
- 应用场景一:swap
-
Void swap (int*pa,int*pb)
-
{
-
Int t=*pa;
-
*pa=*pb;
-
*pb=t;
-
}
- 应用场景二:
- 函数返回多个值(return只有一个值)
- 函数返回状态,结果右指针返回(return-1/0有可能是有效结果)
-
四、指针与地址运算
- 指针的加减运算时以sizeof(ptype)为单位。
p+1/p++指的是指向数组的下一个元素,相当于执行p=p+sizeof(地址值),改变了指针p的指向,并非赋值操作
2.假设p1、p2两个指针变量
p1==p2表示p1和p2指向同一内存单元
p1>p2表示p1处于高地址位置。
p1<p2表示p1处于低地址位置。
3.两个指针的地址差(sizeof()的结果)
指针由加减无乘除
-
五、指针与一维数组
- 指针指向数组:
Int a[10],*p=a;(此时无需用&对a取地址)(*p=a&[0];)
或
Int a[10],*p;
P=a;(p=&a[0];)
数组的指针是数组名在内存中的起始地址,整个数组的首地址即元素a[0]的地址&a[0]。*a或*(a+0)即是首地址a所指的内容,*(a+i)表示取出首地址后第i个元素的内容,即下标为i的元素a[i]。
for(k=0;k<10;k++)
{
a[k]=k;
}
等价于
p=a;
for(k=0;k<10;k++)
{
*(p+k)=k;/p++=k;
}
-
六、指针与二维数组
- 二维数组的行地址和列地址
如int a[3][4]
二维数组a可以看成是由a[0]、a[1]、a[2]三个一维数组组成a[0]即为*(a+0),a[1]即为*(a+1),a[2]即为*(a+2),a+0、a+1、a+2即为行地址。再出基础上*(a[0]+0)表示a[0][0]即是列地址。以下是a[i][j]的四种等价形式:
a[i][j]==*(a[i]+j)==*(*(a+i)+j)==(*(a+i))[j]
- 初始化
- 以行指针进行初始化
int(*p)[4]
p=a;/p=&a[0];
定义了一个指向四个元素的的一维数组,本质上是一个二维数组
(2)以列指针进行初始化
int*p
p=a[0];/p=a;/p=&a[0][0];
由于p初始化代表第0行第0列的地址,(假设a是一个m行n列的数组)从a[0][0]到a[i][j]中间需要跳过i*n+j个元素,因此p+i*n+j代表数组第i行第j列的地址,即&a[i][j]。
a[i][j]=*(p+i*n+j)=p[i*n+j]
注意不能用p[i][j]表示数组,因为本质上p是m*n的一维数组。
-
七、动态数组
- c程序的内存映象
一个编译的c程序获得四块不同的内存储区。分别是第一块内存只读存储区(存放机器代码和字符串字面量等只读数据),第二块是静态存储区(存放全局变量和静态变量),第三块和第四块分别为堆和栈,为动态存储区。堆是一个自由存储区,调用动态内存分配函数。栈用于保存函数调用时的返回地址、函数的形参、局部变量。
2.c语言中变量的内存分配方式
(1)从静态存储区分配
程序的全局变量和静态变量都在静态存储区上分配,在程序运行过程中始终占据这些内存,仅在程序终止时才被系统回收。
(2)从堆上分配
程序运行期间,用动态函数分配函数的内存是从堆上分配的。为防止内存泄漏,可及时调用free()释放不再使用的内存。(3)从栈上分配
在执行函数调用时,系统为栈上的函数内的局部变量及形参分配内存,函数结束时自动释放这些内存。栈内存运算内置于处理器的指令集没,效率高但容量有限。如果往栈压入过多数据,有可能会导致栈溢出。
3.动态内存分配函数
(1)函数malloc()
malloc()用于分配若干字节的内存空间,返回以个指向该内存首地址的指针。
原型:void*malloc(unsigned int size);
(void*时无类型指针)
int*pi=NULL;
pi=(int*)malloc(2);(void强转为int)
如果不清楚该类型所占字节数,可以用sizeof()计算所占字节再向malloc()申请
Pi=(int*)malloc(sizeof())
(2)函数calloc()
calloc用于给若干同一类型的数据项分配连续的储存空间并赋值为0
原型:void*calloc(unsigned int num,unsigned int size);
它相当于申明了一个一维数组。第一个参数表示向系统申请的内存空间数量,第二个参数表示申请的每个空间的字节数。函数的返回值是数的首地址。
float*pf=NULL;
pf=(float*)calloc(10.sizeof(float))
(3)函数free()
free()是一个释放向系统动态申请的由指针p指向的内存空间。
原型:void free(void*p);
(4)函数realloc()
realloc()用于改变原来分配的存储空间的大小。
原型:void*realloc(void*p,unsigned int size)