【C/C++和指針】深度解析---指針與數組 【精華】

一,引例子

二維數組可以使用指向數組的指針代替,而指針數組才可以用指向指針的指針代替。

[html] view plaincopy
  1. #include<iostream>??
  2. using?namespace?std;??
  3. ??
  4. void?main()??
  5. {??
  6. ?????char?*a[]={"Hello","the","World"};?//指針數組??
  7. ??
  8. ?????char?**pa=a;?//指向指針的指針??
  9. ??
  10. ?????pa++;???
  11. ??
  12. ?????cout<<*pa;??
  13. }??

?

類型確定的數組,元素的類型和元素的個數是確定的;數組退化為對應的指針,元素的類型保持一致;

所謂的二維數組只是數組的數組,因此二維數組不能退化為二級指針,而只能退化為指向一維數組的指針

例子:

#include <iostream.h>?
void main()

{?

??? int a[2][3];??? //二維數組

??? int **p=a;????? //二維數組不能退化為指向指針的指針

}?

請問為什么是錯誤的??(請不要說數組名是一個指針這個我知道,我想知道為什么不能用二級指針指向二維數組)

??

解析:首先數組和指針的概念你沒分清楚,數組的本質你沒搞清楚。這是導致問題出現的根源。

????? int x[5]; 這個定義里面,我們說定義了一個數組x,此數組有5個數組元素,元素的類型為int類型。

????? 首先要問的是,x到底為什么東西?x是數組名,x代表了數組第一個元素的首地址。沒錯,x確實是數組的名字,x的值也確實是第一個數組元素的地址值。

????? 注意這里我們說x代表的值與數組第一個元素的地址值相等,但類型不一樣

????? 那么數組x的類型到底是什么呢? 有人說就是int * 類型。有如下語句可以做證:

???????????????????????? int *p=x; //這句話是正確的。

????? 數組x的類型真是int *嗎?我們說不是,因為下面的語句是不正確的:

???????????????????????? int a=10; x=&a; // int *類型的變量時可以接受值的。

????? 所以x不是int*? 那么我們可以猜測x的類型是不是 int *const呢?也就是說x是一個地址值不可以改變的指針。這句話貌似有點正確。但是請大家看看下面的例子:

???????????????????????? int x[5]={0}; int a=sizeof(x); // a的值到底是多少?

?????實際上這里a的值是5*4=20 這里是整個數組占用的字節數。 我們不是說x的類型是int * const類型的嗎,也就是x應該是一個指針類型,應該是4個字節的啊,為什么sizeof出來時整個數組占用的字節數呢?例如? sizeof(int *)這個的結果就是4。所以有此可以看出,x的類型并不是int*,也不是int * const。? i

?????? int x[5];中的x到底是什么呢,我們說x是數組,此數組有5個元素,并且每個元素都是int類型。 我們有一個識別數據類型的規律例如:?

?????? int x; //x類型為int

?????? int *x;//x類型為int *

?????? int **x;//x類型為int **

?????? int (*x)[10];//x類型為int(*)[10]實際上是指向數組的指針

?????? int (*x)(int ,int);//x的類型為int(*)(int,int)實際上是指向函數的指針?

??? 由此可以看出,一個符號是什么數據類型,我們只要在其定義的表達式中去掉符號本身剩下的就是符號的類型了。

??? 照此推斷,int x[5];中x的類型應該是 int [5]這個類型,可以看出此類型并不是int *類型。?

??? 那么int x[5];中的x可以這樣賦值: int *p=x; 為什么呢?只能說這里面將x的類型隱式轉換為了int *類型。所以這里是可以賦值的,因為進行了類型轉換。

?? 再請看下面的例子:?

???????? void function(int x[5])

?????????{??

???????????? cout<<sizeof(x)<<endl; //這里輸出4

????????? }? 為什么會輸出4,而不是4*5呢,可以看出上面的函數形參實際上類型是int*,并不是數組類型,

?????? 所以我們在定義函數的時候,下面的都是與上面等價的:?

?????? void function(int x[])//元素個數是多少可以省略 {?? cout<<sizeof(x)<<endl; //這里輸出4 }

