C語言【指針篇】(四)

    • 前言:
    • 正文
      • 1. 字符指針變量
      • 2. 數組指針變量
        • 2.1 數組指針變量是什么?
        • 2.2 數組指針變量怎么初始化
      • 3. 二維數組傳參的本質
      • 4. 函數指針變量
        • 4.1 函數指針變量的創建
        • 4.2 函數指針變量的使用
        • 4.3 兩段有趣的代碼
        • 4.3.1 typedef關鍵字
      • 5. 函數指針數組
      • 6. 轉移表
    • 總結

前言:

這是指針第四篇,主要介紹:字符指針變量、數組指針變量、二維數組傳參的本質、函數指針變量、函數指針數組以及函數指針數組的應用——轉移表

正文

1. 字符指針變量

在指針的類型中有一種指針類型為字符指針char*
一般使用方式如下:

int main()
{char ch = 'w';char *pc = &ch;*pc = 'w';return 0;
}

還有一種使用方式如下:

int main()
{const char* ps = "hello C.";printf("%s\n", ps);return 0;
}

代碼const char* ps = "hello C";容易讓人以為是把字符串hello C放到字符指針ps里了,但是本質是把字符串hello C.首字符的地址放到了ps中。實際是把一個常量字符串的首字符h的地址存放到指針變量ps中。

《劍指offer》中又一道與之相關的題目,代碼如下:

#include <stdio.h>
int main()
{char str1[] = "hello C.";char str2[] = "hello C.";const char *str3 = "hello C.";const char *str4 = "hello C.";if(str1 ==str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if(str3 ==str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}

結果
為什么會有這樣的結果呢?const修飾常量字符串,str3和strr4是指向同一個字符串的,在C語言中會把常量字符串單獨存儲到一個內存區域,雖然str3和str4變量名不同,但實際上指向同一片內存空間。然而,用同樣的字符串初始化不同數組時會形成不同空間,即不同的內存塊。所以str1和str2不同,str3和str4相同。

2. 數組指針變量

2.1 數組指針變量是什么?

先區分一下,之前學過指針數組,它是一種數組,用來存放指針(也就是地址)
數組指針變量,和·指針數組·不同,它是指針變量
舉個栗子:

int* pint 表示整型指針變量用來存放整型變量的地址
float* pf表示浮點型指針變量 , 用來存放浮點型數據的地址

數組指針變量存放的是數組的地址,能夠指向數組的指針變量。

這里用兩個代碼經常混淆

int *p1[10];
int(*p2)[10];

哪一個是數組指針變量呢?答案是p2即int(*p2)[10];p1是指針數組,p2先和結合,說明p2是一個指針變量,然后指針指向的是一個大小為 10 個整型的數組,所以p2是一個指針,指向一個數組,叫數組指針。
注意:[]的優先級是高于
的,所以必須加上()保證*和p是一起的

2.2 數組指針變量怎么初始化

數組指針變量是用來存放數組地址的,通過&數組名可以獲得數組的地址。例如:

int arr[10] = {0};
&arr;

如果要存放數組的地址,就得存放在數組指針變量中,如下:

int(*p)[10] = &arr;

調試可以看到&arrp的類型是完全一致的。
數組指針類型解析:

描述

3. 二維數組傳參的本質

有了數組指針的基礎理解,就能夠講一下二維數組傳參的本質。

過去二維數組傳參給一個函數時,寫法如下:

#include <stdio.h>
void test(int a[3][5], int a, int b)
{int i = 0;int j = 0;for(i=0; i<a; i++){for(j=0; j<b; j++){printf("%d ", a[i][j]);}printf("\n");}
}
int main()
{int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}};test(arr, 3, 5);return 0;
}
0 0 0 1 1 1 2 2 2 3 3 3 4 4 4
0 0 0 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5
1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6
2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7

arr數組 內容如上
實參是二維數組,形參也是二維數組,但是前面說過二維數組其實是一維數組的數組,二維數組的首元素是第一行,是一維數組,所以我們可以這樣理解:二維數組數組名是第一行的地址,是一維數組的地址(指針),第一行的一維數組類型就是int [5],so,第一行的地址類型就是數組指針類型int(*) [5].這表示二維數組傳參本質上也是傳遞了地址,傳遞的是第一行這個一維數組的地址,那么形參也是可以寫成指針形式的,如下:

#include <stdio.h>
void test(int (*p)[5], int r, int c)
{int i = 0;int j = 0;for(i=0; i<r; i++){for(j=0; j<c; j++){printf("%d ", *(*(p+i)+j));}printf("\n");}
}
int main()
{int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}};test(arr, 3, 5);return 0;
}

