今天就不講新知識了,記得復習前面的內容哦,在微信上看到一篇文章挺好的,直接轉了過來,和大家分享一下原文鏈接:嵌入式Linux
前言
在C語言中,數組和指針似乎總是“曖昧不清”,有時候很容易把它們混淆。本文就來理一理數組和指針之間到底有哪些異同。
數組回顧
在分析之前,我們不妨回顧一下數組的知識。數組是可以存儲一個固定大小的相同類型元素的順序集合。為了便于我們說明,假設有以下數組聲明:
int a[5];
char b[] = "hello";
數組大小必須在編譯期就作為一個常數確定下來。
但C99中引入了變長數組,允許數組的維度是表達式 ,但在數組分配內存時,其表達式的值可以被求出。
數組下標運算實際上都是通過指針進行的,也就是說a[4]與*(a+4)是等價的,甚至你會發(fā)現和4[a]也是一樣的。
數組名一般代表了指向該數組下標為0的元素的指針,并且printf("%s\n",hello)與printf("%s\n",&hello[0])等效。
數組和指針不相等
考慮下面的聲明:
int c[4];//假設int占4字節(jié)
int *d;
對于上面的聲明,編譯器會給c預留內存空間4*4字節(jié),并且數組名代表著指向數組第一個元素的指針。但對于d,卻只為指針本身保留了內存空間。
所以此時有下面的操作:
c[3]; //合法
*(c+3); //合法
*d; //不合法,d指向了內存中不確定位置
c++; //不合法,一維數組名是指針常量,常量不能被修改掉
d++; //可通過編譯.
另外,下面的兩種情況也是不一樣的:
char c[] = "hello";
char *d = "hello";
前者對字符數組c進行了初始化,后者將d指向了字符串常量。字符串常量存儲在只讀區(qū),因此有下面的操作:
c[0] = 'H'; //合法,可修改數組內容
*d = 'H'; //不合法,字符串常量內容不可更改
d[0] = 'H' //不合法
數組名的含義
絕大多數情況,數組名都代表著指向該數組中下標為0的元素的指針,但是有例外:
int e[4];//假設int為4字節(jié)
sizeof(e);
上面的sizeof(e)的值并非4或8(指針占用空間),而是4*4 = 16。也就是說,當數組名被用作運算符sizeof的參數時,它的計算結果是整個數組的大小,而非第一個元素的指針大小。
再來看下面這種情況:
int temp[5];
char *p = &temp;
char *q = temp;
在這里,p和q的值是一樣的,含義卻不一樣,前者是指向數組的指針,而后者是指向該數組中下標為0的元素的指針。因此p+1指向了temp的末尾,而q+1指向了temp的第2個元素。
數組長度計算
如何計算數組長度?考慮下面的代碼:
int f[] = {1,2,3,4,5,6};
int *g = f;
size_t len_f = sizeof(f)/sizeof(int)//正確計算方法
size_t len_g = sizeof(g)/sizeof(int)
上面的len_f和len_g的值相等嗎?顯然并不相等。事實上,只有l(wèi)en_f得到了數組f的長度,而len_g的值并沒有任何實際意義。
不能作為參數的數組
所謂的數組不能作為參數,并不是指聲明的數組不能作為參數傳遞,而是指當數組名作為參數時,數組名會被轉換為指向該數組下標為0的元素的指針。
而下面的兩種聲明,其實也是等效的:
size_t arrayLen(const int *arr);
size_t arrayLen(const int arr[]);
我們來看一個例子,說明數組作為參數的情況:
#include <stdio.h>
int arraySum(const int arr[])
{
unsigned int loop = 0;
/*循環(huán)前計算好長度,提高性能*/
unsigned int len = sizeof(arr)/sizeof(int);
int sum = 0;
if(NULL == arr)
{
return 0;
}
for(loop = 0; loop < len; loop++)
{
sum+=arr[loop];
}
return sum;
}
int main(void)
{
int a[] = {1,2,3,4,5,6};
int sum = arraySum(a);
printf("arr sum is %d",sum);
return 0;
}
我們運行上面的程序,發(fā)現最終結果并不是我們預期的21,而是3。問題在于,a作為參數傳入到arraySum中時,它是作為指針的,那么在函數內部計算sizeof(arr)自然只是得到了指針占用的內存大小。對于64位程序,這個大小是8,那么len的值為2,最終只計算了兩個元素的和。
思考:該如何修改上面的程序才能得到正確的結果?
其實我們在使用數組名作為參數時,傳遞過去的僅僅是數組第一個元素的首地址,上面例子算出來的len為1,我們算不出來sum的值。想要求出sum的值,必須把數組的長度傳遞過去,下面給出一個答案示例:
#include <stdio.h>
int arraySum(const int arr[],int len)
{
unsigned int loop = 0;
/*循環(huán)前計算好長度,提高性能*/
// unsigned int len = sizeof(arr)/sizeof(int);
int sum = 0;
if(NULL == arr)
{
return 0;
}
for(loop = 0; loop < len; loop++)
{
sum+=arr[loop];
}
return sum;
}
int main(void)
{
int a[] = {1,2,3,4,5,6},len;
len=sizeof(a)/sizeof(int);
int sum = arraySum(a,len);
printf("arr sum is %d",sum);
return 0;
}
總結
我們來總結一下前面的核心內容:
數組下標運算實際上都是通過指針進行的。
數組名代表著指向該數組中下標為0的元素的指針,但有例外:sizeof(數組名)返回整個數組的大小,而非指針大?。?amp;數組名返回一個指向數組的指針,而不是指向該數組中下標為0的元素的指針的指針。
數組名作為參數時,數組名會被轉換成指向該數組下標為0的元素的指針。
指針操作可能比下標操作效率高,但可維護性卻不一定有下標操作好。
數組和指針不相等。
思考
下面的代碼輸出結果是什么?
#include<stdio.h>
int main(void)
{
int a[5] = {1,2,3,4,5};
int *p = (int*)(&a+1);
printf("%d,%d",*(a+1),*(p-1));
return 0;
}
//輸出2 5
本文章僅供學習交流用禁止用作商業(yè)用途,文中內容來水枂編輯,如有侵權請聯系刪除,謝謝合作
微信公眾號:zhjj0729
微博:文藝to青年