指針---進階篇(二)

指針---進階篇(二)

  • 前言
  • 一、函數指針
    • 1.拋磚引玉
    • 2.如何判斷函數指針?(方法總結)
  • 二、函數指針數組
    • 1.什么是函數指針數組?
    • 2.講解函數指針數組
    • 3.模擬計算器:講解函數指針數組
  • 三、指向函數指針數組的指針
  • 四、回調函數
  • 五、qsort排序

前言

那么好了好了,寶子們,從今天開始開始總結暑假博客,從指針開始,后續,來吧開始整活!??

一、函數指針

1.拋磚引玉

直接上代碼:

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

在這里插入圖片描述
我打印的是函數的地址。但是如果我們想要把函數的地址保存起來,我們該怎么樣操作呢?

void test()
{printf("hehe\n");
}
//下面pfun1和pfun2哪個有能力存放test函數的地址?
void (*pfun1)();
void *pfun2();

在這里我們需要明白一點,當時我們學數組的時候,我們知道數組名就是數組首元素的地址,在這里函數也是一樣的,函數名也可以代表函數的首元素地址!
首先,能給存儲地址,就要求pfun1或者pfun2是指針,那哪個是指針?前面的進階一里面我們講過了:數組指針。我們呢可以類比一下
答案是:pfun1可以存放。pfun1先和*結合,說明pfun1是指針,指針指向的是一個函數,指向的函數無參數,返回值類型為void。

2.如何判斷函數指針?(方法總結)

比如說下面這段代碼:


int add(int x, int y)//加法
{return x + y;
}int main()
{int (*pf)(int, int) = add;int m = add(3, 4);int n = pf(4, 5);printf("%d %d\n", m, n);return 0;
}

在這里我來教一下大家怎樣判別,什么是函數指針,指針函數,數組指針,指針數組之類的。
就以這個為例:int (*pf)(int,int)=add;
在這個語句里面變量是pf,pf被一個小括號擴住,pf先與 *結合,所以說它以指針結尾。首先分析完了他是一個指針,然后他是什么類型的指針呢?后面是參數,兩個參數類型都是int,前面是返回類型也是int,所以說它是一個標標準準的函數指針。
(在這里一個規律就是:變量和XX先結合就以XX為結尾)

二、函數指針數組

1.什么是函數指針數組?

首先我們要明白什么是數組?
數組的概念是:數組是一個儲存相同元素的集合。
不要害怕他前面這么復雜。又是函數,又是指針,又是數組,我們該如何判斷呢?還是運用我上面的規律總結。

2.講解函數指針數組

好的,我們現在直接上栗子:


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 (*jia)(int, int) = add;int (*jian)(int, int) = sub;int (*cheng)(int, int) = mul;int (*chu)(int, int) = div;int (*pfarr[4])(int, int) = { add,sub,mul,div };//上面的數字里面儲存的都是各個函數名,所以就是儲存的函數的地址return 0;
}

我們來分析一下這個 函數指針數組:
int (*pfarr[4])(int, int) = { add,sub,mul,div };
看這樣復雜的語句的時候,我們先看變量名,變量名是pfarr,先看變量名與誰先結合,由于這里的方括號[ ]的結合度比 *高,所以pfarr先與方括號[ ]結合,所以說它就是以數組來結尾的。

只要你有幾個函數,并且函數的返回類型都是一模一樣的,你就可以把這幾個函數的地址放在一個數組里面,那么這個數組就叫做函數指針數組!
如何寫一個函數指針數字呢?那當然是從函數指針來寫起,然后再加一個數組

3.模擬計算器:講解函數指針數組

1.常規的使用普通函數來實現


#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("*************************\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;
}

2.函數指針數組實現


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

三、指向函數指針數組的指針

何為指向函數指針數組的指針?簡單的來講就是函數指針數組的地址
指向函數指針數組的指針是一個 指針 指針指向一個 數組 ,數組的元素都是 函數指針 ;

上代碼:

#define _CRT_SECURE_NO_WARNINGS 1 
void test(const char* str)
{printf("%s\n", str);
}
int main()
{//函數指針pfunvoid (*pfun)(const char*) = test;//函數指針的數組pfunArrvoid (*pfunArr[5])(const char* str);pfunArr[0] = test;//指向函數指針數組pfunArr的指針ppfunArrvoid (*(*ppfunArr)[10])(const char*) = &pfunArr;return 0;
}

四、回調函數

