歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> C/C++ 數組形參

C/C++ 數組形參

日期:2017/3/1 9:36:15   编辑:Linux編程

一維數組

數組形參會被弱化為指針,所以處理數組的函數通常通過指向數組中元素的指針來處理數組。

// three equivalent definitions of printValues
void printValues(int *) { /* … */ }
void printValues(int []) { /* … */ }
void printValues(int [10]) { /* … */ }

上面3種定義等價,形參類型都是int *,通常使用第1種。第2種形式雖然看起來更直觀,但容易引起誤解,因為函數操縱的畢竟不是數組本身,而是指向數組元素的指針。第3種形式的數組長度是被忽略的,這裡的10並無實際的約束作用,在printValues內部不應依賴這個數組長度做事情。

和其他類型一樣,形參也可以是數組的引用。這時編譯器不會將數組實參轉換為指針,而是傳遞數組的引用本身。數組大小成為形參和實參類型的一部分。編譯器檢查數組實參的大小與形參的大小是否匹配。

// ok: parameter is a reference to an array; size of array is fixed
void printValues(int (&arr)[10])
{
for (size_t i = 0; i != 10; ++i)
cout << arr[i] << endl;
}

int main()
{
int i = 0, j[2] = {0, 1};
int k[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

printValues(&i); // error: argument is not an array of 10 ints
printValues(j); // error: argument is not an array of 10 ints
printValues(k); // ok: argument is an array of 10 ints

return 0;
}

這個版本的printValues只嚴格接受含有10個ints的數組,所以在printValues函數體內依賴數組大小也是安全的。&arr兩邊的括號是必須的,否則int &arr[10],會被當做是含有10個引用的數組。

也可以傳遞多維數組,所謂多維數組實際就是數組的數組。多維數組同樣以指向首元素的指針方式傳遞。除了第一維以外的所有維的長度都必須明確指定。

// first parameter is an array whose elements are arrays of 10 ints
void printValues(int (*matrix)[10], int rowSize);
void printValues(int matrix[][10], int rowSize);實際上形參還是被弱化為一個指針,只不過它現在所指向的是含有10個ints的數組。

因為非引用型數組形參被弱化為指針,所以無法得知數組的大小,這就容易造成數組越界。一般處理方法有以下三種。

(1)在數組尾端放置一個結束標記,類似於c-style字符串的\0結束符。

(2)傳遞指向數組的第一個和最後一個元素的下一位置的指針,c++標准庫程序采用這種方法。

void printValues(const int *beg, const int *end)
{
while (beg != end)
cout << *beg++ << endl;
}(3)顯示傳遞表示數組大小的形參

void printValues(const int ia[], size_t size)
{
for (size_t i = 0; i != size; ++i)
cout << ia[i] << endl;
}


二維數組

一道面試題引發的問題,首先要知道[]的優先級高於*,題目:

char **p,a[6][8]; 問p=a是否會導致程序在以後出現問題?為什麼?

直接用程序說明:

#include<stdio.h>

void main()
{
char **p,a[6][8];
p = a;
printf("\n");
}

編譯,然後就會發現通不過,報錯:錯誤 1 error C2440: “=”: 無法從“char [6][8]”轉換為“char **”

於是乎,我看了下《C專家編程》裡10.5節—使用指針向函數傳遞一個多維數組。

方法一,函數是 void fun(int arr[2][3]); 這種方法只能處理2行3列的int型數組。

方法二,可以省略第一維的長度。函數是 void fun(int arr[][3]);這種方式雖然限制寬松了一些,但是還是只能處理每行是3個整數長度的數組。或者寫成這種形式 void fun(int (*arr)[3]);這是一個數組指針或者叫行指針,arr和*先結合使得arr成為一個指針,這個指針指向具有3個int類型數據的數組。

方法三,創建一個一維數組,數組中的元素是指向其他東西的指針,也即二級指針。函數是 int fun(int **arr);這種方法可以動態處理各行各列不一樣長度的數據。

注意:只有把二維數組改成一個指向向量的指針數組的前提下才可以這麼做!比如下面的程序可以正常輸出abc:

#include <iostream>
using namespace std;
void test(char **ptr)
{
cout << *ptr << endl;
}

int main()
{
char *p[3] = {"abc", "def", "ghi"};
test(p);
return 0;
}

在《C專家編程》10.3節的小啟發裡講的很透徹:(以下這段文字及對比一定要認真分析!)

數組和指針參數是如何被編譯器修改的?

“數組名被改寫成一個指針參數”規則並不是遞歸定義的。數組的數組會被改寫成“數組的指針”,而不是“指針的指針”:

實參 所匹配的形參

數組的數組 char c[8][10]; char (*c)[10]; 數組指針 c和*先結合使得arr成為一個指針,這個指針指向具有10個char類型數據的數組


指針數組 char *c[10]; char **c; 指針的指針

數組指針(行指針) char (*c)[10]; char (*c)[10]; 不改變

指針的指針 char **c; char **c; 不改變

下面再看一個網友的一段分析相當給力的代碼:

#include "stdafx.h"
#include <iostream>
using namespace std;

int _tmain(int argc, _TCHAR* argv[])
{
int arr1[3];
int arr2[3];
int arr3[3];
int * ptr;
// ptr1是一個指向 int [3] 的指針,即ptr的類型和&arr1的類型是一樣的,注意:arr1指向的內存區域定長
int ptr1[3][3]={{1,2,3},{1,2,3},{1,2,3}};
// ptr2是一個指向 int * 的指針,即ptr2的類型和&ptr是一樣的,注意:ptr指向的內存區域不定長
int * ptr2[3]={arr1,arr2,arr3};
// ptr3是一個指向 int [3] 的指針,即ptr3的類型和&arr1的類型是一樣的,注意:arr1指向的內存區域定長
int(* ptr3)[3]=&arr1;
ptr3=ptr1; // 沒錯,他們的類型相同
// ptr3=ptr2;//error 無法從“int *[3]”轉換為“int (*)[3]
// ptr4是一個指向 int * 的指針,即ptr4的類型和&ptr是一樣的,注意:ptr指向的內存區域不定長
int ** ptr4;
//ptr4=&arr1; //error 無法從“int (*)[3]”轉換為“int **
ptr4=ptr2; // 沒錯,他們的類型相同
//ptr4=ptr3; // error 無法從“int (*)[3]”轉換為“int **
return 0;
}

C++ 設計新思維》 下載見 http://www.linuxidc.com/Linux/2014-07/104850.htm

C++ Primer Plus 第6版 中文版 清晰有書簽PDF+源代碼 http://www.linuxidc.com/Linux/2014-05/101227.htm

讀C++ Primer 之構造函數陷阱 http://www.linuxidc.com/Linux/2011-08/40176.htm

讀C++ Primer 之智能指針 http://www.linuxidc.com/Linux/2011-08/40177.htm

讀C++ Primer 之句柄類 http://www.linuxidc.com/Linux/2011-08/40175.htm

將C語言梳理一下,分布在以下10個章節中:

  1. Linux-C成長之路(一):Linux下C編程概要 http://www.linuxidc.com/Linux/2014-05/101242.htm
  2. Linux-C成長之路(二):基本數據類型 http://www.linuxidc.com/Linux/2014-05/101242p2.htm
  3. Linux-C成長之路(三):基本IO函數操作 http://www.linuxidc.com/Linux/2014-05/101242p3.htm
  4. Linux-C成長之路(四):運算符 http://www.linuxidc.com/Linux/2014-05/101242p4.htm
  5. Linux-C成長之路(五):控制流 http://www.linuxidc.com/Linux/2014-05/101242p5.htm
  6. Linux-C成長之路(六):函數要義 http://www.linuxidc.com/Linux/2014-05/101242p6.htm
  7. Linux-C成長之路(七):數組與指針 http://www.linuxidc.com/Linux/2014-05/101242p7.htm
  8. Linux-C成長之路(八):存儲類,動態內存 http://www.linuxidc.com/Linux/2014-05/101242p8.htm
  9. Linux-C成長之路(九):復合數據類型 http://www.linuxidc.com/Linux/2014-05/101242p9.htm
  10. Linux-C成長之路(十):其他高級議題

Copyright © Linux教程網 All Rights Reserved