【C語言】指針(三)

目錄

一、字符指針

1.1?? 使用場景

1.2 ? 有關字符串筆試題

二、數組指針

2.1 ? 數組指針變量

2.2 ? 數組指針類型

2.3 ? 數組指針的初始化

三、數組指針的使用

3.1 ? 二維數組和數組名的理解

3.2 ? 二維數組傳參

四、函數指針

4.1 ? 函數的地址

4.2 ? 函數指針變量

4.3 ? 函數指針變量的使用

五、函數指針數組

六、轉移表


一、字符指針

字符指針:指向字符的指針?

1.1?? 使用場景

【使用場景一】

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

【使用場景二】

#include <stdio.h>int main()
{char* p = "abcdef";//這里把首字符a的地址賦給了變量pprintf("%s\n", p);return 0;
}

注意

這里printf中%s的格式邏輯是:從給定的地址處開始,逐個向后輸出字符,直到遇見結束標記\0為止。解引用的操作符在printf函數內部造成。

如果由用戶解引用,那printf函數將只能拿到單個字符,反而無法實現功能。

易錯點1:

場景二中如果修改*p的值,代碼就會報錯。報錯類型如下:

原因:

"abcdef"是個常量字符串。

常量字符串的意思是:這個字符串本身是不能被更改的。

而這個*p沒有被限制,它其實是可以去改變后面的字符串的,所以char* p="abcdef"; 報警告是正常的。

防止方法:在char*p 前加const

易錯點2:

不能解引用p,解引用打印的是一個字符,一個字符不能用%s打印。

char* p = "abcdef";
printf("%s", *p);//error

以下是將字符串放在數組里面:

char arr[] = "abcdef";

而數組的內容是可變的。

1.2 ? 有關字符串筆試題

#include <stdio.h>int main()
{char arr1[] = "hello";char arr2[] = "hello";const char* p1 = "hello";const char* p2 = "hello";if (arr1 == arr2)printf("arr1=arr2\n");elseprintf("arr1!=arr2\n");if (p1 == p2)printf("p1=p2\n");elseprintf("p1!=p2\n");return 0;
}

運行結果如下:

原因如下:

arr1!=arr2

  1. 我們知道:arr1和arr2是數組首元素的地址,這兩個數組是兩塊獨立的內存空間,它們只是存儲的內容相同,都是hello字符串。

p1=p2

  1. 這里的p1和p2指向的是同一個常量字符串。c/c++會把常量字符串存儲到一個單獨的一個內存區域,當幾個指針指向同一個字符串的時候,它們實際會指向同一塊內存(代碼段中)。
  2. arr1,arr2,p1,p2都是放在棧區,指向的hello(常量字符串)是放在代碼段。

二、數組指針

整型指針:存放整型變量的地址,能夠指向整型數據的指針

浮點型指針:存放浮點型變量的地址,能夠指向浮點型數據的指針

數組指針:存放數組的地址,指向數組的指針

2.1 ? 數組指針變量

看下面兩行代碼,p1,p2分別是什么?

int* p1[10];
int(*p2)[10];
  • p1是指針數組

p1是數組名,該數組里存放了10個元素,每個元素是int*類型

  • p2是數組指針

因為p2先和*結合,說明p是一個指針變量,然后指向一個大小為10個整型的數組,所以p是一個指針,指向一個數組,所以叫數組指針。

注意:

  1. [ ]的優先級要高于*號,所以必須加上()來保證p先和*結合。
  2. int(*p2)[10];里面的*不是解引用的意思,這顆星就代表是指針,只有前面不加類型的時候才是解引用。

2.2 ? 數組指針類型

去掉指針變量名就是指針(變量)的類型。

看如下代碼:

它們跳過的字節不同就是因為他們的類型不同導致。

注意:

  1. 一個指針是否是野指針取決于你是否用它。這個指針雖然指向這里,但是沒有產生壞的結果。只要不使用它就沒關系。
  2. 雖然它指向的空間不屬于“我”,但是它并不危險。

2.3 ? 數組指針的初始化

數組指針存放的是數組的地址。

所以初始化的時候要給整個數組的地址。代碼如下:

注意:

[ ]里的元素個數不能省略,不然編譯器會自動認為是數組元素個數為0

三、數組指針的使用

3.1 ? 二維數組和數組名的理解