回調函數就是一個通過函數指針調用的函數。如果你把函數的指針(地址)作為參數傳遞給另一個函數,當這個指針被用來調用其所指向的函數時,我們就說這是回調函數。回調函數不是由該函數的實現方直接調用,而是在特定的事件或條件發生時由另外的一方調用的,用于對該事件或條件進行響應。

五、qsort排序

接下來我通過用qsort排序來展示一下回調函數的魅力:

#include <stdio.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* 的指針,講解 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*)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 };//char *arr[] = {"aaaa","dddd","cccc","bbbb"};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;
}

好了,今天的分享就到這里了

如果對你有幫助,記得點贊👍+關注哦!
我的主頁還有其他文章,歡迎學習指點。關注我,讓我們一起學習,一起成長吧!
?

?在這里插入圖片描述

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

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

相關文章

Maven基礎之倉庫、命令、插件機制

文章目錄 Maven 倉庫中央倉庫和本地倉庫中央倉庫本地倉庫 Maven 命令generate 命令compile 命令clean 命令test 命令package 命令install 命令 Maven 插件機制官方插件&#xff1a;Compile 插件Tomcat 7 插件 Maven 倉庫 中央倉庫和本地倉庫 [?] 簡單一點說 中央倉庫是一個網…

Redis復制

在Redis中&#xff0c;用戶可以通過執行SLAVEOF命令或者設置slaveof選項&#xff0c;讓一個服務器去復制(replicate) 另一個服務器&#xff0c;我們稱呼被復制的服務器為主服務器(master)&#xff0c;而對主服務器進行復制的服務器則被稱為從服務器(slave)&#xff0c;如下圖所…

Vue修飾符

事件修飾符 在Vue 2.0中&#xff0c;事件修飾符允許我們在處理事件時對其進行修改或增強。以下是一些常用的事件修飾符&#xff1a; .stop&#xff1a;阻止事件冒泡。使用此修飾符后&#xff0c;父元素的相同事件不會再觸發。.prevent&#xff1a;阻止事件的默認行為。比如&…

mybatis 中的<![CDATA[ ]]>用法及說明

<![CDATA[ ]]>作用 <![CDATA[ ]]> 在mybatis、ibatis等書寫SQL的xml中比較常見&#xff0c;是一種XML語法&#xff0c;他的作用是 可以忽略xml的轉義&#xff08;在該標簽中的語句和字符原本是什么樣的&#xff0c;在拼接成SQL后還是什么樣的&#xff09; 使用&a…

代碼生成模型任務設計

背景&#xff1a; 模型應該具備&#xff0c;理解代碼的能力、知道代碼規則的能力、知道關鍵詞和變量的能力、知道代碼邏輯的能力、文本到代碼翻譯能力、代碼關聯能力、代碼續寫能力。 代碼理解能力&#xff1a;pretrain讓模型讀足夠多代碼、記住代碼一些規則、代碼問答、基于…

PHP codeigniter4 搭配Nginx

