C語言學習筆記丨帶你五分鐘瞭解指針!

来源:https://www.cnblogs.com/L928/archive/2019/03/27/10611868.html
-Advertisement-
Play Games

指針、引用和取值 什麼是指針?什麼是記憶體地址?什麼叫做指針的取值?指針是一個存儲電腦記憶體地址的變數。在這份教程里“引用”表示電腦記憶體地址。從指針指向的記憶體讀取數據稱作指針的取值。指針可以指向某些具體類型的變數地址,例如int、long和double。指針也可以是void類型、NULL指針和未初始 ...


指針、引用和取值

什麼是指針?什麼是記憶體地址?什麼叫做指針的取值?指針是一個存儲電腦記憶體地址的變數。在這份教程里“引用”表示電腦記憶體地址。從指針指向的記憶體讀取數據稱作指針的取值。指針可以指向某些具體類型的變數地址,例如int、long和double。指針也可以是void類型、NULL指針和未初始化指針。本文會對上述所有指針類型進行探討。

根據出現的位置不同,操作符 * 既可以用來聲明一個指針變數,也可以用作指針的取值。當用在聲明一個變數時,*表示這裡聲明瞭一個指針。其它情況用到*表示指針的取值。

&是地址操作符,用來引用一個記憶體地址。通過在變數名字前使用&操作符,我們可以得到該變數的記憶體地址。

// 聲明一個int指針

int *ptr;

// 聲明一個int值

int val = 1;

// 為指針分配一個int值的引用

ptr = &val;

// 對指針進行取值,列印存儲在指針地址中的內容

int deref = *ptr;

printf("%d\n", deref);

第2行,我們通過*操作符聲明瞭一個int指針。接著我們聲明瞭一個int變數並賦值為1。然後我們用int變數的地址初始化我們的int指針。接下來對int指針取值,用變數的記憶體地址初始化int指針。最終,我們列印輸出變數值,內容為1。

第6行的&val是一個引用。在val變數聲明並初始化記憶體之後,通過在變數名之前使用地址操作符&我們可以直接引用變數的記憶體地址。

第8行,我們再一次使用*操作符來對該指針取值,可直接獲得指針指向的記憶體地址中的數據。由於指針聲明的類型是int,所以取到的值是指針指向的記憶體地址存儲的int值。

這裡可以把指針、引用和值的關係類比為信封、郵箱地址和房子。一個指針就好像是一個信封,我們可以在上面填寫郵寄地址。一個引用(地址)就像是一個郵件地址,它是實際的地址。取值就像是地址對應的房子。我們可以把信封上的地址擦掉,寫上另外一個我們想要的地址,但這個行為對房子沒有任何影響。


 

void指針、NULL指針和未初始化指針

一個指針可以被聲明為void類型,比如void *x。一個指針可以被賦值為NULL。一個指針變數聲明之後但沒有被賦值,叫做未初始化指針。

int *uninit; // int指針未初始化

int *nullptr = NULL; // 初始化為NULL

void *vptr; // void指針未初始化

int val = 1;

int *iptr;

int *castptr;

 

// void類型可以存儲任意類型的指針或引用

iptr = &val;

vptr = iptr;

printf("iptr=%p, vptr=%p\n", iptr, vptr);

 

// 通過顯示轉換,我們可以把一個void指針轉成

// int指針併進行取值

castptr = (int *)vptr;

printf("*castptr=%d\n", *castptr);

 

// 列印null和未初始化指針

printf("uninit=%p, nullptr=%p\n", uninit, nullptr);

// 不知道你會得到怎樣的返回值,會是隨機的垃圾地址嗎?

// printf("*nullptr=%d\n", nullptr);

// 這裡會產生一個段錯誤

// printf("*nullptr=%d\n", nullptr);

執行上面的代碼,你會得到類似下麵對應不同記憶體地址的輸出。

iptr=0x7fff94b89c6c, vptr=0x7fff94b89c6c

*castptr=1

uninit=0x7fff94b89d50, nullptr=(nil)

第1行我們聲明瞭一個未初始化int指針。所有的指針在賦值為NULL、一個引用(地址)或者另一個指針之前都是未被初始化的。第2行我們聲明瞭一個NULL指針。第3行聲明瞭一個void指針。第4行到第6行聲明瞭一個int值和幾個int指針。

第9行到11行,我們為int指針賦值為一個引用並把int指針賦值為void指針。void指針可以保存各種其它指針類型。大多數時候它們被用來存儲數據結構。可以註意到,第11行我們列印了int和void指針的地址。它們現在指向了同樣的記憶體地址。所有的指針都存儲了記憶體地址。它們的類型只在取值時起作用。

第15到16行,我們把void指針轉換為int指針castptr。請註意這裡需要顯示轉換。雖然C語言並不要求顯示地轉換,但這樣會增加代碼的可讀性。接著我們對castptr指針取值,值為1。

第19行非常有意思,在這裡列印未初始化指針和NULL指針。值得註意的是,未初始化指針是有記憶體地址的,而且是一個垃圾地址。不知道這個記憶體地址指向的值是什麼。這就是為什麼不要對未初始化指針取值的原因。最好的情況是你取到的是垃圾地址接下來你需要對程式進行調試,最壞的情況則會導致程式崩潰。

NULL指針被初始化為o。NULL是一個特殊的地址,用NULL賦值的指針指向的地址為0而不是隨機的地址。只有當你準備使用這個地址時有效。不要對NULL地址取值,否則會產生段錯誤。

 

指針和數組

C語言的數組表示一段連續的記憶體空間,用來存儲多個特定類型的對象。與之相反,指針用來存儲單個記憶體地址。數組和指針不是同一種結構因此不可以互相轉換。而數組變數指向了數組的第一個元素的記憶體地址。

