C語言數組參數與指針參數

我們都知道參數分為形參和實參。形參是指聲明或定義函數時的參數,而實參是在調用函數時主調函數傳遞過來的實際值。

一、一維數組參數

1、能否向函數傳遞一個數組?看例子:
void fun(char a[10])
{
? ?char c = a[3];
}
intmain()
{
? ?char b[10] = “abcdefg”;
? ?fun(b[10]);
? ?return 0;
}
先看上面的調用,fun(b[10]);將b[10]這個數組傳遞到fun 函數。但這樣正確嗎?b[10]是代表一個數組嗎?

顯然不是,我們知道b[0]代表是數組的一個元素,那b[10]又何嘗不是呢?只不過這里數組越界了,這個b[10]并不存在。但在編譯階段,編譯器并不會真正計算b[10]的地址并取值,所以在編譯的時候編譯器并不認為這樣有錯誤。雖然沒有錯誤,但是編譯器仍然給出了兩個警告:
? ?warning C4047: 'function' : 'char *' differs in levels of indirection from 'char '
? ?warning C4024: 'fun' : different types for formal and actual parameter 1
這是什么意思呢?這兩個警告告訴我們,函數參數需要的是一個char*類型的參數,而實際參數為char 類型,不匹配。雖然編譯器沒有給出錯誤,但是這樣運行肯定會有問題。如圖:
這是一個內存異常,我們分析分析其原因。其實這里至少有兩個嚴重的錯誤。

第一:b[10]并不存在,在編譯的時候由于沒有去實際地址取值,所以沒有出錯,但是在運行時,將計算b[10]的實際地址,并且取值。這時候發生越界錯誤。
第二:編譯器的警告已經告訴我們編譯器需要的是一個char*類型的參數,而傳遞過去的是一個char 類型的參數,這時候fun 函數會將傳入的char 類型的數據當地址處理,同樣會發生錯誤。(這點前面已經詳細講解)


第一個錯誤很好理解,那么第二個錯誤怎么理解呢?fun 函數明明傳遞的是一個數組啊,編譯器怎么會說是char *類型呢?別急,我們先把函數的調用方式改變一下:
? ?fun(b);
b 是一個數組,現在將數組b 作為實際參數傳遞。這下該沒有問題了吧?調試、運行,一切正常,沒有問題,收工!很輕易是吧?但是你確認你真正明白了這是怎么回事?數組b真的傳遞到了函數內部?

2、無法向函數傳遞一個數組
我們完全可以驗證一下:
void fun(char a[10])
{
? ?int i = sizeof(a);
? ?char c = a[3];
}
如果數組b 真正傳遞到函數內部,那i 的值應該為10。但是我們測試后發現i 的值竟然為4!為什么會這樣呢?難道數組b 真的沒有傳遞到函數內部?是的,確實沒有傳遞過去,這是因為這樣一條規則:
C 語言中,當一維數組作為函數參數的時候,編譯器總是把它解析成一個指向其首元素首地址的指針。

這么做是有原因的。在C 語言中,所有非數組形式的數據實參均以傳值形式(對實參做一份拷貝并傳遞給被調用的函數,函數不能修改作為實參的實際變量的值,而只能修改傳遞給它的那份拷貝)調用。然而,如果要拷貝整個數組,無論在空間上還是在時間上,其開銷都是非常大的。更重要的是,在絕大部分情況下,你其實并不需要整個數組的拷貝,你只想告訴函數在那一刻對哪個特定的數組感興趣。這樣的話,為了節省時間和空間,提高程序運行的效率,于是就有了上述的規則。同樣的,函數的返回值也不能是一個數組,而只能是指針。這里要明確的一個概念就是:函數本身是沒有類型的,只有函數的返回值才有類型。很多書都把這點弄錯了,甚至出現“XXX 類型的函數”這種說法。簡直是荒唐至極!

經過上面的解釋,相信你已經理解上述的規定以及它的來由。上面編譯器給出的提示,說函數的參數是一個char*類型的指針,這點相信也可以理解。既然如此,我們完全可以把fun 函數改寫成下面的樣子:
void fun(char *p)
{
? ?char c = p[3];//或者是char c = *(p+3);
}