首先我們來理解一下二維數組及其數組名:

在c語言中,只有一維數組(N維數組的元素是數組),數組名作為指針時永遠指向第一個元素。

如:

  • 數組a[3];? *a=a[0]
  • 數組a[3][4];? *a=a[0]? ?只不過這時候a[0]又是一個數組。

這時候的a[0]又是指向它自己元素的第一個元素,又有 *a[0]=a[0][0]

  • 這種方式可以推廣到N維數組,所有數組直接對數組名取地址(如:&a),得到的指針指向該數組,而不是指向第一個元素。注意這點區別。

舉個例子:??int board[3][4];

board:一維數組的地址。

二維數組的數組名,數組名就是首元素地址。我們知道,可以把一維數組看作二維數組的元素。所以,board就是一維數組的地址。

&board:取出的是整個二維數組的地址。

board[0]:第一行第一個元素的地址。

解引用,相當于拿到第一行數組的數組名,也就是首元素地址,即第一行第一個元素的地址。

board[0]=*board=&board[0][0]

&board[0]:第一行的地址。

board=&board[0]

3.2 ? 二維數組傳參

清楚了上面的概念之后,我們來看下面一段代碼:

之前二維數組傳參時,形參部分用的是數組接收。

#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;
}

發現:實參arr是數組名,通過剛才的分析,直到數組名是首元素地址,首元素地址就是第一行的地址,也就是一維數組的地址,那么它的類型就是數組指針類型。

那么實參就可以寫成數組指針的形式,代碼如下:

#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;
}

問題:為什么*(p+i) 跳過的是一行數組?

回答:

數組的類型決定了它+1跳過幾個字節。

p的類型是:int(*)[5];

p是指向一個整型數組的,數組5個元素 int [5]

p+1 :跳過一個5個int元素的數組

四、函數指針

函數指針:存放函數的地址,指向函數的指針

4.1 ? 函數的地址

函數是否有地址呢?我們來測試一下:

由測試結果可知:函數存在地址,取&函數名函數名拿到的都是函數的地址。

4.2 ? 函數指針變量

我們通過函數指針來存儲函數的地址。

int (*pf) (int x, int y) = &Add;
int (*pf) (int , int ) = Add;//xy可以省略,只寫類型
  1. 地址要存起來,放到(指針)變量里去。
  2. pf是變量名,*pf說明是指針,指向的是函數,所以加上括號()。
  3. 函數的參數是int (參數名寫不寫無所謂,只要類型交代清楚即可),函數的返回值類型也是int。
int (*pf) (int x, int y)
|?? ? ?|?? ?------------
|?? ? ?|?? ??? ?|
| ? ? ?|?? ??? ?pf指向函數的參數類型和個數的交代
|?? ? ? 函數指針變量名
pf指向函數的返回類型int (*) (int x, int y) //pf函數指針變量的類型

4.3 ? 函數指針變量的使用

通過函數指針調用指針指向的函數。

代碼如下:

#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));//輸出結果為5printf("%d\n", pf(2, 3));//輸出結果為5return 0;
}

把函數的地址存到pf里,通過解引用pf找到函數,找到這個函數要調用這個函數,調用函數需要傳參,所以();(傳參),傳2,3。這樣的話它會把2和3相加,得到5。

問題一:為什么*pf必須帶上括號()呢?

回答:因為假如不帶上括號,調用返回5,就會對5進行解引用。

問題二:為什么能寫成pf(2, 3)這種形式?

回答:在C語言里,pf前的*其實是個擺設,可以不寫,也可以寫多個。

這是個技術細節問題,不涉及到語法原則,從不同的思考角度出發,觀點會略有不同,但不影響C語言實踐,初學者也不必過多糾結。

應用:通過函數指針的方式進行調用

#include <stdio.h>int Add(x, y)
{return x + y;}
void cale(int(*pf)(int,int))
{int a = 3;int b = 5;int ret = pf(a, b);printf("%d\n", ret);
}
int main()
{cale(Add);return 0;
}

這里的cale沒有直接調用Add函數,而是通過函數指針的方式進行調用。

五、函數指針數組

函數指針數組:把一個函數的地址存放到一個數組中。

是個函數指針類型的數組。

去掉 函數名+[ ] 就是該數組的類型。

int (*parr[3])( );

