C語言中「指標」作為「函式引數」時的注意事項

2021-07-06 03:01:33 字數 3029 閱讀 7735

在c語言中,指標是最難掌握的知識點之一,不過在平常的教材中都會比較詳細地講解指標,如果大家想深入學習c中指標的詳細知識,推薦大家可以看一下《c和指標》,也可以參考《c專家程式設計》或者《c陷阱與缺陷》,這些書對c語言的講解是十分深入的,比基本的教材講的深入一些,可以看一下。

言歸正傳,本編文章的主要目的是和大家分享乙個我在做專案時遇到的乙個問題,也就是當指標作為函式引數,並且想通過這個函式為這個指標分配記憶體的時候出現的乙個問題,在初學者看來,這個可能沒什麼問題,也很自然地在使用,但是這個問題曾經導致我做的專案出現了很大的bug,最終經過一些除錯手段才找到這個問題,下面通過**和敘述進行詳細地講解。

我們經常會遇到這樣的函式,比如說:為乙個指標分配記憶體並初始化其記憶體中的變數,這個指標一般都是乙個較為完善的結構體指標或者其他複雜的指標,這個函式一般命名為:mallocandinit(); 具體的形式下面會給出來,那麼下面我們先來看一下這個函式的第乙個版本,大家可以先看看「版本1」有沒有錯誤。

// 版本1

#include #include #include #define len 20

typedef struct person

person;

// 返回0表示失敗,返回1表示成功

int mallocandinit(person *pps)

// 初始化

pps->age = 0;

strcpy(pps->name, "");

return 1;

}void main()

// 這裡會出錯,你看出來為什麼了嗎?

printf("the person's name is %s, age is %d.\n", ptest->name, ptest->age);

if (ptest != null)

}

「版本1」中定義了乙個person結構體,其中有幾點需要說明一下:

1. 陣列的長度一般習慣採用巨集定義,方便後續專案較大時的修改;

2. 程式中盡量有一些錯誤控制,即考慮完善一些,例如上面的記憶體分配是否成功的判斷以及函式呼叫後是否達到預期目的的判斷;

3. 使用typedef簡化定義;

4. 使用null == pps,而不是pps == null是一種良好的程式設計習慣,這在有些面試中也會被問到,之所以這樣是因為當將「==」誤寫成「=」時,前者直接編譯出錯,後者則可以執行,這種隱蔽的錯誤很可能導致整個專案出現混亂,這種bug有時候也很難被發現,所以這個習慣很重要,一句話就是「判斷時,等號左邊寫常量,等號右邊寫變數,防止少寫乙個等號帶來的嚴重bug」;

5. 最後一定記得使用完指標要釋放記憶體,否則會出現記憶體洩露等問題。

回到我們的主題,「版本1」中出現了乙個嚴重的錯誤,並且很難被發現,編譯時不會報錯,但是執行時就會出現錯誤,出現這個情況建議大家可以單步除錯看看這個指標到底有沒有被分配到記憶體。

為什麼會出錯呢?我們來分析一下,我們要搞清楚乙個問題:指標變數作為函式引數傳遞的時候,當你需要修改指標變數本身的位址值的時候,相當於我們常說的「值傳遞」,大家都知道,值傳遞是不會改變實參的值的,常見的例子就是用乙個函式交換兩個變數的值,如果大家對這一點還有疑問,建議大家可以檢視基本的教材中函式形參和實參相關的章節,會有詳細的介紹。說回來,對於需要修改指標的位址值的函式實參,我們相當於是「值傳遞」,所以呢,我們呼叫mallocandinit()函式後實際上main函式中的指標pps還是未分配記憶體的指標,這個時候我們呼叫printf函式去訪問這個指標的內容肯定是錯誤的,因為這相當於訪問了乙個未知的記憶體,這是一種很危險的操作。要引起重視。

總結來說就是一句話,當你需要通過函式修改函式引數中指標引數的位址值時,這就相當於是指標的值傳遞操作,和普通的變數值傳遞是一樣的結果,而當你只是通過函式訪問或修改函式引數中指標引數的內容(例如修改person中的age的值)時,這就相當於位址傳遞,實參結果可以被修改並傳遞出函式。

下面給出兩種解決方案:

1. 在函式內部對指標分配記憶體後 ,通過返回該指標來獲得分配後的位址,見「版本2」;

2. 給函式傳遞二級指標來修改一級指標的位址值,這樣會比較麻煩,見「版本3」。

// 版本2

#include #include #include #define len 20

typedef struct person

person;

// 返回null表示失敗,返回位址表示成功

person* mallocandinit(person *pps)

// 初始化

pps->age = 0;

strcpy(pps->name, "");

return pps; // 注意這裡的變化!!!

}void main()

// 這裡不會再出錯

printf("the person's name is %s, age is %d.\n", ptest->name, ptest->age);

// 釋放記憶體並還原

if (ptest != null)

}

// 版本3

#include #include #include #define len 20

typedef struct person

person;

// 返回0表示失敗,返回1表示成功

int mallocandinit(person **ppps)

// 初始化

(*ppps)->age = 0;

strcpy((*ppps)->name, "");

return 1;

}void main()

// 這裡不會再出錯

printf("the person's name is %s, age is %d.\n", ptest->name, ptest->age);

// 釋放記憶體並還原

if (ptest != null)

}

總之,c語言中的指標確實比較令人頭疼,經常會因為指標出現各種各樣的問題,所以大家要時刻保持警惕,遇到指標要高度細心。    

指標作為函式引數,C語言指標作為函式引數詳解

include void swap int a,int b 函式宣告 intmain void void swap int a,int b 大家想一下,執行這個程式是否能互換 i 和 j 的值?不能!i 還是3,j 還是5。因為實參和形參之間的傳遞是單向的,只能由實參向形參傳遞。被調函式呼叫完之後系...

c 中指標作為函式引數的詳細理解

在c語言中,函式的引數不僅可以是整數 小數 字元等具體的資料,還可以是指向它們的指標。用指標變數作函式引數可以將函式外部的位址傳遞到函式內部,使得在函式內部可以操作函式外部的資料,並且這些資料不會隨著函式的結束而被銷毀。像陣列 字串 動態分配的記憶體等都是一系列資料的集合,沒有辦法通過乙個引數全部傳...

c語言指標作為函式的引數

在c語言中實參和形參之間的資料傳輸是單向的 值傳遞 方式,也就是實參可以影響形參,而形參不能影響實參。指標變數作為引數也不例外,但是可以改變實參指標變數所指向的變數的值。include void swap1 int x,int y swap2 int px,int py swap3 int px,i...