同樣,你還可以試試這樣子:
void fun(char a[10])
{
? ?char c = a[3];
}
intmain()
{
? ?char b[100] = “abcdefg”;
? ?fun(b);
? ?return 0;
}
運行完全沒有問題。實際傳遞的數組大小與函數形參指定的數組大小沒有關系。既然如此,那我們也可以改寫成下面的樣子:
void fun(char a[ ])
{
? ?char c = a[3];
}
改寫成這樣或許比較好,至少不會讓人誤會成只能傳遞一個10 個元素的數組。

二、一級指針參數

1、能否把指針變量本身傳遞給一個函數
我們把上一節討論的列子再改寫一下:
void fun(char *p)
{
? ?char c = p[3];//或者是char c = *(p+3);
}
intmain()
{
? ?char *p2 = “abcdefg”;
? ?fun(p2);
? ?return 0;
}
這個函數調用,真的把p2 本身傳遞到了fun 函數內部嗎?

我們知道p2 是main 函數內的一個局部變量,它只在main 函數內部有效。( 這里需要澄清一個問題:main 函數內的變量不是全局變量,而是局部變量,只不過它的生命周期和全局變量一樣長而已。全局變量一定是定義在函數外部的。初學者往往弄錯這點。)既然它是局部變量,fun 函數肯定無法使用p2 的真身。那函數調用怎么辦?好辦:對實參做一份拷貝并傳遞給被調用的函數。即對p2 做一份拷貝,假設其拷貝名為_p2。那傳遞到函數內部的就是_p2 而并非p2 本身。

2、無法把指針變量本身傳遞給一個函數
這很像孫悟空拔下一根猴毛變成自己的樣子去忽悠小妖怪。所以fun 函數實際運行時,用到的都是_p2 這個變量而非p2 本身。如此,我們看下面的例子:
void GetMemory(char * p, int num)
{
? ?p = (char *)malloc(num*sizeof(char));
}
intmain()
{
? ?char *str = NULL;
? ?GetMemory(str,10);
? ?strcpy(str,”hello”);
? ?free(str);//free 并沒有起作用,內存泄漏
? ?return 0;
}
在運行strcpy(str,”hello”)語句的時候發生錯誤。這時候觀察str 的值,發現仍然為NULL。也就是說str 本身并沒有改變,我們malloc 的內存的地址并沒有賦給str,而是賦給了_str。

而這個_str 是編譯器自動分配和回收的,我們根本就無法使用。所以想這樣獲取一塊內存是不行的。那怎么辦? 兩個辦法:
第一:用return。
char * GetMemory(char * p, int num)
{
? ?p = (char *)malloc(num*sizeof(char));
? ?return p;
}
intmain()
{
? ?char *str = NULL;
? ?str = GetMemory(str,10);
? ?strcpy(str,”hello”);
? ?free(str);
? ?return 0;
}
這個方法簡單,容易理解。

第二:用二級指針。
void GetMemory(char ** p, int num)
{
? ?*p = (char *)malloc(num*sizeof(char));
? ?return p;
}
intmain()
{
? ?char *str = NULL;
? ?GetMemory(&str,10);
? ?strcpy(str,”hello”);
? ?free(str);
? ?return 0;
}
注意,這里的參數是&str 而非str。這樣的話傳遞過去的是str 的地址,是一個值。在函數內部,用鑰匙(“*”)來開鎖:*(&str),其值就是str。所以malloc 分配的內存地址是真正賦值給了str 本身。
另外關于malloc 和free 的具體用法,內存管理那章有詳細討論。

三、二維數組參數與二維指針參數

前面詳細分析了二維數組與二維指針,那它們作為參數時與不作為參數時又有什么區別呢?看例子:
? ?void fun(char a[3][4]);
我們按照上面的分析,完全可以把a[3][4]理解為一個一維數組a[3],其每個元素都是一個含有4 個char 類型數據的數組。上面的規則,“C 語言中,當一維數組作為函數參數的時候,編譯器總是把它解析成一個指向其首元素首地址的指針。”在這里同樣適用,也就是說我們可以把這個函數聲明改寫為:
? ?void fun(char (*p)[4]);
這里的括號絕對不能省略,這樣才能保證編譯器把p 解析為一個指向包含4 個char 類型數據元素的數組,即一維數組a[3]的元素。

