C語言 —— 此去經年夢浪蕩魂音 - 深入理解指針(卷一)

目錄

1. 內存和地址

2. 指針變量和地址

2.1 取地址操作符(&)

2.2?指針變量

2.3?解引用操作符 (*)

3. 指針的解引用

3.1 指針 + - 整數

3.2?void* 指針

4. const修飾指針

4.1 const修飾變量

4.2?const修飾指針變量

5. 指針運算

5.1?指針 ± 整數

5.2指針 - 指針

5.3?指針的關系運算

6. 野指針

6.1 野指針成因

6.2 如何規避野指針

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

7.1 strlen的模擬實現

7.2 傳值調用和傳址調用


1. 內存和地址

什么是內存,我們先舉個例子:假設有?棟宿舍樓,把你放在樓?,樓上有100個房間,但是房間沒有編號,你的?個朋友來找你玩,如果想找到你,就得挨個房?去找,這樣效率很低,但是我們如果根據樓層和樓層的房間的情況,給每個房間編上號

??

一樓:101 102 103...
二樓:201 202 203...以此類推...

有了房間號,如果你的朋友得到房間號,就可以快速的找房間找到你

??

將這個例子對應我們的計算機里面就是:

??

CPU在處理數據的時候,需要的數據是在內存中讀取的,處理后的數據也會放回內存中,那我們買電腦的時候,電腦上內存是8GB/16GB/32GB等,這些內存空間是把內存劃分為?個個的內存單元,每個內存單元的大小取1個字節(1個字節=8 個比特位)

? ??

每個內存單元就相當于每間酒店房間,每個房間能住 8 個比特位,房間(內存單元)都有一個門牌編號(地址),有了門牌號,就能快速找到相應的房間,即CPU能通過地址快速找到內存空間,在C語言中,給地址起了個名字叫指針

所以我們可以理解為:

??
內存單元的編號 == 地址 == 指針

??

1byte = 8bit
1KB = 1024byte
1MB = 1024KB
1GB = 1024MB
1TB = 1024GB
1PB = 1024TB


2. 指針變量和地址


2.1 取地址操作符(&)

創建變量其實就是向內存申請空間,調試下面代碼:

#include <stdio.h>int main()
{int a = 10;&a;//取出a的地址printf("%p\n", &a);return 0;
}

&a取出的是a所占4個字節中地址較?的字節的地址也就是第一個地址

??

雖然整型變量占用4個字節,我們只要知道了第?個字節地址,順藤摸?訪問到4個字節的數據也是可行的


2.2?指針變量

我們通過取地址操作符(&)拿到的地址是?個數值,這個數值有時候也是需要存儲起來,?便后期再使用的,那我們就可以把這樣的地址值存放在指針變量

int main()
{int a = 10;int* pa = &a;//取出a的地址并存儲到指針變量pa中return 0;
}

指針變量就是用來存放地址的,存放在指針變量中的值都可以當成為地址

2.3?解引用操作符 (*)

我們把地址存儲在指針變量后要如何將存放在里面的東西取出使用呢?在知道地址的前提下,可以通過解引用操作符找到指針指向的對象

int main()
{int a = 100;int* p = &a;*p = 0;return 0;
}

上面這段代碼就是p?通過解引用(*)找到 a 并將其值改成 0 ,就像是通過門牌號找到特定酒店房間里的特定物品,并將其替換


3. 指針的解引用
?

#include <stdio.h>int main()
{int n = 0x11223344;int* pi = &n;*pi = 0;return 0;
}

調試一下這段代碼,代碼會將n的4個字節全部改為0,如果把指針類型改成 char ,那么代碼只是將n的第?個字節改為0

int main()
{int n = 0x11223344;char* pc = (char*)&n;*pc = 0;return 0;
}

結論:指針的類型決定了,對指針解引?的時候有多?的權限(?次能操作?個字節)

? ?
?如: char* 的指針解引?就只能訪問?個字節,? int* 的指針的解引?就能訪問四個字?

3.1 指針 + - 整數

#include <stdio.h>int main()
{int n = 10;char* pc = (char*)&n;int* pi = &n;printf("%p\n", &n);printf("%p\n", pc);printf("%p\n", pc + 1);printf("%p\n", pi);printf("%p\n", pi + 1);return 0;
}

char* 類型的指針變量+1跳過1個字節, int* 類型的指針變量+1跳過了4個字節

