日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

C语言之指针知识大总结

發布時間:2024/3/12 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C语言之指针知识大总结 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 一、地址
    • 二、指針與指針變量
    • 三、指針的作用
    • 四、初學指針時常見的錯誤
    • 五、通過調用函數修改主調函數中的值
    • 六、指針與一維數組
    • 七、使用函數操作一維數組
    • 八、指針變量所占字節數
    • 九、靜態數組的缺陷
    • 十、malloc函數
    • 十一、動態數組的構造
    • 十二、靜態內存與動態內存的對比
    • 十三、多級指針
    • 十四、跨函數使用內存


一、地址

內存中的最小單元是字節,一個字節對應一個編號,這里的編號就是對應字節的地址。換句話說,地址就是內存單元的編號。

二、指針與指針變量

指針與指針變量是兩個不同的概念,指針是某個普通變量的地址,所以可以理解為,指針就是地址,地址就是指針。指針變量是一種變量,它的作用是存儲其它變量的地址。

#include <stdio.h>int main() {int * p; // int *是指針類型,p是對應的變量,定義的指針變量p只能用來存儲int類型變量的地址int i = 3, j;p = &i; // 指針變量只能用來存儲對應類型變量的地址,所以這里需要對變量i進行取地址操作,即&i/*指針變量p保存了變量i的地址,這樣的話,我們稱:p指向i。通俗地說,通過p可以找到i雖然p指向i,但變量p和變量i不是同一個變量更準確地說,修改其中任何一個變量p值不會影響變量i,反之同理*/printf("%d %d\n", *p, i);/*如果一個指針變量指向某個普通變量,則在指針變量前加上*后就完全等同于指向的普通變量換句話說,可以通過指針變量前加*來找到那個指向的普通變量以本程序為例,這里的*p就是以p的內容為地址的變量*/j = *p; // *p就是i,所以相當于將i的值賦給了jprintf("i = %d, j = %d\n", i, j); // 輸出結果為i = 3, j = 3return 0; }

三、指針的作用