同樣,作為參數時,一維數組“[]”號內的數字完全可以省略:
? ?void fun(char a[ ][4]);
不過第二維的維數卻不可省略,想想為什么不可以省略?

注意:如果把上面提到的聲明void fun(char (*p)[4])中的括號去掉之后,聲明“void f un(char *p[4])”可以改寫成:
? ?void fun(char **p);
這是因為參數*p[4],對于p 來說,它是一個包含4 個指針的一維數組,同樣把這個一維數組也改寫為指針的形式,那就得到上面的寫法。

上面討論了這么多,那我們把二維數組參數和二維指針參數的等效關系整理一下:

這里需要注意的是:C 語言中,當一維數組作為函數參數的時候,編譯器總是把它解析成一個指向其首元素首地址的指針。這條規則并不是遞歸的,也就是說只有一維數組才是如此,當數組超過一維時,將第一維改寫為指向數組首元素首地址的指針之后,后面的維再也不可改寫。比如:a[3][4][5]作為參數時可以被改寫為(*p)[4][5]。

至于超過二維的數組和超過二級的指針,由于本身很少使用,而且按照上面的分析方法也能很好的理解,這里就不再詳細討論。有興趣的可以好好研究研究。

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

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

相關文章

maven文件結構

pom.xml 用于maven的配置文件 /src 源代碼目錄 /src/main 工程源代碼目錄 /src/main/java 工程java源代碼目錄 /src/main/resource 工程的資源目錄 /src/test 單元測試目錄 /src/test/java /target 輸出目錄,所有的輸出都存放在這個目錄下 /target/classes 編譯之…

php如何使用高階函數

