深入理解c指針(六)

目錄

九、函數指針數組

1、字符指針變量

2、數組指針變量

3、二維數組傳參的本質

?4、函數指針變量

4.1 分析《C陷阱和缺陷》中的兩端代碼

4.2?typedef關鍵字

5、函數指針數組

6、函數指針數組的用途---轉移表


九、函數指針數組

1、字符指針變量

在指針的類型中我們知道有一種指針類型為字符指針 char* ;一般使用:

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

? ? ? ?還有?種使用方式如下:代碼 const char* pstr = "hello bit."; 特別容易讓同學以為是把字符串 hello bit 放到字符指針 pstr 里了,但是本質是把字符串 hello bit. 首字符的地址放到了pstr中

int main()
{const char* pstr = "hello bit.";//這?是把?個字符地址存放在p中printf("%s\n", pstr);return 0;
}

?所以可以把字符串想象成一個數組:

注:①當常量字符串常量出現在表達式中的時候,他的值是第一個字符的地址。? ? ? ? ? ? ? ? ? ? ? ? ? ? 雖然可以把字符串看成一個字符數組,但是這個數組是不能修改的,因為它是常量不是變量。?

《劍指offer》中的一道和字符串相關的筆試題:

#include <stdio.h>
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";const char* str3 = "hello bit.";const char* str4 = "hello bit.";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;
}

? ? ? ? 這里 str3 和 str4 指向的是一個同一個常量字符串。C/C++會把常量字符串存儲到單獨的?個內存區域,當幾個指針指向同一個字符串的時候,他們實際會指向同一塊內存。但是用相同的常量字符串去初始化不同的數組的時候就會開辟出不同的內存塊。所以str1和str2不同,str3和str4相同。

2、數組指針變量

注意:指針數組是一種數組,數組中存放的是地址(指針)。

數組指針變量是指針變量,而不是數組。

#include <stdio.h>
int main()
{int n = 100;int* pn = &n;char ch = 'w';char* pc = &ch;float f = 3.14;float* pf = &f;int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int(*parr)[10] = &arr;//取出的是數組的地址//parr就是數組指針return 0;
}

元素地址與數組地址的區別:?

?數組指針如何應用:(打印數組元素為例)(因為數組指針的特點,一維數組基本不會使用)?

#include <stdio.h>
int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };//指針訪問數組int(*p)[10] = &arr;  //p3是數組地址int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);for (i = 0; i < sz; i++){printf("%d ", (*p)[i]);//  p == &arr// *p == *&arr == arr}return 0;
}
3、二維數組傳參的本質

