C語言中的初階指針詳解

1.指針是什麼

​ 初學者都有一個疑問,那就是指針是什麼?簡單的說,就是通過它能找到以它為地址的內存單元。

地址指向瞭一個確定的內存空間,所以地址形象的被稱為指針。

int main()
{
	int a = 10;
	int* pa = &a;
    return 0;
}
//pa是用來存放地址(指針),所以pa是指針變量。

總結:指針就是變量,用來存放地址的變量。(存放在指針中的值都被當成地址處理)。

​ 地址是唯一標識一塊空間的。

​ 指針的大小在32位平臺是4個字節,在64位平臺是8個字節。

2.指針和指針類型

​ 我們知道變量有不同的類型(整型、浮點型、字符型等),其實指針也是有不同類型的。

​ 指針類型的意義1:
指針類型決定瞭指針解引用操作的時候,一次訪問幾個字節(訪問內存的大小)
char* 指針解引用訪問1個字節
int* 指針解引用訪問四個字節

int main()
{
	char* pc = &a;
	*pc = 0;
	return 0;
}

​ 指針類型的意義2:

指針類型決定瞭,指針±整數的時候的步長(指針±整數的時候,跳過幾個字節)

int* 指針+1 跳過四個字節

char* 指針+1 跳過一個字節

int main()
{
	int a = 10;
	int* pa = &a;
	char* pc = &a;
	printf("%p\n", pa);
	printf("%p\n", pc);
	printf("%p\n", pa+1);
	printf("%p\n", pc+1);
	return 0;
}

3.野指針

​ 野指針就是指針指向的位置是不可知的(隨機的、不正確的、沒有明確限制的)。

3.1野指針成因

1.指針未初始化

int main()
{
    int* p;//局部變量指針未初始化,默認為隨機值
    *p = 20;//通過p中存的隨機值作為地址,找到一個空間,這個空間不屬於我們當前的程序
    //就造成瞭非法訪問,p就是野指針
    return 0;
}

2.指針越界訪問

int main()
{
    int arr[10] = 0;
    int i = 0;
    int* p = arr;
    for(i =0; i <= 10; i++)
    {
        *p = i;
        p++;//當指針指向的范圍超出數組arr的范圍時,p就是野指針
    }
    return 0;
}

3.指針指向的空間釋放

int* test()
{
    int a = 10;
    return &a;
}
int main()
{
    int* p = test();
    printf("%d\n",*p);
    return 0;
}

3.2如何規避野指針

1.指針初始化

2.小心指針越界

3.指針指向空間釋放即使置NULL

4.避免返回局部變量的地址

5.指針使用之前檢查有效性

int main()
{
    int a = 10;
    int* p = &a;//明確地初始化,確定指向
    int* p2 = NULL;//不知道一個指針當前應該指向哪裡時,可以初始化為NULL
    return 0;
}

4.指針的運算

4.1指針±整數

#define N_VALUES 5
float values[N_VALUES];
float* vp;
for(vp = &values[0]; vp < &values[N_VALUES];)
{
    *vp++ = 0;
}
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    int* p = &arr[9];
    printf("%p\n",p);
    printf("%p\n",p-2);
    return 0;
}

4.2指針-指針

​ 指針-指針 得到的數字的絕對值是指針和指針之間元素的個數

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("%d\n", &arr[9] - &arr[0]);
	printf("%d\n", &arr[0] - &arr[9]);
	return 0;
}

指針-指針 的前提是兩個指針指向同一塊區域

int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    char ch[5] = {0};
    printf("%d\n",&arr[9] - &ch[0]);//err
    return 0;
}

應用 求字符串長度

int my_strlen(char* s)
{
	int count = 0;
	char* start = s;
	while(*s!='\0')
	{
		s++;
	}
	return s - start;
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

4.3指針的關系運算

#define N_VALUES 5
float values[N_VALUES];
float *vp;
for(vp = &values[N_VALUES]; vp > &values[0];)
{
    *--vp = 0;
}

上述程序也可以寫成這樣

for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
    *vp = 0;
}

實際在絕大部分的編譯器上是可以順利完成任務的,然而我們還是應該避免這麼寫,因為標準並不保證它可行。

標準規定

​ 允許指向數組元素的指針與指向數組最後一個元素後面的那個內存位置的指針比較,但是不允許與指向第一個元素之前的那個內存位置的指針進行比較。

5.指針和數組

數組 – 是一塊連續的空間,放的是相同類型的元素

數組大小和元素類型,元素個數有關系

指針(變量) – 是一個變量,放地址

指針變量的大小 是4(32bit)/8(64bit)個byte

數組名確實是首元素地址

但是有兩個例外:

1.sizeof(數組名) – 這裡的數組名不是首元素的地址,是表示整個數組的,這裡計算的是整個數組的大小,單位還是字節。

2.&數組名 – 這裡的數組名不是首元素的地址,是表示整個數組的,拿到的是整個數組的地址

int main()
{
	int arr[10] = { 0 };
	int sz = sizeof(arr);
	printf("%d\n", sz);
	return 0;
}
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0;i < sz;i++)
	{
		*(p + i) = i;
	}
	for (i = 0;i < sz;i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

6.二級指針

​ 我們都知道,指針變量是變量,是變量就有地址,那麼指針變量的地址存放在哪裡呢?

這就是我們要瞭解的二級指針。

int main()
{
    int a = 10;
    int* p = &a;
    int** pp = &p;//pp就是二級指針
    **pp = 20;
    printf("%d\n", a);//a = 20
    return 0;
}

7.指針數組

​ 從名字上來看,大傢覺得指針數組是指針還是數組?

答案是數組,是存放指針的數組。

整型數組 – 存放整型的數組就是整型數組

字符數組 – 存放字符的數組就是字符數組

指針數組 – 存放指針的數組就是指針數組

int* 整型指針的數組

char* 字符指針的數組

int main()
{
	int arr[10];
	char ch[5];
	int* parr[5];
	char* pc[6];
	return 0;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int* parr[3] = { &a,&b,&c };
	for (int i = 0;i < 3;i++)
	{
		printf("%d\n", *(parr[i]));
	}
	return 0;
}

​ 總結

以上就是我們初始C語言指針的全部內容瞭,後續我還會更新C語言指針的進階版本,希望大傢能夠對C語言的指針能夠有更深層次的瞭解。

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!

推薦閱讀: