User: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;
 }