解釋:parr先和[ ]結合,說明parr是數組,數組的內容是int(*)()類型的函數指針。

當對函數指針數組進行初始化的時候,后面初始化的可以省略掉數組的大小,它會根據后面初始化的內容來確定數組的大小。

例如:

int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; 
int(*p[])(int x, int y) = { 0, add, sub, mul, div };

六、轉移表

使用了函數指針數組,避免大篇幅地修改內容;也可實現跳轉的功能。

所以函數指針數組也叫:轉移表。

計算機的一般實現

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

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

相關文章

JAVA面試題大全(十一)

1、為什么要使用 hibernate&#xff1f; 對JDBC訪問數據庫的代碼做了封裝&#xff0c;大大簡化了數據訪問層繁瑣的重復性代碼基于jdbc的主流持久化框架&#xff0c;是一個優秀的ORM實現&#xff0c;很大程度的簡化了dao層的編碼工作使用java的反射機制性能好&#xff0c;是一個…

【STL】C++ list 基本使用

目錄 一 list 常見構造 1 空容器構造函數&#xff08;默認構造函數&#xff09; 2 Fill 構造函數 3 Range 構造函數 4 拷貝構造函數 二 list迭代器 1 begin && end 2 rbegin && rend 三 list 容量操作 四 list 修改操作 1 assign 2 push_front &a…

【深度學習中的數據預處理技巧:提升模型性能的關鍵步驟】

文章目錄 前言數據標準化&#xff08;Normalization&#xff09;數據增強&#xff08;Data Augmentation&#xff09;缺失值處理&#xff08;Handling Missing Values&#xff09;特征編碼&#xff08;Feature Encoding&#xff09;結論 前言 在深度學習領域&#xff0c;數據預…

牛客NC362 字典序排列【中等 DFS Java/Go/PHP】

題目 題目鏈接&#xff1a; https://www.nowcoder.com/practice/de49cf70277048518314fbdcaba9b42c 解題方法 DFS&#xff0c;剪枝Java代碼 import java.util.*;public class Solution {/*** 代碼中的類名、方法名、參數名已經指定&#xff0c;請勿修改&#xff0c;直接返回…

【小筆記】如何在docker中更新或導入neo4j數據?

如何在docker中更新或導入neo4j數據&#xff1f; &#xff08;1&#xff09;背景&#xff1a; 我嘗試了4.4.9和5.19.0版本的Neo4j社區版&#xff0c;基于他們的鏡像創建容器后&#xff0c;需要導入我準備好的csv文件或dump文件&#xff0c;因為數據量非常大&#xff0c;所以采…

2024電工杯數學建模B題Python代碼+結果表數據教學

2024電工杯B題保姆級分析完整思路代碼數據教學 B題題目&#xff1a;大學生平衡膳食食譜的優化設計及評價 以下僅展示部分&#xff0c;完整版看文末的文章 import pandas as pd df1 pd.read_excel(附件1&#xff1a;1名男大學生的一日食譜.xlsx) df1# 獲取所有工作表名稱 e…

HarmonyOS-MPChart繪制一條虛實相接的曲線

本文是基于鴻蒙三方庫mpchart&#xff08;OpenHarmony-SIG/ohos-MPChart&#xff09;的使用&#xff0c;自定義繪制方法&#xff0c;繪制一條虛實相接的曲線。 mpchart本身的繪制功能是不支持虛實相接的曲線的&#xff0c;要么完全是實線&#xff0c;要么完全是虛線。那么當我…

面試總結之:socket線路切換

"socket線路切換"通常指的是在網絡通信過程中,根據當前網絡狀態或策略來動態更換數據傳輸路徑的技術。這種技術可以提高通信的可靠性和性能。 在實際應用中,線路切換可能涉及到多種技術,例如: 負載均衡:根據每條路徑的當前負載情況,動態地選擇一條較為空閑的路…

MySql超大Sql文件導入效率優化 —— 筑夢之路

使用場景 日常我們對mysql數據庫、mariadb數據庫進行定時備份&#xff0c;而隨著時間增長&#xff0c;導出來的備份文件越來越大&#xff0c;使用備份sql文件進行還原的時候&#xff0c;大文件非常慢&#xff0c;有些要執行很長時間&#xff0c;效率很低。 如何優化&#xff…