? ?
也就是說:指針類型決定了指針加減整數時一步走多大距離

3.2?void* 指針

在指針類型中有?種特殊的類型是 void* 類型的,可以理解為?具體類型的指針(或者叫泛型指針),這種類型的指針可以?來接受任意類型地址。但是也有局限性, void* 類型的指針不能直接進?指針的+-整數和解引?的運算

#include <stdio.h>int main()
{int a = 10;void* pa = &a;void* pc = &a;*pa = 10;*pc = 0;return 0;
}

這?我們可以看到, void* 類型的指針可以接收不同類型的地址,但是無法直接進?指針運算

? ?
那么 void* 類型的指針到底有什么?呢?

? ?
?般 void* 類型的指針是使?在函數參數的部分,?來接收不同類型數據的地址,這樣的設計可以實現泛型編程的效果。使得?個函數來處理多種類型的數據


4. const修飾指針


4.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;printf("n = %d\n", n);int* p = &n;*p = 20;printf("n = %d\n", n);return 0;
}

4.2?const修飾指針變量

當const修飾指針變量的時候:

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

int const * const p = &a;

?當const放在*的左邊時,限制的就是p所指向的內容,也就是&a

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

當const放在*的右邊時,限制的就是p本身


5. 指針運算

??

5.1?指針 ± 整數

因為數組在內存中是連續存放的,只要知道第?個元素的地址,順藤摸?就能找到后?的所有元素

#include <stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = &arr[0];int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);for (i = 0; i < sz; i++){printf("%d ", *(p + i));//p+i 這?就是指針+整數}return 0;
}

5.2指針 - 指針

代替 strlen 函數(計算字符或字符串長度),實現一個自定義的函數 my_strlen 來計算輸入字符串的長度

#include <stdio.h>int my_strlen(char* s)
{char* p = s;while (*p != '\0')p++;return p - s;
}int main()
{printf("%d\n", my_strlen("abc"));return 0;
}

5.3?指針的關系運算

//指針的關系運算
#include <stdio.h>int main()
{int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };int* p = &arr[0];int i = 0;int sz = sizeof(arr) / sizeof(arr[0]);while (p < arr + sz) //指針的???較{printf("%d ", *p);p++;}return 0;
}

指針的關系運算,實際上就是:


6. 野指針

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

6.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;
}

當循環執行到 i = 10 及之后時,指針 p 已經超出了數組 arr 的范圍指向了數組 arr 所占用內存空間之外的未知區域,此時 p 就變成了野指針

3. 指針指向的空間釋放

6.2 如何規避野指針

1.對指針變量都進行初始化操作

? ?
2.注意數組等變量的范圍,小心指針越界

? ?
3.指針不使用時,及時置之為NULL空指針

? ??
4.不要返回局部變量的地址

?


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

? ?

7.1 strlen的模擬實現

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

size_t strlen ( const char * str );

參數str接收?個字符串的起始地址,然后開始統計字符串中 \0 之前的字符個數,最終返回?度

? ??
如果要模擬實現只要從起始地址開始向后逐個字符的遍歷,只要不是 \0 字符,計數器就+1,這樣直到 \0 就停??

strlen鏈接:

??

strlen - C++ Referencehttps://legacy.cplusplus.com/reference/cstring/strlen/?kw=strlen

int my_strlen(const char* str)
{int count = 0;assert(str);while (*str){count++;str++;}return count;
}int main()
{int len = my_strlen("abcdef");printf("%d\n", len);return 0;
}

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

我們發現其實沒產?交換的效果,調試?下

Swap1函數在使?的時候,是把變量本?直接傳遞給了函數,這種調?函數的?式我們之前在函數的時候就知道了,值是會出了作用域就會自動銷毀,這種叫傳值調用

? ?

實參傳遞給形參的時候,形參會單獨創建?份臨時空間來接收實參,對形參的修改不影響實

我們使用指針的方法:

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);Swap1(&a, &b);printf("交換后:a=%d b=%d\n", a, b);return 0;
}

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

傳址調?,可以讓函數和主調函數之間建?真正的聯系,在函數內部可以修改主調函數中的變量

? ??

所以未來函數中只是需要主調函數中的變量值來實現計算,就可以采?傳值調?。如果函數內部要修改主調函數中的變量的值,就需要傳址調??


