指针学习

前言

下面是我自学的笔记以及源代码,系统的学习完,其实发现 指针其实也并没那么可怕! 还有一些比较绕的公式其实没有必要去死记硬背!仔细一想,我们在学习指针之前学习过它,还不止一次,scanf,数组,都应用到指针这个东西!!所以指针并不复杂!!一步一步的学总会学会它!!!

C语言内存模型:

堆栈:自顶向下,以及每种数据类型所对应的十六进制下的位数!这都是要有一定的基础!
先从变量的地址学起:

#include <stdio.h>
int main()
{
    int a=10;
    int p=(int)&a;
    printf("%p\n",a);//a对应的16进制
    printf("0x%x\n",&a);//a的地址,按十六进制
    printf("%p\n",p);//赋值后与a的地址值相同
    printf("0x%x\n",&p);//p的地址
    printf("%p\n",&p);//p的地址
    return 0;
}

运行结果:
在这里插入图片描述

数组的地址:

#include <stdio.h>
int main()
{
    int a[10];
    printf("%p\n",&a);
    printf("%p\n",a);
    printf("%p\n",&a[0]);
    printf("%p\n",&a[1]);
    return 0;
}

运行结果:
在这里插入图片描述

指针:就是保存地址的变量。
普通变量:放的值---->【值】int a;
指针变量:放的别人的地址---->【别人的地址】int *a;
作为参数的指针:

#include <stdio.h>
void fun(int *p);
int main(void)
{
    int i=6;
    printf("&i=%p\n",&i);
    fun(&i);
    g(i);
    return 0;
}
void fun(int *p)
{
    printf("p=%p\n",p);
    printf("*p=%d\n",*p);
}
void g(int k)
{
    printf("k=%d\n",k);
}

运行结果:
指针访问外部变量
函数内部拥有了main函数的变量地址,从而可以访问外部变量。
当我们访问到一个外部变量的地址是,我们可以使用*p(指针)来修改此变量。实际上,我们是需要通过传进来的指针参数来得到我们想要的结果。

#include <stdio.h>
void fun(int *p);
int main(void)
{
    int i=6;
    printf("&i=%p\n",&i);
    fun(&i);
    g(i);//i在上一步已经被fun函数修改成26
    return 0;
}
void fun(int *p)
{
    printf("p=%p\n",p);
    printf("*p=%d\n",*p);
    *p=26;//通过地址访问外部变量,修改i的值
}
void g(int k)
{
    printf("k=%d\n",k);
}

运行结果:
指针修改外部变量
1.*左值叫左值:*p=指向的地址所对应的值(叫值)
2.传入地址:int i;scanf("%f",i);当编译的时候不会报错,因为你传进来的数值被当作地址,写到了不该写的地方,所以运行时会报错!
指针的使用
使用指针交换变量

#include <stdio.h>
int main(void)
{
    int a=5;
    int b=6;
    swap(&a,&b);
    printf("a=%d,b=%d",a,b);
    return 0;
}
void swap(int *pa,int *pb)
{
    int t=*pa;
    *pa=*pb;
    *pb=t;
}

交换数值
因为有返回值的函数只能返回一个,所以如果需要函数返回多个值,这时候是体验到指针所带来的便利!
接下来还是一个通过指针参数来求数组中最大值和最小值。这是原来通过函数很难解决的。

#include <stdio.h>
int main(void)
{
    int a[]={1,2,3,4,5,10,100,20,22};
    int min,max;
    minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
    printf("min=%d,max=%d",min,max);

    return 0;
}
void minmax(int a[],int len,int *min,int *max)
{
    int i;
    *min=*max=a[0];
    for(i=0;i<len;i++){
        if(a[i]<*min){
            *min=a[i];
        }
        if(a[i]>*max){
            *max=a[i];
        }
    }
}

求最值
下面我们以这个求最值的函数来研究数组和指针的关系

#include <stdio.h>
int main(void)
{
    int a[]={1,2,3,4,5,10,100,20,22};
    int min,max;
       printf("main  sizeof(a)=%lu\n",sizeof(a));//main中数组a的大小
       printf("main a=%p\n",a);//main中的a的地址
    minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
    printf("a[0]=%d\n",a[0]);
    printf("min=%d,max=%d\n",min,max);
    int *p=&min;//有一个变量min=2,p指向了min,可以看做把p看作一个数组,p[1]
    printf("*p=%d\n",*p);//
    printf("p[0]=%d\n",p[0]);
    printf("*a=%d\n",*a);//指的就是a[0];
    int b[]=a;//不可以
    int *q=a;//这个可以
    //因为int b[]被看作 int * const b;//数组是常量指针
    return 0;
}
//数组变量组作为参数
void minmax(int *a,int len,int *min,int *max)//数组可以修改成指针
{//a[]可以修改为指针*a,两者没有区别,在参数表中是等价的,数组变量是特殊的指针
    int i;
    printf("minmax sizeof(a)=%lu\n",sizeof(a));//函数中a的大小
    printf("minmax a=%p\n",a);//函数里的数组地址,和main里的一摸一样,可以修改
    a[0]=1000;
    *min=*max=a[0];
    for(i=0;i<len;i++){
        if(a[i]<*min){
            *min=a[i];
        }
        if(a[i]>*max){
            *max=a[i];
        }
    }
}

