当前位置:网站首页>抽絲剝繭C語言(高階)指針的進階
抽絲剝繭C語言(高階)指針的進階
2022-07-07 07:16:00 【ℳℓ白ℳℓ夜ℳℓ】
指針的進階
1. 字符指針
在指針的類型中我們知道有一種指針類型為字符指針 char*
一般使用:
int main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
還有一種使用方式如下:
int main()
{
const char* pstr = "hello baiye.";//這裏是把一個字符串放到pstr指針變量裏了嗎?
printf("%s\n", pstr);
return 0;
}
代碼 const char* pstr = “hello baiye.”;
特別容易讓我們以為是把字符串 hello bit 放到字符指針 pstr 裏了,但是/本質是把字符串 hello baiye. 首字符的地址放到了pstr中。
上面代碼的意思是把一個常量字符串的首字符 h 的地址存放到指針變量 pstr 中。
2. 指針數組
之前我們介紹過了,這裏就不多說了。
int* arr1[10]; //整形指針的數組
char *arr2[4]; //一級字符指針的數組
char **arr3[5];//二級字符指針的數組
3. 數組指針
3.1 數組指針的定義
數組指針是指針?還是數組?
答案是:指針。
我們已經熟悉:
整形指針: int * pint; 能够指向整形數據的指針。
浮點型指針: float * pf; 能够指向浮點型數據的指針。
那數組指針應該是:能够指向數組的指針。
下面代碼哪個是數組指針?
int *p1[10];
int (*p2)[10];
//p1, p2分別是什麼?
P1:是指針數組。
P2:
//解釋:p先和*結合,說明p是一個指針變量,然後指著指向的是一個大小為10個整型的數組。所以p是一個指針,指向一個數組,叫數組指針。
//這裏要注意:[ ]的優先級要高於*號的,所以必須加上()來保證p先和*結合。
3.2 &數組名VS數組名
我們來看一段代碼:
#include <stdio.h>
int main()
{
int arr[10] = {
0};
printf("%p\n", arr);
printf("%p\n", &arr);
return 0;
}
arr 和 &arr 分別是啥?
我們知道arr是數組名,數組名錶示數組首元素的地址。
那&arr數組名到底是啥?
我們先來運行一下看看結果:
可見數組名和&數組名打印的地址是一樣的。
難道兩個是一樣的嗎?
我們再看一段代碼:
#include <stdio.h>
int main()
{
int arr[10] = {
0 };
printf("arr = %p\n", arr);
printf("&arr= %p\n", &arr);
printf("arr+1 = %p\n", arr + 1);
printf("&arr+1= %p\n", &arr + 1);
return 0;
}
運行結果:
根據上面的代碼我們發現,其實&arr和arr,雖然值是一樣的,但是意義應該不一樣的。
實際上: &arr 錶示的是數組的地址,而不是數組首元素的地址。(細細體會一下)
本例中 &arr 的類型是: int(*)[10] ,是一種數組指針類型
數組的地址+1,跳過整個數組的大小,所以 &arr+1 相對於 &arr 的差值是40.
3.3 數組指針的使用
那數組指針是怎麼使用的呢?
既然數組指針指向的是數組,那數組指針中存放的應該是數組的地址。
看代碼:
#include <stdio.h>
int main()
{
int arr[10] = {
1,2,3,4,5,6,7,8,9,0 };
int(*p)[10] = &arr;//把數組arr的地址賦值給數組指針變量p
//但是我們一般很少這樣寫代碼
return 0;
}
一個數組指針的使用:
#include <stdio.h>
void print_arr1(int arr[3][5], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
void print_arr2(int(*arr)[5], int row, int col)
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {
1,2,3,4,5,6,7,8,9,10 };
print_arr1(arr, 3, 5);
//數組名arr,錶示首元素的地址
//但是二維數組的首元素是二維數組的第一行
//所以這裏傳遞的arr,其實相當於第一行的地址,是一維數組的地址
//可以數組指針來接收
print_arr2(arr, 3, 5);
return 0;
}
讓我們來看一下運行結果:
那麼,這段代碼是什麼意思呢?
int (*parr3[10])[5];
首先parr3和[10]結合,這說明是一個數組,此時數組還缺少一個類型,那麼剩下的int(*)[5]就是類型,總的來講parr3是存放數組指針的數組。
4. 數組參數、指針參數
在寫代碼的時候難免要把【數組】或者【指針】傳給函數,那函數的參數該如何設計呢?
4.1 一維數組傳參
#include <stdio.h>
void test(int arr[])//ok?
{
}
void test(int arr[10])//ok?
{
}
void test(int* arr)//ok?
{
}
void test2(int* arr[20])//ok?
{
}
void test2(int** arr)//ok?
{
}
int main()
{
int arr[10] = {
0 };
int* arr2[20] = {
0 };
test(arr);
test2(arr2);
}
上面這些是都對的。
第一個是數組的寫法,第二個[]裏面的數字可有可無。
第三個是地址,之前說過數組名是首元素的地址,也是一個類型,所以可以。
第四個也是形參與實參相同。
第五個用二級指針接受是可以的,因為實參是指針數組,數組名是首元素的地址,而元素都是指針,我們也知道二級指針是存放一級指針的地址的,所以他們是一個類型。
4.2 二維數組傳參
void test(int arr[3][5])//ok?
{
}
void test(int arr[][])//ok?
{
}
void test(int arr[][5])//ok?
{
}
//總結:二維數組傳參,函數形參的設計只能省略第一個[]的數字。
//因為對一個二維數組,可以不知道有多少行,但是必須知道一行多少元素。
//這樣才方便運算。
void test(int* arr)//ok?
{
}
void test(int* arr[5])//ok?
{
}
void test(int(*arr)[5])//ok?
{
}
void test(int** arr)//ok?
{
}
int main()
{
int arr[3][5] = {
0 };
test(arr);
}
第一個可以,因為傳過去的是二維數組,用二維數組接受,所以可以。
第二個不行,因為二維數組能省略行,不能省略列。
第三個可以。
第四個不行,因為arr是代錶二維數組的首元素地址,也代錶是數組的第一行,也就是一維數組,所以不能用一個整型指針來接收。
第五個也不行,因為這是一個指針數組,不是一個類型。
第六個可以,因為是用一個數組指針來接收一維數組。
第七個不行,一維數組的地址不可能放進二級指針裏。
4.3 一級指針傳參
#include <stdio.h>
void print(int* p, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
printf("%d\n", *(p + i));
}
}
int main()
{
int arr[10] = {
1,2,3,4,5,6,7,8,9 };
int* p = arr;
int sz = sizeof(arr) / sizeof(arr[0]);
//一級指針p,傳給函數
print(p, sz);
return 0;
}
運行結果:
4.4 二級指針傳參
#include <stdio.h>
void test(int** ptr) {
printf("num = %d\n", **ptr);
}
int main()
{
int n = 10;
int*p = &n;
int **pp = &p;
test(pp);
test(&p);
return 0; }
運行結果是:
5. 函數指針
那麼,函數也一定有地址,也有相應的指針來接收函數的地址。
首先看一段代碼:
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
代碼運行結果:
輸出的是兩個地址,這兩個地址是 test 函數的地址。
也就說明函數名就是函數的地址。
那我們的函數的地址要想保存起來,怎麼保存?
下面我們看代碼:
void test()
{
printf("hehe\n");
}
//下面pfun1和pfun2哪個有能力存放test函數的地址?
void (*pfun1)();
void* pfun2();
首先,能給存儲地址,就要求pfun1或者pfun2是指針,那哪個是指針?
答案是:
pfun1可以存放。pfun1先和*結合,說明pfun1是指針,指針指向的是一個函數,指向的函數無參
數,返回值類型為void。
6. 函數指針數組
數組是一個存放相同類型數據的存儲空間,那我們已經學習了指針數組,
比如:
int *arr[10];
//數組的每個元素是int*
那要把函數的地址存到一個數組中,那這個數組就叫函數指針數組,那函數指針的數組如何定義呢?
int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];
答案是:parr1
parr1 先和 [] 結合,說明 parr1是數組,數組的內容是什麼呢?
是 int (*)() 類型的函數指針。
至於函數指針的用途:
寫一個計算器
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int sub(int a, int b) {
return a - b;
}
int mul(int a, int b) {
return a * b;
}
int div(int a, int b) {
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
int(*p[5])(int x, int y) = {
0, add, sub, mul, div }; //轉移錶
while (input)
{
printf("*************************\n");
printf(" 1:add2:sub \n");
printf(" 3:mul4:div \n");
printf("*************************\n");
printf("請選擇:");
scanf("%d", &input);
if ((input <= 4 && input >= 1))
{
printf("輸入操作數:");
scanf("%d %d", &x, &y);
ret = (*p[input])(x, y);
}
else
printf("輸入有誤\n");
printf("ret = %d\n", ret);
}
return 0;
}
這裏我們看到,選擇運算法後,也就等於選擇到了數組裏面的函數指針,直接就到了上面的運算函數中。
這樣比較方便。
7. 指向函數指針數組的指針
指向函數指針數組的指針是一個指針,
指針指向一個 數組 ,數組的元素都是函數指針 ;
如何定義?
#include <stdio.h>
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函數指針pfun
void (*pfun)(const char*) = test;
//函數指針的數組pfunArr
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函數指針數組pfunArr的指針ppfunArr
void (*(*ppfunArr)[5])(const char*) = &pfunArr;
return 0;
}
哈哈,這就是套娃
8. 回調函數
回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個
函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數
的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用於對該事件或條件進
行響應。
首先演示一下qsort(點擊這裏可詳細查看qsort函數)函數的使用:
#include <stdio.h>
#include <stdlib.h>
//qosrt函數的使用者得實現一個比較函數
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}
int main()
{
int arr[] = {
1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
代碼的運行結果:
這裏我們可以用冒泡排序和回調函數模擬實現qsort函數。
下面的void*是沒有具體類型的指針,作用是可以接收任何類型的指針,也可以賦給任何類型的指針。
#include <stdio.h>
int int_cmp(const void* p1, const void* p2)
{
return (*(int*)p1 - *(int*)p2);
}
void _swap(void* p1, void* p2, int size)
{
int i = 0;
for (i = 0; i < size; i++)
{
char tmp = *((char*)p1 + i);
*((char*)p1 + i) = *((char*)p2 + i);//用char類型的指針因為長度為1個字節,無論我們要int類型還是ling類型都是一個字節一個字節相加的。
*((char*)p2 + i) = tmp;
}
}
void bubble(void* base, int count, int size, int(*cmp)(void*, void*))//這裏就用了回調函數。
{
int i = 0;
int j = 0;
for (i = 0; i < count - 1; i++)
{
for (j = 0; j < count - i - 1; j++)
{
if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0)
{
_swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
}
}
}
}
int main()
{
int arr[] = {
1, 3, 5, 7, 9, 2, 4, 6, 8, 0 };
int i = 0;
bubble(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), int_cmp);
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
代碼的運行結果如下:
結束語
這裏我們C語言的指針就結束了,因為之前有一篇指針的基礎章,所以本章的字數偏少。
請大佬們指點錯誤和不足,如果覺得文章不錯請家人們點個贊吧!!!
边栏推荐
- Abnova immunohistochemical service solution
- Apache AB stress test
- 详解机器翻译任务中的BLEU
- Esxi attaching mobile (Mechanical) hard disk detailed tutorial
- Matlab tips (30) nonlinear fitting lsqcurefit
- Databinding exception of kotlin
- Basic introduction of JWT
- Role of virtual machine
- . Net 5 fluentftp connection FTP failure problem: this operation is only allowed using a successfully authenticated context
- Get the city according to IP
猜你喜欢
FPGA course: application scenario of jesd204b (dry goods sharing)
How to model and simulate the target robot [mathematical / control significance]
IP address
Lm11 reconstruction of K-line and construction of timing trading strategy
Jesd204b clock network
Big coffee gathering | nextarch foundation cloud development meetup is coming
关于二进制无法精确表示小数
计算机服务中缺失MySQL服务
Basic introduction of JWT
The currently released SKU (sales specification) information contains words that are suspected to have nothing to do with baby
随机推荐
Hidden Markov model (HMM) learning notes
Basic introduction of JWT
Bindingexception exception (error reporting) processing
Prime partner of Huawei machine test questions
Procedure in PostgreSQL supports transaction syntax (instance & Analysis)
Select the product attribute pop-up box to pop up the animation effect from the bottom
ViewModelProvider. Of obsolete solution
Please tell me how to monitor multiple schemas and tables by listening to PgSQL
Pass child component to parent component
freeswitch拨打分机号源代码跟踪
Libcurl returns curlcode description
Network foundation - header, encapsulation and unpacking
Common function detect_ image/predict
Abnova immunohistochemical service solution
云备份项目
Software acceptance test
Fast quantitative, abbkine protein quantitative kit BCA method is coming!
$refs:组件中获取元素对象或者子组件实例:
Détailler le bleu dans les tâches de traduction automatique
Advantages of using net core / why