C語言超詳細指針知識(二)

在上一篇有關指針的博客中,我們介紹了指針的基礎知識,如:內存與地址,解引用操作符,野指針等,今天我們將更加深入的學習指針的其他知識。

1.指針的使用和傳址調用

1.1strlen的模擬實現

庫函數strlen的功能是求字符串長度,統計的是字符串中\0之前的字符個數

?函數原型如下:

size_t strlen (const char* str);

?參數str接收一個字符串的起始地址,然后開始統計字符串中\0之前的字符個數,最終返回長度。關于strlen函數的詳細介紹網頁:
strlen - C++ Referencehttps://legacy.cplusplus.com/reference/cstring/strlen/?kw=strlen我們要實現該函數的模擬實現,就要從字符串起始地址開始逐個遍歷字符,只要不是\0字符,計數器就加一,?直到\0結束。

#include<stdio.h>
#include<assert.h>int my_strlen(char* str)
{assert(str != NULL);char* pz = str;while (*pz != '\0'){pz++;}return pz - str;
}int main()
{int len = my_strlen("abcdef");printf("%d\n", len);return 0;
}

?在代碼里,我們用到了assert斷言,他幫助我們判斷str是否為空指針,如果是,程序會報錯。


1.2傳值調用和傳址調用

學習指針的目的是使?指針解決問題,那什么問題,非指針不可呢?
例:寫一個函數,交換兩個整型變量的值
我們可能會寫出這樣的代碼:
?
void Swap(int x, int y)
{int temp = x;x = y;y = temp;
}int main()
{int num1 = 10;int num2 = 20;printf("交換前:num1 = %d,num2 = %d\n", num1, num2);Swap(num1, num2);printf("交換后:num1 = %d,num2 = %d\n", num1, num2);return 0;
}

?代碼運行結果:
?

我們發現沒有產生預期的效果,為什么呢?調試觀察一下:

?

我們發現在main函數內部,創建了num1和num2,num1的地址是0x0019f7e8,num2的地址是0x00197fdc,在調用Swap函數時,將num1和num2傳遞給了Swap函數,在Swap函數內部創建了形參x和y接收num1和num2的值,但是x的地址是0x0019f704,y的地址是0x0019f708,x和y確實接收到了num1和num2的值,不過x的地址和num1的地址不?樣,y的地址和num2的地址不?樣,相當于x和y是獨?的空間,那么在Swap函數內部交換x和y的值,?然不會影響num1和num2,當Swap函數調?結束后回到main函數,num1和num2的沒法交換。Swap1函數在使用的時候,是把變量本身直接傳遞給了函數,這種調用函數的?式我們之前在函數的時候就知道了,這種叫傳值調用。
實參傳遞給形參的時候,形參會單獨創建?份臨時空間來接收實參,對形參的修改不影響實
參。

?所以上述代碼實際是不符合題目要求的。

該怎樣設計代碼呢,既然我們把交換數值傳到函數內無法實現交換,那如果傳輸的是地址呢,同一份內存空間還會不會失敗?

void Swap(int* px, int* py)
{int* temp = px;*px = *py;*py = *temp;
}int main()
{int num1 = 10;int num2 = 20;printf("交換前:num1 = %d,num2 = %d\n", num1, num2);Swap(&num1, &num2);printf("交換后:num1 = %d,num2 = %d\n", num1, num2);return 0;
}

查看結果,我們發現交換成功了。

?調用Swap函數的時候是將變量的地址傳遞給了函數,這種函數調用方法叫:傳址調用。

傳址調用,可以讓函數和主調函數之間建立真正的聯系,在函數內部可以修改主調函數中的變量;所以未來函數中只是需要主調函數中的變量值來實現計算,就可以采用傳值調用。如果函數內部要修改主調函數中的變量的值,就需要傳址調用。

?2.數組名的理解

在上篇博客我們在使用指針訪問數組的內容時,有這樣的代碼:
?

int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int* p = arr;

?這里我們使用arr的方式拿到了數組第一個元素的地址,有些人可能會有疑惑?不應該&arr[0]才是數組第一個元素的地址嗎,其實,大部分情況這兩種寫法是一樣的意思,并沒有含義的不同,我們來做一個測試。

我們發現數組名和數組首元素的地址打印出的結果一模一樣?,數組名就是數組首元素的地址