???????void function(int *x) //直接寫成指針變量也沒錯 {?? cout<<sizeof(x)<<endl; //這里輸出4 }?

??

?? 那么我們看一個類似的問題:

????????? int x[5]; int **p=&x; //為什么會報錯? 因為類型不匹配。?

??????? ?p的類型是int **,而&x的類型卻不是int **。 &x的類型實際上是int(*)[5],因為取的是x的地址,也就是說這個地址是數組的地址,并不是指向數組第一個元素的指針的指針(也就是二維指針),而是整個數組的地址。

???????? 所以我們可以改成下面的: int (*p)[5]=&x;//這就對了。

?

二,指向數組的指針,和指向數組元素的指針有什么不同??

??? 例如int *p;我們要注意的是,p的類型是int*,p占用的空間4個字節,p指向的數據類型是int。P指向的數據類型占用4個字節。

??? 所以對于指針變量,我們要明白指針變量本身是占用空間的,本身是有類型的,其次指針變量所指向的空間是有類型的,是有空間的。?

????那么int *p; char *p1; 對于指針變量來說p,p1里面都放的是地址值,說白了就是一個數值,他們都占用4個字節的空間,但是他們的類型不一樣,p里面的地址指向的是int類型的數據,p1指向的是char類型的數據,這主要體現在p++與p1++中他們在內存中移動的字節數是不一樣的,我們假設int占4個字節,char占1個字節。那么對于p來說向前移動了4個字節,p1來說移動了一個字節。這就是他們的類型不同,導致運算過程中的不同。 int x[5];

???? int (*p3)[5]; 此時p3指向數組x,那么p3++實際上向前移動了多少呢,可以算出移動了4*5個字節。也就是p3指向的是一個數組,是整個數組,所以p3移動的時候是將一個數組當做一個整體來看待的。所以向前移動了一整個數組的距離。? 再看你的問題之前,我們來看一個類似的問題:?

?????int a[2][3]; int? **p=&a; //這里我用&a來賦值行不行呢。是不行的。? 這里為什么是錯誤的,原因就是因為&a的類型不是int**類型。所以類型不兼容,導致不能賦值,同時這兩種類型是不可以相互轉換的。那么&a到底是一個什么樣的類型呢。 我們說&a取的是整個數組的地址,那么&a自然就是指向整個數組的指針了。

???? int (*p)[2][3]=&a; 此時這樣賦值才是正確的。如果我們要用a直接賦值,那該定義一個什么樣的變量來接受它呢,首先要明白,數組名代表的地址類型是指向數組的第一個元素的指針,

??? 例如:? int a[10]; int *p=a; 實際上這里與 int *p=&a[0];是等價的。因為指向a[0]的指針類型就是int*類型。? 那么&a的是取數組的地址,其類型是指向數組的指針,而不是指向數組第一個元素的指針,整個是要區別的,他們的類型就不一樣。 int(*p)[10]=&a;?

???? 所以說這里的a和&a絕對不是同一個東西,雖然本質上他們的地址值是一樣的,但是他們的類型不一樣。就決定他們代表不同的意義。?

??? ?那么剛剛說了對于下面的例子:

??????????? int a[2][3];

??????????? int (*p)[2][3]=&a;

???? 我們可以定義這樣的一個變量p來接受&a的值。?

???? 那么我們要接受a應該定義一個什么樣的變量呢。a[2][3]是一個二維數組,可以看成是這樣的a是一個數組,具有兩個元素,分別為a[0],a[1]其中這兩個元素的值a[0],a[1]他們的值又是一個具有3個元素的數組。此時我們可以將a[0],a[1]看成是數組名,那么a[0][0]就是數組a[0]的第0個元素了。對應關系如下: a[0] ---->? a[0][0],a[0][1],a[0][2] a[1] ---->? a[1][0],a[1][1],a[1][2]? 那么a到底是什么,其實a數組有兩個元素,a[0],a[1],那么a的值自然就是其第一個元素的地址了,也就是&a[0]了。這是一個什么類型?? 我們知道如果我們將a[0]看成一個整體,例如我們用A來代替a[0],那么A[0],A[1]就相當于a[0][0],a[0][1] 。 此時A就是一個int類型的數組,&A,的類型實際上就是 int(*p)[3]這個類型。? 所以下面的代碼也是正確的:???

