char ac[]={0,1,2,3,4,5,6,7,8,9,};
char *p = ac;
printf("p =%pn",p);
printf("p+1=%pn",p);
- 两个地址相差1
int ai[]={0,1,2,3,4,5,6,7,8,9,};
int *q = ai;
printf("q =%pn",q);
printf("q+1=%pn",q);
- 两个地址相差4
为何如此?很简单,char变量占据一字节而int类型占据4字节。指针加一,增加的是sizeof(char/int/double...)
给指针加一表示要让指针指向下一个变量。
在讲到数组和指针的关系时,我们提到可以将指针当作数组操作,也可以将数组当作指针操作。那么我们试试用*操作符。
- *p等价于ac[0]
- 那么*(p+1)等价于ac[1]
char ac[]={0,1,2,3,4,5,6,7,8,9,};
char *p = ac;
printf("p =%pn",p);
printf("p+1=%pn",p);
printf("*p+1=%pn",*(p+1));
因此*(p+n)< == >ac[n]
tips:如果指针不是指向一片连续分配的空间,比如数组,那么这种运算就没有意义了
指针运算- 这些算术运算可以对指针做
- 指针加减一个整数:+、-、+=、-=
- 指针递增或递减:++、--
- 两个指针相减:此时得到的结果是(地址差/sizeof()),注意要以%d输出结果
int ac[]={0,1,2,3,4,5,6,7,8,9,};
int *p = &ac[0];
int *p1= &ac[5];
printf("p =%pn",p);
printf("p+1=%pn",p+1);
printf("p1-p=%dn",p1-p);
*p++
- ++的优先级要比*高
- 因此先执行p++然后取地址,但是由于++在调用后才执行
- 所以其作用是,取出p的地址,再将p移动到下一位
- 适用于数组类的连续空间操作(如遍历)
- 在一些cpu上可以被直接翻译为一条汇编语言
之前的遍历操作:
int ac[]={1,2,3,4,5,6,};
int *p=&ac[0];
for(int i=0;i
现在的遍历操作:
int ac[]={1,2,3,4,5,6,-1};
int *p=&ac[0];
//写法1
for(p=ac;*p!=-1;p++)
{
printf("%dn",*p);
}
//写法2
for(p=ac;*p!=-1;)
{
printf("%dn",*p++);
}
//写法3
while(*p!=-1)
{
printf("%dn",*p++);
}
指针比较
- <、<= 、==、 >、 >=、 != 都可以对指针做
- 实际上是比较它们在内存中的地址
- 数组中单元地址是线性递增的
0地址
在计算机运行时会给所有进程分配一块虚拟内存,其特征是地址从0开始,但是0地址是不能随便碰的!0地址不能被写入,有些甚至不能读取。因此你的指针不应该具有0值.
然而0地址可以用来表示一些特殊的东西:
- 返回0值代表返回的指针是无效的
- 定义指针变量时先让指针指向0,这意味着指针没有被真正初始化
- C语言定义了一个符号NULL来表示0地址(一些编译器不想让你用0来表示0地址)
指针的类型
- 无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
- 但指向不同类型的指针不能直接相互赋值,这是为了避免用错指针
指针类型转换
- void* 表示指针不确定指向什么东西,但是指向一块内存空间
- 强制类型转换
int *p = &i;
void *q = (void*)p;
此处将p强制转换为void型指针。这并没有改变p指向的变量的类型,而是通过q去看指向的变量时,不再将i看作int类型而是void类型。
总结一下,用指针可以做什么
9.2.2 动态内存分配
C99里可以用变量定义数组大小,而C99之前人们使用动态内存分配:
malloc函数
malloc(size_t size) ; 返回的类型是void* (因为类型是无法传递的),需要额外类型转换
- 要使用该函数需要包含
头文件 - 向malloc申请的空间大小是以字节为单位,要申请多少,括号里写数量*sizeof(需要的类型)
- 使用结束后要释放申请的空间,使用free()函数
- 返回的结果是void*,需要转换为自己需要的类型(类型是人为将数值定义的种类)
- eg:要申请数量为n,int类型的,返回类型为int*的空间给一个指针变量a
- int *a; a=(int*)malloc(n*sizeof(int));
#include
#include
int main(void)
{
int number;
int *a;
int i;
printf("请输入变量:");
scanf("%d",&number);
//C99可以直接写 int a[number];
a=(int*)malloc(number*sizeof(int));
//读入数据
for(i=0;i=0;i--)
{
printf("%d",a[i]);
}
free(a);//申请了一块空间,现在需要释放。程序将指针当作数组用,所以直接释放a即可
return 0;
}
申请失败
- 如果申请内存失败则会返回0(或者称为NULL)
- 你的系统能给你多大的空间?用下列代码试一下吧!
#include
#include
int main(void)
{
void *p;
int cnt=0;
while((p=malloc(100*1024*1024)))
{
cnt++;
}
printf("分配了%d00MB的空间n",cnt);
return 0;
}
while((p=malloc(100*1024*1024)))
这条语句同时做了两件事,第一:申请100MB空间给p;第二:将p值作为while条件
这样在申请失败,也就是没有可申请空间时循环停止,可以得出系统最大可分配的内存值。
free()
- 俗话说有借有还,再借不难,free函数的作用就是将借来的内存空间还给系统,只需要将空间首地址传入free即可
- 申请过的空间最终都是需要还回去的
- free(NULL); 完全没问题,相当于啥也没做。或者是形成一个习惯:定义指针时,先给指针赋0值。如果没有申请或者申请失败,此时就free(NULL),free(0)
常见问题
申请了却没有free
- 后果是长时间运行内存会逐渐减少
- 新手可能是忘记了
- 进阶者可能是找不到合适的free时机
free了多次
- 会崩溃
地址改变后直接free
- 比如申请的是指针p,执行free(p++)会提示p++所指的变量没有申请
因此有一个malloc就要配套一个free。需要对程序结构有一定的规划,还需要一些经验。