完結撒花~?

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

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

相關文章

【AI】從頭到腳詳解如何創建部署Azure Web App的OpenAI項目

【AI】從頭到腳詳解如何創建部署Azure Web App的OpenAI項目 在Azure Web應用上,您可以使用Python的OpenAI包方便快捷地調用官方API,上傳您的訓練數據,并利用他們的算法進行處理。本教程提供了一個逐步指南,幫助您在Azure Web應用上部署您的OpenAI項目,涵蓋了從資源設置到…

機器視覺工程師紅外相機的選擇:紅外長波工業相機和短波紅外工業相機玄機大總結

紅外長波(LWIR)和短波(SWIR)工業相機在原理、應用場景和技術特點上有顯著差異。以下是它們的對比分析: 1. 波長范圍與成像原理 2. 技術特點 3. 典型應用場景 4. 優缺點對比 LWIR優勢: 無需光照,適用于完全黑暗環境。 直接反映物體溫度分布。 對煙霧、灰塵穿透能力強。…

uni-app學習筆記——自定義模板

一、流程 1.這是一個硬性的流程&#xff0c;只要按照如此程序化就可以實現 二、步驟 1.第一步 2.第二步 3.第三步 4.每一次新建頁面&#xff0c;都如第二步一樣&#xff1b;可以選擇自定義的模版&#xff08;vue3Setup——這是我自己的模版&#xff09;&#xff0c;第二步的…

DeepSeek模型本地化部署方案及Python實現

DeepSeek實在是太火了&#xff0c;雖然經過擴容和調整&#xff0c;但反應依舊不穩定&#xff0c;甚至小圓圈轉半天最后卻提示“服務器繁忙&#xff0c;請稍后再試。” 故此&#xff0c;本文通過講解在本地部署 DeepSeek并配合python代碼實現&#xff0c;讓你零成本搭建自己的AI…

Vue3計算屬性深度解析:經典場景與Vue2對比

一、計算屬性的核心價值 計算屬性&#xff08;Computed Properties&#xff09;是Vue響應式系統的核心特性之一&#xff0c;它通過依賴追蹤和緩存機制優雅地解決模板中復雜邏輯的問題。當我們需要基于現有響應式數據進行派生計算時&#xff0c;計算屬性總能保持高效的性能表現…

python-leetcode-刪除鏈表的倒數第 N 個結點

LCR 021. 刪除鏈表的倒數第 N 個結點 - 力扣&#xff08;LeetCode&#xff09; 可以使用雙指針方法來解決這個問題&#xff0c;這樣可以在一次遍歷內完成刪除操作&#xff0c;從而達到 O(n) 的時間復雜度。以下是 Python 代碼實現&#xff1a; 解題思路&#xff1a; 初始化快…

vue2的webpack(vue.config.js) 怎么使用請求轉發 devServer.proxy

首先用 express 搭建后端服務器&#xff0c;注意使用中間件解析json格式的請求體&#xff0c;才會獲取到 post 參數 app.use(express.json()); app.js const express require(express) const app express() app.use(express.json()); const port 3000app.post(/api/vue2, …

Linux:基本指令與內涵理解

1.文件操作指令 1.1 ls ls指令用于查看指定層級文件夾下的文件或文件夾 基本格式&#xff1a;ls (選項) (查看層級&#xff09; 其中選項處不寫就默認是顯示文件名&#xff0c;查看層級默認是當前層級 選項1&#xff1a; -l 作用&#xff1a;將查找文件的詳細信息顯示出來 我們…

SpaceSync智能排班:重構未來辦公空間的神經中樞

文心智能體平臺可免費使用DeepSeek 滿血版啦&#xff0c;使用DeepSeek模型創建并提交智能體&#xff0c;即有機會瓜分萬元獎金&#xff01;有這等好事還不快沖&#xff01; 文心智能體官網&#xff1a;文心智能體平臺AgentBuilder | 想象即現實 本片文章為作者參加文心智能體平…

flutter dio庫 源碼賞析

1. factory函數 //調用factory構造方法后&#xff0c;實際返回的是Dio的子類 Dio dio Dio();abstract class Dio {factory Dio([BaseOptions? options]) > createDio(options); } 2. CancelToken 作用:取消操作 CancelToken cancelToken CancelToken();//監聽取消 ca…

RGV調度算法

