【C/C++】深入理解指針(二)

文章目錄

  • 深入理解指針(二)
    • 1.const修飾指針
      • 1.1 const修飾變量
      • 1.2 const修飾指針變量
    • 2.野指針
      • 2.1 野指針成因
        • 1.指針未初始化
        • 2. 指針越界訪問
        • 3.指針指向的空間釋放
      • 2.2 如何規避野指針
        • 2.2.1 指針初始化
        • 2.2.2 小心指針越界
        • 2.2.3 指針變量不再使?時,及時置NULL,指針使?之前檢查有效性
      • 2.2.4 避免返回局部變量的地址
    • 3.assert斷?
    • 4.指針的使用和傳址調用
      • 4.1 strlen的模擬實現
      • 4.2 傳值調?和傳址調用

深入理解指針(二)

1.const修飾指針

1.1 const修飾變量

變量是可以修改的,如果把變量的地址交給?個指針變量,通過指針變量的也可以修改這個變量。 但是如果我們希望?個變量加上?些限制,不能被修改,怎么做呢?這就是const的作?。

#include <stdio.h>
int main()
{int m = 0;m = 20;//m是可以修改的 const int n = 0;n = 20;//n是不能被修改的 return 0;
}

上述代碼中n是不能被修改的,其實n本質是變量,只不過被const修飾后,在語法上加了限制,只要我們在代碼中對n就?修改,就不符合語法規則,就報錯,致使沒法直接修改n。

但是如果我們繞過n,使?n的地址,去修改n就能做到了,雖然這樣做是在打破語法規則。

#include <stdio.h>
int main()
{const int n = 0;//n具備了常屬性(不能被修改了),a在這還是變量,但在cpp中是常量printf("n = %d\n", n);int* p = &n;*p = 20;//*是解引用操作符(間接訪問操作符)通過p存放的地址找到p指向的對象printf("n = %d\n", n);return 0;
}

輸出結果:

在這里插入圖片描述

我們可以看到這??個確實修改了,但是我們還是要思考?下,為什么n要被const修飾呢?就是為了 不能被修改,如果p拿到n的地址就能修改n,這樣就打破了const的限制,這是不合理的,所以應該讓 p拿到n的地址也不能修改n,那接下來怎么做呢?

1.2 const修飾指針變量

?般來講const修飾指針變量,可以放在的左邊,也可以放在的右邊,意義是不?樣的。

nt * p;//沒有const修飾? 
int const * p;//const 放在*的左邊做修飾 
int * const p;//const 放在*的右邊做修飾 

我們看下?代碼,來分析具體分析?下:

#include <stdio.h>
//代碼1 - 測試?const修飾的情況 
void test1()
{int n = 10;int m = 20;int* p = &n;*p = 20;//ok?yesp = &m; //ok?yes
}
//代碼2 - 測試const放在*的左邊情況 
void test2(){int n = 10;int m = 20;const int* p = &n;//限制指針指向內容,不能通過指針來修改*p = 20;//ok?nop = &m; //ok?yes
}
//代碼3 - 測試const放在*的右邊情況 
void test3()
{int n = 10;int m = 20;int * const p = &n;//此時限制指針變量p本身,原本放的是n的地址,不能改成m的地址//但是可以通過指針改變指針指向對象的內容 也就是n的值*p = 20; //ok?yesp = &m; //ok?no
}
//代碼4 - 測試*的左右兩邊都有const 
void test4()
{int n = 10;int m = 20;int const * const p = &n;*p = 20; //ok?nop = &m; //ok?no
}
int main()
{//測試?const修飾的情況 test1();//測試const放在*的左邊情況 test2();//測試const放在*的右邊情況 test3();//測試*的左右兩邊都有const test4();return 0;
}

結論:const修飾指針變量的時候

? const如果放在的左邊,修飾的是指針指向的內容,保證指針指向的內容不能通過指針來改變。但是指針變量本?的內容可變。

? const如果放在的右邊,修飾的是指針變量本?,保證了指針變量的內容不能修改,但是指針指向的內容,可以通過指針改變。

2.野指針

概念:野指針就是指針指向的位置是不可知的(隨機的、不正確的、沒有明確限制的)

2.1 野指針成因

1.指針未初始化
#include <stdio.h>
int main()
{ int *p;//局部變量指針未初始化,默認為隨機值 *p = 20;return 0;
}
2. 指針越界訪問
#include <stdio.h>
int main()
{int arr[10] = {0};int *p = &arr[0];int i = 0;for(i = 0; i <= 11; i++){//當指針指向的范圍超出數組arr的范圍時,p就是野指針 *(p++) = i;}return 0;
}
3.指針指向的空間釋放
#include <stdio.h>
int* test()
{int n = 100;return &n;
}
int main()
{int*p = test();printf("%d\n", *p);//n的地址放進了p,但是空間已經釋放,p非法訪問return 0;
}

2.2 如何規避野指針

2.2.1 指針初始化

如果明確知道指針指向哪?就直接賦值地址,如果不知道指針應該指向哪?,可以給指針賦值NULL. NULL 是C語?中定義的?個標識符常量,值是0,0也是地址,這個地址是?法使?的,讀寫該地址 會報錯。

#ifdef __cplusplus#define NULL 0#else#define NULL ((void *)0)#endif

初始化如下:

#include <stdio.h>
int main()
{int num = 10;int*p1 = &num;int*p2 = NULL;return 0;
}
2.2.2 小心指針越界

?個程序向內存申請了哪些空間,通過指針也就只能訪問哪些空間,不能超出范圍訪問,超出了就是 越界訪問。

2.2.3 指針變量不再使?時,及時置NULL,指針使?之前檢查有效性

當指針變量指向?塊區域的時候,我們可以通過指針訪問該區域,后期不再使?這個指針訪問空間的時候,我們可以把該指針置為NULL。因為約定俗成的?個規則就是:只要是NULL指針就不去訪問, 同時使?指針之前可以判斷指針是否為NULL。

我們可以把野指針想象成野狗,野狗放任不管是?常危險的,所以我們可以找?棵樹把野狗拴起來, 就相對安全了,給指針變量及時賦值為NULL,其實就類似把野狗栓起來,就是把野指針暫時管理起 來。

不過野狗即使拴起來我們也要繞著?,不能去挑逗野狗,有點危險;對于指針也是,在使?之前,我們也要判斷是否為NULL,看看是不是被拴起來的野狗,如果是不能直接使?,如果不是我們再去使?。

int main()
{int arr[10] = {1,2,3,4,5,6,7,8,9,10};int *p = &arr[0];int i = 0;for(i = 0; i < 10; i++){*(p++) = i;}//此時p已經越界了,可以把p置為NULL p = NULL;//下次使?的時候,判斷p不為NULL的時候再使? //...p = &arr[0];//重新讓p獲得地址 if(p != NULL) //判斷 {//...}return 0;
}

2.2.4 避免返回局部變量的地址

如造成野指針的第3個例?,不要返回局部變量的地址。

3.assert斷?

assert.h 頭?件定義了宏 assert() ,?于在運?時確保程序符合指定條件,如果不符合,就報 錯終?運?。這個宏常常被稱為“斷?”。

assert(p != NULL);

上?代碼在程序運?到這??語句時,驗證變量 p 是否等于 NULL 。如果確實不等于 NULL ,程序繼續運?,否則就會終?運?,并且給出報錯信息提?。

assert() 宏接受?個表達式作為參數。如果該表達式為真(返回值?零), assert() 不會產? 任何作?,程序繼續運?。如果該表達式為假(返回值為零), assert() 就會報錯,在標準錯誤 流 stderr 中寫??條錯誤信息,顯?沒有通過的表達式,以及包含這個表達式的?件名和?號。

assert() 的使?對程序員是?常友好的,使? assert() 有?個好處:它不僅能?動標識?件和 出問題的?號,還有?種?需更改代碼就能開啟或關閉 assert() 的機制。如果已經確認程序沒有問 題,不需要再做斷?,就在 #include 語句的前?,定義?個宏 NDEBUG 。

#define NDEBUG
#include <assert.h>

然后,重新編譯程序,編譯器就會禁??件中所有的 assert() 語句。如果程序?出現問題,可以移 除這條 #define NDEBUG 指令(或者把它注釋掉),再次編譯,這樣就重新啟?了 assert() 語 句。

assert() 的缺點是,因為引?了額外的檢查,增加了程序的運?時間。

?般我們可以在 Debug 中使?,在 Release 版本中選擇禁? assert 就?,在 VS 這樣的集成開 發環境中,在 Release 版本中,直接就是優化掉了。這樣在debug版本寫有利于程序員排查問題, 在 Release 版本不影響??使?時程序的效率。

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

4.1 strlen的模擬實現

庫函數strlen的功能是求字符串?度,統計的是字符串中 \0 之前的字符的個數。 函數原型如下:

size_t strlen ( const char * str );

參數str接收?個字符串的起始地址,然后開始統計字符串中 \0 之前的字符個數,最終返回?度。 如果要模擬實現只要從起始地址開始向后逐個字符的遍歷,只要不是 \0 字符,計數器就+1,這樣直 到 \0 就停?。 參考代碼如下:

int my_strlen(const char * str)
{int count = 0;assert(str);//assert(str!=NULL)while(*str)// while(*str !='\0') char屬于整型家族{count++;str++;}return count;
}
int main()
{int len = my_strlen("abcdef");printf("%d\n", len);return 0;
}

4.2 傳值調?和傳址調用

學習指針的?的是使?指針解決問題,那什么問題,?指針不可呢?

例如:寫?個函數,交換兩個整型變量的值?番思考后,我們可能寫出這樣的代碼:

#include <stdio.h>
void Swap1(int x, int y)
{int tmp = x;x = y;y = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交換前:a=%d b=%d\n", a, b);Swap1(a, b);printf("交換后:a=%d b=%d\n", a, b);return 0;
}

當我們運?代碼,結果如下:

在這里插入圖片描述

我們發現其實沒產?交換的效果,這是為什么呢? 調試?下,試試呢?

在這里插入圖片描述

我們發現在main函數內部,創建了a和b,a的地址是0x00cffdd0,b的地址是0x00cffdc4,在調? Swap1函數時,將a和b傳遞給了Swap1函數,在Swap1函數內部創建了形參x和y接收a和b的值,但是 x的地址是0x00cffcec,y的地址是0x00cffcf0,x和y確實接收到了a和b的值,不過x的地址和a的地址不 ?樣,y的地址和b的地址不?樣,相當于x和y是獨?的空間,那么在Swap1函數內部交換x和y的值, ?然不會影響a和b,當Swap1函數調?結束后回到main函數,a和b的沒法交換。Swap1函數在使? 的時候,是把變量本?直接傳遞給了函數,這種調?函數的?式我們之前在函數的時候就知道了,這 種叫傳值調?

**結論:**實參傳遞給形參的時候,形參會單獨創建?份臨時空間來接收實參,對形參的修改不影響實 參。 所以Swap1是失敗的了。

那怎么辦呢? 我們現在要解決的就是當調?Swap函數的時候,Swap函數內部操作的就是main函數中的a和b,直接 將a和b的值交換了。那么就可以使?指針了,在main函數中將a和b的地址傳遞給Swap函數,Swap 函數?邊通過地址間接的操作main函數中的a和b,并達到交換的效果就好了。

#include <stdio.h>
void Swap2(int*px, int*py)
{int tmp = 0;tmp = *px;*px = *py;*py = tmp;
}
int main()
{int a = 0;int b = 0;scanf("%d %d", &a, &b);printf("交換前:a=%d b=%d\n", a, b);Swap2(&a, &b);printf("交換后:a=%d b=%d\n", a, b);return 0;
}

在這里插入圖片描述

我們可以看到實現成Swap2的?式,順利完成了任務,這?調?Swap2函數的時候是將變量的地址傳 遞給了函數,這種函數調??式叫:傳址調?。

*py;
*py = tmp;
}
int main()
{
int a = 0;
int b = 0;
scanf(“%d %d”, &a, &b);
printf(“交換前:a=%d b=%d\n”, a, b);
Swap2(&a, &b);
printf(“交換后:a=%d b=%d\n”, a, b);
return 0;
}


[外鏈圖片轉存中...(img-Eq2WLgKI-1744880216123)].assets\image-20250417165536566.png)我們可以看到實現成Swap2的?式,順利完成了任務,這?調?Swap2函數的時候是將變量的地址傳 遞給了函數,這種函數調??式叫:**傳址調?。**傳址調?,可以讓函數和主調函數之間建?真正的聯系,在函數內部可以修改主調函數中的變量;所 以未來函數中只是需要主調函數中的變量值來實現計算,就可以采?傳值調?。如果函數內部要修改 主調函數中的變量的值,就需要傳址調?。

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

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

相關文章

【verilog】在同一個 always 塊中寫了多個“看起來獨立”的 if / if-else,到底誰先誰后,怎么執行?會不會沖突?

&#x1f50d; 問題本質 在一個 always (posedge clk) 塊中&#xff0c;所有的代碼都是順序執行的。但這不意味著它就像軟件一樣“一條一條執行”&#xff0c;因為最終是電路&#xff01;電路是并行存在的&#xff01; Verilog 是硬件描述語言&#xff08;HDL&#xff09;&am…

【React】什么是 Hook

useStateuseEffectuseRef 什么是hook&#xff1f;16.8版本出現的新特性。可以在不編寫class組件的情況下使用state以及其它的React特性 為什么有hook&#xff1f;class組件很難提取公共的重用的代碼&#xff0c;然后反復使用&#xff1b;不編寫類組件也可以使用類組件的狀態st…