指針是C語言的靈魂

  • 通過指針可以表示一些復雜的數據結構,例如,鏈表、樹、圖等
  • 通過指針可以提高傳輸效率
  • 利用指針可以在被調函數中修改主調函數中的多個值
  • 利用指針可以直接訪問硬件
  • 通過指針可以更方便地處理字符串
  • 指針是理解面向對象語言中引用的基礎
  • 四、初學指針時常見的錯誤

    錯誤1:指針變量無明確指向

    #include <stdio.h>int main() {int * p;int i = 5;*p = i; // 出錯行printf("%d\n", *p);return 0; }

    錯誤原因:由于p未初始化,所以p的內容是垃圾值。*p是以p的內容為地址的變量,即以一個垃圾值為地址的變量,該變量是未知的,所以當把i的值賦值給p指向的未知變量時,有可能會篡改內存中其它變量的值,而這樣的操作是不允許的。

    錯誤2:賦值時變量類型不一致

    #include <stdio.h>int main() {int * p;int * q;int i = 5;p = &i;*q = p; // 出錯行printf("%d\n", *q);return 0; }

    錯誤原因:由于p是指針類型變量,而*q是int類型變量,所以不能相互復制。

    五、通過調用函數修改主調函數中的值

    • 思考1:在下述程序中,f函數中的變量i與main函數中的變量i是不是同一個變量?
      f函數中的變量i與main函數中的變量i都屬于局部變量,只在自己對應的函數中起作用,所以,f函數中的變量i與main函數中的變量i不是同一個變量。
    #include <stdio.h>void f(int i) {i = 99; }int main() {int i = 66printf("%d\n", i);f(i);printf("%d\n", i);return 0; }
    • 思考2:在上述程序中,能否通過調用f函數修改main函數中變量i的值?
      由于f函數中的變量i與main函數中的變量i不是同一個變量,所以把實參i傳遞給形參i只會改變f函數中變量i的值,當f函數執行完畢后,分配給形參i的空間會被釋放,故而無法改變main函數中變量i的值。換句話說,f函數中的變量i與main函數中的變量i本質上沒有任何關系,所以不管怎么修改f函數中變量i的值都不會影響main函數中變量i的值。

    那要如何才能通過其它函數來修改主調函數中的值?
    此時,指針就派上用場了,如下述程序:

    #include <stdio.h>void f(int * p) // 通過接收地址來確定要修改的變量 {*p = 99; // *p就是以p變量的內容為地址的變量,也就是要通過該函數修改的變量 }int main() {int i = 66printf("%d\n", i); // 輸出結果為66f(&i); // 由于函數f的形參是指針變量,故需將變量i的地址發送過去printf("%d\n", i); // 輸出結果為99return 0; }

    上述程序可以實現在被調函數中修改主調函數中變量的值是因為通過向被調函數傳遞了需要修改的變量的地址,從而確定并指向了需要修改的變量,但如果不傳入地址,就會導致主調函數中的變量無法與被調函數產生關聯,從而無法實現目的。

    • 活學活用:自定義一個swap函數,用該函數互換main函數中的兩個變量的值

    常見錯誤:只傳入數值,不傳入地址

    void swap(int a, int b) {int t;t = a;a = b;b = t; }int main() { int a = 3, b = 5;swap(a, b);printf("a = %d, b = %d\n", a, b);return 0; }

    出現上述錯誤的原因是,main函數中的變量a和b與swap函數中的形參a和b無關,導致的結果是,主函數將3和5發送給形參a和b后,swap函數只是對3和5進行了操作,而未能對main函數中的變量a和b進行操作,所以無法互換main函數中的變量a和b的值。

    正確實現方法:傳入地址,定位需要互換的變量

    void swap(int * p, int * q) {int t;t = *p;*p = *q;*q = t; }int main() { int a = 3, b = 5;swap(&a, &b);printf("a = %d, b = %d\n", a, b);return 0; }
    • 思考:如下方法是否可以實現互換功能?
    #include <stdio.h>void swap(int * p, int * q) {int * t;t = p;p = q;q = t; }int main() { int a = 3, b = 5;swap(&a, &b);printf("a = %d, b = %d\n", a, b);return 0; }

    答案是不行的,上述程序將變量a和b的地址發送給了指針變量p和q,此時,變量p和q中儲存的是變量a和b的地址,然而,swap函數中的操作是互換變量p和q的內容,也就是說,當swap函數執行完畢后,變量p中儲存的是變量b的地址變量q中儲存的是變量a的地址,言下之意,只是將變量p和q的內容互換了而已,并沒有對main函數中的變量a和b進行操作,所以無法實現互換功能,此外,幾乎所有的編程語言都無法通過互換兩個變量的地址實現互換變量中的值。

    六、指針與一維數組

    • 一維數組的數組名
      一維數組的數組名是一個指針常量,該常量是一維數組中第一個元素的地址。
    #include <stdio.h>int main() {int a[5];int b[5];a = b; // 錯誤,因為a和b都是常量,所以無法進行賦值操作a = &a[2]; // 錯誤,因為a是常量,無法對一個常量進行賦值操作return 0; } #include <stdio.h>int main() {int a[5];printf("%#X", &a[0]);printf("%#X", a); // 與上一行輸出結果相同,因為一維數組名就是數一維組中第一個元素的地址return 0; }
    • 引用一維數組中的元素
  • 通過下標引用:如a[i]表示第i+1個元素
  • 通過指針引用:如*(a+i)表示第i+1個元素
  • #include <stdio.h>int main() {int a[5];int i;for (i = 0; i < 5; i++) // 向一維數組中讀入元素scanf("%d", &a[i]); // 通過下標引用數組中的元素for (i = 0; i < 5; i++) // 輸出一維數組的內容printf("%d ", *(a + i)); // 通過指針引用數組中的元素return 0; }
    • 指針變量的運算
      指針變量不能相加,相乘以及相除,只能進行相減。如果兩個指針變量指向同一塊連續空間的不同存儲單元,則這兩個指針變量才可以進行相減運算。兩個指針變量相減得到的結果是兩個指針變量間相隔的元素個數。
    #include <stdio.h>int main() {int a[5];int * p = &a[1];int * q = &a[4];printf("%d\n", q - p); // 輸出結果為3,證明相隔3個元素return 0; }

    七、使用函數操作一維數組

    使用函數對一維數組進行操作,首先要將數組名傳遞給函數,因為一維數組名是函數第一個元素的地址,傳遞數組名就相當于傳遞起始位置,其次,普通數組不同于字符數組,它們沒有結束的標志,所以還需要向函數傳遞數組長度以確定數組何時結束。故想要在另外一個函數中對一維數組進行操作需要向該函數傳入兩個參數,數組名和數組長度。

    定義函數時的形參有兩種寫法,第一種是(int a[], int length),第二種是(int * a, int length)。可以寫第二種的原因是一位數組名本身就是指針常量,所以可以直接用指針變量來接收。

    • 定義一個函數,該函數的功能是對一維數組的內容進行輸出
    #include <stdio.h>// 自定義的print函數,其功能是將一維數組輸出 void print(int a[], int length) {int i;for (i = 0; i < length; i++)printf("%d ", a[i]); // 也可以寫成printf("%d ", *(a+i));printf("\n"); }int main() {int a[5] = { 1, 2, 3, 4, 5 };int b[6] = { -1, -2, -3, -4, -5, -6 };int c[100] = { 23, 88, 99, 44 };print(a, 5);print(b, 6);print(c, 100);return 0; }

    八、指針變量所占字節數

    • 預備知識:sizeof運算符的用法
    • sizeof(數據類型):其值為對應數據類型所占的字節數
      例如:sizeof(int)值為4;sizeof(double)值為8;sizeof(char)值為1
    • sizeof(變量):其值為對應變量所占的字節數
    #include <stdio.h>int main() {char c = 'A';int i = 99;double x = 66.66;char * p = &ch;int * q = &i;double r = &x;printf("%d %d %d\n", sizeof(c), sizeof(i), sizeof(x)); // 輸出結果為1 4 8printf("%d %d %d\n", sizeof(p), sizeof(q), sizeof(r)); // 輸出結果均為4return 0; }

    上述程序證明,盡管普通類型變量所占的空間大小不一致,但它們對應的指針變量都占四個字節

    九、靜態數組的缺陷

    • 數組長度必須事先指定,且只能是常整數,不能是變量
    • 數組的長度在長度不能在函數執行過程中動態的增減,數組一旦定義,其長度就無法改變
    • 程序員無法手動釋放靜態數組的內存,數組一旦定義,操作系統為該數組分配的存儲空間就會一直存在,直到該數組所在的函數執行完畢后,該數組的空間才會被操作系統釋放
    • 在某個函數內定義的靜態數組在該函數執行期間可以被其它函數使用,但當該函數執行完畢后,該函數中定義的數組就無法在其它函數中使用,這是因為該函數執行完畢后,靜態數組的內存就被會被釋放

    十、malloc函數

    malloc這個詞是由memory(內存)與allocate(分配)這兩個單詞合成的,顧名思義,malloc函數就是用來分配內存的函數。

    #include <stdio.h> #include <malloc.h>int main() {int i; // 靜態分配了4個字節的存儲空間int* p = (int*)malloc(4);/*1.要使用malloc函數,需添加malloc.h頭文件2.malloc函數只有一個形參,其類型為整型3.實參中的4表示請求操作系統為本程序分配4個字節的動態存儲空間4.malloc的返回值是分配給該系程序的第一個字節的地址5.由于第一個字節的地址不能確定具體的變量類型,所以需要強制類型轉換6.第7行代碼一共分配了8個字節的存儲空間,其中變量p占4個字節,p指向的空間也占是4個字節7.變量p所占的內存是靜態分配的,p所指向的內存是動態分配的*/free(p); /*free(p)表示釋放p所指向的內存free函數只能用來釋放動態內存,不能用來釋放靜態內存,靜態內存只能由操作系統自動釋放free(p)只是釋放了p對應的內存空間,但p的內容依舊存在*/return 0; }

    十一、動態數組的構造

    #include <stdio.h> #include <malloc.h>int main() {int a[10]; // 靜態構造一維數組int length;int* pArray;int i;scanf("%d", &length);pArray = (int*)malloc(sizeof(int) * length);/*動態構造一維數組該動態數組數組名為pArray,數組長度為length,數組中每個元素都是int類型*/// 對該動態一維數組手動賦值for (i = 0; i < length; i++)scanf("%d", &pArray[i]);// 輸出該動態一維數組的內容for (i = 0; i < length; i++)printf("%d ", *(pArray + i));printf("\n");free(pArray); // 釋放該動態數組return 0; }

    十二、靜態內存與動態內存的對比

    靜態內存是由操作系統自動分配,自動釋放的。靜態內存是在棧中分配的;動態內存是由程序員手動分配,手動釋放,但如果只分配不釋放就會導致內存泄露,也就是內存越用越少。動態內存是在堆中分配的。

    十三、多級指針

    #include <stdio.h>int main() {int i = 10;int * p = &i;int ** q = &p;int *** r = &q;r = &p; // 錯誤,因為r是int *** 類型,它只能用來存儲int ** 類型變量的地址printf("i = %d\n", ***r);return 0; }

    表解上述程序:

    變量名變量地址變量內容
    i1000H10
    p2000H1000H
    q3000H2000H
    r4000H3000h
    變量名對應變量
    *rq
    **rp
    ***ri
    #include <stdio.h>void g(int ** q) // 由于p的類型是int *,所以q的類型必須是int **,因為q要用來存放p的地址 {}void f() {int i;int * p;p = &i;g(&p); // 要通過g函數修改p的內容,則必須發送p變量的地址 }int main() {f();return 0; }

    十四、跨函數使用內存

    由于靜態內存是在棧中分配的,而函數執行完畢后,棧中的靜態內存就會全部出棧,而動態內存是在堆中分配的,當函數執行完畢后,堆中分配的內存并不會像棧中分配的內存一樣直接被釋放掉,所以
    靜態內存是不能跨函數使用的,而動態內存是可以的。

    • 思考:下述程序是否有語法錯誤?是否有邏輯錯誤?
    #include <stdio.h>void f(int** q) {int i = 5;*q = &i; // 由于q儲存了p的地址,所以*q就是p,這行代碼實質是將i的地址賦給了p }int main() {int* p;f(&p);printf("i = %d\n", *p); // 由于p儲存了i的地址,所以*p就是ireturn 0; }

    上述程序沒有語法錯誤,但是有邏輯上的錯誤,這是因為,當f函數執行完畢后,f函數中所有的靜態變量的內存都會被釋放掉,所以當執行到printf("i = %d\n", *p);時,p所指向的變量空間的訪問權限已經返還給了操作系統,這樣就會導致*p訪問了不屬于該程序的空間。這個程序說明了在一個函數內部定義的靜態變量在該函數中執行完畢后就不再可以垮函數使用。

    • 思考:對比上一程序,下述程序是否有語法錯誤?是否有邏輯錯誤?
    #include <stdio.h>void f(int** q) {*q = (int*)malloc(sizeof(int));**q = 5; }int main() {int* p;f(&p);printf("%d\n", *p);return 0; }

    上述程序是完全沒有語法錯誤的,因為當f函數執行完畢后,其中分配的動態內存不會自動釋放,所以在main函數中依然可以使用這段內存。這個程序體現出,在一個函數中分配的動態存儲空間在該函數執行完之后,仍然可以在另外一個函數中使用。

    • 趁熱打鐵:下列四個程序中,哪個程序能夠通過調用fun函數使main函數中的指針變量p指向一個合法的整型單元?
    #include <stdio.h>void fun(int * p) {int s;p = &s; }int main() {int * p;fun(p);return 0; } #include <stdio.h>void fun(int ** p) {int s;*p = &s; }int main() {int * p;fun(&p);return 0; } #include <stdio.h> #include <malloc.h>void fun(int * p) {p = (int *)malloc(sizeof(int)); }int main() {int * p;fun(p);return 0; } #include <stdio.h> #include <malloc.h>void fun(int ** p) {*p = (int *)malloc(4); }int main() {int * p;fun(&p);return 0; }

    總結

    以上是生活随笔為你收集整理的C语言之指针知识大总结的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。