C語言指針之必須要掌握的指針基礎(chǔ)知識
一、指針概述
指針是個變量,存放內(nèi)存單元的地址(編號)。
指針的創(chuàng)建
在定義指針變量的時候,在變量前面加上' * ',代表這個變量是一個指針,再往前面加上一個類型名,就代表指針的類型,稱為XX指針。
指針的初始化:
使用&(取地址操作符)可以獲得變量的地址,將其賦值給已經(jīng)定義好的指針變量,需要它們的類型相同,類型不同的時候可以使用強(qiáng)制轉(zhuǎn)換。如果暫時不知道需要存放什么地址的時候,可以先讓它指向NULL((void*)0),NULL本質(zhì)就是0,0這個地址是不允許存儲的。
#include <stdio.h>
int main()
{
int a = 10;//在內(nèi)存中開辟一塊空間
int *p = &a;//這里我們對變量a,取出它的地址,可以使用&操作符。
//將a的地址存放在p變量中,p就是一個指針變量。
return 0;
}
指針的大小
指針可以是不同的類型,char*、int*、float*、double*、long*等我們熟知的類型,那指針在內(nèi)存中占多大的空間呢?
對于32位的機(jī)器,假設(shè)有32根地址線,那么假設(shè)每根地址線在尋址的是產(chǎn)生一個電信號正電/負(fù)電(1或者0)
那么32根地址線產(chǎn)生的地址就會是:
00000000 00000000 00000000 00000000
……
111111111 111111111 111111111 111111111
共有232個地址,每一個地址能指向一個內(nèi)存單元(一字節(jié)),那么有232個內(nèi)存單元。
我們知道一個內(nèi)存單元的大小為一字節(jié),一根地址線是32位01組成的,那么要存放指針(地址),也就需要32個bit,也即4字節(jié)。所以在32位平臺下不論是什么類型的指針都是4個字節(jié)。
同理,在64位平臺下有64根地址線,每個地址是64位,所以需要8個字節(jié)。
如何使用指針
使用' * '操作符對指針進(jìn)行解引用,可以獲取到指針指向空間的值
我們來運(yùn)行下方代碼。
#include <stdio.h>
int main()
{
int n = 0x11223344;
char* pc = (char*)&n;//將n的地址(指針)強(qiáng)制轉(zhuǎn)換成char* 類型
int* pi = &n;
*pc = 0;//改變它指向空間的值
*pi = 0;
return 0;
}
我們開始調(diào)試代碼,查看n的內(nèi)存,最左邊是地址,右邊是以16進(jìn)制顯示內(nèi)存里存放的數(shù)據(jù),而n也是以16進(jìn)制存進(jìn)去的,兩個16進(jìn)制代表一個字節(jié),如下圖:

當(dāng)執(zhí)行完語句 *pc=0;

因為pc指向的是char* 類型,只能訪問一個字節(jié),所以只能修改一個字節(jié)。
再執(zhí)行*pi = 0;

此時n的內(nèi)存數(shù)據(jù)全部變成了0,說明int*類型的指針可以訪問4個字節(jié)。
小結(jié):
指針的類型決定了,對指針解引用的時候有多大的權(quán)限(能操作幾個節(jié))。 比如: char* 的指針解引用就只能訪問一個字節(jié),而 int* 的指針的解引用就能訪問四個字節(jié)。
二級指針
指針變量也是變量,是變量就有地址,那指針變量的地址存放在哪里? 這就是 二級指針 。
即存放指針變量的地址的指針,二級指針指向的空間的值是一個一級指針。
int main()
{
int a = 20;
int* p = &a;
int** pp = &p;
printf("%d\n", *p);//解引用即可拿到指針指向空間里面的值
printf("%d\n", *p); //對二級指針,需要兩次解引用才能拿到最開始的值
return 0;
}


二、野指針
野指針就是指針指向的位置是不可知的(隨機(jī)的、不正確的、沒有明確限制的)。
形成野指針的原因
1.指針未初始化
#include <stdio.h>
int main()
{
int *p;//局部變量指針未初始化,默認(rèn)為隨機(jī)值
*p = 20;//往一個隨機(jī)的地址(不屬于本程序的空間)里面放入數(shù)據(jù),是不允許的
return 0;
}
2.指針越界訪問
指針想要訪問不屬于本程序的空間,造成越界訪問,程序出錯,在使用數(shù)組的時候最容易發(fā)生。
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = arr;
int i = 0;
for(i=0; i<=11; i++)
{
//當(dāng)指針指向的范圍超出數(shù)組arr的范圍時,p就是野指針
*(p++) = i;
}
return 0;
}
3.指針指向的空間釋放
當(dāng)動態(tài)開辟的內(nèi)存被釋放后,此時的指針就屬于野指針,它指向的位置它不能正常訪問。關(guān)于動態(tài)內(nèi)存分配,后續(xù)會細(xì)講。
int main()
{
//開辟一個整形空間
int* p = (int*)malloc(sizeof(int));
//釋放該空間
free(p);
//此時p即為野指針,因為它指向的空間已經(jīng)無法訪問,一般需要將其置空
//將其指向空指針,防止后續(xù)調(diào)用出錯
p = NULL;
return 0;
}
如何規(guī)避野指針
- 指針初始化
- 小心指針越界
- 指針指向空間釋放即使置NULL
- 指針使用之前檢查有效性
三、指針的基本運(yùn)算
關(guān)于指針的運(yùn)算一般有兩個,指針+指針,語義是合法,但沒有意義。
- 指針± 整數(shù)
- 指針-指針
指針± 整數(shù)
對int和char類型的指針分別進(jìn)行+1操作,%p以16進(jìn)制打印
int main()
{
int n = 10;
char* pc = (char*)&n;//將n的地址(指針)強(qiáng)制轉(zhuǎn)換成char* 類型
int* pi = &n;
printf("&n: %p\n", &n);
printf("pc: %p\n", pc);
printf("pc + 1:%p\n", pc + 1);
printf("pi: %p\n", pi);
printf("pi + 1:%p\n", pi + 1);
return 0;
}

