目錄
- 1.數組名的理解
- 2.使用指針訪問數組
- 3.一維數組傳參的本質
- 4.二級指針
- 5.指針數組
- 6.字符指針變量
- 7.數組指針變量
- 8.二維數組傳參的本質
- 9.函數指針變量
- 10.函數指針數組
- 11.回調函數
- 12.qsort函數
- 13.使用回調函數模擬實現qsort函數
1.數組名的理解
int main() {int arr[] = { 1,2,3 };printf("%p\n", &arr[0]);printf("%p\n", arr);return 0;
}
從結果可以看出,&arr[0] == arr,這是用為數組名就是地址,而且是首元素的地址
但是,arr作為數組名在兩種情況下不表示首元素的地址:
- sizeof(數組名),sizeof中單獨存放數組名表示求整個數組的大小,單位是字節
- &arr,&arr是&后面直接加上一個數組名,它表示整個數組的地址。(&arr在數值上可能與&arr[0]相同,但是本質上是不一樣的,即&arr[0] == arr != &arr )
除此之外,數組名都表示首元素的地址。
其實arr與&arr的區別不在于直接打印的數值上,而在于運算上,
int main() {int arr[] = { 1,2,3 };printf("%p\n", &arr[0]);printf("%p\n", &arr[0]+1);printf("%p\n", arr);printf("%p\n", arr+1);printf("%p\n", &arr);printf("%p\n", &arr+1);return 0;
}
可以看出,arr和&arr[0]加一的結果都是跳過4個字節,而&arr則跳過12個字節(E8-F4 =(15 * 16 ^ 1 + 4 * 16 ^ 0)- ( 14*16^1 + 8 *16 ^0) =12).
&arr表示整個數組,加一表示跳過整個數組,數組一共3個int類型的數據,所以一次跳過12個字節。
2.使用指針訪問數組
數組可以使用下標引用操作符來訪問,比如:
int ret = arr[9];
既然數組名相當于地址,那么還可以這樣訪問:
int* p = &arr[9];
int ret = *p;
3.一維數組傳參的本質
以前將數組作為參數傳遞給函數時需要將數組的數據個數一起傳遞給函數,那么,不傳遞數據個數可以嗎?
void test(int arr[]) {int sz2 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz2);
}
int main() {int arr[] = {1,2,3,4,5};int sz1 = sizeof(arr) / sizeof(arr[0]);printf("sz1 = %d\n", sz1);test(arr);return 0;
}
從結果來看是不行的,
其實,將數組作為參數是將數組的首元素的地址作為參數傳遞給函數
所以,在函數內部使用sizeof(arr)實際上計算的是一個地址的大小。正因為參數部分的本質是指針,所以不能不傳數據個數。
4.二級指針
指針變量也是變量,是變量就有地址,所以二級指針是用來存放一級指針變量的地址的
畫圖說明:
對于二級指針的運算:
- *ppa通過對ppa中的地址解引用,找到的是pa,*ppa訪問的就是p
- **ppa先對ppa解引用得到pa然后對pa解引用得到a
5.指針數組
存放整型的數組叫做整型數組
存放字符的數組叫做字符數組
那么存放指針的數組就叫做指針數組
6.字符指針變量
在指針的類型中有一種指針類型叫做字符指針char*
還有一種使用方法:
int main() {const char* p = "abcdef";printf("%s\n", p);return 0;
}
這種表示方法相當于將字符串看作為一個字符數組,指針變量p存放的不是字符串而是首字符’a‘的地址。
如果兩個指針指向同一個字符串,那么不會開辟兩個空間來存放,也就是說兩個指針指向的是同一個內存
7.數組指針變量
存放整型數據地址的指針叫做整型指針
存放字符型數據的指針叫做字符指針
那么存放數組的地址的指針叫做數組指針
指針數組是數組,而數組指針是變量
int (*P)[5];
p先于*結合說明p是一個指針,再于【】和 int結合說明他是一個數組指針,數組元素類型是int。
數組自指針的初始化:
int main() {int arr[10] = { 0 };int(*p)[10] = &arr;return 0;
}
&arr == p
8.二維數組傳參的本質
當我們將二位數組作為參數傳遞給函數時,其實傳遞的是第一行的地址(不是第一個元素的地址)
void test(int(*p)[3], int r, int c) {for (int i = 0; i < r; i++) {for (int j = 0; j < c; j++) {printf("%d ", *(*(p + i) + j));}printf("\n");}
}
int main() {int arr[2][3] = { {1,2,3},{2,3,4} };test(arr, 2, 3);return 0;
}
因為傳遞的是第一行元素的地址,那么該地址就該用函數指針來接收。
9.函數指針變量
函數也有地址,那么該地址就可以使用指針變量來接受,那么該指針變量就是函數指針變量。
int (*p)(int, int);
可以看出函數名就是函數的地址,也可以使用&函數名來獲得函數的地址。
要將函數的地址存放起來,就可以使用函數指針變量
可以使用函數指針變量來調用函數
void test() {printf("haha\n");
}
int main() {int(*p)() = test;p();return 0;
}
10.函數指針數組
函數指針是用來存放函數的地址的,那么有很多函數,要將它們的地址存放在一起,那么就可以使用函數指針數組。
int (*p[10])(int, int);
p先于【】結合說明是一個數組,那么數組的內容就是int (*)().
int Add(int x, int y) {return x + y;
}
int Sub(int x, int y) {return x - y;
}
int Mul(int x, int y) {return x * y;
}
int Div(int x, int y) {return x / y;
}
void Menu() {printf("**************************\n");printf("*****1.加法 2.減法*****\n");printf("*****3.乘法 4.除法*****\n");printf("*****0.退出 *****\n");printf("**************************\n");
}
int main() {int n = 0;int num1 = 0, num2 = 0;int(*p[5])(int, int) = { 0,Add,Sub,Mul,Div };do {Menu();printf("請輸入要選擇的功能:>");scanf("%d", &n);if (n >= 1 && n <= 4) {printf("\n請輸入兩個數用于計算:>");scanf("%d%d", &num1, &num2);printf("\n計算結果:%d\n", (*p[n])(num1, num2));}else if (n == 0) {printf("已退出\n");break;}else {printf("選擇錯誤,重新選擇:>");}} while (n);return 0;
}
這個代碼實現了一個簡單的計算器。
int( * p[5])(int, int) = { 0,Add,Sub,Mul,Div };
這段代碼就是將四個函數的地址放在一個函數指針數組里面
( * p[n])(num1, num2);
這段代碼函數指針數組來調用函數n表示想調用哪個函數,num1和num2就是傳遞給函數的參數
11.回調函數
回調函數就是一個通過函數指針調用的函數
如果將函數的指針作為一個參數傳遞給另一個函數,但這個指針被用來調用其所指的函數時,被調用的函數就是回調函數。
void test2() {printf("hahaha\n");
}
void test1(void (*test2)()) {printf("haha\n");test2();
}
int main() {test1(test2);return 0;
}
在main函數中,將test2的地址傳遞給test1,然后test1用函數指針變量來接收,然后調用test2函數,那么test2函數就是回調函數。
12.qsort函數
qsort函數的功能是將任意類型的數據排列,底層原理是快速排序。
#include<stdlib.h>void com_arr(const void* p1, const void* p2) {return *(int*)p1 - *(int*)p2;
}void print(int arr[], int sz) {for (int i = 0; i < sz; i++) {printf("%d ", arr[i]);}printf("\n");
}
int main() {int arr[] = { 5,2,1,7,3,4,6,14,8 };int sz = sizeof(arr) / sizeof(arr[0]);print(arr, sz);qsort(arr, sz, 4, com_arr);print(arr, sz);return 0;
}
qsort函數需要4個參數,第一個參數是數組首元素的地址,第二個參數是數組中元素的個數,第三個參數是數組中數據類型的大小,第四個參數是一個函數,作用是實現排序這種數據的方法。
在上述代碼中排序整型數組中的數據,那么第四個參數就可以讓兩個整數相減,返回負數表示第一個元素小于第二個元素,反之大于。
13.使用回調函數模擬實現qsort函數
我們可以采用冒泡排序的方式來實現qsort函數
int Method(const void* p1, const void* p2) {//用以判斷兩個數時候該交換return (*(int*)p1 - *(int*)p2);
}void Swap(void* p1, void* p2 ,int sz) {for (int i = 0; i < sz; i++) {char tmp = *((char*)p1 + i);*((char*)p1 + i) = *((char*)p2 + i);*((char*)p2 + i) = tmp;}
}
void Maopao(void* arr, int sz, size_t num, int (*method)(void*, void*)) {for (int i = 0; i < sz - 1; i++) {for (int j = 0; j < sz - 1 - i; j++) {if (method((char*)arr + j * num, (char*)arr + (j+1) * num) > 0){Swap((char*)arr + j * num, (char*)arr + (j+1) * num, num);}}}
}
int main() {int arr[] = { 4,2,6,1,7,5,9,8 };int sz = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < sz; i++) {printf("%d ", arr[i]);}printf("\n");Maopao(arr, sz, sizeof(arr[0]), Method);for (int i = 0; i < sz; i++) {printf("%d ", arr[i]);}return 0;
}