指針詳解2
運行環境以Dev-C++、Visual Studio 2022、MacOS的命令行和Xcode為主
1.數組名的實質
-
數組名就是數組首元素的地址,但是有兩個意外
-
sizeof(數組名),sizeof中單獨存放數組名,此處的數組名表示整個數組,計算的是整個數組的大小,單位為字節 -
&數組名,此處的數組名表示整個數組,取出的是整個數組的地址,該地址與數組首元素的地址有區別,具體參考下文代碼
// 代碼1:分別打印數組名和數組首元素的地址,結果一樣,數組名就是數組第一個元素的地址 #include <stdio.h> int main(int argc, const char * argv[]) { int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; printf("&arr[0] = %p\n", &arr[0]); printf("arr = %p\n", arr); return 0; }![image]()
// 代碼2:由于sizeof中單獨放數組名,表示的是整個數組,計算數組占用內存的大小,共 40B #include <stdio.h> int main(int argc, const char * argv[]) { int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; printf("%zd\n", sizeof(arr)); return 0; }![image]()
// 代碼3:數組首元素的地址、數組名和數組的地址在數值上一樣 #include <stdio.h> int main(int argc, const char * argv[]) { int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; printf("&arr[0] = %p\n", &arr[0]); printf("arr = %p\n", arr); printf("&arr = %p\n", &arr); return 0; }![image]()
// 代碼4:數組首元素的地址、數組名和數組的地址分別加 1 的區別 #include <stdio.h> int main(int argc, const char * argv[]) { int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // 兩者相差4個字節,&arr[0] 是首元素的地址,加 1 就是跳過一個元素 printf(" &arr[0] = %p\n", &arr[0]); printf("&arr[0] + 1 = %p\n", &arr[0] + 1); // 兩者相差4個字節,arr 也是數組首元素的地址,加 1 就是跳過一個元素 printf(" arr = %p\n", arr); printf(" arr + 1 = %p\n", arr + 1); // 兩者相差40個字節,因為 &arr 是數組的地址,加 1 操作是跳過整個數組 // 十進制40的十六進制為28,16fdf280 + 00000028 = 16fdf2a8 printf(" &arr = %p\n", &arr); printf(" &arr + 1 = %p\n", &arr + 1); return 0; }![image]()
-
2.利用指針訪問數組
-
在積累了前面的知識后,結合數組的特點,可以很方便地使用指針訪問數組
-
編譯器在處理數組元素訪問的時候,通過首元素的地址加偏移量求得元素的地址,然后解引用來訪問
#include <stdio.h>
int main(int argc, const char * argv[]) {
int arr[10] = {0};
int sz = sizeof(arr) / sizeof(arr[0]);
int i = 0;
int *p = &arr[0];
// int *p = arr; // 這里的數組名就是數組首元素的地址,兩者等價
// 輸入
for (i = 0; i < sz; i++) {
scanf("%d", p + i);
// scanf("%d", arr + i);
}
// 輸出
for (i = 0; i < sz; i++) {
printf("%d ", *(p + i));
// printf("%d", p[i]); // p[i] 本質上等價于 *(p + i)。類似地,arr[i] 等價于 *(arr + i)
// printf("%d", *(arr + i));
// printf("%d", arr[i]);
// printf("%d", i[arr]); // [] 作為運算符,i 和 arr 是它兩邊的運算數,類比加法運算,[] 滿足交換律
}
printf("\n");
return 0;
}
3.一維數組傳參的實質
-
3.1 數組名作為函數參數
- 之前的代碼通常在自定義函數外部求數組元素的個數。如果將數組傳給一個自定義函數,理論上在函數內部也能求出數組的元素個數。參考以下代碼
#include <stdio.h> void test1(int arr[]) { int sz1 = sizeof(arr) / sizeof(arr[0]); printf("sz1 = %zd\n", sz1); } void test2(int arr[10]) { int sz2 = sizeof(arr) / sizeof(arr[0]); printf("sz2 = %zd\n", sz2); } int main(int argc, const char * argv[]) { int arr[10] = {0}; int sz = sizeof(arr) / sizeof(arr[0]); printf("sz = %zd\n", sz); test1(arr); test2(arr); return 0; } //![image]()
-
觀察運行結果發現在函數內部并沒有獲得正確的數組元素個數
-
數組名是數組首元素的地址,那么在數組傳參時傳遞的是數組名,數組傳參在本質上傳遞的是數組首元素的地址
-
理論上,函數形參的部分應使用指針變量來接收首元素的地址,函數內部的
sizeof(arr)計算的是一個地址的大小(單位是字節)而不是數組的大小(單位是字節) -
正是因為函數的參數部分本質是指針,所以在函數內部無法求得數組元素的個數
-
3.2 指針作為函數參數
- 一維數組傳參,形參的部分可以寫成數組的形式,也可以寫成指針的形式
#include <stdio.h> void test1(int arr[]) { printf("%zd\n", sizeof(arr)); } void test2(int *arr) { printf("%zd\n", sizeof(arr)); } int main(int argc, const char * argv[]) { int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; test1(arr); test2(arr); return 0; }![image]()
4.冒泡排序
-
核心思想:升序排序時,兩兩相鄰的元素進行比較,如果前面的比后面的大,那就交換這兩個數。詳細的過程可參考排序專題
-
代碼實現
// 方法1
#include <stdio.h>
void bubble_sort(int arr[], int sz) {
int i = 0;
for (i = 0; i < sz - 1; i++) {
int j = 0;
for (j = 0; j < sz - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
int main(int argc, const char * argv[]) {
int arr[] = {3, 1, 7, 5, 8, 9, 0, 2, 4, 6};
int sz = sizeof(arr) / sizeof(arr[0]);
bubble_sort(arr, sz);
int i = 0;
for (i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
return 0;
}
// 方法2
#include <stdio.h>
int count = 0; // 計數變量,統計相鄰兩個數比較的次數
void input(int *arr, int sz) {
int i = 0;
for (i = 0; i < sz; i++) {
scanf("%d", arr + i);
}
}
void print_arr(int *arr, int sz) {
int i = 0;
for (i = 0; i < sz; i++) {
printf("%d ", *(arr + i));
}
printf("\n");
}
void bubble_sort(int *arr, int sz) {
int i = 0;
int j = 0;
int temp = 0;
for (i = 0; i < sz - 1; i++) {
int flag = 1; // 假設這一趟數已經有序了
for (j = 0; j < sz - i - 1; j++) {
count++;
if (arr[j] > arr[j + 1]) {
flag = 0; // 發生交換就說明此時數組無序
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
if (flag == 1) { // 一趟過后沒有交換說明已經有序,后續無需在排列
break;
}
}
}
int main(int argc, const char * argv[]) {
int arr[10] = {0};
int sz = sizeof(arr) / sizeof(arr[0]);
input(arr, sz);
bubble_sort(arr, sz);
print_arr(arr, sz);
printf("count = %d\n", count);
return 0;
}
5.二級指針
-
5.1 定義和指向原理
-
C語言中的普通變量都有地址。類似地,指針變量是一種特殊的變量,它也應當有地址
-
指針變量的地址存放在二級指針中
#include <stdio.h> int main(int argc, const char * argv[]) { int a = 10; int *pa = &a; int **ppa = &pa; printf("&a = %p\n", &a); printf("&pa = %p\n", &pa); printf("&ppa = %p\n", &ppa); return 0; }![image]()
![image]()
-
-
5.2 二級指針的運算
*ppa通過解引用ppa中的地址,找到了pa,*ppa訪問的其實就是pa
int a = 20; *ppa = &a; // 等價于 pa = &a;**ppa先通過*ppa找到pa,然后對pa進行解引用操作,即*pa找到了a
**ppa = 30; // 等價于 *pa = 30; // 等價于 a = 30;
6.指針數組
-
6.1 定義和指向原理
-
整型數組是存放整型數據的數組,字符數組是存放字符型數據的數組
-
類似地,指針數組應該是存放指針類型數據的數組
![image]()
-
-
6.2 指針數組模擬二維數組
parr[i]是訪問parr數組的元素,parr[i]找到的數組元素指向了整型一維數組,parr[i][j]就是整型一維數組中的元素- 代碼模擬出二維數組的效果,實際上并非完全是二維數組,因為每一行不是連續的
#include <stdio.h> int main(int argc, const char * argv[]) { int arr1[] = {1, 2, 3, 4, 5}; int arr2[] = {2, 3, 4, 5, 6}; int arr3[] = {3, 4, 5, 6, 7}; // 數組名是數組首元素的地址,類型是int *的,就可以存放在parr數組中 int *parr[] = {arr1, arr2, arr3}; int i = 0, j = 0; for (i = 0; i < 3; i++) { for (j = 0; j < 5; j++) { printf("%d ", parr[i][j]); // printf("%d ", *(*(parr + i) + j)); } printf("\n"); } return 0; }![image]()











浙公網安備 33010602011771號