指針學習

前言

下面是我自學的筆記以及源代碼,系統的學習完,其實發現 指針其實也並沒那麼可怕! 還有一些比較繞的公式其實沒有必要去死記硬背!仔細一想,我們在學習指針之前學習過它,還不止一次,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

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