指针本质&传递&偏移&动态申请空间 c语言(day05)

发布于:2025-04-09 ⋅ 阅读:(28) ⋅ 点赞:(0)

1. 指针本质

通俗的讲,指针就是地址,我们通常所说的指针就是指针变量,也就是保存地址的变量

  • 定义方式 例如:int *a;//a就是指针变量
  • “&”和“*” 两个运算符的优先级相同,都是按照自右向左的方向结合,因此 *&a与a等价,不将取值和取地址放在一起使用
#include <stdio.h>
int main(){
    int i=5;//定义整型变量
    char c='a';//定义字符变量
    int *i_pointer=&i;//定义整型变量指针,并把i的地址赋值给i_pointer
    char *c_pointer=&c;//定义整型变量指针,并把i的地址赋值给i_pointer
    return 0;
}

指针分不同类型是因为取值可以拿到的空间大小不同,*i_pointer可以拿到 4个字节空间,*c_pointer可以拿到一个字节空间
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
通过间接访问可以修改变量的值, 其中指针的本质就是间接访问
在这里插入图片描述

  • 指针变量本身的大小是固定的对于64位系统,sizeof (i_pointer)sizeof (c_pointer)是8个字节对于32位系统,它们都是4个字节
  • sizeof (*i_pointer)等价为sizeof(i)是4个字节sizeof (*c_pointer)等价于sizeof(c),是1个字节

2. 指针的传递

  • 函数调用时值传递
    在这里插入图片描述

为什么i的值没有改变
我们再启动每个程序的时候,程序都会变成一个进程,每一个进程都有自己的进程空间,我们执行的程序被编译后是放在代码段的,pc指针就会一句一句执行我们写的代码交给cpu里面的译码器, main函数的栈空间就会给i分配4个字节空间,里面存的是10,调用change函数的时候,系统会为change函数分配栈空间, 每一个函数的栈空间是独立的,每调用一个函数,函数有自己的栈空间,当调用change函数的一瞬间,i的值赋给jj=10,然后执行change函数,把j=10改成j=5了main函数栈空间的i不会改变。
在这里插入图片描述

解决方法:

#include <stdio.h>
//指针的传递
void change(int *j)//等价于j=&i
{
    *j=5;//间接访问得到变量i
}
int main(){
    int i=10;
    printf("before i= %d\n",i);
    change(&i);//传递变量i的地址
    printf("after i= %d\n",i);

    return 0;
}

在这里插入图片描述

实际效果是j=&i,依然是值传递只是这时j是一个指针变量,存储的是变量i的地址,所以通过*j就间接访问到了与变量i相3同的区域,通过*j=5就实现了对变量i的值的改变

3. 指针的偏移

指针即地址,就像我们找到了一栋楼,这栋楼的楼号是B,那么往前就是A,往后就是C,所以应用指针的另一个场景就是对其进行加减把对指针的加减称为指针的偏移,加就是向后偏移,减就是向前偏移。

#include <stdio.h>
# define N 5
//指针的偏移,对指针进行加加减减
int main(){
    int a[N]={1,2,3,4,5};//数组名内存储了数组的起始地址,
    int *p;//定义指针变量p
    p=a;//不需要取地址,因为a中存的就是一个地址值
    int i;
    for(i=0;i<N;i++){
        printf("%3d",*(p+i));//这里于写a[i]等价
    }
    printf("\n------------------\n");
    p=&a[4];//指针变量p指向数组的最后一个元素,需要取地址,因为a[4]表明的是一个具体的元素,通过&取地址
    for (int i = 0; i < N; i++)
    {
        printf("%3d",*(p-i));
    }
    return 0;
}
  • 指针变量加1后,偏移的长度是其基类型的长度,如果是int类型就是偏移 sizeof(int),这样通过*(p+1)就可以得到元素 a[1]
    在这里插入图片描述

指针与一维数组

  • 数组名作为实参传递给子函数,是弱化为指针的
#include <stdio.h>
//指针与一维数组的传递
//数组名作为实参传递给子函数,是弱化为指针的
//练习传递与偏移
void change(char *d)//char *d 与char d[]完全等价
{
    *d='H';
    *(d+1)='E';//等价与d[1]

}
int main(){
    char c[10]="hello";
    change(c);
    puts(c);
    return 0;
}

4. 指针与动态内存申请

C语言的数组长度固定是因为其定义的整型、浮点型、字符型变量、数组变量都在栈空间中,而栈空间的大小在编译时是确定的。 如果数组使用的空间大小不确定,那么就要使用堆空间

  • 使用malloc动态申请堆空间,必须加上头文件#include <stdlib.h>
  • malloc函数的返回值类型是void*(无类型指针),因为无类型的指针时不能偏移的,使用时需要强制类型转换
  • 在使用malloc时,一般参数传递的形式为(sizeof(要开辟的变量名)*要开辟的个数).例:p=(int *)malloc(sizeof(int)*initsize)
  • 使用完之后需要释放申请的空间,释放申请的空间给的地址,必须是最初malloc返回给我们的地址
  • 指针的大小与其指向空间的大小是两码事,指针的大小永远是8个字节(64位系统)

在这里插入图片描述

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
    int size;//size代表我们要申请的字节空间
    char *p;//void*类型指针不能偏移的
    scanf("%d",&size);
    p=(char *)malloc(size);
//    p[0]='H';
//    p[1]='O';
//    p[2]='W';
//    p[3]='\0';
    strcpy(p,"malloc success");
    puts(p);
    free(p);//释放申请空间
    return 0;
}

4. 栈空间和堆空间差异

栈在计算机执行任何程序都要转成汇编变成机器码执行,分配有专门的寄存器存放栈的地址,压栈操作、出栈操作都有专门的指令,所以栈的效率比较高。堆需要专门的函数库(malloc和free)来使用,内部有一定的算法,所以堆的效率比栈低的多但堆可以动态分配内存空间

#include <stdio.h>
//堆和栈的差异
char* print_stack(){
    char a[100]="I am print_stack func";
    char *p;
    p=a;
    puts(p);
    return p;
}
int main(){
    char *p;
    p=print_stack();
    puts(p);
    //每次去调用print_stack函数以后,当函数执行完了以后,函数的栈空间操作系统就把它释放掉了,就会给下一个函数使用,
    // 也就是puts(栈空间释放,后面的空间被其他函数使用)
    return 0;
}


在这里插入图片描述
主函数puts(p);出现乱码原因
在这里插入图片描述

每次调用print_stack函数以后,当函数执行完了以后,函数的栈空间操作系统就把它释放掉了,就会给下一个函数使用, 也就是puts(栈空间释放,后面的空间被其他函数使用),所以出现乱码
解决方法:用malloc申请空间

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//堆和栈的差异
char* print_stack(){
    char a[100]="I am print_stack func";
    char *p;
    p=a;
    puts(p);
    return p;
}
char* print_malloc(){
    char *p=(char*) malloc(100);//堆空间再整个进程中一直有效,不因为函数结束而消亡,需要自己free(),释放申请空间
    strcpy(p,"i am print_malloc func");
    puts(p);
    return p;
}
int main(){
    char *p;
    p=print_stack();
    puts(p);
    //每次去调用print_stack函数以后,当函数执行完了以后,函数的栈空间操作系统就把它释放掉了,就会给下一个函数使用,
    // 也就是puts(栈空间释放,后面的空间被其他函数使用)
    p=print_malloc();
    puts(p);
    return 0;
}

  • 堆空间再整个进程中一直有效,不因为函数结束而消亡,需要自己free(),释放申请空间