[C]基礎11.深入理解指針(3)

  • 博客主頁:向不悔
  • 本篇專欄:[C]
  • 您的支持,是我的創作動力。

文章目錄

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


0、總結

在這里插入圖片描述

1、字符指針變量

思考字符數組和常量字符串的區別?經思考,發現,字符數組里的數組是可變的,常量字符串是不可變的。

#include <stdio.h>
int main()
{char arr[] = "abcdef";char* p1 = arr;*p1 = 'w';const char* p2 = "abcdef";// *p2 = 'w';printf("%s\n", p1);printf("%s\n", p2);return 0;
}
運行:
wbcdef
abcdef

如圖加深理解:

我們知道,字符指針變量一般使用如下:

#include <stdio.h>
int main()
{char ch = 'w';char* pc = &ch;*pc = 'z';printf("%c\n", *pc);return 0;
}

那么,為什么這個也可以呢?

#include <stdio.h>
int main()
{const char* pstr = "hello world.";printf("%s\n", pstr);return 0;
}

經思考,代碼const char* pstr = "hello world.";本質上是把字符串首字符的地址放到了pstr中。如圖:

《劍指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;
}
運行:
str1 and str2 are not same
str3 and str4 are same

在C/C++中,常量字符串會被存儲到一個共享的內存區域。當多個指針指向相同的常量字符串時,它們實際上指向同一塊內存。而如果用相同的常量字符串初始化不同的數組,則會為每個數組開辟獨立的內存塊。因此,str1str2指向不同的內存,而str3str4指向同一塊內存。

2、數組指針變量

2.1 數組指針變量是什么?

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

思考以下,哪個是數組指針變量?

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

結論:

  • p1是指針數組,具體的說,一個包含10個指針的數組,每個指針指向一個int
  • p2是數組指針,具體地說,指向一個包含10個int的數組。
  • 主要區別在于括號的使用,括號的位置決定了聲明的優先級。p1是數組優先,p2是指針優先。
  • 值得注意:[]的優先級要高于*號的,所以聲明數組指針,就必須加上()來保證p2先和*結合。

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

數組指針變量是用來存放數組地址的,因此用&數組名。如下:

#include <stdio.h>
int main()
{char arr[10] = { 0 };char* p1 = arr;printf("%p\n", p1);printf("%p\n", p1+1);// 初始化數組指針變量char (*p2)[10] = &arr;printf("%p\n", p2);printf("%p\n", p2+1);return 0;
}
運行(32位平臺):
00B5F7A8
00B5F7A9
00B5F7A8
00B5F7B2

經調試,可知,&arrp2的類型是完全一致的。

數組指針類型解析:

int      (*p)        [10]    = &arr;|         |           ||         |           ||         |           p指向數組的元素個數|         p是數組指針變量名 p指向的數組的元素類型

3、二維數組傳參的本質

二維數組的數組名表示的就是第一行的地址,是一維數組的地址。所以第一行的地址的類型是數組指針類型。二維數組傳參本質上也是傳遞了地址,傳遞的是第一行這個一維數組的地址。形參可以寫成指針形式的,如下:

#include <stdio.h>
void print(int (*ptr)[5], int r, int c)
{for (int i = 0; i < r; i++){for (int j = 0; j < c; j++){printf("%d ", *(*(ptr + 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} };print(arr, 3, 5);return 0;
}
運行:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7

總結:

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

4、函數指針變量

4.1 函數指針變量的創建

函數指針變量用來存放函數地址的,未來通過地址能夠調用函數。如下:

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

可知,函數是有地址的,有兩個方式:

  • 函數名就是函數的地址
  • &函數名獲得函數的地址。

如果要把函數的地址存放起來,就需要創建函數指針變量,函數指針變量的寫法跟數組指針寫法非常類似,如下:

#include <stdio.h>
void test()
{printf("hello\n");
}
void (*pf1)() = &test;
void (*pf2)() = test;int Add(int x, int y)
{return x + y;
}
int (*pf3)(int, int) = &Add;
int (*pf4)(int, int) = Add;  // x和y寫上或者省略都是可以的。

函數指針類型解析:

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

4.2 函數指針變量的使用

#include <stdio.h>int Add(int x, int y)
{return x + y;
}int main()
{int (*pf)(int, int) = Add;printf("%d\n", (*pf)(2, 3));printf("%d\n", pf(3, 5));return 0;
}
運行:
5
8

4.3 兩端有趣的代碼

代碼1:

(*(void (*)())0)();

代碼2:

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

這兩段代碼均出自:《C陷阱和缺陷》這本書。

4.3.1 typedef關鍵字

typedef是用來類型重命名的,可以將復雜的類型進行簡單化。

例如,可以對unsigned int進行簡化,如下:

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(*pf_t)(int);

因此,如果對void (*signal(int, void(*)(int)))(int);進行簡化,可以這樣寫:

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

5、函數指針數組

把函數的地址存放到一個數組中,那么這個數組就叫函數指針數組,以下哪個是函數指針數組?

int (*parr1[3])();
int *parr2[3]();
int (*)() parr3[3];

答案是:parr1

parr1先和[]結合,說明parr1是數組,那么數組內容是什么呢?

int (*)()類型的函數指針。

6、轉移表

函數指針數組的用途一般為轉移表

舉個例子,計算機通過函數指針數組的實現:

#define _CRT_SECURE_NO_WARNINGS 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;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/bicheng/77771.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/77771.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/77771.shtml

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

相關文章

【漏洞復現】CVE-2024-38856(ApacheOfbiz RCE)

【漏洞復現】CVE-2024-38856&#xff08;ApacheOfbiz RCE&#xff09; 1. 漏洞描述 Apache OFBiz 是一個開源的企業資源規劃&#xff08;ERP&#xff09;系統。它提供了一套企業應用程序&#xff0c;用于集成和自動化企業的許多業務流程。 這個漏洞是由于對 CVE-2023-51467 的…

C++入門小館: 深入string類(二)

嘿&#xff0c;各位技術潮人&#xff01;好久不見甚是想念。生活就像一場奇妙冒險&#xff0c;而編程就是那把超酷的萬能鑰匙。此刻&#xff0c;陽光灑在鍵盤上&#xff0c;靈感在指尖跳躍&#xff0c;讓我們拋開一切束縛&#xff0c;給平淡日子加點料&#xff0c;注入滿滿的pa…

【nginx】服務的信號控制

目錄 1. 說明2. 常用信號及作用3. 信號控制的具體操作3.1 獲取 Nginx 主進程 PID3.2 發送信號 4. 應用場景4.1 重新加載配置文件4.2 日志切割 5. 平滑升級 Nginx6. 注意事項 1. 說明 1.Nginx 的信號控制是其管理服務的重要機制&#xff0c;通過向主進程發送特定信號&#xff0…

Ubuntu下展銳刷機工具spd_dump使用說明

spd_dump使用說明 源碼地址&#xff1a;https://github.com/ilyakurdyukov/spreadtrum_flash 編譯環境準備&#xff1a; sudo apt update sudo apt install git sudo apt install build-essential sudo apt install libusb-1.0-0-devIf you create /etc/udev/rules.d/80-spd…

鴻蒙NEXT開發LRUCache緩存工具類(單例模式)(ArkTs)

import { util } from kit.ArkTS;/*** LRUCache緩存工具類&#xff08;單例模式&#xff09;* author 鴻蒙布道師* since 2025/04/21*/ export class LRUCacheUtil {private static instance: LRUCacheUtil;private lruCache: util.LRUCache<string, any>;/*** 私有構造函…

筆記:react中 父組件怎么獲取子組件中的屬性或方法

在子組件中我們可以使用下面兩個方法去暴露你所要放行的屬性或方法&#x1f447; 1.useImperativeHandle 2.orwardRef 搭配使用例子 import React, { useState, forwardRef, useImperativeHandle } from "react"function Son(props, ref) {const [data] useStat…

《潯川代碼編輯器v2.0內測(完整)報告》

一、測試概述 潯川代碼編輯器v2.0經過為期五周的封閉內測&#xff0c;累計提交了186份測試報告。本次內測主要針對v2.0新增的多語言支持、AI輔助編碼、性能優化等核心功能進行全面驗證。 二、測試環境 - 硬件配置&#xff1a;i7-12700H/16GB RAM/512GB SSD - 操作系統&#xf…

ubuntu18.04安裝QT問題匯總

1、Could not determine which ”make“ command to run. Check the ”make“ step in the build configuration.” sudo apt-get install clang sudo apt-get install build-essential sudo apt-get install libqt4-dev 2、fatal error: sqlite3.h: No such …

基于單片機的BMS熱管理功能設計

標題:基于單片機的BMS熱管理功能設計 內容:1.摘要 摘要&#xff1a;在電動汽車和儲能系統中&#xff0c;電池管理系統&#xff08;BMS&#xff09;的熱管理功能至關重要&#xff0c;它直接影響電池的性能、壽命和安全性。本文的目的是設計一種基于單片機的BMS熱管理功能。采用…

CSS基礎-即學即用 -- 筆記1

目錄 前言CSS 基礎1. 層疊樣式表來源理解優先級源碼順序經驗法則繼承inherit 關鍵字initial 關鍵字 2. 相對單位em 和 rem響應式面板視口的相對單位使用vw定義字號使用calc()定義字號自定義屬性&#xff08;即CSS變量&#xff09; 3. 盒模型調整盒模型 前言 只需一分鐘就能學會…

Linux中服務器時間同步

簡單介紹 在 redhat 8 之前&#xff0c;時間同步服務是使用 NTP&#xff08;網絡時間協議&#xff09;來實現的&#xff0c;在 redhat 8 及之 后使用是 NTP 的實現工具 chrony 來實現時間同步。 在 redhat 8 及之后&#xff0c;默認情況下已經安裝好 chrony 軟件并已經開機啟…

讓SQL飛起來:搭建企業AI應用的SQL性能優化實戰

我上一篇文章已經講解過了如何使用公開的AI模型來優化SQL.但這個優化方法存在一定的局限性.因為公開的AI模型并不了解你的數據表結構是什么從而導致提供的優化建議不太準確.而sql表結構又是至關重要的安全問題,是不能泄露出去的.所以在此背景下我決定搭建一個自己的AI應用在內網…

小迪安全-112-yii反序列化鏈,某達oa,某商場,影響分析

yii是和tp一樣的框架 入口文件 web目錄下 相對tp比較簡單一些&#xff0c;對比tp找一下他的url結構 對應的位置結構 這個contorllers文件的actionindex就是觸發的方法 控制器&#xff0c;指向的index文件&#xff0c;就可以去視圖模塊看index文件 這就是前端展示的文件 自…

自定義多頭注意力模型:從代碼實現到訓練優化

引言 在自然語言處理和序列生成任務中,自注意力機制(Self-Attention)是提升模型性能的關鍵技術。本文將通過一個自定義的PyTorch模型實現,展示如何構建一個結合多頭注意力與前饋網絡的序列生成模型(如文本或字符生成)。該模型通過創新的 MaxStateSuper 模塊實現動態特征…

動態LOD策略細節層級控制:根據視角距離動態簡化遠距量子態渲染

動態LOD策略在量子計算可視化中的優化實現 1. 細節層級控制:動態簡化遠距量子態渲染 在量子計算的可視化中,量子態通常表現為高維數據(如布洛赫球面或多量子比特糾纏態)。動態LOD(Level of Detail)策略通過以下方式優化渲染性能: 距離驅動的幾何簡化: 遠距離渲染:當…

Java 泛型使用教程

簡介 Java 泛型是 JDK 5 引入的一項特性&#xff0c;它提供了編譯時類型安全檢測機制&#xff0c;允許在編譯時檢測出非法的類型。泛型的本質是參數化類型&#xff0c;也就是說所操作的數據類型被指定為一個參數。 泛型的好處&#xff1a; 編譯期檢查類型安全 避免強制類型轉…

Leetcode - 周賽446

目錄 一、3522. 執行指令后的得分二、3523. 非遞減數組的最大長度三、3524. 求出數組的 X 值 I四、3525. 求出數組的 X 值 II 一、3522. 執行指令后的得分 題目鏈接 本題就是一道模擬題&#xff0c;代碼如下&#xff1a; class Solution {public long calculateScore(String…

【更新完畢】2025泰迪杯數據挖掘競賽A題數學建模思路代碼文章教學:競賽論文初步篩選系統

完整內容請看文末最后的推廣群 基于自然語言處理的競賽論文初步篩選系統 基于多模態分析的競賽論文自動篩選與重復檢測模型 摘要 隨著大學生競賽規模的不斷擴大&#xff0c;參賽論文的數量激增&#xff0c;傳統的人工篩選方法面臨著工作量大、效率低且容易出錯的問題。因此&…

計算機視覺與深度學習 | RNN原理,公式,代碼,應用

RNN(循環神經網絡)詳解 一、原理 RNN(Recurrent Neural Network)是一種處理序列數據的神經網絡,其核心思想是通過循環連接(隱藏狀態)捕捉序列中的時序信息。每個時間步的隱藏狀態 ( h_t ) 不僅依賴當前輸入 ( x_t ),還依賴前一時間步的隱藏狀態 ( h_{t-1} ),從而實現…

AI速讀:解鎖LLM下Game Agent的奇妙世界

在 AI 浪潮中&#xff0c;大語言模型&#xff08;LLMs&#xff09;正重塑游戲智能體格局。想知道基于 LLMs 的游戲智能體如何運作&#xff0c;在各類游戲中有何驚艷表現&#xff0c;未來又將走向何方&#xff1f; 大型語言模型&#xff08;LLMs&#xff09;的興起為游戲智能體的…