如何查看自己抖音的IP屬地?詳細教程及如何修改

在當今互聯網時代&#xff0c;IP屬地信息已成為各大社交平臺&#xff08;如抖音、微博、快手等&#xff09;展示用戶真實網絡位置的重要功能。以下是關于如何查看抖音IP屬地的詳細教程及常見問題解答&#xff0c;幫助您快速了解相關信息&#xff1a; 一、如何查看抖音賬號的IP屬…

深度學習算力革新:AI服務器在運維工作中的智能化實踐

【導語】作為IT基礎設施服務領域的從業者&#xff0c;我們在日常工作中發現&#xff0c;AI服務器的智能化運維能力正在重塑傳統IDC的管理模式。本文將以DeepSeek系列服務器為例&#xff0c;分享智能算力設備在真實運維場景中的創新應用。 一、傳統服務器集群的運維痛點 在數據…

安裝部署RabbitMQ

一、RabbitMQ安裝部署 1、下載epel源 2、安裝RabbitMQ 3、啟動RabbitMQ web管理界面 啟用插件 rabbitmq數據目錄 創建rabbitmq用戶 設置為管理員角色 給用戶賦予權限 4、訪問rabbitmq

中間件--ClickHouse-4--向量化執行(什么是向量?為什么向量化執行的更快?)

1、向量&#xff08;Vector&#xff09;的概念 &#xff08;1&#xff09;、向量的定義 向量&#xff1a;在計算機科學中&#xff0c;向量是一組同類型數據的有序集合&#xff0c;例如一個包含多個數值的數組。在數據庫中&#xff0c;向量通常指批量數據&#xff08;如一列數…

Python PDF 轉 Markdown 工具庫對比與推薦

根據最新評測及開源社區實踐&#xff0c;以下為綜合性能與適用場景的推薦方案&#xff1a; 1. ?Marker? ?特點?&#xff1a; 轉換速度快&#xff0c;支持表格、公式&#xff08;轉為 LaTeX&#xff09;、圖片提取&#xff0c;適配復雜排版文檔?。依賴 PyTorch&#xff0c…

Vue 和 Spring boot 和 Bean 不同生命周期

一、Vue 組件生命周期 父子組件生命周期順序&#xff1a; 創建時&#xff1a; 父 beforeCreate → 父 created → 父 beforeMount → 子組件生命周期 → 父 mounted 更新時&#xff1a; 父 beforeUpdate → 子組件更新 → 父 updated。 銷毀時&#xff1a; 父 beforeDestroy…

Microsoft Azure 基礎知識簡介

Microsoft Azure 基礎知識簡介 已完成100 XP 2 分鐘 Microsoft Azure 是一個云計算平臺&#xff0c;提供一系列不斷擴展的服務&#xff0c;可幫助你構建解決方案來滿足業務目標。 Azure 服務支持從簡單到復雜的一切內容。 Azure 具有簡單的 Web 服務&#xff0c;用于在云中托…

C語言鏈接數據庫

目錄 使用 yum 配置 mysqld 環境 查看 mysqld 服務的版本 創建 mysql 句柄 鏈接數據庫 使用數據庫 增加數據 修改數據 查詢數據 獲取查詢結果的行數 獲取查詢結果的列數 獲取查詢結果的列名 獲取查詢結果所有數據 斷開鏈接 C語言訪問mysql數據庫整體源碼 通過…

