溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C語言的指針詳細介紹

發布時間:2021-08-11 19:01:19 來源:億速云 閱讀:225 作者:chen 欄目:大數據

這篇文章主要講解了“C語言的指針詳細介紹”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“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_memalloc_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語言的指針詳細介紹這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女