小結(jié):
指針的類型決定了指針向前或者向后走一步有多大(距離)。
指針-指針
int main()
{
int n = 10;
char* pc = (char*)&n;//將n的地址(指針)強(qiáng)制轉(zhuǎn)換成char* 類型
int* pi = &n;
printf("&n: %p\n", &n);
printf("pc: %p\n", pc);
printf("pc + 1:%p\n", pc + 1);
printf("pi: %p\n", pi);
printf("pi + 1:%p\n", pi + 1);
return 0;
}

小結(jié):
指針-指針等于它們之間相差的類型數(shù)據(jù)的個數(shù)。即本例的第10個元素和第1個元素之間相差9。
四、指針和數(shù)組
一起來看一下數(shù)組名是什么
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
printf("%p\n", arr);
printf("%p\n", &arr[0]);
return 0;
}

可見數(shù)組名和數(shù)組首元素的地址是一樣的。
結(jié)論: 數(shù)組名表示的是數(shù)組首元素的地址。也是一個指針
那么我們就可以用一個指針來代替數(shù)組名,如下代碼:
int main()
{
int arr[10] = { 0 };
int* p = arr;
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);//計算數(shù)組元素個數(shù)
//對數(shù)組賦值
for (i = 0; i < sz; i++)
{
*(p + i) = i;
}
//打印數(shù)組數(shù)據(jù)
for (i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}

注意:
數(shù)組名在下面兩種情況下不是首元素的地址
1.sizeof(數(shù)組名) - 這里的數(shù)組名不是首元素的地址,是表示整個數(shù)組的,這里計算的是整個數(shù)組的大小,單位還是字節(jié)
2.&數(shù)組名 - 這里的數(shù)組名不是首元素的地址,是表示整個數(shù)組的,拿到的是整個數(shù)組的地址
int main()
{
int arr[10] = { 0 };
int sz = sizeof(arr);
printf("sizeof(arr)計算的是整個數(shù)組的大?。?d\n", sz);
printf("數(shù)組首地址: %p\n", arr);
printf("數(shù)組首元素地址: %p\n", &arr[0]);
printf("數(shù)組的地址: %p\n", &arr);
printf("數(shù)組首地址+1: %p\n", arr + 1);
printf("數(shù)組首元素地址+1:%p\n", &arr[0] + 1);
printf("數(shù)組的地址+1: %p\n", &arr + 1);
//數(shù)組名確實是首元素的地址
//但是有2個例外:
//1. sizeof(數(shù)組名) - 這里的數(shù)組名不是首元素的地址,是表示整個數(shù)組的,這里計算的是整個數(shù)組的大小,單位還是字節(jié)
//2. &數(shù)組名 - 這里的數(shù)組名不是首元素的地址,是表示整個數(shù)組的,拿到的是整個數(shù)組的地址
//
return 0;
}

五、指針數(shù)組
存放指針的數(shù)組。
int main()
{
int a = 1;
int b = 2;
int c = 3;
int* arr[10] = { &a,&b,&c };
for (int i = 0; i < 3 ; i++)
{
printf("%d ", *(arr[i]));
}
return 0;
}

總結(jié)
指針是C語言非常重要的一部分,內(nèi)容繁多不易懂,本文僅介紹了一些基本知識,后續(xù)還會深入了解指針。
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Visual?Studio?2022使用MinGW來編譯調(diào)試C/C++程序的圖文教程
這篇文章主要介紹了Visual?Studio?2022使用MinGW來編譯調(diào)試C/C++程序,以實例來簡單介紹一下VS2022中如何使用MinGW來編譯、調(diào)試C/C++程序,需要的朋友可以參考下2022-08-08
C語言中的函數(shù)指針基礎(chǔ)學(xué)習(xí)教程
這篇文章主要介紹了C語言中的函數(shù)指針基礎(chǔ)學(xué)習(xí)教程,包括函數(shù)指針作為參數(shù)來傳遞等重要知識,需要的朋友可以參考下2016-04-04