1、首先學會數組轉集合的方式 (1)使用collect函數 $arr [1, 2, 3, 4, 5]; $collect collect($arr); (2)使用array_map函數 $arr [1, 2, 3, 4, 5]; $collect array_map(function($item){ return $item *…

Git 使用,命令說明

前些天發現了一個巨牛的人工智能學習網站,通俗易懂,風趣幽默,忍不住分享一下給大家。點擊跳轉到教程。 1. D:\ChengXu\git\Git中雙擊Git Bash啟動git窗口。 2. 這條不能放到博客,是我的賬號密碼。 3. 添加: git add …

2017ACM/ICPC亞洲區沈陽站 C Hdu-6219 Empty Convex Polygons 計算幾何 最大空凸包

題面 題意:給你一堆點,求一個最大面積的空凸包,里面沒有點. 題解:紅書板子,照抄完事,因為題目給的都是整點,所以最后答案一定是.5或者.0結尾,不用對答案多做處理 1 #include<bits/stdc.h>2 #define N 553 using namespace std;4 struct rec5 {6 double x,y;7 };8 rec…

python讀xml文件

# -*- coding:utf-8 -*- import jsonimport requestsimport oscurpathos.path.dirname(os.path.realpath(__file__))xmlpathos.path.join(curpath,read1.xml)with open(xmlpath,encoding"utf-8") as fp: bodyfp.read() print(body)轉載于:https://www.cnblogs.…

C語言數組應用

一、數組的內存布局 先看下面的例子&#xff1a;int a[5];所有人都明白這里定義了一個數組&#xff0c;其包含了5 個int 型的數據。我們可以用a[0],a[1]等來訪問數組里面的每一個元素&#xff0c;那么這些元素的名字就是a[0],a[1]…嗎&#xff1f;看下面的示意圖&#xff1a; 如…

Installation failed, deleting ./composer.json.安裝phpunit報錯解決方案

是因為你沒有裝全局的phpunit&#xff0c;安裝命令 composer global require phpunit/phpunit 之后你輸入 composer require --dev phpunit/phpunit 就發現你安裝成功了

MyBatis在Oracle中插入數據并返回主鍵的問題解決

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 前言&#xff1a;我早期用過這個方法&#xff0c;但是返回的依舊是影響行數&#xff0c;不是主鍵。 只是這種寫法可以達到我要的效果&a…

在 Intellij IDEA 里使用 OpenJFX (JavaFX)

2019獨角獸企業重金招聘Python工程師標準>>> JDK 11 把 JavaFX 剝離了出來&#xff0c;形成了單獨且開源的 OpenJFX 模塊。 本文的目的是通過簡單的例子解釋這一變化對使用 JavaFX 所造成的影響&#xff0c;并找到一種在 IDEA 2018.2 上使用它的辦法。 首先&#xf…

使用phpunit新建項目

1、mkdir test-project 新建一個test-project 2、cd test-project 跑到文件夾中 3、實例化git git init 4、新建phpunit項目 composer require --dev phpunit/phpunit 5、使用gi實例化.gitignore gi composer>.gitignore (如果沒有安裝gi&#xff0c;請使用命令ec…

如何解決eclipse里面tomcat 8080端口被占用

很多時候運行tomcat 的時候總是會提示tomcat 的端口被占用 但是任務管理器里面還找不到是哪個端口被占用了 因此很多人就重新配置tomcat 或者去修改tomcat的端口號 &#xff0c;其實這么做太麻煩了 &#xff0c;小弟在這里告訴你一個非常簡單的方法。 1.在開始菜單中選擇運行 …

Selenium UI 舉例 getCssValue

selenium jar包中&#xff0c;在WebElement的接口中&#xff0c; String getCssValue(String var1);可以通過標簽&#xff0c;獲取對應的css值。具體要怎么用呢&#xff0c;如下&#xff1a; WebElement baidu driver.findElement(By.id("su"));su.getCssValue(&quo…

java集合框架中contains(),containsKey()和containsValue()的用法

前些天發現了一個巨牛的人工智能學習網站&#xff0c;通俗易懂&#xff0c;風趣幽默&#xff0c;忍不住分享一下給大家。點擊跳轉到教程。 java集合框架中contains(),containsKey()和containsValue()的用法&#xff1a; List集合的contains()方法用于判斷集合中包不包含某個元…

敏捷視頻

規模化極限編程的關鍵抓手&#xff1a;驗收條件https://mp.weixin.qq.com/s/aHlSxpMx7DTQXaoEgcAQ3g 5分鐘讓你子解持續集成https://www.bilibili.com/video/BV1SK411W77W/?spm_id_fromtrigger_reload 5分鐘讓你學會返工率降低1倍的神技--開卡、驗卡https://www.bilibili.com/…

提問的智慧

提問的智慧轉載于:https://www.cnblogs.com/whigym/p/10028642.html

C語言指針和數組概述

幾乎每次講課講到指針和數組時&#xff0c;我總會反復不停的問學生&#xff1a;到底什么是指針&#xff1f;什么是數組&#xff1f;他們之間到底是什么樣的關系。從幾乎沒人能回答明白到幾乎都能回答明白&#xff0c;需要經歷一段“慘絕人寰”的痛。指針是C/C的精華&#xff0c…

Linux tee的花式用法和pee

1.tee多重定向 tee [options] FILE1 FILE2 FILE3... tee的作用是將一份標準輸入多重定向&#xff0c;一份重定向到標準輸出/dev/stdout&#xff0c;然后還將標準輸入重定向到每個文件FILE中。 例如&#xff1a; $ cat alpha.log | tee file1 file2 file3 | cat $ cat alpha.log…

[CF893F]Subtree Minimum Query

題目大意&#xff1a; 給你一顆有根樹&#xff0c;點有權值&#xff0c;m次詢問&#xff0c;每次問你某個點的子樹中距離其不超過k的點的權值的最小值。&#xff08;邊權均為1&#xff0c;點權有可能重復&#xff0c;k值每次詢問有可能不同&#xff0c;強制在線&#xff09; 做…

mac電腦快捷鍵(持續更新)

1、快速查找軟件 commandspace 2、顯示/隱藏文件夾 shiftcmmand. 3、路徑輸入 commandshiftg 4、快速打開軟件 commandtab 5、截圖 commandshift3 commandshift4 6、注銷 Command-Shift-Q 7、強制注銷 ommand-Shift-Option-Q 8、睡眠 controlshift電源鍵 9、選…

C語言typedef關鍵字—偉大的縫紉師

關于馬甲的笑話。有這樣一個笑話&#xff1a;一個獵人在河邊抓捕一條蛇&#xff0c;蛇逃進了水里。過一會&#xff0c;一個烏龜爬到岸邊。獵人一把抓住這個烏龜&#xff0c;大聲的說道&#xff1a;小樣&#xff0c;別你為你穿了個馬甲我就不認識你了&#xff01;typedef 關鍵字…