跳至內容

使用者:WhitePhosphorus/磷原子1號

維基教科書,自由的教學讀本

C/指針 - en:C Programming/Pointers and arrays

指針與數組

[編輯]

Up to now, we've carefully been avoiding discussing arrays in the context of pointers. 初學者很容易混淆指針與數組,下面是兩條規律以供參考:

  • 一個數組與對應類型的指針行為類似,例如變量名代表指向數組第一個元素的指針。
  • 指針也可以像數組一樣使用下標,表示指針偏移。

當數組作為參數傳遞給函數體時,第一條規律經常起作用,實際傳入的變量是指向首元素的指針。第二條規律在訪問動態分配的內存時很有用。接着我們看兩個例子,代碼中的calloc()函數動態分配了內存給一個數組,其類型為結構體MyStruct。

 float KrazyFunction( struct MyStruct *parm1, int p1size, int bb )
 { 
   int ix; //declaring an integer variable//
   for (ix=0; ix<p1size; ix++) {
      if (parm1[ix].m_aNumber == bb )
          return parm1[ix].num2;
   }
   return 0.0f;
 }

 /* ... */
 struct MyStruct myArray[4];
 #define MY_ARRAY_SIZE (sizeof(myArray)/sizeof(*myArray))
 float v3;
 struct MyStruct *secondArray;
 int   someSize;
 int   ix;
 /* initialization of myArray ... */
 v3 = KrazyFunction( myArray, MY_ARRAY_SIZE, 4 );
 /* ... */
 secondArray = calloc( someSize, sizeof(myArray) );
 for (ix=0; ix<someSize; ix++) {
     secondArray[ix].m_aNumber = ix *2;
     secondArray[ix].num2 = .304 * ix * ix;
 }

Pointers and array names can pretty much be used interchangeably. There are exceptions. You cannot assign a new pointer value to an array name. The array name will always point to the first element of the array. In the function KrazyFunction above, you could however assign a new value to parm1, as it is just a pointer to the first element of myArray. It is also valid for a function to return a pointer to one of the array elements from an array passed as an argument to a function. A function should never return a pointer to a local variable, even though the compiler will probably not complain.

When declaring parameters to functions, declaring an array variable without a size is equivalent to declaring a pointer. Often this is done to emphasize the fact that the pointer variable will be used in a manner equivalent to an array.

	
 /* two equivalent function definitions */
 int LittleFunction( int *paramN );
 int LittleFunction( int paramN[] );

Now we're ready to discuss pointer arithmetic. You can add and subtract integer values to/from pointers. If myArray is declared to be some type of array, the expression *(myArray+j), where j is an integer, is equivalent to myArray[j]. So for instance in the above example where we had the expression secondArray[i].num2, we could have written that as *(secondArray+i).num2 or more simply (secondArray+i)->num2.

Note that for addition and subtraction of integers and pointers, the value of the pointer is not adjusted by the integer amount, but is adjusted by the amount multiplied by the size (in bytes) of the type to which the pointer refers. One pointer may also be subtracted from another, provided they point to elements of the same array (or the position just beyond the end of the array). If you have a pointer that points to an element of an array, the index of the element is the result when the array name is subtracted from the pointer. Here's an example.

 struct MyStruct someArray[20];
 struct MyStruct *p2;
 int idx;
 
 .
 /* array initialization .. */
 . 
 for (p2 = someArray; p2 < someArray+20;  ++p2) {
    if (p2->num2 > testValue) break;
 }
 idx = p2 - someArray;

You may be wondering how pointers and multidimensional arrays interact. Let's look at this a bit in detail. Suppose A is declared as a two dimensional array of floats (float A[D1][D2];) and that pf is declared a pointer to a float. If pf is initialized to point to A[0][0], then *(pf+1) is equivalent to A[0][1] and *(pf+D2) is equivalent to A[1][0]. The elements of the array are stored in row-major order.

	
 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] */

Let's look at a slightly different problem. We want to have a two dimensional array, but we don't need to have all the rows the same length. What we do is declare an array of pointers. The second line below declares A as an array of pointers. Each pointer points to a float. Here's some applicable code:

 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. */

We also note here something curious about array indexing. Suppose myArray is an array and idx is an integer value. The expression myArray[idx] is equivalent to idx[myArray]. The first is equivalent to *(myArray+idx), and the second is equivalent to *(idx+myArray). These turn out to be the same, since the addition is commutative.

Pointers can be used with preincrement or post decrement, which is sometimes done within a loop, as in the following example. The increment and decrement applies to the pointer, not to the object to which the pointer refers. In other words, *pArray++ is equivalent to *(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;
 }