> 主要是為了用Nginx運行PHP環境 1. Nginx 官方文檔的配置 default.conf? This configuration enables URLs without “index.php” in them and using CodeIgniter’s “404 - File Not Found” for URLs ending with “.php”. server {listen 80;listen [::]:80;se…

springboot 基礎

鞏固基礎&#xff0c;砥礪前行 。 只有不斷重復&#xff0c;才能做到超越自己。 能堅持把簡單的事情做到極致&#xff0c;也是不容易的。 SpringBoot JavaEE 簡介 JavaEE的局限性&#xff1a; 1、過于復雜&#xff0c;JavaEE正對的是復雜的分布式企業應用&#xff0c;然而現實…

MySQL表的基礎的增刪改查

增(insert into) 插入所有列的數據 不寫具體列名要確保字段都對應正確 -- 假設你有一個名為 "employees" 的表&#xff0c;有多個列 INSERT INTO employees VALUES (101, Alice, Manager, 50000);插入指定列的數據 -- 假設你有一個名為 "students" 的表&…

爬蟲如何應對網站的反爬機制?如何查找user-agent對應的值

import requestsurl https://movie.douban.com/top250 response requests.get(url) # 查看結果 print(response)在requests使用一文中我們有講到&#xff0c;當狀態碼不是200時表示爬蟲不可用&#xff0c;也就是說我們獲取不到網頁源代碼。但是我們還是可以掙扎一下&#xff…

一文秒懂HTTP協議到底是什么?原理?

目錄 1.什么是http協議&#xff1f; 2.http協議的版本&#xff1f; 3.http文本框架 4.http請求報文 5.http報文格式 6.http響應報文 7.HTTP的狀態碼 8.HTTP首部介紹 9.什么是URL和URI&#xff1f; 10.CGI是什么&#xff1f; 1.什么是http協議&#xff1f; http&#…

測試架構師如何落地性能測試方案(一)

背景描述&#xff1a; 最近剛接手一個新項目&#xff0c;在最開始的時候要求對這個項目做性能測試&#xff0c;產品經理也給不出性能需求&#xff0c;只因為這個項目是電商項目&#xff0c;可能會有高并發&#xff0c;秒殺的場景&#xff0c;所以產品經理要求我們對這個項目必…

vue3父子組件傳值;vue3子組件傳值給父組件;vue3子組件監聽父組件接口傳值;父子組件事件調用

代碼在文末&#xff0c;均可直接復制使用 本文主要描述&#xff0c;父子組件傳值、調用等問題 文章目錄 問題1&#xff1a;子組件接收不到父組件傳值問題2&#xff1a;子組件接受的值&#xff0c;修改后&#xff0c;發現父組件值也改變了問題3&#xff1a;子組件接受值&#xf…

07-HDFS入門及shell命令

1 文件系統 是一種存儲和組織數據的方法&#xff0c;它使得文件訪問和查詢變得容易使得文件和樹形目錄的抽象邏輯概念代替了磁盤等物理設備使用數據塊的概念&#xff0c;用戶使用文件系統來保存數據不必關心數據底層存在硬盤哪里&#xff0c;只需記住這個文件的所屬目錄和文件…

STM32基于CubeIDE和HAL庫 基礎入門學習筆記:物聯網項目開發流程和思路

文章目錄&#xff1a; 第一部分&#xff1a;項目開始前的計劃與準備 1.項目策劃和開發規范 1.1 項目要求文檔 1.2 技術實現文檔 1.3 開發規范 2.創建項目工程與日志 第二部分&#xff1a;調通硬件電路與驅動程序 第三部分&#xff1a;編寫最基礎的應用程序 第四部分&…

opencv帶GStreamer之Windows編譯

目錄 1、下載GStreamer和安裝2. GSTReamer CMake配置3. 驗證是否配置成功 1、下載GStreamer和安裝 下載地址如下&#xff1a; gstreamer-1.0-msvc-x86_64-1.18.2.msi gstreamer-1.0-devel-msvc-x86_64-1.18.2.msi 安裝目錄無要求&#xff0c;主要是安裝完設置環境變量 xxx\1…

【css】漸變

漸變是設置一種顏色或者多種顏色之間的過度變化。 兩種漸變類型&#xff1a; 線性漸變&#xff08;向下/向上/向左/向右/對角線&#xff09; 徑向漸變&#xff08;由其中心定義&#xff09; 1、線性漸變 語法&#xff1a;background-image: linear-gradient(direction, co…

一圖搞懂二層交換機、三層交換機和路由器的區別

二層交換機、三層交換機、路由器的區別 二層交換機、三層交換機、路由器對比二層交換機三層交換機路由器工作在第幾層數據鏈路層&#xff08;第二層&#xff09;網絡層&#xff08;第三層&#xff09;網絡層&#xff08;第三層&#xff09;功能學習和轉發幀根據IP地址轉發數據…

數據鏈路層是什么?

數據鏈路層承接上層的數據&#xff0c;然后使用下層提供的信道&#xff0c;按照一定的規則&#xff0c;進行數據傳輸。 物理層提供了傳輸媒體與連接&#xff08;信道&#xff09;&#xff08;數據鏈路層使用的信道通常是物理層提供的信道&#xff09;&#xff0c;即提供了比特…

webpack自動注冊全局組件

例如&#xff0c;在form文件夾下有許多流程類表單&#xff0c;一個一個注冊引入非常浪費時間&#xff0c;可使用webpack的require.context方法來自動導入 import { firstToUpper } from /utils/methodsconst taskTable {install: vue > {const req require.context(../fo…

springboot結合element-ui實現增刪改查,附前端完整代碼

實現功能 前端完整代碼 后端接口 登錄&#xff0c;注冊&#xff0c;查詢所有用戶&#xff0c;根據用戶名模糊查詢&#xff0c;添加用戶&#xff0c;更新用戶&#xff0c;刪除用戶 前端 注冊&#xff0c;登錄&#xff0c;退出&#xff0c;用戶增刪改查&#xff0c;導航欄&#…