目录
一、指针的本质
1.指针的定义
内存区域中的每字节都对应一个编号,这个编号就是“地址”,如果在程序中定义了一个变量,那么在对程序进行编译时,系统就会给这个变量分配内存单元,把变量地址存取变量值的方法称为“直接访问”,eg.printf("%d\n",i);scanf("%d\n",&i)等;另一种存取变量的方式称为“间接访问",即将变量i的地址存放到另一个变量中,在c语言中,指针变量是一种特殊的变量,他用来存放变量地址
定义格式:
基础类型 *指针变量名;
例如:int *i_pointer;
指针与指针变量是两个概念,一个变量的地址称为该变量的”指针”,例如:地址2000是变量i的地址,如果有一个变量专门用来存放另一变量的地址(即指针),那么称他为“指针变量”。
2.取地址操作符与取值操作符,指针本质
取地址操作符为&,也称引用,通过该操作符我们可以获取一个变量的地址值,取值操作符为*,也称解引用,通过该操作符我们可以得到一个地址对应的数据,
#include<stdio.h>
int main(){
int i=5;
int* p=&i;
printf("i=%d\n",i);
printf("*p=%d\n",&i);
return 0;
}
但是要注意以下:
指针变量前面的“*”表示该变量为指针型变量
例如:float *pointer_1;
注意指针变量名是pointer_1,而不是*pointer_1;
指针的本质说白了就是地址。
二、指针的传递使用场景
1.指针的传递
我们来看下面代码
在本代码的主函数中,定义了整型变量,其初始化值为10,然后通过子函数修改整型变量i的值,但是,我们发现执行语句printf("after change i=%d\n",i);后,打印的值仍为10,子函数change并未改变变量的值
#include<stdio.h>
void change(int j){
j=5;
}
int main(){
int i = 10;
printf("brfore change i = %d\n",i);
change(i);
printf("after change i = %d\n",j);
return 0;
}
接下来 我们通过另一种代码来看他们的变化
#include<stdio.h>
void change(int* j){
*j=5;
}
int main(){
int i = 10;
printf("brfore change i = %d\n",i);
change(&i);
printf("after change i = %d\n",i);
return 0;
}
我们可以看到程序执行后,语句printf("after change i =%d\n",i);打印的值为5,难道是c语言的值传递的原理变了?并非如此,我们将变量i的值的地址传递给change函数时,实际效果j=&i,依然是值传递,只是这时我们的j是一个指针变量,内部存储的是变量i的地址,所以就通过*j就间接访问到了与变量i相同的区域,通过*j=5就实现了对变量i的值的改变,通过单步调试,我们依然可以看到变量j自身的地址是与变量i的地址依然不相等。
三、指针的偏移使用场景
1.指针的偏移
上面我们介绍了指针的传递,指针即地址,就像我们找到了一栋楼,这栋楼的楼号是B,那么前面就是A,在后就是C,所以应用指针的另一个场景就是对其进行加减,但我们不用指针进行乘除,没有意义,就像把五个家庭乘以5一样,所以 我们把对指针的加减叫做对指针的偏移,指针的加法就是对指针向后偏移,指针的减法就是对指针向前偏移。
#include<stdio.h>
#define N 5
int main(){
int a[N]={1,2,3,4,5};
int *p;
int i;
p=a;
for(i=0;i<N;i++){
printf("%3d",*(p+i));//正序输出
}
p=&a[4];//指针指向最后一个元素
for(i=0li<N;i++){
printf("%3d",*(p-i));//逆序输出
}
printf("\n");
return 0;
}
2.指针与一维数组
为什么一维数组在函数调用进行传递时,他的子长度函数无法知道呢,这时由于一维数组名中存储的是数组的首地址,如下例所示,数组名c中存储的是一个起始地址,所以子函数change中其实传入了一个地址,定义一个指针变量时,指针变量的类型和数组的数据类型保持一致,通过取值操作,就可将“h”改为“H”,这种方法称为指针法,获取数组元素时,也可以通过取下标的方式来获取数组元素并进行修改,这种方法称为下标法
#include<stdio.h>
void change(char* d){
*d='H';
d[1]='E';
*(d+2)='L';
}
int main(){
char c[10]="hello";
change(c);
puts(c);
return 0;
}
四、指针与malloc动态内存申请
1.指针与动态内存申请
很多人在学习c语言的时候都会觉得数组长度固定很不方便,其实C语言的数组长度固定是因为其定义的整型,浮点型,字符型变量,数组变量都在栈空间里,而栈空间的大小在编译时是确定的,如果使用的空间大小不确定,那么就要使用堆空间。
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main(){
int i;
char *p;
scanf("%d",&i);
p=(char)malloc(i);
strcpy(p,"malloc success");
puts(c);
free(p);
printf("free success\n");
return 0;
}
要注意:malloc函数是用来申请空间的,在后续数据结构中会用到(此处记住即可)
2.堆空间与栈空间的差异
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//栈空间释放后,函数内部的所有的局部变量消失
char *print_stack(){
char c[17]="I am print_stack";
puts(c);
return c;
}
//堆空间不会因函数执行结束而释放
char *print_malloc(){
char *p;
p=(char *)malloc(20);
strcpy(p,"I am print_malloc");
puts(p);
return p;
}
int main(){
char *p;
p=print_stack();
printf("p=%s\n",p);
p=print_malloc();
puts(p);
return 0;
}