指针的本质(间接访问原理)
指针的定义
- 按变量地址存取变量值的方式称为**“直接访问”**,如printf(”%d”,i);scanf(”%d”,i);
- 间接访问:即讲变量i的地址存放到另一个变量中。
- 说某个变量的地址,都是说起始地址。
- 指针变量是一种特殊的变量,它用来存放变量地址。
- 🦑 指针变量的定义格式: 基类型 *指针变量名;
- 指针与指针变量是两个概念,一个变量的地址称为该变量的“指针”。例如,地址2000是变量i的指针。如果一个变量专门用来存放另一个变量的地址(即指针),那么称它为“指针变量”。
- 编写的程序是64位应用程序,寻址范围为64即8字节。sizeof(i_pointer)=8;
- 编写的程序是32位应用程序,寻址范围为32即4字节。(考研中往往会强调程序是32位的程序)
取地址操作符与取值操作符,指针本质
#include <stdio.h>
int main() {
int i = 5;
//指针变量的初始化是某个变量取地址来赋值,不能随机写个数
int *i_pointer;
i_pointer=&i;//定义一个指针变量,i_pointer是指针变量名
printf("i=%d\n",i);//直接访问
printf("*i_pointer=%d",*i_pointer);//间接访问
return 0;
}
✍️ 注意:
- 指针变量前面的”*”表示该变量为指针型变量。
- 在定义指针变量时必须指定其类型。注意:只有整型变量的地址才能放到指向整型变量的指针变量中。
- pointer_1=&a; & 和*两个运算符的优先级别相同,但要按自右向左的方向结合。&*pointer_1与&按相同,都表示变量a的地址,也就是pointer_1.
指针的传递使用场景
#include <stdio.h>
//在子函数内去改变主函数中某个变量的值
void change(int j){//j是形参
j = 5;
}
int main() {
int i = 10;
printf("before change i = %d\n",i);
change(i);//C语言的函数调用是值传递,实参赋值给形参,j = i
printf("after change i = %d\n",i);
return 0;
}
解释值传递:进程地址空间图
❓ 如何再子函数中修改main函数的某个变量的值?
✔️
#include <stdio.h>
//在子函数内去改变主函数中某个变量的值
void change(int *j){//j是形参
*j = 5;//*j等价于变量i,只是间接访问
}
int main() {
int i = 10;
printf("before change i = %d\n",i);
change(&i);//传递变量i的地址
printf("after change i = %d\n",i);
return 0;
}
//输出
before change i = 10
after change i = 5
指针的偏移使用场景
指针的偏移
#include <stdio.h>
//指针的偏移使用场景,也就是指针的加减
#define N 5
int main() {
int a[N] = {1,2,3,4,5};//数组名内存储了数组的起始地址,a中存储的就是一个地址值
int *p;//定义指针变量p
p = a;
int i;
for(i = 0;i < N;i++){
printf("%3d",*(p+i));//这里和写a[i]是等价的
}
printf("\n");
printf("---------------------\n");
p = &a[4];//指针变量p指向了数组的最后一个元素
for(i = 0;i < N;i++){
printf("%3d",*(p-i));
}
return 0;
}
p的地址是0x61fdf0,p+1的地址时0x61fdf4
👩💻 偏移的长度是其基类型的长度,也就是偏移sizeof(int),这样通过*(p+1)就可以得到元素a[1]
指针与一维数组
#include <stdio.h>
//指针与一维数组的传递
//数组名作为实参传递给子函数,是弱化为指针的
void change(char *d){
*d = 'H';
d[1] = 'E';//等价于*(d + 1) = 'E'
*(d + 2) = 'L';
}
int main() {
char c[10] = "hello";
change(c);
puts(c);
return 0;
}
//输出
HELlo
指针与malloc动态内存申请,栈与堆的差异
指针与动态内存申请
C语言的数组长度固定是因为其定义的整型、浮点型、浮点型变量、数组变量都在栈空间中,而栈空间的大小再编译时是确定的。
❓ 动态内存申请
✔️
#include <stdio.h>
#include <stdlib.h>//malloc使用的头文件
#include <string.h>
int main() {
int size;//size代表我们要申请多大字节空间
char *p;//void*类型的指针不能偏移,因此不会定义无类型指针
scanf("%d",&size);//输入要申请的空间大小
//malloc返回的void*代表无类型指针
p = (char*)malloc(size);
strcpy(p,"malloc success");
puts(p);
free(p);//释放申请的空间
printf("free success\n");
return 0;
}
👩💻 要注意指针本身大小和其指向的空间大小,是两码事!
栈空间与堆空间的差异(了解)
- 栈是计算机系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈操作、出栈操作都有专门的指令执行,这就决定了栈的效率表较高。
- 堆是C\C++函数库提供的数据结构,它的机制很复杂,例如分配了一块内存,库函数会按照一定的算法再堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间,那么就有可能调用系统共嗯那个去增加程序数据段的内存Jon关键,这样就有机会分到足够大小的内存,然后返回。
- 堆的效率要比栈低得多。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//堆和栈的差异
char *print_stack(){
char c[100] = "I am print_stack func";
char *p;
p = c;
puts(p);
return p;
}
char *print_malloc(){
char *p =(char*) malloc(100);//堆空间再整个进程中一直有效,不因为函数结束而消亡
strcpy(p,"I am print_stack func");
puts(p);
return p;
}
int main() {
char *p;
p = print_stack();
puts(p);
p = print_malloc();
puts(p);
free(p);//只有free时,堆空间才会释放
return 0;
}
//输出
I am print_stack func
I am print_stack lk
I am print_stack func
I am print_stack func