C語言多維數組與多級指針

多維數組與多級指針也是初學者感覺迷糊的一個地方。超過二維的數組和超過二級的指針其實并不多用。如果能弄明白二維數組與二級指針,那二維以上的也不是什么問題了。所以本節重點討論二維數組與二級指針。

一、二維數組

1、假想中的二維數組布局
我們前面討論過,數組里面可以存任何數據,除了函數。下面就詳細討論討論數組里面存數組的情況。Excel 表,我相信大家都見過。我們平時就可以把二維數組假想成一個excel表,比如:
? ?char a[3][4];

2、內存與尺子的對比
實際上內存不是表狀的,而是線性的。見過尺子吧?尺子和我們的內存非常相似。一般尺子上最小刻度為毫米,而內存的最小單位為1 個byte。平時我們說32 毫米,是指以零開始偏移32 毫米;平時我們說內存地址為0x0000FF00 也是指從內存零地址開始偏移0x0000FF00 個byte。既然內存是線性的,那二維數組在內存里面肯定也是線性存儲的。實際上其內存布局如下圖:

以數組下標的方式來訪問其中的某個元素:a[i][j]。編譯器總是將二維數組看成是一個一維數組,而一維數組的每一個元素又都是一個數組。a[3]這個一維數組的三個元素分別為:
a[0],a[1],a[2]。每個元素的大小為sizeof(a[0]),即sizof(char)*4。由此可以計算出a[0],a[1],a[2]三個元素的首地址分別為& a[0],& a[0]+ 1*sizof(char)*4,& a[0]+ 2*sizof(char)*4。亦即a[i]的首地址為& a[0]+ i*sizof(char)*4。這時候再考慮a[i]里面的內容。就本例而言,a[i]內有4個char 類型的元素,其每個元素的首地址分別為&a[i],&a[i]+1*sizof(char),&a[i]+2*sizof(char)&a[i]+3*sizof(char),即a[i][j]的首地址為&a[i]+j*sizof(char)。再把&a[i]的值用a 表示,得到a[i][j]元素的首地址為:a+ i*sizof(char)*4+ j*sizof(char)。同樣,可以換算成以指針的形式表示:*(*(a+i)+j)。

經過上面的講解,相信你已經掌握了二維數組在內存里面的布局了。下面就看一個題:
#include <stdio.h>
intmain(int argc,char * argv[])
{
? ?int a [3][2]={(0,1),(2,3),(4,5)};
? ?int *p;
? ?p=a [0];
? ?printf("%d",p[0]);
}
問打印出來的結果是多少?

很多人都覺得這太簡單了,很快就能把答案告訴我:0。不過很可惜,錯了。答案應該是1。如果你也認為是0,那你實在應該好好看看這個題。花括號里面嵌套的是小括號,而不是花括號!這里是花括號里面嵌套了逗號表達式!其實這個賦值就相當于
? ?int a [3][2]={ 1, 3,5};
所以,在初始化二維數組的時候一定要注意,別不小心把應該用的花括號寫成小括號
了。

3、&p[4][2] - &a[4][2]的值為多少?
上面的問題似乎還比較好理解,下面再看一個例子:
? ?int a[5][5];
? ?int (*p)[4];
? ?p = a;
問&p[4][2] - &a[4][2]的值為多少?

這個問題似乎非常簡單,但是幾乎沒有人答對了。我們可以先寫代碼測試一下其值,然后分析一下到底是為什么。在Visual C++6.0 里,測試代碼如下:
intmain()
{
? ?int a[5][5];
? ?int (*p)[4];
? ?p = a;
? ?printf("a_ptr=%#p,p_ptr=%#p\n",&a[4][2],&p[4][2]);
? ?printf("%p,%d\n",&p[4][2] - &a[4][2],&p[4][2] - &a[4][2]);
? ?return 0;
}
經過測試,可知&p[4][2] - &a[4][2]的值為-4。這到底是為什么呢?下面我們就來分析一下:前面我們講過,當數組名a 作為右值時,代表的是數組首元素的首地址。這里的a 為二維數組,我們把數組a 看作是包含5 個int 類型元素的一維數組,里面再存儲了一個一維數組。

如此,則a 在這里代表的是a[0]的首地址。a+1 表示的是一維數組a 的第二個元素。a[4]表示的是一維數組a 的第5 個元素,而這個元素里又存了一個一維數組。所以&a[4][2]表示的是&a[0][0]+4*5*sizeof(int) + 2*sizeof(int)。

根據定義,p 是指向一個包含4 個元素的數組的指針。也就是說p+1 表示的是指針p 向后移動了一個“包含4 個int 類型元素的數組”。這里1 的單位是p 所指向的空間,即4*sizeof(int)。所以,p[4]相對于p[0]來說是向后移動了4 個“包含4 個int 類型元素的數組”,即&p[4]表示的是&p[0]+4*4*sizeof(int)。由于p 被初始化為&a[0],那么&p[4][2]表示的是&a[0][0]+4*4*sizeof(int)+2* sizeof(int)。