????? int a[2][3];? int(*p)[3]=a; //所以對于你的問題,可以這樣子


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

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

相關文章

Redis常見面試題詳解

文章目錄1. Redis1.1 Redis可以用來做什么&#xff1f;1.2 Redis和傳統的關系型數據庫有什么不同&#xff1f;1.3 Redis有哪些數據類型&#xff1f;1.4 Redis是單線程的&#xff0c;為什么還能這么快&#xff1f;1.5 Redis在持久化時fork出一個子進程&#xff0c;這時已經有兩個…

IntelliJ 創建main函數、for循環、輸出語句快捷鍵

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 今天偶然發現了IntelliJ中 創建main函數的快捷鍵&#xff0c;依次還有for循環&#xff0c;System.out.println(); 在編寫代碼的時候直接…

CentOS新增用戶并授予sudo權限

2019獨角獸企業重金招聘Python工程師標準>>> 新增用戶 添加用戶useradd demo設置用戶密碼passwd demo授予sudo權限 輸入以下命令&#xff0c;編輯sudoers配置&#xff1a;visudo找到以下行root ALL(ALL) ALL增加以下內容&#xff1a;demo ALL(ALL) ALL保存后登錄dem…

跨站腳本功攻擊,xss,一個簡單的例子讓你知道什么是xss攻擊

跨站腳本功攻擊&#xff0c;xss&#xff0c;一個簡單的例子讓你知道什么是xss攻擊 一、總結 一句話總結&#xff1a;比如用戶留言功能&#xff0c;用戶留言中寫的是網頁可執行代碼&#xff0c;例如js代碼&#xff0c;然后這段代碼在可看到這段留言的不同一戶的顯示上就會執行。…

用業績建立信任

當年從香港到美國&#xff0c;對于中學畢業的郭尊華來說&#xff0c;算是一個失敗。他盡管家境普通&#xff0c;但因為成績不佳進不了香港的大學&#xff0c;不得不遠赴美國讀書。在課余做過挖地、當侍者等多份工作的他&#xff0c;在大三的時候終于得到一份初級工程師的工作。…

idea 快捷鍵 小結

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。1.setting 打開快捷鍵&#xff1a;ctrl alt s 在 file - setting 中。 2.回到默認設置&#xff1a;刪除如下路徑的文件夾 C:\Users…

Jenkins常用插件

Generic Webhook Trigger Plugin觸發器webhook用戶觸發構建Deploy to container Plugin部署到tomcatGradle Plugin Gradle插件構建項目Maven Plugin Maven插件構建項目Git Plugin Git插件克隆項目GitHub plugin GitHub插件集成githubSSH Slaves plugin SSH插件用于遠程登錄Buil…

spark廣播變量 和 累加器

1 為什么使用廣播變量 和 累加器 變量存在的問題&#xff1a;在spark程序中&#xff0c;當一個傳遞給Spark操作(例如map和reduce)的函數在遠程節點上面運行時&#xff0c;Spark操作實際上操作的是這個函數所用變量的一個獨立副本。這些變量會被復制到每臺機器上&#xff0c;并且…

分布式常見面試題詳解

文章目錄1. 分布式1.1 什么是CAP原則&#xff1f;1.2 說一說你對高并發的理解1.3 如何實現分布式存儲&#xff1f;1.4 說一說你對分布式事務的了解1.5 分布式系統如何保證最終一致性&#xff1f;1.6 談談你對分布式的單點問題的了解1.7 HTTP和RPC有什么區別&#xff1f;1.7 HTT…

C11標準委員會成員解讀C語言新標準

導讀&#xff1a;C語言國際標準新的新草案之前已經公布&#xff0c;新標準提高了對C的兼容性&#xff0c;并將新的特性增加到C語言中。此外支持多線程的功能也受到了開發者的關注&#xff0c;基于ISO/IEC TR 19769:2004規范下支持Unicode&#xff0c;提供更多用于查詢浮點數類型…

