跳轉到內容

C 程式設計/指標和陣列

來自華夏公益教科書,開放的書籍,開放的世界
先前:複合資料型別 C 程式設計 下一個:記憶體管理
指標 *a* 指向變數 *b*。請注意,*b* 儲存一個數字,而 *a* 在記憶體中儲存 *b* 的地址 (1462)

一個指標是一個值,它指定某個值的地址(即記憶體中的位置)。指標是儲存記憶體位置的變數。

關於指標,你需要了解四個基本內容

  • 如何宣告它們(使用地址運算子 '&':int *pointer = &variable;
  • 如何給它們賦值(pointer = NULL;
  • 如何引用指標指向的值(稱為 *解引用*,使用解引用運算子 '*':value = *pointer;
  • 它們與陣列的關係(C 中絕大多數陣列都是簡單的列表,也稱為“一維陣列”,但我們將在 後面的章節 中簡要介紹一些指標的多維陣列)。

指標可以引用任何資料型別,甚至函式。我們還將討論指標與文字字串的關係以及更高階的函式指標概念。

宣告指標

[編輯 | 編輯原始碼]

考慮以下程式碼片段,它聲明瞭兩個指標

struct MyStruct {
    int   m_aNumber;
    float num2;
}
 
int main()
{
     int *pJ2;
     struct MyStruct *pAnItem;
}

第 1-4 行定義了一個 結構。第 8 行宣告一個指向一個int的變數,第 9 行宣告一個指向具有結構 MyStruct 的東西的變數。因此,要將一個變數宣告為指向某個型別而不是包含某個型別的東西,星號(*)將放在變數名前面。

在下面,第 1 行將 var1 宣告為指向一個 long 的指標,將 var2 宣告為一個 long,而不是一個指向 long 的指標。在第 2 行中,p3 被宣告為一個指向指向一個 int 的指標的指標。

long  *  var1, var2;
int   ** p3;

指標型別通常用作函式呼叫的引數。以下顯示瞭如何宣告使用指標作為引數的函式。由於 C 透過值傳遞函式引數,為了允許函式修改呼叫例程中的值,必須傳遞該值的指標。指向結構的指標也用作函式引數,即使結構中的任何內容都不會在函式中被修改。這樣做是為了避免將結構的完整內容複製到堆疊中。後面會詳細介紹指標作為函式引數的內容。

int MyFunction(struct MyStruct *pStruct);

給指標賦值

[編輯 | 編輯原始碼]

到目前為止,我們已經討論瞭如何宣告指標。接下來是給指標賦值的過程。要將變數的地址賦給指標,使用 & 或“地址運算子”。

int myInt;
int *pPointer;
struct MyStruct dvorak;
struct MyStruct *pKeyboard;

pPointer = &myInt;
pKeyboard = &dvorak;

這裡,pPointer 將現在引用 myInt,pKeyboard 將引用 dvorak。

指標還可以被賦予引用動態分配的記憶體。malloc() 和 calloc() 函式通常用於執行此操作。

#include <stdlib.h>
/* ... */
struct MyStruct *pKeyboard;
/* ... */
pKeyboard = malloc(sizeof *pKeyboard);

malloc 函式返回一個指向動態分配記憶體的指標(如果失敗則返回 NULL)。此記憶體的大小將適當調整以包含 MyStruct 結構。

以下示例展示了一個指標被賦予另一個指標以及一個指標被賦予函式的返回值。

static struct MyStruct val1, val2, val3, val4;

struct MyStruct *ASillyFunction( int b )
{
    struct MyStruct *myReturn;
 
    if (b == 1) myReturn = &val1;
    else if (b==2) myReturn = &val2;
    else if (b==3) myReturn = &val3;
    else myReturn = &val4;
  
    return myReturn;
}

struct MyStruct *strPointer;
int     *c, *d;
int     j;

c = &j;                           /* pointer assigned using & operator */
d = c;                            /* assign one pointer to another     */
strPointer = ASillyFunction( 3 ); /* pointer returned from a function. */

從函式返回指標時,不要返回指向函式區域性變數的值的指標,也不要返回指向函式引數的指標。指向區域性變數的指標在函式退出時變得無效。在上面的函式中,返回的值指向一個靜態變數。返回指向動態分配記憶體的指標也是有效的。

指標解引用

[編輯 | 編輯原始碼]
指標p指向變數a.

要訪問指標指向的值,使用 * 運算子。另一個運算子 -> 與指向結構的指標一起使用。以下是一個簡短的示例。

int   c, d;
int   *pj;
struct MyStruct astruct;
struct MyStruct *bb;

c   = 10;
pj  = &c;             /* pj points to c */
d   = *pj;            /* d is assigned the value to which pj points, 10 */
pj  = &d;             /* now points to d */
*pj = 12;             /* d is now 12 */
 
bb = &astruct;
(*bb).m_aNumber = 3;  /* assigns 3 to the m_aNumber member of astruct */
bb->num2 = 44.3;      /* assigns 44.3 to the num2 member of astruct   */
*pj = bb->m_aNumber;  /* equivalent to d = astruct.m_aNumber;          */

表示式 bb->m_aNumber(*bb).m_aNumber 完全等效。它們都訪問 bb 指向的結構的 m_aNumber 元素。還有一種解引用指標的方法,將在下一節中討論。

解引用指向無效記憶體位置的指標時,通常會導致錯誤,從而導致程式終止。該錯誤通常報告為分段錯誤。一個常見的原因是嘗試解引用指標之前未對其進行初始化。

C 以給你足夠的繩索讓你吊死自己而聞名,指標解引用就是一個典型的例子。你可以隨意編寫訪問超出你明確從系統請求的記憶體的程式碼。而且很多時候,由於系統記憶體分配的反覆無常,這些記憶體可能看起來對你的程式可用。但是,即使 99 次執行都允許你的程式執行無誤,第 100 次執行可能是系統捕獲你的“記憶體竊取”並導致程式失敗的時候。請務必確保你的指標偏移量在分配記憶體的範圍內!

宣告 void *somePointer; 用於宣告一個某種非指定型別的指標。你可以給 void 指標賦值,但必須將變數強制轉換為指向某種指定型別的指標,然後才能解引用它。void 指標也不支援指標算術運算。

指標和陣列

[編輯 | 編輯原始碼]

到目前為止,我們一直小心地避免在指標的上下文中討論陣列。指標和陣列的互動可能令人困惑,但這裡有關於它們的兩個基本陳述

  • 宣告為某種型別陣列的變數充當指向該型別的指標。當單獨使用時,它指向陣列的第一個元素。
  • 指標可以像陣列名稱一樣被索引。

第一種情況通常發生在將陣列作為引數傳遞給函式時。函式將引數宣告為指標,但實際引數可能是陣列的名稱。第二種情況通常發生在訪問動態分配的記憶體時。

讓我們看看每個示例。在以下程式碼中,對 calloc() 的呼叫有效地分配了一個 struct MyStruct 項的陣列。

struct MyStruct {
    int someNumber;
    float otherNumber;
};

float returnSameIfAnyEquals(struct MyStruct *workingArray, int size, int bb)
{ 
    /* Go through the array and check if any value in someNumber is equal to bb. If
     * any value is, return the value in otherNumber. If no values are equal to bb, 
     * return 0.0f. */
    for (int i = 0; i < size; i++) {
        if (workingArray[i].someNumber == bb ) {
            return workingArray[i].otherNumber;
        }
    }
    return 0.0f;
}

// Declare our variables
float  someResult;
int    someSize;
struct MyStruct myArray[4];
struct MyStruct *secondArray; // Notice that this is a pointer

const int ArraySize = sizeof(myArray) / sizeof(*myArray);

// Initialization of myArray occurs
someResult = returnSameIfAnyEquals(myArray, ArraySize, 4);

secondArray = calloc(someSize, sizeof(struct MyStruct));
for (int i = 0; i < someSize; i++) {
    /* Fill secondArray with some data */
    secondArray[i].someNumber = i * 2;
    secondArray[i].otherNumber = 0.304f * i * i;
}

指標和陣列名稱幾乎可以互換使用;但是,也有一些例外。你不能給陣列名稱賦予新的指標值。陣列名稱將始終指向陣列的第一個元素。在函式 returnSameIfAnyEquals 中,你可以給 workingArray 賦予一個新值,因為它只是一個指向 workingArray 的第一個元素的指標。對於函式從作為函式引數傳遞的陣列中返回指向陣列元素之一的指標也是有效的。函式永遠不應該返回指向區域性變數的指標,即使編譯器可能不會抱怨。

在宣告函式的引數時,宣告沒有大小的陣列變數等同於宣告一個指標。這通常是為了強調指標變數將以類似於陣列的方式使用。

/* Two equivalent function prototypes */

int LittleFunction(int *paramN);
int LittleFunction(int paramN[]);

現在我們準備討論指標算術運算。你可以將整數加到或減去指標。如果 myArray 被宣告為某種型別的陣列,表示式 *(myArray+j)(其中 j 是一個整數)等同於 myArray[j]。例如,在上面的示例中,我們有表示式 secondArray[i].otherNumber,我們可以寫成 (*(secondArray+i)).otherNumber 或更簡單的 (secondArray+i)->otherNumber

請注意,對於整數和指標的加減運算,指標的值不會根據整數量調整,而是根據乘以指標指向的型別的位元組大小的量調整。(例如,pointer + x 可以看作 pointer + (x * sizeof(*type)))。

也可以從一個指標中減去另一個指標,前提是它們指向同一個陣列的元素(或陣列末尾的下一個位置)。如果你有一個指向陣列元素的指標,則元素的索引是在從指標中減去陣列名稱時產生的結果。以下是一個示例。

struct MyStruct someArray[20];
struct MyStruct *p2;
int i;

/* array initialization .. */

for (p2 = someArray; p2 < someArray+20;  ++p2) {
    if (p2->num2 > testValue) 
        break;
}
i = p2 - someArray;

你可能想知道指標和多維陣列如何互動。讓我們詳細介紹一下。假設 A 被宣告為一個包含浮點數的二維陣列(float A[D1][D2];),並且 pf 被宣告為指向浮點數的指標。如果 pf 初始化為指向 A[0][0],則 *(pf+1) 等同於 A[0][1],*(pf+D2) 等同於 A[1][0]。陣列的元素以行優先順序儲存。

	
float A[6][8];
float *pf;
pf = &A[0][0]; 
*(pf+1) = 1.3;   /* assigns 1.3 to A[0][1] */
*(pf+8) = 2.3;   /* assigns 2.3 to A[1][0] */

讓我們看看一個稍微不同的問題。我們想要一個二維陣列,但我們不需要所有行都具有相同的長度。我們要做的是宣告一個指標陣列。下面的第二行將 A 宣告為一個指標陣列。每個指標都指向一個浮點數。以下是一些適用的程式碼

float  linearA[30];
float *A[6];

A[0] = linearA;              /*  5 - 0 = 5 elements in row  */
A[1] = linearA + 5;          /* 11 - 5 = 6 elements in row  */
A[2] = linearA + 11;         /* 15 - 11 = 4 elements in row */
A[3] = linearA + 15;         /* 21 - 15 = 6 elements        */
A[4] = linearA + 21;         /* 25 - 21 = 4 elements        */
A[5] = linearA + 25;         /* 30 - 25 = 5 elements        */
 
*A[3][2] = 3.66;          /* assigns 3.66 to linearA[17];     */
*A[3][-3] = 1.44;         /* refers to linearA[12];           
                             negative indices are sometimes useful. But avoid using them as much as possible. */

我們在此還注意到陣列索引的有趣之處。假設 myArray 是一個數組,i 是一個整數值。表示式 myArray[i] 等同於 i[myArray]。第一個等同於 *(myArray+i),第二個等同於 *(i+myArray)。由於加法是可交換的,所以它們最終是一樣的。

指標可以與前置遞增或後置遞減一起使用,有時在迴圈中完成,如下例所示。遞增和遞減適用於指標,而不是指標所指向的物件。換句話說,*pArray++ 等效於 *(pArray++)

long  myArray[20];
long  *pArray;
int   i;

/* Assign values to the entries of myArray */
pArray = myArray;
for (i=0; i<10; ++i) {
    *pArray++ = 5 + 3*i + 12*i*i;
    *pArray++ = 6 + 2*i + 7*i*i;
}

函式引數中的指標

[編輯 | 編輯原始碼]

通常我們需要呼叫一個函式,其引數本身是一個指標。在很多情況下,該變數本身是當前函式的引數,並且可能是指向某種結構的指標。在這種情況下,不需要 與號 (&) 字元來獲取指標值,因為該變數本身就是一個指標。在下面的示例中,變數 pStruct(一個指標)是函式 FunctTwo 的引數,並作為引數傳遞給 FunctOne

FunctOne 的第二個引數是 int。由於在函式 FunctTwo 中,mValue 是指向 int 的指標,因此必須首先使用 * 運算子解除指標的引用,因此呼叫中的第二個引數是 *mValueFunctOne 的第三個引數是指向 long 的指標。由於 pAA 本身是指向 long 的指標,因此在將其用作函式的第三個引數時不需要使用與號。

int FunctOne(struct someStruct *pValue, int iValue, long *lValue)
{
    /*  do some stuff ... */
    return 0;
}

int FunctTwo(struct someStruct *pStruct, int *mValue)
{
    int j;
    long  AnArray[25];
    long *pAA;
     
    pAA = &AnArray[13];
    j = FunctOne( pStruct, *mValue, pAA ); /* pStruct already holds the address that the pointer will point to; there is no need to get the address of anything.*/
    return j;
}

指標和文字字串

[編輯 | 編輯原始碼]

從歷史上看,C 中的文字字串已實現為字元陣列,字串中的最後一個位元組為零或空字元 '\0'。大多數 C 實現都附帶一個用於操作字串的標準庫函式。許多更常用的函式期望字串是空終止的字元字串。要使用這些函式,需要包含標準 C 標頭檔案 "string.h"。

靜態宣告的初始化字串將類似於以下內容

static const char *myFormat = "Total Amount Due: %d";

變數 myFormat 可以被視為一個包含 21 個字元的陣列。在 'd' 之後作為陣列中的第 21 個專案,隱含地添加了一個空字元 ('\0') 到字串的末尾。你也可以按如下方式初始化陣列的各個字元

static const char myFlower[] = { 'P', 'e', 't', 'u', 'n', 'i', 'a', '\0' };

初始化的字串陣列通常如下完成

static const char *myColors[] = {
    "Red", "Orange", "Yellow", "Green", "Blue", "Violet" };

特別長的字串的初始化可以在原始碼行中拆分,如下所示。

static char *longString = "Hello. My name is Rudolph and I work as a reindeer "
    "around Christmas time up at the North Pole.  My boss is a really swell guy."
    " He likes to give everybody gifts.";

與字串一起使用的庫函式將在後面的章節中討論。

指向函式的指標

[編輯 | 編輯原始碼]

C 還允許你建立指向函式的指標。指向函式的指標語法可能相當混亂。作為示例,考慮以下函式

static int Z = 0;

int *pointer_to_Z(int x) {
    /* function returning integer pointer, not pointer to function */
    return &Z;
}

int get_Z(int x) {
    return Z;
}
  
int (*function_pointer_to_Z)(int); // pointer to function taking an int as argument and returning an int
function_pointer_to_Z = &get_Z;

printf("pointer_to_Z output: %d\n", *pointer_to_Z(3));
printf("function_pointer_to_Z output: %d", (*function_pointer_to_Z)(3));

為函式指標宣告 typedef 通常可以使程式碼更清晰。這是一個使用函式指標和 void * 指標來實現所謂的回撥的示例。DoSomethingNice 函式使用呼叫方提供的函式 TalkJive 和呼叫方資料進行呼叫。請注意,DoSomethingNice 實際上並不知道 dataPointer 指向什麼。

typedef  int (*MyFunctionType)( int, void *);      /* a typedef for a function pointer */

#define THE_BIGGEST 100

int DoSomethingNice( int aVariable, MyFunctionType aFunction, void *dataPointer )
{
    int rv = 0;
    if (aVariable < THE_BIGGEST) {
       /* invoke function through function pointer (old style) */
       rv = (*aFunction)(aVariable, dataPointer );
     } else {
         /* invoke function through function pointer (new style) */
       rv = aFunction(aVariable, dataPointer );
    };
    return rv;
}

typedef struct {
    int    colorSpec;
    char   *phrase;
} DataINeed;
 
int TalkJive( int myNumber, void *someStuff )
{
    /* recast void * to pointer type specifically needed for this function */
    DataINeed *myData = someStuff;
    /* talk jive. */
    return 5;
}
 
static DataINeed sillyStuff = { BLUE, "Whatcha talkin 'bout Willis?" };
  
DoSomethingNice( 41, &TalkJive,  &sillyStuff );

某些版本的 C 可能不需要在 DoSomethingNice 呼叫中的 TalkJive 引數前面使用與號。某些實現可能需要將引數專門轉換為 MyFunctionType 型別,即使函式簽名與 typedef 完全匹配。

函式指標可用於在 C 中實現一種多型性。首先宣告一個結構,該結構的元素是用於多型指定各種操作的函式指標。還宣告一個包含指向先前結構的指標的第二個基本物件結構。透過將特定於類的資料擴充套件到第二個結構來定義類,以及型別為第一個結構的靜態變數,該變數包含與類關聯的函式的地址。這種型別的多型性在呼叫檔案 I/O 函式時在標準庫中使用。

類似的機制也可用於在 C 中實現 狀態機。定義一個包含函式指標的結構,用於處理可能在狀態內發生的事件,以及在進入和退出狀態時要呼叫的函式。此結構的例項對應於一個狀態。每個狀態都使用指向適用於該狀態的函式的指標進行初始化。狀態機的當前狀態實際上是指向其中一個狀態的指標。更改當前狀態指標的值實際上會更改當前狀態。當發生某些事件時,將通過當前狀態中的函式指標呼叫相應的函式。

C 中函式指標的實際應用

[編輯 | 編輯原始碼]

函式指標主要用於降低 switch 語句的複雜性。使用 switch 語句的示例

#include <stdio.h>
int add(int a, int b);
int sub(int a, int b);
int mul(int a, int b);
int div(int a, int b);
int main()
{
    int i, result;
    int a=10;
    int b=5;
    printf("Enter the value between 0 and 3 : ");
    scanf("%d",&i); 
    switch(i)
    {
        case 0:  result = add(a,b); break;
        case 1:  result = sub(a,b); break;
        case 2:  result = mul(a,b); break;
        case 3:  result = div(a,b); break;
    }
}
int add(int i, int j)
{
    return (i+j);
}
int sub(int i, int j)
{
    return (i-j);
}
 int mul(int i, int j)
{
    return (i*j);
}
int div(int i, int j)
{
    return (i/j);
}

不使用 switch 語句

#include <stdio.h>
int add(int a, int b);
int sub(int a, int b);
int mul(int a, int b);
int div(int a, int b);
int (*oper[4])(int a, int b) = {add, sub, mul, div};
int main()
{
    int i,result;
    int a=10;
    int b=5;
    printf("Enter the value between 0 and 3 : ");
    scanf("%d",&i); 
    result = oper[i](a,b);
}
int add(int i, int j)
{
    return (i+j);
}
int sub(int i, int j)
{
    return (i-j);
}
int mul(int i, int j)
{
    return (i*j);
}
int div(int i, int j)
{
    return (i/j);
}

函式指標可用於建立結構成員函式

typedef struct
{
    int (*open)(void);
    void (*close)(void);
    int (*reg)(void);
} device;

int my_device_open(void)
{
    /* ... */
}

void my_device_close(void)
{
    /* ... */
}

void register_device(void)
{
    /* ... */
}

device create(void)
{
    device my_device;
    my_device.open = my_device_open;
    my_device.close = my_device_close;
    my_device.reg = register_device;
    my_device.reg();
    return my_device;
}

用於實現此指標(以下程式碼必須放在庫中)。

static struct device_data
{
    /* ... here goes data of structure ... */
};

static struct device_data obj;

typedef struct
{
    int (*open)(void);
    void (*close)(void);
    int (*reg)(void);
} device;

static struct device_data create_device_data(void)
{
    struct device_data my_device_data;
    /* ... here goes constructor ... */
    return my_device_data;
}

/* here I omit the my_device_open, my_device_close and register_device functions */

device create_device(void)
{
    device my_device;
    my_device.open = my_device_open;
    my_device.close = my_device_close;
    my_device.reg = register_device;
    my_device.reg();
    return my_device;
}

指標構造示例

[編輯 | 編輯原始碼]

以下是一些示例構造,它們可能有助於建立指標。

int i;          // integer variable 'i'
int *p;         // pointer 'p' to an integer
int a[];        // array 'a' of integers
int f();        // function 'f' with return value of type integer
int **pp;       // pointer 'pp' to a pointer to an integer
int (*pa)[];    // pointer 'pa' to an array of integer
int (*pf)();    // pointer 'pf' to a function with return value integer
int *ap[];      // array 'ap' of pointers to an integer
int *fp();      // function 'fp' which returns a pointer to an integer
int ***ppp;     // pointer 'ppp' to a pointer to a pointer to an integer
int (**ppa)[];  // pointer 'ppa' to a pointer to an array of integers
int (**ppf)();  // pointer 'ppf' to a pointer to a function with return value of type integer
int *(*pap)[];  // pointer 'pap' to an array of pointers to an integer
int *(*pfp)();  // pointer 'pfp' to function with return value of type pointer to an integer
int **app[];    // array of pointers 'app' that point to pointers to integer values
int (*apa[])[]; // array of pointers 'apa' to arrays of integers
int (*apf[])(); // array of pointers 'apf' to functions with return values of type integer
int ***fpp();   // function 'fpp' which returns a pointer to a pointer to a pointer to an int
int (*fpa())[]; // function 'fpa' with return value of a pointer to array of integers
int (*fpf())(); // function 'fpf' with return value of a pointer to function which returns an integer

sizeof 運算子通常用於引用在同一函式中較早宣告的靜態陣列的大小。

要找到陣列的結尾(來自 wikipedia:Buffer overflow 的示例)

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
  char buffer[10];
  if (argc < 2)
  {
    fprintf(stderr, "USAGE: %s string\n", argv[0]);
    return 1;
  }
  strncpy(buffer, argv[1], sizeof(buffer));
  buffer[sizeof(buffer) - 1] = '\0';
  return 0;
}


要遍歷陣列中的每個元素,請使用

 #define NUM_ELEM(x) (sizeof (x) / sizeof (*(x)))

 for( i = 0; i < NUM_ELEM(array); i++ )
 {
     /* do something with array[i] */
     ;
 }


請注意,sizeof 運算子僅對在同一函式中較早定義的內容起作用。編譯器會將其替換為某些固定的常數。在本例中,buffer 較早地在同一函式中宣告為包含 10 個 char 的陣列,編譯器會將 sizeof(buffer) 在編譯時替換為數字 10(相當於我們在程式碼中將 sizeof(buffer) 硬編碼為 10)。有關 buffer 長度的資訊實際上沒有儲存在記憶體中的任何位置(除非我們單獨跟蹤它),並且無法在執行時從陣列/指標本身以程式設計方式獲取。

通常,函式需要知道傳入陣列的大小 - 在其他函式中定義的陣列。例如,

/* broken.c - demonstrates a flaw */

#include <stdio.h>
#include <string.h>
#define NUM_ELEM(x) (sizeof (x) / sizeof (*(x)))

int sum( int input_array[] ){
  int sum_so_far = 0;
  int i;
  for( i = 0; i < NUM_ELEM(input_array); i++ ) // WON'T WORK -- input_array wasn't defined in this function.
  {
    sum_so_far += input_array[i];
  };
  return( sum_so_far );
}

int main(int argc, char *argv[])
{
  int left_array[] = { 1, 2, 3 };
  int right_array[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
  int the_sum = sum( left_array );
  printf( "the sum of left_array is: %d", the_sum );
  the_sum = sum( right_array );
  printf( "the sum of right_array is: %d", the_sum );

  return 0;
}

不幸的是,(在 C 和 C++ 中),無法從執行時傳入的陣列中獲取陣列的長度,因為(如上所述)陣列的大小沒有儲存在任何地方。編譯器總是將 sizeof 替換為常量。此 sum() 例程需要處理的不僅僅是陣列的一種恆定長度。

有一些常見的解決方法

  • 編寫函式以要求每個陣列引數都包含一個“長度”引數(其型別為“size_t”)。(通常我們在呼叫此函式的地方使用 sizeof)。
  • 使用約定,例如 空終止字串 來標記陣列的結束。
  • 不要傳遞原始陣列,而是傳遞一個包含陣列長度(例如“.length”)以及陣列(或指向第一個元素的指標)的結構;類似於 C++ 中的 stringvector 類。
/* fixed.c - demonstrates one work-around */

#include <stdio.h>
#include <string.h>
#define NUM_ELEM(x) (sizeof (x) / sizeof (*(x)))

int sum( int input_array[], size_t length ){
  int sum_so_far = 0;
  int i;
  for( i = 0; i < length; i++ )
  {
    sum_so_far += input_array[i];
  };
  return( sum_so_far );
}

int main(int argc, char *argv[])
{
  int left_array[] = { 1, 2, 3, 4 };
  int right_array[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
  int the_sum = sum( left_array, NUM_ELEM(left_array) ); // works here, because left_array is defined in this function
  printf( "the sum of left_array is: %d", the_sum );
  the_sum = sum( right_array, NUM_ELEM(right_array) ); // works here, because right_array is defined in this function
  printf( "the sum of right_array is: %d", the_sum );

  return 0;
}

值得一提的是 sizeof 運算子有兩種變體sizeof (型別)(例如sizeof (int)sizeof (struct some_structure)) 和sizeof 表示式(例如sizeof some_variable.some_fieldsizeof 1).

[編輯 | 編輯原始碼]
與 Binky 一起玩指標
先前:複合資料型別 C 程式設計 下一個:記憶體管理
華夏公益教科書