再由上面的講述,&p[4][2] 和&a[4][2]的值相差4 個int 類型的元素。現在,上面測試出來的結果也可以理解了吧?其實我們最簡單的辦法就是畫內存布局圖:

這里最重要的一點就是明白數組指針p 所指向的內存到底是什么。解決這類問題的最好辦法就是畫內存布局圖。

二、二級指針

1、二級指針的內存布局
二級指針是經常用到的,尤其與二維數組在一起的時候更是令人迷糊。例如:
? ?char **p;
定義了一個二級指針變量p。p 是一個指針變量,毫無疑問在32 位系統下占4 個byte。

它與一級指針不同的是,一級指針保存的是數據的地址,二級指針保存的是一級指針的地址。下圖幫助理解:
我們試著給變量p 初始化:
A)
p = NULL;
B)
char *p2; p = &p2;
任何指針變量都可以被初始化為NULL(注意是NULL,不是NUL,更不是null),二級指針也不例外。也就是說把指針指向數組的零地址。聯想到前面我們把尺子比作內存,如果把內存初始化為NULL,就相當于把指針指向尺子上0 毫米處,這時候指針沒有任何內存可用。

當我們真正需要使用p 的時候,就必須把一個一級指針的地址保存到p 中,所以B)的賦值方式也是正確的。

給p 賦值沒有問題,但怎么使用p 呢?這就需要我們前面多次提到的鑰匙(“*”)。
第一步:根據p 這個變量,取出它里面存的地址。
第二步:找到這個地址所在的內存。
第三步:用鑰匙打開這塊內存,取出它里面的地址,*p 的值。
第四步:找到第二次取出的這個地址。
第五步:用鑰匙打開這塊內存,取出它里面的內容,這就是我們真正的數據,**p 的值。


我們在這里用了兩次鑰匙(“*”)才最終取出了真正的數據。也就是說要取出二級指針所真正指向的數據,需要使用兩次兩次鑰匙(“*”)。

至于超過二維的數組和超過二維的指針一般使用比較少,而且按照上面的分析方法同樣也可以很輕松的分析明白,這里就不再詳細討論。讀者有興趣的話,可以研究研究。

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

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

相關文章

spring-data-jpa 使用

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 只是記錄下使用方法 &#xff1a; 1. RPC接口 service實現類調用&#xff0c;PageRequest對象會實現分頁、排序。 Overridepublic Obj…

docker的安裝與安裝mysql(mac,centos為例)

一、mac安裝docker 1、去這個網站下載安裝https://www.docker.com/get-started 2、運行docker 運行開啟docker docker run -dp 80:80 docker/getting-started 二、centos8安裝docker 1、安裝docker 下載低版本的 yum install -y docker-ce --nobest 2、安裝擴展 wget http…

配置PPPOE

先配置服務端PPPOE-Server先為路由添加一個賬號為PPP所使用[PPPOE-Server]aaa [PPPOE-Server-aaa]local-user test password cipher 123 //添加一個本地賬號[PPPOE-Server-aaa]local-user test service-type ppp//設置test賬號類型為PPP賬號[PPPOE-Server]ip pool test//添加一…

eclipse棄坑記第一篇之在idea上配置Tomcat環境并創建Javaweb項目的詳細步驟原創

IntelliJ IDEA是一款功能強大的開發工具&#xff0c;在代碼自動提示、重構、J2EE支持、各類版本工具(如git、svn、github)、maven等方面都有很好的應用。 IntelliJ IDEA有免費的社區版和付費的旗艦版。免費版只支持Java等為數不多的語言和基本的IDE特性&#xff0c;旗艦版還支持…

laravel安裝prettier,git hook代碼格式化工具

1、安裝prettier的php擴展 npm install --global prettier prettier/plugin-php 2、安裝husky&#xff0c;lint-staged&#xff08;git鉤子&#xff09;使用 npm i prettier lint-staged husky -D 3、修改package.json文件 在scripts后面添加兩個函數 "husky": …

C語言指針數組和數組指針

一、指針數組和數組指針的內存布局 初學者總是分不出指針數組與數組指針的區別。其實很好理解&#xff1a;指針數組&#xff1a;首先它是一個數組&#xff0c;數組的元素都是指針&#xff0c;數組占多少個字節由數組本身決定。它是“儲存指針的數組”的簡稱。 數組指針&#xf…

求二叉樹的高度