總結:二維數組傳參,形參的部分可以寫成數組,也可以寫成指針形式。

4. 函數指針變量

4.1 函數指針變量的創建

函數指針變量應該是用來存放函數地址的,未來通過地址能夠調用函數。

函數是有地址的,通過以下測試可以證明:

#include <stdio.h>
void test()
{printf("hehe\n");
}
int main()
{printf("test: %p\n", test);printf("&test: %p\n", &test);return 0;
}

結果

輸出結果中test&test的地址相同,函數名就是函數的地址,也可以通過&函數名的方式獲得函數的地址。

如果要將函數的地址存放起來,就得創建函數指針變量,函數指針變量的寫法和數組指針非常類似。例如:

void test()
{printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)()= test;
int Add(int x, int y)
{return x+y;
}
int(*pf3)(int x, int y) = &Add;
int(*pf3)(int, int) = Add;

函數指針類型解析:

int (*pf3) ( int x, int y)
//( int x, int y)——指向函數的參數類型和個數的交代
//(*pf3)         ——函數指針變量名
//int            ——pf3指向函數的返回類型int (*) (int x, int y) //pf3函數指針變量的類型
4.2 函數指針變量的使用

現在寫一個加法的函數,你可能會這樣寫:

int add(int x,int y)
{return x + y;
}
int main()
{int r = add(2,5);printf("%d",r);return 0;
}

但我們學過上述內容后,通過函數指針調用指針指向的函數,示例代碼如下:

#include <stdio.h>
int Add(int x, int y)
{return x+y;
}
int main()
{int(*pf3)(int, int) = Add;printf("%d\n", (*pf3)(2, 3));printf("%d\n", pf3(3, 5));return 0;
}
4.3 兩段有趣的代碼
(*(void (*)())0)();
void (*signal(int , void(*)(int)))(int);

兩段代碼均出自《C陷阱和缺陷》這本書。
可以自己寫出來理解一下,正常情況我們不會這樣寫,實在理解不了也沒關系,(也可以問問deepseek).

4.3.1 typedef關鍵字

typedef是用來類型重命名的,可以將復雜的類型簡單化。比如將unsigned int重命名為uint

typedef unsigned int uint;

對于指針類型也能重命名,將int*重命名為ptr_t

typedef int* ptr_t;

對于數組指針和函數指針重命名稍有區別。將數組指針類型int(*)[5]重命名為parr_t

typedef int(*parr_t)[5]; 

將函數指針類型void(*)(int)重命名為pf_t

typedef void(*pfun_t)(int);

簡化代碼2可以這樣寫:

typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);

5. 函數指針數組

數組是一個存放相同類型數據的存儲空間,已經學習了指針數組,例如int * arr[10];,數組的每個元素是int*

把函數的地址存到一個數組中,這個數組就叫函數指針數組。函數指針數組定義為int (*parr1[3])();parr1先和[]結合,說明parr1是數組,數組的內容是int (*)()類型的函數指針。

6. 轉移表

函數指針數組的用途之一是轉移表。

例如計算器的一般實現代碼如下:

#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;do{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf(" 0:exit \n" );printf("*************************\n");printf("請選擇:");scanf("%d", &input);switch (input){case 1:printf("輸入操作數:");scanf("%d %d", &x, &y);ret = add(x, y);printf("ret = %d\n", ret);break;case 2:printf("輸入操作數:");scanf("%d %d", &x, &y);ret = sub(x, y);printf("ret = %d\n", ret);break;case 3:printf("輸入操作數:");scanf("%d %d", &x, &y);ret = mul(x, y);printf("ret = %d\n", ret);break;case 4:printf("輸入操作數:");scanf("%d %d", &x, &y);ret = div(x, y);printf("ret = %d\n", ret);break;case 0:printf("退出程序\n");break;default:printf("選擇錯誤\n");break;}} while (input);return 0;
}

使用函數指針數組的實現:

#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 }; do{printf("*************************\n");printf(" 1:add 2:sub \n");printf(" 3:mul 4:div \n");printf(" 0:exit \n" );printf("*************************\n");printf("請選擇:");scanf("%d", &input);if ((input <= 4 && input >= 1)){printf("輸入操作數:");scanf("%d %d", &x, &y);ret = (*p[input])(x, y);printf("ret = %d\n", ret);}else if(input == 0){printf("退出計算器\n");}else{printf("輸入有誤,重新選擇\n");}}while (input);return 0;
}