运行结果:
数组和指针的关系
一:是当数组作为参数时,它的相当就是把数组的地址,即与a[0]的地址相同,所以在minmax函数中求sizeof(a)时,大小就是固定的:4,在mian函数中sizeof(a)求的的大小:4*9=36
二:地址相同,&a由于在mian和普通函数中传入的都是a[0],所以地址相同。
三:数组就是特殊的指针,因为当你在普通函数里传入数组时,修改里面的数据,也会使得main中的数据同时改变,如我修改的a[0]=1000
四:a与a[0]的值相同。
五:还有就是数组不可以相互赋值,例如:int b[ ]=a;//会直接出现编译错误,但是
q=a;可以赋值给指针,因为,数组本质上可以看作:int * const b; 就是一个常量的数组,所以数组间不能相互赋值。

const和指针:
如果用const来修饰指针变量,那么有以下三种情况:

int i;
const int *P1=&i;
int const *P2=&i;
int *const P3=&i;

其实判断是指针的指向不能变,还是通过指向来修改变量的值,就看*****是在const前还是后。
const在前:一,二相同,指的不能通过指向来修改变量的值,即:
修饰*
const在
后,说明这个指针是个常量,一旦指向莫格变量后就不能修改,即:
修饰指针
指针的运算:

#include <stdio.h>
int main(void)
{

    char ac[]={0,1,2,3,4,5,6,7,8,9};
    char *p=&ac[0];//指向了数组的第一个单元
    char *p1=&ac[5];
    while(*p!=-1){//2.可以实现遍历
        printf("%d\n",*p++);//取出p所指向的那个数据,完事之后顺便把p移到下一个位置上去
        }
    //不同类型的指针不能相互赋值
    printf("p1-p=%p\n",p1-p);
    printf("ac =%p\n",ac);
    printf("ac+1 =%p\n",ac+1);
    int ai[]={0,1,2,3,4,5,6,7,8,9};
    int *q=ai;
    printf("q=%p\n",q);
    printf("q+1=%p\n",q+1);//1.不是+1,是加一个sizeof

    return 0;
}

运行结果:
指针运算
一:你可以使用指针变量指向一个数组,从而实现数组的赋值操作,赋值后的指针变量完全可以看作数组来用。
二:可以使用while(*p!=-1){printf("%d ",*p++)},来实现数组的遍历
三:指针变量+1,加的是sizeof个地址值
用指针来做什么呢?
需要传入较大的数据时用作参数
传入数组后对数组操作
函数返回不止一个结果
需要用函数修改不止一个变量

动态申请的内存
输入数据:
C99之前:
int *a=(int )malloc(nsizeof(int))//n是你需要申请的数据个数
下面我们来讲一讲malloc。

#include <stdio.h>
#include <stdlib.h>
int main(void)
{
    int number;
    int *a;
    int i;
    printf("输入数量:");
    scanf("%d",&number);
    //int a[number];
    //占据多少空间
    a=(int*)malloc(number*sizeof(int));//malloc的参数是这个数组占据多少个字节,所以需要使用number来确定数组的大小,同时要乘以sizeof
    for(i=0;i<number;i++){
        scanf("%d",&a[i]);
    }
    for(i=number-1;i>=0;i--){
        printf("%d ",a[i]);
    }
    free(a);

    return 0;
}

运行结果:
malloc
一:malloc函数就是stdlib.h这个库函数中的,所以我没如果要使用它,就在main函数头声明一下
二:这是一个动态内存申请的函数,主要是用来使用指针的方式给数组申请空间。例如:int a;如何给a赋予一定的空间呢?你可以这样做:a=(int)malloc(n*sizeof(int ));n是你要申请的数组的大小。
三:使用malloc申请空间后,在程序需结束时,一定要free掉。要不然大型的程序会崩掉!
四:就是free只能free一次,并且是free掉malloc的地址,其他的不行!

总结:

学到这里,你估计也就明白了,重要的是研究指针的和数组的关系,以及指针的运算,前提还需要你明白内存的意义,这也是我所领悟的,后期还会继续把字符串,链表都给总结一下。文章一定有许多不足之处,敬请指出,我会及时更正!!
我学习时参考的链接:

https://www.bilibili.com/video/av15267247?p=88

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章