C語言入坑指南-數組之謎

今天就不講新知識了,記得復習前面的內容哦,在微信上看到一篇文章挺好的,直接轉了過來,和大家分享一下原文鏈接:嵌入式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青年

?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容