1、基于時間窗 https://wenku.baidu.com/view/470e9fd8b4360b4c2e3f5727a5e9856a57122693.html?_wkts_1741880736197&bdQuery%E7%8E%AF%E7%A9%BF%E8%B0%83%E5%BA%A6%E7%AE%97%E6%B3%95 2.2019年MathorCup高校數學建模挑戰賽B題 2019-mathorcupB題-環形穿梭機調度模型&a…

基于CATIA VBA與Python的自動化音樂生成技術對比研究

在工程軟件二次開發領域&#xff0c;CATIA 也可以許多另類的玩法。通過CATIA自帶的VBA可以演奏歌曲&#xff0c;但實際效果往往差強人意。為了進一步優化實際演奏效果&#xff0c;本文以自動生成林宥嘉《說謊》鋼琴前奏旋律為案例&#xff0c;探討兩種語言在多媒體控制領域的技…

最大數位置(信息學奧賽一本通-2038)

【題目描述】 輸入n個整數,存放在數組a[1]至a[n]中&#xff0c;輸出最大數所在位置(n≤1000)。 【輸入】 第一行&#xff0c;數的個數n; 第二行&#xff0c;n個正整數&#xff0c;每個數在232?1之內。 【輸出】 最大數所在位置。 【輸入樣例】 5 67 43 90 78 32 【輸出樣例】 …

【AIGC】OpenAI 集成 Langchain 操作實戰使用詳解

目錄 一、前言 二、前置準備 2.1 安裝 Langchain必須的依賴 2.1.1 python環境 2.1.2 langchain openai 環境 2.1.3 準備一個apikey 2.1.4 langchain 核心組件 三、Langchain 各組件使用 3.1 Chat models組件 3.1.1 Invocation 使用 3.1.1.1 結果解析 3.2 提示詞模板…

【C#學習筆記04】深入掌握C語言格式化輸出

引言 ??printf()??函數不僅可以將數據輸出到控制臺&#xff0c;還可以通過格式化字符串靈活地控制輸出的格式。??printf()???函數的使用規則&#xff0c;包括標志說明、字段寬度、轉換精度、長度修飾、轉換說明、轉義字符和返回結果等內容。 1. ??printf()??函數…

python-leetcode-定長子串中元音的最大數目

1456. 定長子串中元音的最大數目 - 力扣&#xff08;LeetCode&#xff09; 可以使用 滑動窗口 方法來解決這個問題。步驟如下&#xff1a; 初始化&#xff1a;計算前 k 個字符中元音字母的個數&#xff0c;作為初始窗口的值。滑動窗口&#xff1a;遍歷字符串&#xff0c;每次右…

藍橋真題講解

第一題 題目鏈接 0貪吃蛇長度 - 藍橋云課 題目解析 題意&#xff1a;數#個數和個數再加上首尾 代碼原理 略 代碼編寫 略 填空題技巧 眼看手數 當然并不是真的一個一個數&#xff0c;我們需要借助一些工具&#xff0c;不過各位小伙伴們放心&#xff0c;我們借助的工具…

【C++ 函數模板】—— 模板參數推導、實例化策略與編譯優化

歡迎來到ZyyOvO的博客?&#xff0c;一個關于探索技術的角落&#xff0c;記錄學習的點滴&#x1f4d6;&#xff0c;分享實用的技巧&#x1f6e0;?&#xff0c;偶爾還有一些奇思妙想&#x1f4a1; 本文由ZyyOvO原創??&#xff0c;感謝支持??&#xff01;請尊重原創&#x1…

Java基礎入門流程控制全解析:分支、循環與隨機數實戰

引言 流程控制是編程語言的核心邏輯結構&#xff0c;決定了程序的執行順序與邏輯判斷能力。本文以 分支結構、循環結構 和 隨機數生成 為核心&#xff0c;結合代碼示例與底層原理&#xff0c;全面解析Java中流程控制的應用場景與實戰技巧。 一、分支結構 1. if分支 作用&am…

Redis 數據持久化之RDB

Redis數據持久化策略 持久化策略之RDB RDB:在指定的時間間隔&#xff0c;執行數據集的時間點快照。 實現類似照片記錄效果的方式&#xff0c;就是把某一時刻的數據和狀態以文件的形式寫到磁盤上&#xff0c;也就是讀快照。這樣一來即使故障宕機&#xff0c;快照文件也不會丟失&…