【Maven】手動安裝依賴到本地倉庫

【Maven】手動安裝依賴到本地倉庫 【一】下載依賴【二】安裝 JAR 文件到本地倉庫【三】驗證安裝【四】在項目中使用該依賴【1】注意事項【2】額外提示 【一】下載依賴 登錄到中央倉庫下載依賴&#xff0c;中央倉庫地址&#xff1a;https://mvnrepository.com/ 搜搜你的依賴的a…

騰訊云golang一面

go垃圾回收機制 參考自&#xff1a;https://zhuanlan.zhihu.com/p/334999060 go 1.3 標記清除法 缺點 go 1.5 三色標記法 屏障機制 插入屏障 但是如果棧不添加,當全部三色標記掃描之后,棧上有可能依然存在白色對象被引用的情況(如上圖的對象9). 所以要對棧重新進行三色標記掃…

跨平臺嵌入式音視頻開發指南:EasyRTC音視頻通話的多場景適配與AI擴展能力

在數字化通信技術飛速發展的今天&#xff0c;實時音視頻通信已成為眾多智能設備和應用的核心功能。從智能家居到遠程辦公&#xff0c;從在線教育到智能安防&#xff0c;音視頻通信技術的應用場景不斷拓展&#xff0c;對低延遲、高穩定性和跨平臺兼容性的需求也在持續增長。在這…

Android 11 去掉性能受到影響通知

源碼位置: frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java final void finishBooting() {TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",Trace.TRACE_TAG_ACTIVITY_MANAGER);t.traceBegin("Finis…

Mac idea WordExcel等文件git modify 一直提示修改狀態

CRLF LF CR 換行符自動轉換問題 查看狀態&#xff1a;git config --global --list Mac需要開啟&#xff0c;window下需要關閉 關閉命令&#xff1a;git config --global core.autocrlf false 命令解釋&#xff1a; autocrlf true 表示要求git在提交時將crlf轉換為lf&a…

Apache Commons CLI 入門教程:輕松解析命令行參數

文章目錄 Apache Commons CLI 入門教程&#xff1a;輕松解析命令行參數一、什么是 Commons CLI&#xff1f;二、為什么選擇 Commons CLI&#xff1f;三、快速開始1. 添加依賴2. 基礎示例3. 運行示例1. 在Idea中運行2. 命令行中運行3. 使用 Maven/Gradle 運行&#xff08;推薦&a…

VS2022調試嵌入式linux C# 程序 高效的開發方案

1.目標板子配置好ssh,確保PC可以連上 2.目標板子上傳VSDBG程序&#xff0c;詳見我的上一個文章 3.PC安裝winfsp&#xff0c; sshfs,SSHFS-Win Manager.傻瓜式安裝&#xff0c;將目標板子映射到PC的某個盤 4.VS2022中&#xff0c;你的工程的exe生成目錄到上面盤中某個路徑 5…

Python中如何加密/解密敏感信息(如用戶密碼、token)

敏感信息,如用戶密碼、API密鑰、訪問令牌(token)、信用卡號以及其他個人身份信息(PII),構成了現代應用程序和系統中最為關鍵的部分。這些信息一旦被未經授權的第三方獲取,可能引發災難性的后果,從個人隱私泄露到企業經濟損失,甚至是大規模的社會安全問題。保護這些敏感…

智能體開發的范式革命:Cangjie Magic全景解讀與實踐思考

引言&#xff1a;當智能體開發遇見倉頡魔法 在人工智能技術日新月異的今天&#xff0c;智能體(Agent)開發正從實驗室走向產業應用的核心舞臺。2025年3月&#xff0c;倉頡社區推出的Cangjie Magic開源平臺&#xff0c;以其創新的設計理念和技術架構&#xff0c;為這一領域帶來了…

【Java學習筆記】位運算

位運算 一、原碼&#xff0c;反碼&#xff0c;補碼 (1) 二進制的最高位是符號位&#xff1a;0 表示正數&#xff0c;1 表示負數&#xff08;怎么記&#xff1f; 1旋轉一下變成-&#xff09; (2) 正數的原碼、反碼、補碼都一樣&#xff08;三碼合一&#xff09; (3) 負數的反碼…