指针的概念
指针就是内存中的地址,地址是内存单元的编号
int * p;//p是变量的名字,int *表示p变量存放的是int类型变量的地址(int *是p变量的数据类型)
int i =3;
p = &i;//p保存的是i的地址,因此p指向i,p的值与i的值互不影响
注:如果一个指针变量指向了某个普通变量,则* 指针变量就完全等同于普通变量
int * p;
int i =3;
p = &i;
j = *p;//*p相当于i,所以j=i
//结果为 j =3
//*p就是以p的内容为地址的变量
指针和指针变量
指针是地址,指针变量是存放地址的变量,指针和指针变量是两个不同的概念,但是叙述时会把指针变量简称为指针,实际它们含义不一样。
一个指针变量占几个字节
sizeof(数据类型):返回值是该数据类型所占的字节数
sizeof(变量名):返回值是该变量所占的字节数
sizeof(int)=4 sizeof(char)=1 sizeof(double)=8
- 假设p指向char类型的变量(1个字节)
- 假设q指向int类型的变量(4个字节)
- 假设r指向double类型的变量(8个字节)
- pqr大小均为4个字节(pqr只存地址的第一个字节,但CPU与内存间有32根地址线,地址表示位有32位)
注:一个指针变量,无论它指向的变量占几个字节,该指针变量本身只占四个字节
多级指针
int i =10;
int * p = &i;
int ** q = &p;//q是存放int*类型变量的地址的指针
int *** r = &q;//r是存放int**类型变量的地址的指针
r = &p;//error,r只能存放int**类型的变量的地址
指针的分类
基本类型指针
int * p;
int i=5;
p = &i;
注:在C语言中NULL指针定义为0
基本类型指针的常见错误分析
int * p;
int i =5;
* p =i;//error,相当于将p指针指向的int变量赋值成i,但p指针指向的int变量的控制权限并没有分配给本程序
int i =5;
int * p;
int * q;
p =&i;
//* q =p;//error,*q为int类型,而p为int*类型
//* q = *p;//error
注:q的空间是属于本程序的,所以本程序可以读写q的内容,但如果q的内部是垃圾值,则本程序不能读写* q的内容,因为* q所代表的内存单元的控制权限并没有分配给本程序
指针和数组
指针和一维数组
数组名
一维数组名是一个指针常量,存放的是第一个元素的地址
下标和指针的关系
如果p是个指针变量,则p[i]永远等价于* (p+i)
//通过函数输出数组的内容
void pArr(int * p,int len){
//p为数组名,len为数组大小
//a[0]=*(a+0),a[2]=*(a+2)
for(int i=0;i<len;i++){
printf("%d",*(p+i));
}
printf("\n");
}
int main(void){
a[4]={1,2,3,4};
b[6]={1,2,3,4,5,6};
c[100]={1,2,34};
pArr(a,4);
pArr(b,6);
pArr(c,100);
return 0;
}
指针变量的运算
- 指针变量不能相加,不能相乘,不能相除
- 如果两个指针变量指向的是同一块连续空间中的不同存储单元,则这两个指针变量可以相减
int i =5; int j =10; int * p =&i; int * q =&j; p-q;//没有实际意义
int a[5]; int * p; int * q; p = &a[2]; q = &a[4]; printf("p和q所指向单元相隔%d个单元\n",p-q);
指针和二维数组
指针和函数
通过被调函数修改主调函数普通变量的值
int g(int * i) {
*i = 1;
return 0;
}
int main() {
int a = 2;
g(&a);
printf("%d\n",a);//最终输出为1
return 0;
}
指针使函数返回一个以上值
int g(int * p,int * q){
*p = 1;
*q = 2;
}
int main(void){
int a=3,b=4;
g(&a,&b);
printf("%d/n",a,b);//a=1,b=2
return 0;
}
指针和结构体
动态内存分配
传统数组的缺点(静态数组)
- 数组长度必须事先指定,且只能是常熟,不能是变量
- 传统形式定义的数组内存无法手动释放,只能在函数运行完毕时由系统自动释放
- 数组的长度不能在函数运行的过程中动态的扩充或缩小
- A函数定义的数组,在A函数运行期间可以被其他函数使用,但A函数运行完毕后,A函数中的数组将无法再被其他函数使用(不能跨函数使用)
为什么需要动态分配内存
动态数组很好的解决了传统数组的4个缺陷
### 动态内存分配举例(动态数组的构造)
malloc函数的使用
malloc 是memory allocate(内存分配)的缩写
int i = 5;//静态分配4个字节
int * p = (int *)malloc(4);//4表示请求系统为本程序分配4个字节
//一个分配了8个字节,p占用4个字节,p所指向的内存也占4个字节
//p本身所占的内存是静态分配的,p所指向的内存是动态分配的
* p =5;
free(p);//表示把p所指向的内存释放掉,p本身的内存是静态的,只能在p变量所在函数运行终止时由系统自动释放
注:1.使用malloc函数必须加上malloc.h头文件
2.malloc函数只能返回第一个字节的地址
3.malloc函数只有一个形参,并且形参是整形
动态构建一维数组
int * pArr;
int len;
int i;
printf("请输入数组元素个数:");
scanf("%d",&len);
pArr = (int *)malloc(4*len);//每4个字节代表一个元素(int为4个字节)
//对动态一维数组进行赋值
for(i=0;i<len;i++){
scanf("%d",pArr+i);
}
//对动态一维数组进行输出
for(i=0;i<len;i++){
printf("%d\n",*(pArr+i));
}
realloc(pArr,100);//把pArr指针所指向的内存扩大到100个字节
free(pArr);//释放pArr所指向的内存
静态内存和动态内存的比较
静态内存
- 静态内存是由系统自动分配的,由系统自动释放
- 静态内存是在栈中分配的
动态内存
- 动态内存手动分配,手动释放
- 动态内存是在堆中分配的
跨函数使用内存的问题
void f(int ** q){
int i = 5;
*q = &i;
}
int main(void){
int * p;
f(&p);
printf("%d\n",*p);//error,语法无错误,但是i变量的内存在f()函数执行完毕后已经被释放(i已经不存在),此时*p指向的并不是i
return 0;
}
void f(int ** q){
* q = (int *)malloc(sizeof(int));
** q = 5;//等价于*p=5
}
int main(void){
int * p;
f(&p);
printf("%d\n",*p);//OK,动态分配的内存是手动释放,f()中的** q在函数执行完毕后依然存在
}