這篇文章主要講解了“C語言的指針詳細介紹”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“C語言的指針詳細介紹”吧!
C語言最臭名昭著的是就是其指針的使用,也是C語言的精華所在。很多人在學習C語言時,吐槽指針特別難以學習和使用。今天我們來探討一下C語言指針的問題。
C語言一個特點就是一切皆內存,定義任何類型的變量,都要分配內存。在函數里定義變量,需要在棧內存上非配;定義全局變量,需要在靜態內存區分配內存,就連定義函數,也要在靜態區分配內存(這就是所謂的代碼段存儲區域);不固定大小的內存,需要在動態區分配內存。
有了內存,就需要操作內存,也就是讀取或者寫入內存數據。C語言的最簡單的內存操作就是通過賦值操作符(=
)實現。比如下面的代碼,賦值給變量a
,實際就是改變a
變量占用的內存的數據; 甚至是用戶自定義的結構體變量c
,也可以直接使用賦值運算符將b
占用的內存塊的數據拷貝到c
占用的內存塊,來完成內存的讀寫操作。之
int a; a = 6; strcut a_t { int m_a; char m_b; shor m_c; }; struct a_t b; a.m_a = 1; a.m_b = 2; a.m_c = 3; struct a_t c; //使用賦值運算符,將b里面所有成員的值賦給c里面的成員,即完成內存拷貝操作。 c = b;
所以可以這樣操作,是因為C語言的變量包含內存的地址(通過&
取)和內存大小(通過sizeof
取),知道這兩個信息,即使沒有賦值運算符,也可以使用memcpy
實現賦值的功能。比如下面的代碼,完全可以不使用賦值運算符,但實現了賦值的目的。
#include <stdint.h> static int g_test = 0; int main() { int a = 1; //這里我們使用memcpy操作內存,實現賦值的操作 memcpy(&g_test, &a, sizeof(a)); return 0; }
但是這里有一個問題,即使C語言的函數參數傳遞只有值傳遞,就是說所有的變量通過參數傳遞,都會產生一份拷貝,唯一的辦法就是傳遞變量的地址,這就產生的了用一個專門的類型來存儲變量的內存地址的需求,指針應運而生了,這樣做有下面的好處:
用一個專門的類型保存內存地址,比用現有類型(int
, unsigned int
)更方便,因為指針確定了內存地址的大小。
引入語法,為了方便操作內存:像上面的操作一個語句就可以解決了int *addr = &g_test; *addr = 1
;這個可能還不太明顯,但是到了結構體有很多成員變量,我們就要自己算每個成員的偏移量了,而指針可以直接找到每個成員的地址, 如指針的->
符號操作,也可以使用[]
進行內存的偏移操作。
C語言引入了指針,又衍生出個二級指針的概念,直接弄暈了一部分新生,其實二級指針只是語法的體現,本質上也是指針,是用來存儲內存地址的,這個本質沒有變化。比如:
int a = 0; //p1存儲a的地址 int *p1 = &a; //p2存儲p1的地址 int **p2 = &p;
C語言的所有變量都是內存,都可以取地址,指針變量也不例外。不管是幾級指針,都可以當做一級指針使用, 只要自己知道當前變量的意義然后處理好就可以了,比如下面的代碼:
int alloc_mem(char *p, int size) { char *tmp_p = (char *)malloc(size); //*p存儲的是`p1`或者`p2`變量的內存地址,用memcpy直接將分配的內存地址付給到p指向的內存 memcpy(p, &tmp_p, sizeof(char *)); return 0; } int alloc_mem1(char **p, int size) { *p = (char *)malloc(size); return 0; } int main() { char *p1 = NULL; char *p2 = NULL; //利用c語言的強轉 alloc_mem((char *)&p1, 2); //利用二級指針 alloc_mem1(&p2, 2); }
上面的alloc_mem
和alloc_mem1
效果是一樣的,只不過alloc_mem1
使用的二級指針的概念。
指針與數組有者本質的區別,指針用來存儲一塊內存的地址,嚴格的來說是一塊內存的起始地址,至于這塊內存有多大,是不知道的,必須使用者顯式的指定。比如memcpy
這個函數,原型如下:
void *memcpy(void *dst, const void *src, size_t n);
dst
指針指的是目標內存的起始地址,但是其指向的內存到底有多大,必須由參數n
來給出,負責函數無法知道是否溢出了。而數組本身就是一塊內存,包括數組的起始地址和大小,比如我們定義一個數組:
//該數組的起始地址為&arr,大小為sizeof(arr) int arr[32];
arr數組的地址為
&a,數組的大小為
sizeof(arr)。這里就產生一個問題,就是如何通過函數參數傳遞數組,C語言是不支持傳遞數組的,它會默認把數組轉換為數組的起始地址的指針。所以C語言函數傳遞數組,需要額外帶上數組的大小,以防止內存溢出的問題。實際上,只要傳遞指針參數給函數,都要帶上這個指針指向內存的大小。
大部分人都認為指針難學,其實不在指針本身,而是指針指向的內容——內存。C語言要保存數據,可以在棧上分配,這部分內存的作用域僅限于函數內部;可以在靜態區分配內存,這個區域的內存作用域是全局的,但僅限于確定大小的內存;可以在動態區分配內存,這部分的內存作用域是全局的,支持可變大小的內存。C語言程序存儲數據的大部分內存都是在動態區分配的。這就產生了內存管理問題,包括單不限于:
內存的分配
內存的釋放
內存大小的檢查
內存分配失敗的處理
邊界的處理:字符總是以0結尾
讀越界的處理:在有效內存之外讀數據
寫越界的處理:在有效內存之外寫數據
由于以上問題的存在,導致C語言不像java
,python
那樣使用起來方便,因為這些語言都有垃圾回收器,不用使用者處理關于內存的問題。這才是C語言難學的根本。
指針是存儲內存地址的數據類型,C語言可以根據內存地址操作內存。
指針和數組不相同,指針存儲的是內存的起始地址,不包括內存的大小;數組既有起始地址,也有大小。
通過函數傳遞指針,除了指針參數本身,還要帶上指針指向內存的大小。
指針難學并非指針本身,而是內存管理難處理
感謝各位的閱讀,以上就是“C語言的指針詳細介紹”的內容了,經過本文的學習后,相信大家對C語言的指針詳細介紹這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。