這時候可能有同學有疑問?數組名如果是數組首元素的地址,那下面的代碼又該如何理解?

?我們看到上述代碼的結果是40,如果arr是數組首元素的地址,其結果應該是4/8才對。

其實數組名就是數組首元素的地址是對的,但是有兩個例外:

? sizeof(數組名),sizeof中單獨放數組名,這里的數組名表示整個數組,計算的是整個數組的大小,單位是字節
? &數組名,這里的數組名表示整個數組,取出的是整個數組的地址(整個數組的地址和數組首元素的地址是有區別的)
除此之外,任何地方使用數組名,數組名都表示首元素的地址。

?這時有好奇的同學,嘗試了如下代碼:

三個打印結果一模一樣,他就不會區分了,明明結果是一樣,含義卻不同嗎?

我們再看下面這個代碼:
?

我們發現&arr[0]和&arr[0]+1相差4個字節,arr和arr+1 也相差4個字節,是因為&arr[0] 和 arr 都是首元素的地址,+1就是跳過?個元素。
但是&arr 和 &arr+1相差40個字節,這就是因為&arr是整個數組的地址,+1 操作是跳過整個數組的。
到這里?家應該搞清楚數組名的意義了吧。
數組名是數組首元素的地址,但是有2個例外。

3.使用指針訪問數組

