[TOC]文章目录
目录
前言
本篇是有关C语言指针的知识点的部分总结。
指针的运算
#include<stdio.h>
int main()
{
char ac[]={0,1,2,3,4,5,6,7,8,9,};
cahr *p=ac;
printf("p =%p\n",p);
printf("p+1=%p\n",p+1);
printf("*p+1=%d\n",*(p+1));
int ai[]={0,1,2,3,4,5,6,7,8,9,};
int *q=ai;
printf("q =%p\n",q);
printf("q+1=%p\n",q+1);
printf("*q+1=%d\n",*(q+1));
return 0;
}
输出:
p =0xbffbad5a
p+1=0xbffbad5b
*(p+1)=1
q =0xbffbad2c
q+1=0xbffbad30
*(q+1)=1
十六进制:0x2c=44,0x30=48 30-2c=4
sizeof(char)=1,sizeof(int)=4。所以当我们给一个指针去加1的时候,它不是在地址值上加一,而是在地址值上加一个sizeof。
给一个指针加1表示要让指针指向下一个变量
int a[10];
int *p=a;
*(p+n)<-->a[n]
如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义。
这些算术运算可以对指针做:给指针加、减一个整数(+、+=、-、-=) 递增递减(++/--)
两个指针相减
#include<stdio.h>
int main(void)
{
char ac[]={0,1,2,3,4,5,6,7,8,9,};
cahr *p=ac; //或写成*p=&ac[0]
char *p1=&ac[5];
printf("p1-p=%d\n",p1-p);
int ai[]={0,1,2,3,4,5,6,7,8,9,};
int *q=ai;
int *q1=&ai[6];
printf("q =%p\n",q);
printf("q1 =%p\n",q1);
printf("*q1-q=%d\n",q1-q);
return 0;
}
p1-p=5
q =0xbff11d28
q1 =0xbff11d40
q1-q=6
十六进制:0x40-0x28=0x18 结果换算成十进制,为24
q1与q之间的地址直接相减的话为24,但运行结果为6。6=24/sizeof(int)
所以当两个指针相减时,编译器给的不是两个地址相减的差,给的是两个地址的差除以类型的sizeof,即两者之间能放几个这样类型的东西
指针的其他运算形式
*p++
我们常常在指针中看见 *p++,它指的是 取出p所指的那个数据来,完事之后顺便把p移到下一个位置去。
*的优先级虽然高,但是没有++高
作用:常用于数组类的连续空间操作;在某些CPU上,这可以直接被翻译成一条汇编指令
#include<stdio.h>
int main(void)
{
char ac[]={0,1,2,3,4,5,6,7,8,9,-1};
char *p=&ac[0];
while(*p!=-1){ //-1不作为结果输出,它是一个结尾的标志
printf("%d\n",*p++);
}
return 0;
}
指针比较
<,<=,==,>,>=,!=都可以对指针做。指针不能做乘除,可以做加减,可以做比较。
指针比较其实就是地址大小的比较。数组中的单元的地址肯定是线性递增的
0地址
任何程序中都有0地址,但是0地址通常是个不能随便碰的地址,所以指针不应该具有0值。因此可以用0地址来表示特殊的事情:
返回的指针是无效的;指针没有被真正初始化(先初始化为0)
NULL是一个预定定义的符号,表示0地址,有的编译器不愿意用0来表示0地址。
指针的类型
无论指向什么类型,所有的指针的大小都是一样的,因为都是地址。但是指向不同类型的指针是不能直接相互赋值的,这是为了避免用错指针。
指针的类型转换(初学者不建议使用)
void*表示不知道指向什么东西的指针,计算时与char*相同(但不想通)
指针也可以转换类型 int*p=&i; void*q=(void*)p; //通过p去看i是个int,通过q去看i是个void
这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量(不再当i是int,认为i是个void)
小总结(指针的作用)
- 需要传入较大的数据时用指针作参数的类型,比如传数组
- 传入数组后可以对数组做操作
- 当函数返回不止一个结果时,可以用指针做参数让它带出结果
- 当需要用函数修改不止一个变量的时候,比如swap,可以传指针进去让它帮助修改变量的值
- 当需要动态申请内存的时候……
内存分配函数
如果输入数据时,先告诉个数,然后再输入要记录的每个数据,那么有C99的话,可以用变量做数组定义的大小。但是C99之前如何做呢?
那个时候就必须使用动态内存分配。就像这样写(用一个malloc函数):
int *a=(int*)malloc(n*sizeof(int));
malloc函数给它一个参数(说要多大的内存)。如果说要120个字节,n=30,30*sizeof(int)就是120个字节。malloc给一段空间,得到的是void*,还要把类型转换成int*。
#include<stdio.h>
#include<stdlib.h>
int main()
{
int number;
int* a;
int i;
printf("输入数量: ");
scanf("%d",&number);
//int a[number] 如果是C99的话,可以直接这样写
a=(int*)malloc(number*sizeof(int)); /*malloc要的参数不是有多少个int,不是这个数
组有多少个单元,而是这个数组占据多少个空间。
所以malloc是以字节为单位的。*/
for(i=0;i<number;i++){
scanf("%d",&a[i]);
}
for(i=number-1;i>=0;i--){
printf("%d ",a[i]);
} /*我们一旦用malloc得到了一块空间并给它交给了a,接下来我们所有对a的操作可以完全把a当
做数组来用*/
free(a); //我们用malloc在系统中要了一段空间,走之前要还回去
return 0;
}
这就是过去用malloc做动态内存分配来实现数组的大小在运行的时候才确定。
malloc
需要加头文件: #include<stdio.h>
原型: void* malloc(size_t size); //参数是size_t,返回的类型是void*
说明:向malloc申请的空间的大小是以字节为单位的,如果需要int,那么数量乘以sizeof(int),如果需要short,数量乘以sizeof(short)。因为在计算机中,内存就是一片连续的空间,是我们自己说的这个地方是int,那个地方是short,所以跟计算机要内存后,需要类型转换。
返回的结果是void*,需要类型转换为自己需要的类型。
(int*)malloc(n*sizeof(int))
malloc是跟系统要空间,空间总是有限的,如果申请失败则返回0,或者叫做NULL。
内存释放函数
free()
把申请得来的空间还给“系统”,申请过的空间,最终都是应该要还的,只能还申请来的空间的首地址。
常见问题
- 忘了
- 找不到合适的free的时机
- free过了再free
- 地址变过了,直接去free
申请了空间,但是没free,会导致长时间运行内存逐渐下降
解决方法:写了malloc就记住要去写free;需要对程序的架构有一些良好的设计,保证程序有恰当的时机去写free;需要多读代码,多写代码。