總結

本文就到這里了,如有誤,歡迎指正,指針很難,但堅持下去,終有收獲,朋友,把這件事堅持下去吧。
敬,不完美的明天。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/896981.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/896981.shtml
英文地址,請注明出處:http://en.pswp.cn/news/896981.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

React + TypeScript 實戰指南:用類型守護你的組件

TypeScript 為 React 開發帶來了強大的類型安全保障&#xff0c;這里解析常見的一些TS寫法&#xff1a; 一、組件基礎類型 1. 函數組件定義 // 顯式聲明 Props 類型并標注返回值 interface WelcomeProps {name: string;age?: number; // 可選屬性 }const Welcome: React.FC…

【玩轉正則表達式】將正則表達式中的分組(group)與替換進行結合使用

在文本處理和數據分析領域&#xff0c;正則表達式&#xff08;Regular Expressions&#xff0c;簡稱regex&#xff09;是一種功能強大的工具。它不僅能夠幫助我們匹配和搜索字符串中的特定模式&#xff0c;還能通過分組&#xff08;Grouping&#xff09;和替換&#xff08;Subs…

Flutter 學習之旅 之 flutter 不使用插件,簡單實現一個 Toast 功能

Flutter 學習之旅 之 flutter 不使用插件&#xff0c;簡單實現一個 Toast 功能 目錄 Flutter 學習之旅 之 flutter 不使用插件&#xff0c;簡單實現一個 Toast 功能 一、簡單介紹 二、簡單介紹 Toast 1. 確保正確配置 navigatorKey 2. 避免重復顯示 Toast 3. 確保 Toast …

《OpenCV》——dlib(人臉應用實例)

文章目錄 dlib庫dlib庫——人臉應用實例——表情識別dlib庫——人臉應用實例——疲勞檢測 dlib庫 dlib庫的基礎用法介紹可以參考這篇文章&#xff1a;https://blog.csdn.net/lou0720/article/details/145968062?spm1011.2415.3001.5331&#xff0c;故此這篇文章只介紹dlib的人…

學習日記-250305

閱讀論文&#xff1a;Leveraging Pedagogical Theories to Understand Student Learning Process with Graph-based Reasonable Knowledge Tracing ps:代碼邏輯最后一點還沒理順&#xff0c;明天繼續 4.2 Knowledge Memory & Knowledge Tracing 代碼研究&#xff1a; 一般…

【AI大模型】DeepSeek + Kimi 高效制作PPT實戰詳解

目錄 一、前言 二、傳統 PPT 制作問題 2.1 傳統方式制作 PPT 2.2 AI 大模型輔助制作 PPT 2.3 適用場景對比分析 2.4 最佳實踐與推薦 三、DeepSeek Kimi 高效制作PPT操作實踐 3.1 Kimi 簡介 3.2 DeepSeek Kimi 制作PPT優勢 3.2.1 DeepSeek 優勢 3.2.2 Kimi 制作PPT優…

【ESP-ADF】在 VSCode 安裝 ESP-ADF 注意事項

1.檢查網絡 如果您在中國大陸安裝&#xff0c;請使用魔法上網&#xff0c;避免無法 clone ESP-ADF 倉庫。 2.VSCode 安裝 ESP-ADF 在 VSCode 左側活動欄選擇 ESP-IDF:explorer&#xff0c;展開 advanced 并點擊 Install ESP-ADF 然后會出現選擇 ESP-ADF 安裝目錄。 如果出現…

關于2023新版PyCharm的使用

考慮到大家AI編程的需要&#xff0c;建議大家安裝新版Python解釋器和新版PyCharm&#xff0c;下載地址都可以官網進行&#xff1a; Python&#xff1a;Download Python | Python.org&#xff08;可以根據需要自行選擇&#xff0c;建議選擇3.11&#xff0c;保持交流版本一致&am…

輕松部署 Stable Diffusion WebUI 并實現局域網共享訪問:解決 Conda Python 版本不為 3.10.6 的難題

這篇博文主要為大家講解關于sd webui的部署問題&#xff0c;大家有什么不懂的可以隨時問我&#xff0c;如果沒有及時回復&#xff0c;可聯系&#xff1a;1198965922 如果后續大家需要了解怎么用代碼調用部署好的webui的接口&#xff0c;可以在評論區留言哦&#xff0c;博主可以…

Leetcode 103: 二叉樹的鋸齒形層序遍歷