#include <stdio.h>
void test(int a[3][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 ", 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;
}

? ? ? ? 所以,根據數組名是數組首元素的地址這個規則,二維數組的數組名表示的就是第一行的地址,是一維數組的地址根據上面的例子,第一行的一維數組的類型就是 int [5] ,所以第一行的地址的類型就是數組指針類型 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)); //p = &arr,p+i=&arr+i,*(p+i)=arr[i]}printf("\n");}
}方式二:
void test(int(*arr)[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 ", arr[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;
}

通俗理解:由于二維數組由若干個一維數組構成,那么二維數組的數組名即為第一行的一維數組地址,?地址+整數=地址,所以 +?i 表示指到第 i 行的地址。當拿到第 i 行數組名(即地址),解引用后( *(p+i) )則得到了該行首元素的地址,此時與一維數組原理相似。

?4、函數指針變量

? ? ? ?函數指針變量也是指針變量,其存儲的地址指向函數而不是普通變量。在C語言和C++中,函數指針變量可以用來存儲函數的地址,從而可以通過該指針調用相應的函數。 下面做個測試:

? ? ? ?可以發現函數的確有地址。函數名就是函數的地址,當然也可以通過 &函數名 的方式獲得函數的地址。

數組名:數組首元素的地址

&數組名:整個數組的地址

函數名==&函數名:函數的地址

函數指針變量的聲明通常形式為:

return_type (*function_pointer_name)(parameter_list);

? ? ? ?其中?return_type?表示函數返回類型,function_pointer_name?是函數指針變量的名稱,parameter_list是函數的參數列表。?如果我們要將函數的地址存放起來,就得創建函數指針變量,函數指針變量的寫法其實和數組指針非常類似。

void test()
{printf("hehe\n");
}void (*pf1)() = &test;//&test等價于test
void (*pf2)() = test;int Add(int x, int y)
{return x + y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y寫上或者省略都是可以

函數指針變量的使用:

? ? ? ?聲明了一個指向函數的指針 pf3,它接受兩個整型參數并返回一個整型結果。在這里,pf3 被賦值為 Add 函數的地址,也就是說,它指向了 Add 函數的代碼塊。

  • pf3 是一個指向函數的指針,它里面包含了函數的地址。
  • *pf3 表示解引用這個指針,也就是得到指針所指向的函數。
  • (*pf3)(2, 3) 表示調用這個函數,傳遞參數 2 和 3 給它。

? ? ? ? 而當寫 pf3(2, 3) 時,因為函數名實際上就是函數的地址,所以直接使用 pf3(函數指針)并傳遞參數給它,就相當于直接調用了 Add 函數,所以 (*pf3)(2,3) 和 pf3(2,3) 結果相同。

4.1 分析《C陷阱和缺陷》中的兩端代碼
(  *( void (*)() )0  )();

?void(*)( ) --- 函數指針類型,? ? (int)3.14 --- 強制類型轉換

上面的代碼是一次函數調用:

1.把0這個整數值強制類型轉換成一個函數地址,這個函數沒有參數,返回類型是void

2.去調用0地址處的函數

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

signal沒有和指針括在一起,由于括號的優先級更高,所以signal是函數名。

4.2?typedef關鍵字

? ? ? ?typedef是用來類型重命名的,可以將復雜的類型簡單化。比如,你覺得 unsigned int 寫起來不方便,如果能寫成 uint 就方便多了,那么我們可以使用:?

typedef unsigned int uint;
//將unsigned int 重命名為uint

如果是指針類型,能否重命名呢?其實也是可以的,比如,將 int* 重命名為 ptr_t ,這樣寫:

typedef int* ptr_t;

但是對于數組指針和函數指針稍微有點區別:比如我們有數組指針類型 int(*)[5] ,需要重命名為 parr_t ,那可以這樣寫:

typedef int(*parr_t)[5]; //新的類型名必須在*的右邊

函數指針類型的重命名也是一樣的,比如,將 void(*)(int,int) 類型重命名為 pf_t ,就可以這樣寫:

typedef void(*pfun_t)(int,int);//新的類型名必須在*的右邊
#include<stdio.h>typedef unsigned int uint;typedef int(*pArr_t)[10];   //對于數組、函數指針時不能將名字放在后面,而是*的右邊void (*pf_t)(int, int);int main()
{unsigned int num;uint num2;int(*pb)[10];pArr_t pa;void(*pf1)(int,int);pf_t pf;return 0;
}

?利用 tepedef 對 void (*signal(int , void(*)(int) ) )(int); 進行簡化:

void (*signal(int, void(*)(int)))(int);typedef void(*pf_t)(int);
pf_t signal(int, pf_t);
5、函數指針數組

?指針數組 --- 是數組,每個元素是指針,char* arr[5], double* arr[5]

?數組指針 --- 是指針,指向數組的指針,int(*pa)[10]=&arr, char(*pa)[5]=&arr

? ? ? ?數組是一個存放相同類型數據的存儲空間,那要把函數的地址存到一個數組中,那這個數組就叫函數指針數組,是數組,里面的元素是函數指針。


再次理解函數指針:

#include<stdio.h>
int test(char* c,int n)
{//...return 0;
}
int main()
{int (*pf)(char*, int) = &test;(*pf)("abcdef", 10);  pf("abcdef", 10);test("abcdef", 10)return 0;
}

函數指針數組實例:

#include<stdio.h>
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;
}
int main()
{int (*pf)(int, int) = Add; //pf是函數指針int (*pf1[4])(int,int) = { Add,Sub,Mul,Div };//存放函數指針的數組// 0   1   2   3int i = 0;for (i = 0; i < 4; i++){int result=pf1[i](6, 2);/*int result = (*pf1[i])(6, 2);*/printf("%d\n", result);}return 0;
}
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_s("%d", &input);if ((input <= 4 && input >= 1)){printf("輸?操作數:");scanf_s("%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/713042.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/713042.shtml
英文地址,請注明出處:http://en.pswp.cn/news/713042.shtml

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

相關文章

教你如何判斷Java代碼中異步操作是否完成

在許多應用程序中&#xff0c;我們經常使用異步操作來提高性能和響應度。在Java中&#xff0c;我們可以使用多線程或者異步任務來執行耗時操作&#xff0c;并且在后臺處理過程完成后獲取結果。但是&#xff0c;在使用異步操作時&#xff0c;我們通常需要知道異步任務何時完成&a…

【C++精簡版回顧】15.繼承派生

1.繼承派生的區別 繼承&#xff1a;子繼父業&#xff0c;就是子類完全繼承父類的全部內容 派生&#xff1a;子類在父類的基礎上發展 2.繼承方式 1.public繼承為原樣繼承 2.protected繼承會把public繼承改為protect繼承 3.private繼承會把public&#xff0c;protected繼承改為pr…

怎么摳圖把把人物扣下來?簡單快捷的摳圖方法

相信很多新手小白在初入設計行業時&#xff0c;對于摳圖怎么把人物扣下來都是一頭霧水。摳圖作為設計中常用的一種技術&#xff0c;能夠幫助我們快速提取圖片中的某個部分&#xff0c;進行合成或者修改。對于老手來說&#xff0c;摳圖或許是再熟悉不過的操作&#xff0c;但對于…

【C++】仿函數與函數指針:C++中的強大工具

文章目錄 什么是函數指針&#xff1f;函數指針的基本語法&#xff1a; 什么是仿函數&#xff1f;仿函數的基本用法&#xff1a; 仿函數與函數指針的比較應用場景代碼舉例函數指針示例仿函數示例定義排序規則舉例使用函數指針使用仿函數哪一個更好&#xff1f; 結論 在C編程中&a…

c語言---數組(超級詳細)

數組 一.數組的概念二. 一維數組的創建和初始化2.1數組的創建2.2數組的初始化錯誤的初始化 2.3 數組的類型 三. 一維數組的使用3.1數組的下標3.2數組元素的打印3.2數組元素的輸入 四. 一維數組在內存中的存儲五. 二維數組的創建5.1二維數組的概念5.2如何創建二維數組 六.二維數…

【嵌入式學習】QT-Day4-Qt基礎

簡單實現鬧鐘播報&#xff0c;設置時間&#xff0c;當系統時間與設置時間相同時播報語音5次&#xff0c;然后停止。如果設置時間小于當前系統時間&#xff0c;則彈出消息提示框&#xff0c;并清空輸入框。 #include "my_clock.h" #include "ui_my_clock.h&quo…

批量處理圖片,像素隨心所欲,創意無限釋放!

在數字化時代&#xff0c;圖片批量處理已成為設計、攝影、電商等多個領域不可或缺的一部分。然而&#xff0c;傳統的圖片批量處理方式往往效率低下&#xff0c;難以滿足現代人對高效和精準的需求。現在&#xff0c;我們為您帶來了一款一鍵圖片批量處理工具&#xff0c;讓您自由…

【Vue】更換瀏覽器默認 logo

更換瀏覽器默認logo為自定義圖片 一. 瀏覽器默認 logo二. 替換為自定義logo三. 步驟3.1 轉換大小3.1.1 查看圖片尺寸3.1.2 修改尺寸&#xff08;為32px 32px&#xff09; 3.2 替換成功 一. 瀏覽器默認 logo 二. 替換為自定義logo 三. 步驟 3.1 轉換大小 將自定義 logo 轉為323…

docker搭建zookeeper集群

文章目錄 1. 集群搭建2. Leader選舉3. Zookeeper集群角色 1. 集群搭建 這里我們使用docker-compose 搭建偽集群 version: 3.1 services:zoo1:image: zookeeperrestart: alwayscontainer_name: zoo1ports:- 2181:2181volumes:- /home/zk/zoo1/data:/data- /home/zk/zoo1/datal…

React富文本編輯器開發(二)

我們接著上一節的示例內容&#xff0c;現在有如下需求&#xff0c;我們希望當我們按下某個按鍵時編輯器有所反應。這就需要我們對編輯器添加事件功能onKeyDown, 我們給 Editor添加事件&#xff1a; SDocor.jsx import { useState } from react; import { createEditor } from…

【LeetCode-178】最長重復子串(動歸)

目錄 LeetCode718.最長重復子串 題目描述 解法1&#xff1a;動態規劃 代碼實現 題目鏈接 題目描述 給兩個整數數組 A 和 B &#xff0c;返回兩個數組中公共的、長度最長的子數組的長度。 示例&#xff1a; 輸入&#xff1a; A: [1,2,3,2,1] B: [3,2,1,4,7] 輸出&…

協方差矩陣計算

文章目錄 協方差矩陣計算原理python實現 協方差矩陣 協方差矩陣反映了兩個隨機變量變化時是同向還是反向的&#xff08;相關性&#xff09;。 如果協方差>0&#xff0c;則說明這兩個隨機變量同向變化。 協方差矩陣<0&#xff0c;則說明是反向變化。 協方差矩陣0&#xf…

【LeetCode】347.前 K 個高頻元素

今日學習的文章鏈接和視頻鏈接 leetcode題目地址&#xff1a;347.前 K 個高頻元素 代碼隨想錄題解地址&#xff1a;代碼隨想錄 題目簡介 給你一個整數數組 nums 和一個整數 k &#xff0c;請你返回其中出現頻率前 k 高的元素。你可以按 任意順序 返回答案。 看到題目的第一…

Python-公共操作與推導式

一、公共操作 運算符 (1)&#xff1a;合并操作符 適用范圍&#xff1a;字符串、列表、元組 (2)*&#xff1a;復制 適用范圍&#xff1a;字符串、列表、元組 (3)in&#xff1a;判斷某字符串存在 (4)not in&#xff1a;判斷某字符串不存在 list1[1,2] list2[3,4] t1(1,2) t2(3,…

手把手教你在 CentOS7 上部署Ngrok (踩坑填坑)

一、項目準備 1、一個可用的域名&#xff08;不是必須&#xff0c;但是最好有&#xff09; 2、一臺有公網IP的服務器 二、項目實施 本文的操作過程主要參考了《教你自己服務器搭建Ngrok》&#xff0c;但是隨著時間的推移&#xff0c;很多軟件因版本升級而產生了一些變化&…

掌握 MySQL 的數據類型

知道了表是由不同數據類型的列組成的&#xff0c;然后填充了一行一行的數據。 當我們要創建表的時候&#xff0c;就要根據業務需求&#xff0c;選擇合適的數據類型。比如在實戰項目中&#xff0c;文章表就是由下面這些不同數據類型的字段定義的。 目前用到了 bigint、tinyint、…

vue3+ts+vite使用mock數據

安裝以下命令 npm i vite-plugin-mock --save-dev npm i mockjs --save-dev 在根路徑下創建mock文件夾 mock\user.ts const menuList [{path: /system,component: Layout,name: system,meta: {title: 系統管理,icon: Setting,roles: [sys:manage]},children: [{path: /depar…

blender 導出bvh x軸旋轉90度

目錄 blender導出模型后&#xff0c;x 軸旋轉了 90 度&#xff0c;和縮放不對的問題 bvh&#xff1a; blender導出模型后&#xff0c;x 軸旋轉了 90 度&#xff0c;和縮放不對的問題 博文解決了fbx格式d軸旋轉90度的問題&#xff0c;bvh的沒有解決 Blender - Export FBX to …

Java中使用poi+poi-tl實現根據模板導出word文檔

場景 若依管理系統前后端分離版基于ElementUI和SpringBoot怎樣實現Excel導入和導出: 若依管理系統前后端分離版基于ElementUI和SpringBoot怎樣實現Excel導入和導出_若依導出前端獲得到后端的execl流之后怎么操作-CSDN博客 上面講的是Excel的導出&#xff0c;如果是需要根據w…

即插即用篇 | YOLOv8 引入 MHSA 注意力機制 | 《Bottleneck Transformers for Visual Recognition》

論文名稱:《Bottleneck Transformers for Visual Recognition》 論文地址:https://arxiv.org/pdf/2101.11605.pdf 文章目錄 1 原理2 源代碼3 添加方式4 模型 yaml 文件template-backbone.yamltemplate-small.yamltemplate-large.yamltemplate-neck.yaml