如何將拷貝過來的數據 *.ibd 文件生效

1.將拷貝的數據文件 "qqq.idb"放在自己的數據庫中. 一般存放在 mysql/ data/ databasename 下 2. "qqq.idb" 改個名字-->"qqq--.idb", 主要是避免沖突&#xff01; 3.執行 create table qqq(...) 語句&#xff0c;此時除了會生成一個 qqq…

windows 下啟動zookeeper的zkServer.cmd服務閃退

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 解決方案把conf目錄下的默認zoo_sample.cfg的文件名字改成zoo.cfg 即可&#xff1a; 以上轉自&#xff1a;http://blog.csdn.net/qq4960…

設計模式常見面試真題詳解

文章目錄1. 設計模式1.1 說一說設計模式的六大原則1.2 說一下六大原則中的開閉原則1.3 手寫一個單例模式1.4 手寫一個線程安全的單例模式1.5 說一說你對工廠模式的理解1.6 簡單工廠模式和抽象工廠模式有什么區別&#xff1f;1.7 如何實現工廠模式&#xff1f;1.8 說一說你策略模…

Android內存優化之內存泄漏

內存泄漏 內存泄漏一般有以下幾種情況&#xff1a;單例、靜態變量、Handler、匿名內部類、資源使用未關閉 單例導致的內存泄漏 單例的情況主要是因為單例的生命周期比較長&#xff0c;如果引用的一些資源&#xff08;比如Context、圖片等&#xff09;沒有做特殊處理&#xff0c…

cmd - 使用curl命令的注意點

前言 最近在cmd中使用curl命令來測試rest api&#xff0c;發現有不少問題&#xff0c;這里記錄一下。 在cmd中使用curl命令的注意事項 json不能由單引號包括起來json數據里的雙引號要用反斜杠\轉義json數據里不能帶有空格如果想要在json數據里使用空格則必須用雙引號將整個json…

指針常見定義

再給出常用的C變量的定義方式&#xff1a;a) 一個整型數&#xff08;An integer&#xff09; b) 一個指向整型數的指針&#xff08;A pointer to an integer&#xff09; c) 一個指向指針的的指針&#xff0c;它指向的指針是指向一個整型數&#xff08;A pointer to a pointer …

場景應用題目常見面試真題詳解

文章目錄1. 場景應用1.1 微信紅包相關問題1.2 秒殺系統相關問題1.3 掃碼登錄流程1.4 如何實現單點登錄&#xff1f;1.5 如何設計一個本地緩存&#xff1f;1. 場景應用 1.1 微信紅包相關問題 參考答案 概況&#xff1a;2014年微信紅包使用數據庫硬抗整個流量&#xff0c;2015…

后Kubernetes時代的微服務

\本文要點\\當前微服務架構依然是最流行的分布式系統架構風格。Kubernetes和云原生運動已大規模地重新定義了應用設計和開發中的一些方面。\\t在云原生平臺上&#xff0c;服務僅具備可觀測性是不夠的。更基本的先決條件是使用檢查健康、響應信號、聲明資源消耗等手段實現微服務…

Dynamics CRM On-Premise V9安裝手記

下載地址&#xff1a; https://download.microsoft.com/download/A/D/D/ADDD6898-4EFA-46FA-80B6-6FE9A3CDED63/CRM9.0-Server-CHS-amd64.exe 安裝支持Windows 2016 及SQL Server 2016 SP2以上版本 我想安裝了All in one的&#xff0c;就想著用最新的SQLServer 2017&#xff0c…

金山網絡CEO傅盛:簡約之美

摘要&#xff1a;金山網絡CEO傅盛帶來了主題為《簡約之美》的精彩演講。他表示由于時代的變遷&#xff0c;紅海的競爭&#xff0c;項目的需求等原因&#xff0c;若想項目取得成功&#xff0c;唯有簡單才是王道&#xff0c;唯有簡單定位才能深入人心。那么&#xff0c;如何做到簡…