根據多個坐標經緯度獲取到中心點的經緯度,scala語言

文章目錄 前言scala 代碼 總結 前言 Scala 語言 通過多個經緯度坐標點, 計算出中心點, 這里使用的是 Scala 語言,其他的語言需要自行轉換。求出來的并不是原有的點&#xff0c;而是原有點的中心位置的點。 scala 代碼 package com.dw.process.midimport java.lang.Double.pa…

C語言 | Leetcode C語言題解之第97題交錯字符串

題目&#xff1a; 題解&#xff1a; bool isInterleave(char* s1, char* s2, char* s3) {int n strlen(s1), m strlen(s2), t strlen(s3);int f[m 1];memset(f, 0, sizeof(f));if (n m ! t) {return false;}f[0] true;for (int i 0; i < n; i) {for (int j 0; j &l…

基于UDP的tftp的文件傳輸

#define SER_PORT 69 #define SER_IP "192.168.125.71" #define CLT_PORT 6666 #define CLT_IP "192.168.125.158" int main(int argc, const char *argv[]) {//創建套接字文件描述符int cfd socket(AF_INET,SOCK_DGRAM,0);if(cfd -1){perror("sock…

vue2-computed,vue3+watch 前端實現列表搜索,結合filter+some+indexOf

vue2 computed實現 computed: {FBAAddressListComputed () {if (!this.fbaInput) return this.FBAAddressListconst lowerCaseInput this.fbaInput.toLowerCase()return this.FBAAddressList.filter((item) > {return [item.fbaCode, item.zipCode, item.countryCode, ite…

六(3)、RTKLIB源碼解析 — [postpos]: execses(antpos, outhead, procpos)

目錄 一、antpos() 1.1 avepos() 1.2 getstapos() 二、outhead() 三、procpos() 3.1 inputobs() 3.1.1 nexto

牛客周賽 Round 42

小紅叕戰小紫 #include<bits/stdc.h> using namespace std; void solve(){string s;cin>>s;if(s.length()<1)cout<<"yukari";else cout<<"kou"<<endl; } int main(){ios::sync_with_stdio(false), cin.tie(0), cout.tie…

Qt時間類、日期類、時間日期類介紹

一.時間類&#xff08;QTime&#xff09; Qt中的時間類QTime是用來處理時間的類&#xff0c;它可以表示一個特定的時間&#xff0c;精確到毫秒。QTime類提供了一些方法來訪問和操作時間&#xff0c;例如獲取小時、分鐘、秒以及毫秒部分&#xff0c;還可以進行時間的比較和運算。…

Python列表,元組,集合,字典詳解一篇搞懂

目錄 介紹 列表(List) 集合(Set) 字典(Dict) 元組(Tuple) 列表 列表定義 ?編輯 列表切片 列表常用方法 append extend ?編輯 insert ?編輯 remove pop ?編輯 clear ?編輯 列表修改元素 sort 升序 倒序 reverse count ?編輯 index 淺拷貝和深拷貝 …

《書生·浦語大模型實戰營》第一課 學習筆記:書生·浦語大模型全鏈路開源體系

文章大綱 1. 簡介與背景智能聊天機器人與大語言模型目前的開源智能聊天機器人與云上運行模式 2. InternLM2 大模型 簡介3. 視頻筆記&#xff1a;書生浦語大模型全鏈路開源體系內容要點從模型到應用典型流程全鏈路開源體系 4. 論文筆記:InternLM2 Technical Report簡介軟硬件基礎…

基于Java的地震震中附近城市分析實戰

目錄 前言 一、空間數據說明 1、空間查詢 二、Java后臺開發 1、模型層設計與實現 2、控制層設計與實現 三、Leaflet地圖開發 1、地震震中位置展示 2、附近城市展示 3、成果展示 總結 前言 隨著全球氣候變化和地殼活動的不斷演變&#xff0c;地震作為一種自然災害&…

第十三節:帶你梳理Vue2 : watch偵聽器

官方解釋:> 觀察 Vue 實例變化的一個表達式或計算屬性函數。回調函數得到的參數為新值和舊值。表達式只接受監督的鍵路徑。對于更復雜的表達式&#xff0c;用一個函數取代<br/>## 1. 偵聽器的基本使用偵聽器可以監聽data對象屬性或者計算屬性的變化watch是觀察屬性的…