一個數組變數是一個常量。即使指針變數指向同樣的地址或者一個不同的數組,也不能把指針賦值給數組變數。也不可以將一個數組變數賦值給另一個數組。然而,可以把一個數組變數賦值給指針,這一點似乎讓人感到費解。把數組變數賦值給指針時,實際上是把指向數組第一個元素的地址賦給指針。

int myarray[4] = {1,2,3,0};

int *ptr = myarray;

printf("*ptr=%d\n", *ptr);

 

// 數組變數是常量,不能做下麵的賦值

// myarray = ptr

// myarray = myarray2

// myarray = &myarray2[0]

第1行初始化了一個int數組,第2行用數組變數初始化了一個int指針。由於數組變數實際上是第一個元素的地址,因此我們可以把這個地址賦值給指針。這個賦值與int *ptr = &myarray[0]效果相同,顯示地把數組的第一個元素地址賦值到了ptr引用。這裡需要註意的是,這裡指針需要和數組的元素類型保持一致,除非指針類型為void。

 

指針與結構體

就像數組一樣,指向結構體的指針存儲了結構體第一個元素的記憶體地址。與數組指針一樣,結構體的指針必須聲明和結構體類型保持一致,或者聲明為void類型。

struct person {

  int age;

  char *name;

};

struct person first;

struct person *ptr;

 

first.age = 21;

char *fullname = "full name";

first.name = fullname;

ptr = &first;

 

printf("age=%d, name=%s\n", first.age, ptr->name);

第1至6行聲明瞭一個person結構體,一個變數指向了一個person結構體和指向person結構體的指針。第8行為age成員賦了一個int值。第9至10行我們聲明瞭一個char指針並賦值給一個char數組並賦值給結構體name成員。第11行我們把一個person結構體引用賦值給結構體變數。

第13行我們列印了結構體實例的age和name。這裡需要註意兩個不同的符號,’.’ 和 ‘->’ 。結構體實例可以通過使用 ‘.’ 符號訪問age變數。對於結構體實例的指針,我們可以通過 ‘->’ 符號訪問name變數。也可以同樣通過(*ptr).name來訪問name變數。

 

總結

希望這份簡短的概述能夠有助於瞭解不同的指針類型。在後續的文章中我們會探討其它類型的指針和高級用法,比如函數指針。大家有興趣的可以關註一波哦!

博主是一個有著7年工作經驗的架構師,對於c++,自己有做資料的整合,一個完整學習C語言c++的路線,學習資料和工具。可以進我的Q群7418,18652領取,免費送給大家。希望你也能憑自己的努力,成為下一個優秀的程式員!另外博主的微信公眾號是:C語言編程學習基地,歡迎關註!

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 計數排序與桶排序python實現 計數排序 計數排序原理: 找到給定序列的最小值與最大值 創建一個長度為最大值-最小值+1的數組,初始化都為0 然後遍歷原序列,併為數組中索引為當前值-最小值的值+1 此時數組中已經記錄好每個值的數量,自然也就是有序的了 例如: 計數排序實現 下麵為列表的計數排序 d ...
  • 官網:https://www.easyx.cn/ 線上幫助文檔:https://docs.easyx.cn/ 目標:生成一個邊框為黃色,填充為藍色的圓 遇到的問題:使用以下代碼,只能生成邊框為黃色的圓,藍色沒有填充上。調整setfillcolor(BLUE);的位置,放在circle函數後面填充,發 ...
  • 1 Spring事務 1.1 Spring事務是什麼(百度) 事務是對一系列的資料庫操作(比如插入多條數據)進行統一的提交或是回滾操作,如果插入成功,那麼一起成功,如果中間一條出現異常,那麼回滾之前的所有操作。 Spring事務管理機制使用的是TransactionManager進行管理。回滾註解@ ...
  • 本節內容為學習譚浩強老師編寫的《C++面向對象程式設計》的第2章 類和對象的特性 後的個人總結。只完成1/5,先挖個坑督促自己及時更新過。包括 2.1 面向對象程式設計方法概述,2.2 類的聲明和對象的定義,2.3 類的成員函數,2.4 對象成員的引用,2.5 類和對象的簡單應用舉例,2.6 類的封... ...
  • golang允許使用匿名結構體,形如 一般在使用的時候可以直接這樣初始化 或者 但今天遇到一種情況 匿名結構體的成員上有tag聲明,形如 這時使用第一種初始化方式,始終報錯,說類型不匹配,查了很久,偶然情況下去掉了結構體定義中的tag聲明,居然不報錯了,這時反應過來,使用第一種初始化方式,必須保證跟 ...
  • 問題:大家在學習Java讀取數據的時候一般都是使用Scanner方法讀取數據,但是其中有一個小問題大家可能不知道, 就是我們在使用scanner的時候如果你先讀取一個數字,在讀取一行帶有空格的字元串,勢必會出錯或者字元串讀不到, 那麼這篇文章就是解決此類問題的 ,希望對大家有所幫助。 錯誤代碼: 分 ...
  • 給定兩個以字元串形式表示的非負整數 num1 和 num2,返回 num1 和 num2 的乘積,它們的乘積也表示為字元串形式。 示例 1:輸入: num1 = "2", num2 = "3"輸出: "6"示例 2:輸入: num1 = "123", num2 = "456"輸出: "56088"說 ...
  • 未完待續,預計3.31完成 摘要:java引入註解後,編碼節省了很多需要寫代碼的時間,而且精簡了代碼,本文主要羅列項目中常用註解。 1 事務相關 @Transactional 事務回滾,作用於資料庫操作時,當操作數據失敗,該操作可以還原 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...