目錄
什么是指針?
與指針相關的運算符
指針類型的意義
?指針的大小
初始化
將指針直接指向一個已經存在的變量或內存地址:
使用malloc函數動態分配內存,并將指針指向新分配的內存:
使用calloc函數動態分配內存,并將指針指向新分配的內存,同時將內存初始化為0:
使用靜態初始化:
賦值
直接賦值操作符(=):
使用malloc函數分配內存并賦值給指針:
使用calloc函數分配內存并賦值給指針:
解引用
指針和整數相加
指針和整數相減
遞增指針
遞減指針
指針求差
指針關系運算
空指針
const與指針
指向常量的指針:
常量指針:
指向常量的常量指針:
二級指針
什么是指針?
C語言中的指針是一個變量,用于存儲另一個變量的內存地址。
通過指針,可以間接訪問和修改變量的值。
我們口語中的指針是指的是指針變量,指針變量的值是個地址
指針變量的聲明需要指定所指向的變量類型,例如int、char等。
指針變量的聲明格式為:
指針變量所指向的變量類型 *指針變量名;
*和指針名之間的空格可有可無
與指針相關的運算符
通過取址操作符&,可以獲得變量的內存地址。
例如,對于一個整型變量x,可以通過&x表示其內存地址。
解引用操作符*
*指針名代表該指針指向的地址
比如 int*a=&b;*a就代表變量b的地址
指針類型的意義
1,指針類型決定了指針在被解引用時訪問幾個字節;
比如
int*的指針被解引用時只能訪問一個int類型的大小(即4個字節)
char*的指針被解引用時只能訪問一個char類型大小(即1個字節)
2.指針類型決定了指針加減操作時跳過幾個字節
比如
char*a,則a+1時跳過1個字節
int*a,則a+1時跳過4個字節
我們可以看個例子
int n[2] = { 1,2 };
int* w = n;
printf("%d %d", *w, *(w + 1));
運行結果
1 2
?指針的大小
在C語言中,不同類型的指針在內存中占用的大小是相同的,即指針的大小不依賴于指向的數據類型。
無論是指向char、int、float或其他類型的指針,在32位系統上占用4個字節(32位,即x86),在64位系統上占用8個字節(64位,即x64)。
printf("%zd\n", sizeof(int*));printf("%zd\n", sizeof(char*));printf("%zd\n", sizeof(float*));
在64位上是
8
8
8
這是因為指針的作用是存儲另一個變量的內存地址,而內存地址的大小是由系統的位數決定的。
指針存儲的是一個地址值,不論指針指向的數據類型的大小如何,指針本身的大小都是固定的。
指針大小的固定性使得在C語言中可以實現通用的指針操作和類型轉換。
需要注意的是,指針變量本身的大小是固定的,但指針所指向的內存區域的大小取決于指針指向的數據類型。對于一個指向int類型的指針,它所指向的內存區域大小是sizeof(int)。指針的大小只是存儲指針所需的內存空間,而不是指針所指向的數據的大小。
?
初始化
在C語言中,指針可以通過以下方式進行初始化:
-
將指針直接指向一個已經存在的變量或內存地址:
int num = 10; int *ptr = # // 將指針ptr指向變量num的地址
-
使用malloc函數動態分配內存,并將指針指向新分配的內存:
int *ptr = (int*)malloc(sizeof(int)); // 分配一個整型變量所需的內存
-
使用calloc函數動態分配內存,并將指針指向新分配的內存,同時將內存初始化為0:
int *ptr = (int*)calloc(1, sizeof(int)); // 分配一個整型變量所需的內存,并將內存初始化為0
-
使用靜態初始化:
int *ptr = NULL; // 將指針初始化為空指針
需要注意的是,在使用指針之前,應該確保指針已經初始化,并且指向了有效的內存地址。
否則就將指針指向NULL
賦值
在C語言中,可以使用以下方式將一個指針賦值給另一個指針:
-
直接賦值操作符(=):
int *ptr1; int *ptr2; int num = 10;ptr1 = # // 將ptr1指向num的地址 ptr2 = ptr1; // 將ptr2賦值為ptr1(即ptr2也指向num的地址)
-
使用malloc函數分配內存并賦值給指針:
int *ptr1; int *ptr2;ptr1 = (int*)malloc(sizeof(int)); // 分配內存并將其地址賦值給ptr1 *ptr1 = 10; // 給ptr1指向的內存賦值ptr2 = ptr1; // 將ptr2賦值為ptr1(兩者指向同一塊內存)
-
使用calloc函數分配內存并賦值給指針:
int *ptr1; int *ptr2;ptr1 = (int*)calloc(1, sizeof(int)); // 分配內存并將其地址賦值給ptr1(內存初始化為0) *ptr1 = 10; // 給ptr1指向的內存賦值ptr2 = ptr1; // 將ptr2賦值為ptr1(兩者指向同一塊內存)
需要注意的是,在進行指針賦值操作時,應確保指針指向的內存是有效的,并且在使用指針之前進行了初始化。同時,指針賦值只是將指針指向的內存地址進行了復制,而不是復制內存中的內容。
解引用
在C語言中,指針解引用是指通過指針獲取指針所指向的變量的值。
可以使用*
運算符來對指針進行解引用操作。
下面是使用指針解引用的示例:
int num = 10; // 定義一個整型變量 num
int *ptr = # // 定義一個指向 num 的指針 ptrprintf("num 的值為:%d\n", num); // 直接輸出 num 的值
printf("ptr 所指向的變量的值為:%d\n", *ptr); // 通過指針解引用輸出 num 的值
在上面的示例中,通過*ptr
可以獲取指針ptr
所指向的變量num
的值。在將指針解引用后,可以像操作普通變量一樣使用解引用后的值。
需要注意的是,在對指針進行解引用之前,應確保指針指向的內存是有效的。否則,解引用無效的指針可能會導致程序崩潰或未定義的行為。
指針和整數相加
在C語言中,指針和整數是可以進行相加的。
當一個指針和一個整數相加時,指針的地址值會根據整數的值進行偏移。偏移的單位是指針所指向類型的字節大小。
例如,如果一個指針指向一個整型變量,該整型變量占用4個字節,則相加操作會根據整數值乘以4來改變指針的地址值。
下面是一個示例:
int arr[] = {1, 2, 3, 4, 5}; // 定義一個整型數組
int *ptr = arr; // 定義一個指向數組的指針,初始指向數組的第一個元素// 將指針向前移動兩個元素
ptr = ptr + 2;printf("ptr 所指向的值為:%d\n", *ptr); // 輸出 ptr 所指向的值(即數組的第三個元素)
ptr+2和&arr[2]等價
在上面的示例中,ptr
指向整型數組arr
的第一個元素。通過將ptr
與整數2相加,可以使指針向前移動兩個元素的偏移量,即指向數組的第三個元素。通過對指針進行解引用,可以獲取到新的位置的值。
需要注意的是,指針與整數相加時,結果的類型仍然是指針類型。根據指針所指向的數據類型的大小,相加的整數會根據數據類型的大小進行調整。
指針和整數相減
在C語言中,指針減去一個整數的操作被稱為指針的算術運算。其規則如下:
當一個指針減去一個整數時,整數的值將乘以指針指向的數據類型的大小,然后將所得結果從指針的值中減去。最終的結果是一個新的指針,指向原來指針所指向的位置減去整數的大小。
例如,假設有一個指針p指向一個int類型的數據,它的地址是0x1000,那么p - 2的結果將是一個新的指針,指向地址為0x1000 - 2 * sizeof(i的位置。nt)
看個例子
int arr[] = { 1, 2, 3, 4, 5 }; // 定義一個整型數組int* ptr = &arr[2]; // 定義一個指向數組的指針,初始指向數組的第一個元素// 將指針向前移動兩個元素ptr = ptr -2;printf("ptr 所指向的值為:%d\n", *ptr);
這里ptr-2相當于&arr[0]
運行結果是?
ptr 所指向的值為:1
同樣需要注意,進行指針的算術運算時,必須確保指針指向的內存是有效的,否則結果是未定義的。此外,指針的算術運算還要遵守指針的合法性和邊界條件,以避免訪問非法內存。
遞增指針
在C語言中,可以使用遞增操作符(++)來遞增指針。
遞增指針會使指針指向下一個元素或下一個位置。
示例代碼如下所示:
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // 指向數組的第一個元素ptr++; // 遞增指針,指向數組的第二個元素printf("%d\n", *ptr); // 輸出:2
在上述示例中,開始時,指針ptr指向數組的第一個元素。通過遞增操作符(++),指針ptr的值增加了一個元素的大小(假設int類型占4個字節),指針現在指向了數組的第二個元素。
需要注意的是,遞增操作符(++)不僅可以遞增指針,還可以遞增指向數組、字符串或者任何連續內存塊的指針。另外,遞增操作也可以應用于指針變量本身,而不僅僅是指向的內容。
示例代碼如下所示:
int x = 10;
int *ptr = &x; // 指向整型變量xptr++; // 遞增指針,指向下一個整型變量的地址printf("%d\n", *ptr); // 輸出:未定義,因為ptr指向了未經初始化的內存
在上述示例中,指針ptr開始時指向整型變量x。通過遞增操作符(++),指針ptr的值增加了一個整型變量的大小(假設int類型占4個字節),指針現在指向了未經初始化的內存。因此,對指針解引用會產生未定義的行為。
遞減指針
在C語言中,可以使用遞減操作符(--)來遞減指針。遞減指針會使指針指向上一個元素或上一個位置。
示例代碼如下所示:
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = &arr[4]; // 指向數組的最后一個元素ptr--; // 遞減指針,指向數組的倒數第二個元素printf("%d\n", *ptr); // 輸出:4
在上述示例中,開始時,指針ptr指向數組的最后一個元素。通過遞減操作符(--),指針ptr的值減少了一個元素的大小(假設int類型占4個字節),指針現在指向了數組的倒數第二個元素。
需要注意的是,遞減操作符(--)不僅可以遞減指針,還可以遞減指向數組、字符串或者任何連續內存塊的指針。另外,遞減操作也可以應用于指針變量本身,而不僅僅是指向的內容。
示例代碼如下所示:
int x = 10;
int *ptr = &x; // 指向整型變量xptr--; // 遞減指針,指向上一個整型變量的地址printf("%d\n", *ptr); // 輸出:未定義,因為ptr指向了未經初始化的內存
在上述示例中,指針ptr開始時指向整型變量x。通過遞減操作符(--),指針ptr的值減少了一個整型變量的大小(假設int類型占4個字節),指針現在指向了未經初始化的內存。因此,對指針解引用會產生未定義的行為。
指針求差
在C語言中,可以使用減法操作符(-)來計算兩個指針之間的差值。這個差值表示兩個指針之間相隔的元素個數,而不是它們之間的字節數。
具體用法如下:
int arr[5] = {1, 2, 3, 4, 5};
int *ptr1 = &arr[1]; // 指向數組的第二個元素
int *ptr2 = &arr[4]; // 指向數組的最后一個元素int diff = ptr2 - ptr1; // 計算指針之間的差printf("%d\n", diff); // 輸出:3
在上述示例中,ptr1指向數組的第二個元素,ptr2指向數組的最后一個元素。通過將ptr2減去ptr1,可以得到它們之間的差值,即3。這表示從ptr1移動到ptr2需要3個元素的距離。
需要注意的是,兩個指針只有在指向同一數組(或數組的結尾位置)中的元素時,才能進行指針相減操作。否則,結果將是未定義的。
另外,指針相減的結果是一個整數類型,表示兩個指針相差的單位個數,并不是指向元素的實際大小。
指針關系運算
在C語言中,指針比較的規則如下:
-
相等性比較:使用"=="運算符可以比較兩個指針是否指向同一個內存地址。如果兩個指針指向同一個內存地址,比較的結果為真(1),否則為假(0)。
-
不等性比較:使用"!="運算符可以比較兩個指針是否指向不同的內存地址。如果兩個指針指向不同的內存地址,比較的結果為真(1),否則為假(0)。
-
大小比較:使用">"、">="、"<"和"<="等運算符可以比較兩個指針所指向的內存地址的大小關系。這里的比較是基于內存地址的值的大小。
- 如果兩個指針指向數組中的不同元素,那么比較的結果取決于它們指向的元素在數組中的相對位置。
- 如果兩個指針指向同一個數組中的不同元素,那么比較的結果取決于它們指向的元素在數組中的索引位置。
- 如果兩個指針都指向不同的數組,那么它們之間的比較沒有定義。
需要注意的是,指針比較操作只有在指針指向的內存地址屬于同一個數組或同一個對象時才有意義。否則,比較的結果是未定義的。因此,在進行指針比較之前,應該確保指針引用的對象是有效的。
下面是一些C語言中指針關系運算的例子:
- 判斷兩個指針是否相等:
int* p1 = NULL;
int* p2 = NULL;
if (p1 == p2) {// 兩個指針相等
}
- 判斷兩個指針是否不相等:
int* p1 = NULL;
int* p2 = NULL;
if (p1 != p2) {// 兩個指針不相等
}
- 比較兩個指針的大小關系:
int arr[5] = {1, 2, 3, 4, 5};
int* p1 = &arr[0];
int* p2 = &arr[2];
if (p1 < p2) {// p1指向的元素在p2指向的元素之前
}
if (p1 > p2) {// p1指向的元素在p2指向的元素之后
}
if (p1 <= p2) {// p1指向的元素在p2指向的元素之前或相等
}
if (p1 >= p2) {// p1指向的元素在p2指向的元素之后或相等
}
- 判斷指針和NULL之間的關系:
int* p = NULL;
if (p == NULL) {// 指針為空
}
if (p != NULL) {// 指針不為空
}
這些例子演示了C語言中指針關系運算符的用法。需要注意的是,在進行指針關系運算之前,必須確保指針是有效的,即它指向的內存是有效的。否則,結果是未定義的。
空指針
在C語言中,空指針是指沒有指向任何有效對象或函數的指針。空指針用NULL宏表示,其實質是一個整型常量0。
以下是一些關于空指針的常見用法:
- 聲明空指針:
int* ptr = NULL;
char* str = NULL;
這里將指針ptr和str聲明為空指針,即它們暫時不指向任何有效的內存地址。
- 判斷指針是否為空:
if (ptr == NULL) {// 指針為空
}
可以使用相等運算符(==)將指針與NULL進行比較,來判斷指針是否為空。
- 傳遞空指針到函數:
void foo(int* ptr) {if (ptr == NULL) {// 指針為空}// 其他操作
}int main() {int* ptr = NULL;foo(ptr);return 0;
}
可以將空指針作為參數傳遞給函數,并在函數內部判斷指針是否為空。
需要注意的是,對空指針進行解引用操作(如*ptr)是非法的,會導致程序崩潰或未定義行為。因此,在使用指針之前,應始終確保它不為空。
const與指針
在C語言中,const關鍵字可以與指針結合使用,用于聲明指向常量的指針或常量指針。
指向常量的指針:
使用const修飾指針所指向的內容,表明指針所指向的內容是不可修改的。
const int* p; // 指向常量的指針,p指向的內容不可修改,但p本身可修改
int num = 5;
p = # // 合法
*p = 10; // 非法,指針指向的內容不可修改
p可改,*p不可改
常量指針:
使用const修飾指針本身,表明指針本身是不可修改的。即指針指向的地址不可修改。
int* const p; // 常量指針,p本身不可修改,但p指向的內容可修改
int num = 5;
p = &num // 非法,不可改變指針指向
*p = 10; // 合法,指針指向的內容可修改
p不可改,*p可改
指向常量的常量指針:
既不允許修改指針本身,也不允許修改指針所指向的內容。
const int* const p; // 指向常量的常量指針,p本身和p指向的內容都不可修改
int num = 5;
p = &num // 非法,指針本身不可修改
*p = 10; // 非法,指針指向的內容不可修改
p和*p都不可改
通過使用const關鍵字可以靈活地控制指針所指向的內容是否可修改,以及指針本身是否可修改。常量指針和指向常量的指針在函數參數傳遞中特別有用,可以避免不必要的修改。
二級指針
在C語言中,二級指針(double pointer)也稱為指向指針的指針。它本質上是一個指針變量,存儲的是另一個指針的地址。
語法上,二級指針的聲明和使用方式如下:
int** ptr2; // 聲明一個二級指針int main() {int num = 10;int* ptr1 = # // 聲明一個指針并將其指向變量num的地址ptr2 = &ptr1; // 將指向ptr1的地址賦給二級指針ptr2printf("Value of num: %d\n", **ptr2); // 通過二級指針訪問變量num的值printf("Address of ptr1: %p\n", *ptr2); // 通過二級指針訪問指針ptr1的地址return 0;
}
在上面的例子中,我們聲明了一個二級指針ptr2
,它存儲了指向ptr1
的地址。
通過二級指針ptr2
可以訪問ptr1
所指向的地址和值。通過兩次解引用操作**ptr2
,我們可以獲得指向的變量num
的值。通過一次解引用操作*ptr2
,我們可以獲得指針ptr1
的地址。
二級指針通常用在需要傳遞指針的地址作為參數的函數中,可以通過傳遞二級指針來修改原始指針的值。