Leetcode 103: 二叉樹的鋸齒形層序遍歷 問題描述&#xff1a; 給定一個二叉樹&#xff0c;返回其節點值的鋸齒形層序遍歷&#xff08;即第一層從左到右&#xff0c;第二層從右到左&#xff0c;第三層從左到右&#xff0c;依此類推&#xff09;。 適合面試的解法&#xff1a;廣…

Linux中的進程間通信的方式及其使用場景

在 Linux 系統中&#xff0c;進程間通信&#xff08;Inter-Process Communication, IPC&#xff09;是指不同進程之間傳遞數據、共享信息的機制。Linux 提供了多種進程間通信的方式&#xff0c;每種方式都有不同的特點和使用場景。以下是常見的幾種進程間通信方式及其應用場景&…

springBoot集成emqx 實現mqtt消息的發送訂閱

介紹 我們可以想象這么一個場景&#xff0c;我們java應用想要采集到電表a的每小時的用電信息&#xff0c;我們怎么拿到電表的數據&#xff1f;一般我們會想 直接 java 后臺發送請求給電表&#xff0c;然后讓電表返回數據就可以了&#xff0c;事實上&#xff0c;我們java應用發…

vue Table 表格自適應窗口高度,表頭固定

當表格內縱向內容過多時&#xff0c;可選擇固定表頭。 代碼很簡單&#xff0c;其實就是在table 里面定一個 height 屬性即可。 <template><el-table:data"tableData"height"250"borderstyle"width: 100%"><el-table-columnprop…

多線程-JUC

簡介 juc&#xff0c;java.util.concurrent包的簡稱&#xff0c;java1.5時引入。juc中提供了一系列的工具&#xff0c;可以更好地支持高并發任務 juc中提供的工具 可重入鎖 ReentrantLock 可重入鎖&#xff1a;ReentrantLock&#xff0c;可重入是指當一個線程獲取到鎖之后&…

【每日學點HarmonyOS Next知識】Web Header更新、狀態變量嵌套問題、自定義彈窗、stack圓角、Flex換行問題

【每日學點HarmonyOS Next知識】Web Header更新、狀態變量嵌套問題、自定義彈窗、stack圓角、Flex換行問題 1、HarmonyOS 有關webview Header無法更新的問題&#xff1f; 業務A頁面 打開 webivew B頁面&#xff0c;第一次打開帶了header請求&#xff0c;然后退出webview B頁面…

【ATXServer2】Android無法正確顯示手機屏幕

文章目錄 現象原因分析與解決排查手機內部minicap 解決minicap問題查看移動端Android SDK版本查看minicap支持版本單次方案多次方案 最后問題-如何支持Android SDK 32 現象 原因分析與解決 由于atxserver2在與Android動終端的鏈接過程中使用了agent&#xff1a;atxserver2-and…

【前端跨域】CORS:跨域資源共享的機制與實現

在現代Web開發中&#xff0c;跨域資源共享&#xff08;Cross-Origin Resource Sharing&#xff0c;簡稱CORS&#xff09;是一種非常重要的技術&#xff0c;用于解決瀏覽器跨域請求的限制 CORS允許服務器明確指定哪些外部源可以訪問其資源&#xff0c;從而在保證安全的前提下實…

【設計模式】單例模式|餓漢模式|懶漢模式|指令重排序

目錄 1.什么是單例模式&#xff1f; 2.如何保證單例&#xff1f; 3.兩種寫法 &#xff08;1&#xff09;餓漢模式&#xff08;早創建&#xff09; &#xff08;2&#xff09;懶漢模式&#xff08;緩執行&#xff0c;可能不執行&#xff09; 4.應用場景 &#x1f525;5.多…

RocketMQ順序消費機制

RocketMQ的順序消費機制通過生產端和消費端的協同設計實現&#xff0c;其核心在于局部順序性&#xff0c;即保證同一隊列&#xff08;MessageQueue&#xff09;內的消息嚴格按發送順序消費。以下是詳細機制解析及關鍵源碼實現&#xff1a; 一、順序消費的核心機制 1. 生產端路…

【JavaEE】-- 多線程(初階)4

文章目錄 8.多線程案例8.1 單例模式8.1.1 餓漢模式8.1.2 懶漢模式 8.2 阻塞隊列8.2.1 什么是阻塞隊列8.2.2 生產者消費者模型8.2.3 標準庫中的阻塞隊列8.2.4 阻塞隊列的應用場景8.2.4.1 消息隊列 8.2.5 異步操作8.2.5 自定義實現阻塞隊列8.2.6 阻塞隊列--生產者消費者模型 8.3 …