1 int GetHeight(BinTree BT)2 {3 int HL, HR, MaxH;4 5 if(BT)6 {7 HL GetHeight(BT->Left); //求左子樹的高度8 HR GetHeight(BT->Right); //求右子樹的高度9 MaxH (HL > HR) ? HL : HR; //取左右子樹較大的高…

Json與List的相互轉換

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 問題由來&#xff1a; 最近由于做一個項目&#xff0c;項目的一個功能就是根據Listview的內容生成一個二維碼&#xff0c;然后掃描二維…

機器學習常用模型

&#xff08;原作&#xff1a;MSRA劉鐵巖著《分布式機器學習&#xff1a;算法、理論與實踐》。這一部分敘述很清晰&#xff0c;適合用于系統整理NN知識&#xff09; 線性模型 線性模型是最簡單的&#xff0c;也是最基本的機器學習模型。其數學形式如下&#xff1a;g(X;W)WTX。有…

(轉)告別程序員生涯,一點感慨,與諸君共勉

轉自&#xff1a;https://blog.csdn.net/phphot/article/details/2230411 再過幾天&#xff0c;我就正式告別程序員生涯了&#xff0c;這也是我最后一次以職業程序員身份在CSDN發表文章。小弟談談入行幾年來的感受&#xff0c;做一個人生階段的自我總結&#xff0c;同時希望能…

C語言指針與數組之間的恩恩怨怨

很多初學者弄不清指針和數組到底有什么樣的關系。我現在就告訴你&#xff1a;他們之間沒有任何關系&#xff01;只是他們經常穿著相似的衣服來逗你玩罷了。指針就是指針&#xff0c;指針變量在32 位系統下&#xff0c;永遠占4 個byte&#xff0c;其值為某一個內存的地址。指針可…

CentOS7查看開放端口命令

CentOS7查看開放端口命令CentOS7的開放關閉查看端口都是用防火墻來控制的&#xff0c;具體命令如下&#xff1a;查看已經開放的端口&#xff1a;Linux代碼 firewall-cmd --list-ports 開啟端口Linux代碼 firewall-cmd --zonepublic --add-port80/tcp --permanent 命令含義&…

referer參數和addslashes()函數的騷路子

TIPS: 此函數確實是過濾用戶輸入的&#xff0c;當gpc未開啟的時候使用addslashes()函數進行過濾&#xff0c;當開啟gpc的時候直接返回&#xff0c;那么問題就來了&#xff0c;當php開啟gpc的時候直接返回字符串&#xff0c;但是gpc是只針GET,POST,COOKIE三種超全局變量進行過濾…

MySQL 的各個json 函數說明

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 如果英文的不方便閱讀&#xff0c;請參考個人收錄的另一篇中文解說&#xff1a;MySQL中json函數說明和舉例 只是為了方便 自己用的時候…

訂單編號,遞增且不連續(php版)

1、實現方式&#xff0c;使用while循環&#xff0c;比較low&#xff0c;200w條數據 2s public function getMany ($startNum, $count) {$num $startNum;$codes[] $startNum;for($i1;$i<$count;$i){$num1 random_int(1,9);while($num $num1) {$num1 random_int(1,9);}$…

一文搞懂:詞法作用域、動態作用域、回調函數、閉包

把以前一直只限于知道&#xff0c;卻不清晰理解的這幾個概念完完整整地梳理了一番。內容參考自wiki頁面&#xff0c;然后加上自己一些理解。 詞法作用域和動態作用域 不管什么語言&#xff0c;我們總要學習作用域(或生命周期)的概念&#xff0c;比如常見的稱呼&#xff1a;全局…

C語言函數指針

一、函數指針的定義 顧名思義&#xff0c;函數指針就是函數的指針。它是一個指針&#xff0c;指向一個函數。看例子&#xff1a;A)char * (*fun1)(char * p1,char * p2);B)char * *fun2(char * p1,char * p2);C)char * fun3(char * p1,char * p2);看看上面三個表達式分別是什么…

程序員進階之算法練習:LeetCode專場

歡迎大家前往騰訊云社區&#xff0c;獲取更多騰訊海量技術實踐干貨哦~ 本文由落影發表 前言 LeetCode上的題目是大公司面試常見的算法題&#xff0c;今天的目標是拿下5道算法題&#xff1a; 題目1是基于鏈表的大數加法&#xff0c;既考察基本數據結構的了解&#xff0c;又考察在…

vim 安裝vim-prettier

1、在.vimrc中添加 配置沒有安裝成功的話 git clone https://github.com/prettier/vim-prettier Plug prettier/vim-prettier, { do: yarn install, for: [javascript, typescript, css, less, scss, json, graphql, markdown, vue, yaml, html, php] } let g:prettier#aut…

詳解Mysql中的JSON系列操作函數

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 一、方法羅列&#xff1a; 分類 函數 描述創建jsonjson_array 創建json數組json_object 創建json對象 json_quote 將json轉成json字符串…