指针
定义
- 指针是一个变量,有自己的地址,而指针所占的空间中存放着地址,此地址指向另外一个变量的地址(即指针中存放的是地址,此地址指向另外一块内存空间,此内存空间中存放着变量值)
- 获取地址:&
- int *ptr=&num1;ptr 是一个变量,所属的类型是指针变量,在这块内存空间中存放着一个地址,此地址指向一个int类型的变量,因此,ptr是一个int *类型
- 解析指针:*ptr:获取ptr指向的值:ptr中存放着变量的地址,*ptr获取此地址,访问地址,得到变量的真实值。
- 指针存储其他变量地址之前要进行声明:int *p ,char *p1;
- int *p[4]:定义指针数组p,它由4个指向整型数据的指针元素组成
- int (*p)[4]:p为指向包含4个元素的一维数组的指针变量
指针的算术运算
p++:每次加的是变量所占的字节数
#include <stdio.h>
const int MAX = 3;//常量
int main(){
int var[] = {10,100,20};//数组
int i, *p,*p2;
p=var;//在p中存放的是var[0]的地址
for(i=0;i<MAX;i++){
printf("var[%d]=%p ",i,p);
printf("var[%d]=%d ",i,*p);
printf("\n");
p++;//p++=p+1,p中存放的是地址,p+1,是指p中存放的地址+4个字节(int类型占四个字节)
}
p2 = &var[MAX-1];
for(i=MAX-1;i>=0;i--){
printf("var[%d]=%d ",i,*p2);
p2--;
}
return 0;
}
指针数组
int *p[3]
#include <stdio.h>
int main(){
int var[]={1,2,3};
int i, *ptr[3];
for(i=0;i<3;i++){
ptr[i]=&var[i];//var[i]是一个数,ptr里面存的是地址,所以要取址
}
for(i=0;i<3;i++){
printf("*ptr[%d]=%d ",i,*ptr[i]);
printf("指针中的对应地址:*ptr[%d]=%p ",i,ptr[i]);
printf("对应地址: var[%d]=%p ",i,&var[i]);
printf("指针中存放地址的空间本身的地址:ptr[%d]=%p ",i,&ptr[i]);
}
return 0;
}
应用
指向字符串的字符数组,存放四大名著,并显示
#include <stdio.h>
int main(){
char *p1[5]={"三国演义","西游记","红楼梦","水浒传"};
int i;
for(i=0;i<4;i++){
printf("*p1[%d]=%s ", i,p1[i]);//字符串是用%s
}
return 0;
}
字符串前不加*,比如char P={"凉“},P就能够显示凉,而不是P,因为是直接赋值,直接存在了指针中,而不需要解析
#include <stdio.h>
int main(){
int *p=3;
int i=3;
int *p1=&i;
printf("直接赋值情况:p=%d",p);
printf("间接取址赋值:p1=%d",*p1);
return 0;
}
指针里存的不一定只是地址,存的是地址时需要*p解析访问,而直接赋值时不需要,p就可以输出,要取得指针本身的地址,需要&p
指向指针的指针(多重指针)
一个指向指针的变量在声明时必须:int ** ptr;在解析时也必须 ** ptr
#include <stdio.h>
void main(){
int var;
int *p1;
int **p2;
var=3;
p1=&var;
p2=&p1;
printf("var本身的地址:%p ",&var);
printf("var本身的地址:%p ",p1);
printf("p1本身的地址:%p ",&p1);
printf("p2保存的地址:%p ",p2);
printf("p2的值:%d",**p2);
}
传递指针给函数
实际上我们传入地址给函数,我们操作的是此地址对应的值,所以在函数体中需要用*p操作,如果用指针的地址操作,没有用p;
#include <stdio.h>
void test(int *p);
int main(){
int a[]={1,2,3};
int *p1;
p1=a;
int *p2=a;
printf("%p\n",a);
//test(a);//不能直接给数组名赋值,不会改变,数组名的地址不会改变。传的是地址
test(p1);//+1后的值,没用,因为传递的是地址,操作是对地址操作,站在利用完之后依然会释放
p2=p2+1;//直接对地址的操作,有用
printf("%d ",*p1);
printf("%p\n",p1);
printf("%d ",*(p2+1));//+1后的值
printf("%p\n",p2+1);
int i,num=90;
int *p3;
p3=#
printf("%p ",&num);
test2(&num);
printf("%p ",&num);//地址不会改变
printf("%d",num);
return 0;
}
void test(int *p){
p+=1;
}
void test2(int *p){
*p+=1;
}
返回指针的函数
C语言允许函数返回值是一个指针
返回两个字符串中较长的字符串
#include <stdio.h>
#include <string.h>
char *strlong(char *str1, char *str2){
return strlen(str1)>strlen(str2)?str1:str2;
}
int main(){
char str1[30],str2[30],*str;
printf("请输入str1字符串");
scanf("%s",&str1);
printf("请输入str2字符串");
scanf("%s",&str2);
printf("str1地址%p ",str1);
printf("str2地址%p ",str2);
str=strlong(str1,str2);//这样赋值就是取的地址,因为函数是一个指针类型的
printf("%s ",str);//这里不用加取址符
printf("str地址%p",str);
return 0;
}
注意事项
函数返回的指针不能指向函数内部的局部变量,函数内部的局部变量会被销毁,这里的销毁并不是将局部数据所占用的内存全部清零,而是程序放弃对它专用的使用权限,也就是这块内存可以被其他人使用,所以如果调用后的程序有可能输出正确的值
如果想要返回返回函数中局部变量的地址,那么需要用static修饰,因为static修饰后所占用的不是栈空间,而是静态存储空间。
#include <stdio.h>
#include <stdlib.h>
int *f1(){
static int arr[10];//必须加static让arr的空间在静态存储区存储
int i=0;
for(i=0;i<10;i++){
arr[i]=rand();
}
return arr;
}
void main(){
int *p;
p=f1();
int i;
for(i=0;i<10;i++){
printf("元素值:[%d] ", *(p+i));
}
}
函数指针
函数名的本质是一个地址,需要使用函数指针来接收
函数名可以被转换为函数的首地址,是指针变量指向函数所在的内存区域,然后通过指针变量就可以找到并调用该函数,这种指针就是函数指针
returnType (*pointName) (param list)
cichudepointName为函数名称,*pointName必须加括号
内存分为:栈区,堆区,静态区(全局区),代码区
函数一开始被放在了代码区,当执行到指针函数,指针函数只想代码区的函数
#include <stdio.h>
int max(int a,int b){
return a>b?a:b;
}
int (*pmax) (int a,int b);
int main(){
int x,y,maxV;
int(*pmax)(a,b)=max;//函数指针名字pmax,int表明该函数指针指向的函数返回int类型
printf("请输入两个数:");
scanf("%d %d",&x,&y);
maxV=(*pmax)(x,y);//通过*pmax去调用函数
//maxV=pmax(x,y);也可以调用
printf("maxV=%d",maxV);
return 0;
}
回调函数
函数指针变量可以作为某个函数的参数来使用,回调函数就是一个通过函数指针调用的函数
#include <stdio.h>
#include <stdio.h>
//回调函数,(*f)(void)是一个返回值为int,没有形参的函数指针
//f被intitArray函数回调
void intitArray(int *array ,int arraySize,int(*f)(void)){
int i;
for (i=0;i<arraySize;i++){
array[i]=f();//通过函数指针调用了getRand函数
}
}
int getRand(void){
return rand();
}
int main(){
int myarray[10],i ;//未初始化
intitArray(myarray,10,getRand);//getRand是传入的函数名,函数名的本质是一个地址,需要使用函数指针来接收
for(i=0;i<10;i++){
printf("myarray[%d]=%d",i,myarray[i]);
}
return 0;
}
定义空指针
作用:一开始没有确切的地址赋值
NULL在库stdio.h中
#include <stdio.h>
void main(){
int *p=NULL;
int num=34;
p=#
printf("*p=%d",*p)
动态内存分配
全局变量–内存中的静态存储区
非静态的局部变量–栈区
临时使用的数据–建立动态内存存储区,需要使用,不需要释放
根据需要向系统申请所需要的大小空间,不能通过变量名或数组名直接访问,需要通过堆去访问
所需头文件:stdlib
malloc(100):在堆区分配长度为size的连续空间开辟100个空间,返回值是第一个字节的地址
calloc(n,size):开辟n个长度为size的连续空间,空间较大,分配数组
calloc(50,4)开辟50*4个字节的临时空间
free§:释放空间
realloc(p,size): 重新分配malloc和calloc所获得动态空间大小,将p指向的动态空间大小改变为size,p的值不变,首地址不变。需求是将空间扩大缩小。
void *p就是一个纯地址,不指向任何对象,malloc calloc realloc都是void
int a=3;
int *p=&a;
void *p2;
p2=(void )p;把p指针存的地址赋给p2,但是p指针不改变
p3=(void)&a;
*p3是不对的,对于void类型的指针变量,不能进行解析
#include <stdio.h>
#include <stdlib.h>
int main(){
void check(int *);//函数声明
int *p,i;
p=(int *)malloc(5*sizeof(int));
printf("分配的首地址为:%p",p);
printf("请输入成绩为:");
for(i=0;i<5;i++){
scanf("%d",p+i);
}
check(p);
free(p);
return 0;
}
void check(int *p){
int i;
printf("低于80:");
for(i=0;i<5;i++){
if(*(p+i)<80){
printf("%[d]尚需努力 ",i);
}
}
}
基本原则:
- 尽量避免分配多个很小的内存块
- 及时释放,否则出现内存泄漏
- 释放的时间
- 循环分配内存可能出现覆盖