有了前?知識的?持,再結合數組的特點,我們就可以很?便的使?指針訪問數組了。
int main()
{int arr[10] = {0};int* p = arr;int sz = sizeof(arr) / sizeof(arr[0]);for (int i = 0; i < sz; i++){scanf("%d", p + i);}for (int i = 0; i < sz; i++){printf("%d\n", *(p + i));printf("%d\n", p[i]);//這一行代碼效果與上一行代碼效果完全相等}return 0;
}

在該代碼中,將*(p+i)換成p[i]也是能夠正常打印的,所以本質上p[i] 是等價于 *(p+i)。


4.一維數組傳參的本質

我們發現在函數內部無法正確獲取數組的元素個數。這是為什么呢?

這時候就要學習數組傳參的本質了 ,之前說過,數組名是數組首元素的地址,那么在數組傳參的時候,傳遞的是數組名,本質上傳遞的其實是數組首元素地址,并不會傳遞整個數組,這兩者是有區別的。

所以函數形參的部分理論上應該使用指針變量來接收首元素的地址。那么在函數內部我們寫
sizeof(arr) 計算的是?個地址的大小(單位字節)而不是數組的大小(單位字節)。
void test(int arr[])
{int sz2 = sizeof(arr) / sizeof(arr[0]);printf("sz2 = %d\n", sz2);
}void test(int* p)
{int sz2 = sizeof(p) / sizeof(p[0]);printf("sz2 = %d\n", sz2);
}

這兩者是等效的。

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

?5.二級指針

指針變量也是變量,是變量就有地址,那指針變量的地址存放在哪?? 答案是二級指針。

?如上圖,a是整型變量,他的地址存放在pa中,pa的類型是int*,pa的地址又存放在ppa中,ppa的類型是int**,這就是二級指針。

對于二級指針的運算有:

int a = 20;
*ppa = &a;

?*ppa 通過對ppa中的地址進?解引用,這樣找到的是 pa *ppa 其實訪問的就是 pa。

**ppa = 30;
//等價于*pa = 30
//等價于a = 30

?**ppa 先通過 *ppa 找到 pa ,然后對 pa 進?解引?操作: *pa ,那找到的是 a 。

6.指針數組

6.1指針數組概念

指針數組是指針還是數組?
我們類比?下,整型數組,是存放整型的數組,字符數組是存放字符的數組。
那指針數組呢?是存放指針的數組。

?這是一個典型的指針數組,他的每一個元素都是地址,只想某一塊區域。

6.2指針數組模擬二維數組

int main()
{int arr1[5] = { 1,2,3,4,5 };int arr2[5] = { 2,3,4,5,6 };int arr3[5] = { 3,4,5,6,7 };int* arr[3] = { arr1,arr2,arr3 };for (int i = 0; i < 3; i++){for (int j = 0; j < 5; j++){printf("%d ", arr[i][j]);//printf("%d ", *(*(arr+i)+j));//兩行效果一致}printf("\n");}return 0;
}

arr[i]是訪問arr數組中的元素,arr[i]找到的數組元素指向了一個一維整型數組,所以arr[i][j]就是一維整型數組的元素。

要注意,上述的代碼雖然模擬出了二維數組的效果,實際上卻不是真正的二維數組,因為每一行并非是連續的。

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

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

相關文章

一種替代DOORS在WORD中進行需求管理的方法 (二)

一、前景 參考&#xff1a; 一種替代DOORS在WORD中進行需求管理的方法&#xff08;基于WORD插件的應用&#xff09;_doors aspice-CSDN博客 二、界面和資源 WORD2013/WORD2016 插件 【已使用該工具通過第三方功能安全產品認證】&#xff1a; 1、 核心功能 1、需求編號和跟…

設計模式 Day 6:深入講透觀察者模式(真實場景 + 回調機制 + 高級理解)

觀察者模式&#xff08;Observer Pattern&#xff09;是一種設計結構中最實用、最常見的行為模式之一。它的魅力不僅在于簡潔的“一對多”事件推送能力&#xff0c;更在于它的解耦能力、模塊協作設計、實時響應能力。 本篇作為 Day 6&#xff0c;將帶你從理論、底層機制到真實…

文獻總結:AAAI2025-UniV2X-End-to-end autonomous driving through V2X cooperation

UniV2X 一、文章基本信息二、文章背景三、UniV2X框架1. 車路協同自動駕駛問題定義2. 稀疏-密集混合形態數據3. 交叉視圖數據融合&#xff08;智能體融合&#xff09;4. 交叉視圖數據融合&#xff08;車道融合&#xff09;5. 交叉視圖數據融合&#xff08;占用融合&#xff09;6…

2025藍橋杯python A組題解

真捐款去了&#xff0c;好長時間沒練了&#xff0c;感覺腦子和手都不轉悠了。 B F BF BF 賽時都寫假了&#xff0c; G G G 也只寫了爆搜。 題解其實隊友都寫好了&#xff0c;我就粘一下自己的代碼&#xff0c;稍微提點個人的理解水一篇題解 隊友題解 B 思路&#xff1a; 我…

免費送源碼:Java+ssm+MySQL 校園二手書銷售平臺設計與實現 計算機畢業設計原創定制

摘 要 信息化社會內需要與之針對性的信息獲取途徑&#xff0c;但是途徑的擴展基本上為人們所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人們經常能夠獲得不同類型信息&#xff0c;這也是技術最為難以攻克的課題。針對校園二手書銷售平臺等問題&#xff0c;對校…

工業科學級天文相機:跨界融合的高精密成像解決方案

隨著國內科技的快速發展&#xff0c;工業相機領域正悄然興起一場"天文級"的技術革命。這類兼具工業設備可靠性與天文觀測精度的特殊相機&#xff0c;正在半導體制造、天文觀測、空間探測等領域開辟新的應用疆域。其核心技術突破不僅體現在傳感器性能的提升&#xff0…

論文閱讀筆記——Multi-Token Attention

MTA 論文 在 Transformer 中計算注意力權重時&#xff0c;僅依賴單個 Q 和 K 的相似度&#xff0c;無法有效捕捉多標記組合信息。&#xff08;對于 A、B 兩個詞&#xff0c;單標記注意力需要分別計算兩個詞的注意力分數&#xff0c;再通過后處理定位共同出現的位置或通過多層隱…

301.找出3位偶數

2094. 找出 3 位偶數 - 力扣&#xff08;LeetCode&#xff09; class Solution {List<Integer> resnew ArrayList<>();List<Integer> linew ArrayList<>();public int[] findEvenNumbers(int[] digits) {Arrays.sort(digits);boolean[] numsnew boolea…

【KWDB 創作者計劃】第二卷:開發者實戰篇

?KWDB技術白皮書卷二&#xff1a;開發者實戰篇 ?1. 自然語言到量子查詢的編譯系統 1.1 NL2QSQL翻譯引擎架構 運行時流程圖解&#xff1a; ┌──────────────────────┐ ┌───────────────────┐ ┌─────────────…

前端工程化之新晉打包工具

新晉打包工具 新晉打包工具前端模塊工具的發展歷程分類初版構建工具grunt使用場景 gulp采用管道機制任務化配置與api簡潔 現代打包構建工具基石--webpack基于webpack改進的構建工具rollup 推薦舉例說明package.jsonrollup.config.mjsmy-extract-css-rollup-plugin.mjssrc/index…

ai軟件UI自動化

在AI與UI自動化結合的場景中,通常涉及計算機視覺(CV)、自然語言處理(NLP)和機器學習(ML)等技術。以下是實現AI驅動UI自動化的關鍵方向、工具和步驟: ?一、核心應用場景? ?元素定位增強? ?問題?:傳統工具依賴XPath/CSS選擇器,易因UI變化失效。?AI方案?:CV識別…

關于 C++ 中 cin 對象和 EOF 的詳細解釋

【DeepSeek提問】 給解釋一下下面這段話&#xff08;C編程&#xff09; cin是 iostream 類的一個對象實例&#xff0c;如果輸入正常&#xff0c; cin 將返回本身。 舉個例子&#xff1a;cin>x>>y, 如果 cin>>x 讀入正常&#xff0c;那么將返回cin, 相當于后面繼…

Vue 3 和 Vue 2 的區別及優點

Vue.js 是一個流行的 JavaScript 框架&#xff0c;廣泛用于構建用戶界面和單頁應用。自 Vue 3 發布以來&#xff0c;很多開發者開始探索 Vue 3 相較于 Vue 2 的新特性和優勢。Vue 3 引入了許多改進&#xff0c;優化了性能、增強了功能、提升了開發體驗。本文將詳細介紹 Vue 2 和…

【特權FPGA】之UART串口

0.簡介 通用異步收發器(Universal Asynchronous Receiver&#xff0f;Transmitter&#xff0c;UART)可以和各種標準串行接口&#xff0c;如RS 232和RS 485等進行全雙工異步通信&#xff0c;具有傳輸距離遠、成本低、可靠性高等優點。一般UART由專用芯片如8250&#xff0c;1645…

Vue3中watch監視reactive對象方法詳解

在Vue3中&#xff0c;使用watch監視reactive對象時&#xff0c;需根據監視的目標選擇合適的方法。以下是詳細的步驟和說明&#xff1a; 1. 監視整個reactive對象 自動深度監視&#xff1a;直接監視reactive對象時&#xff0c;Vue3會默認啟用深度監視&#xff0c;無需設置deep:…

如何制定性能調優策略

目錄 性能測試攻略 微基準性能測試 宏基準性能測試 熱身問題 多 JVM 情況下的影響 合理分析結果&#xff0c;制定調優策略 推薦閱讀 性能測試攻略 性能測試是提前發現性能瓶頸&#xff0c;保障系統性能穩定的必要措施。下面我先給你介紹兩種常用 的測試方法&#xff0c;幫…

HarmonyOS-ArkUI V2裝飾器@Local裝飾器:組件內部狀態

@Local裝飾器的作用 @Local裝飾器是用來裝飾組件內的狀態的。而且它修飾的變量可以成為數據源。Local裝飾器,作用跟名字差不多,重點突出了“本地”的特性,也就是使用的范圍僅僅限制在組件內部。且它在初始化的時候必須是在本地進行初始化的,不能在外部組件,同時也禁止了外…

Linux線程屬性與多線程開發:API詳解與實戰代碼解析

Linux 線程的屬性 線程池 多線程的創建 線程的屬性 引入 我們設想一個場景&#xff0c;使用pthread_detach時&#xff0c;發現線程早就已經結束了&#xff0c;這時候pthread_detach還能正常發揮清理線程的 獨有空間 的作用嗎&#xff1f; 答案是可以的&#xff0c;但是這難…

測試第二課-------測試分類

作者前言 &#x1f382; ??????&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f367;&#x1f382; ?&#x1f382; 作者介紹&#xff1a; &#x1f382;&#x1f382; &#x1f382; &#x1f389;&#x1f389;&#x1f389…

MySQL安裝實戰分享

一、在 Windows 上安裝 MySQL 1. 下載 MySQL 安裝包 訪問 MySQL 官方下載頁面。選擇適合你操作系統的版本。一般推薦下載 MySQL Installer。 2. 運行安裝程序 雙擊下載的安裝文件&#xff08;例如 mysql-installer-community-<version>.msi&#xff09;。如果出現安全…