当前位置: 首页 > 业界动态 > 技术实现 > 本文


深度理解C语言的指针与数组




发布时间: 2012-10-19 15:56:30  
    写个简单的yuv读取的库,卡在多维数组动态分配的问题上。唉,还是C基本功不扎实,于是花了一下午时间,算是给自己有了点交代。参考《C专家编程》。水平有限,欢迎看客指正。

  Section 1 左值与右值

  编译器为每个变量分配一个地址(左值),该地址在编译时可知,且变量在运行时一直存于该地址。存于该地址的变量的值(右值)只有在运行时可知。因此,编译器如果需要一个地址来执行某种操作,它可以直接进行操作,如果需要一个变量的值,它需要发出指令从指定地址中读入变量值并存于寄存器中。到这里,可以理解作为一个指针变量,它本身的地址是左值,它变量的值(即指向的地址值)为右值。所以指针首先需要在运行时取得它的当前值,然后才能对它进行解除引用操作。

  数组名是一个左值,即内存中的位置。但数组名是一个不可修改的左值,即不可被赋值。

  int main()

  {

  int a[3] = {0};

  int b = 1;

  a = &b; //ERROR: “=” : 左操作数必须为 l 值。

  return 0;

  }

  Section 2 数组与指针的不同

  一个例子:

  int main()

  {

  char arr[4] = “abc”; // Note 1

  //char arr[4] = {''a'', ''b'', ''c'', ''\0''}; // Note 2

  char *ptr = “ABC”; // Note 3

  //ptr+1 = &arr[2]; // Note 4

  printf(“arr: %x, %x, %x %x \n”, &arr, &arr[0], &arr[1]); //Note 5

  printf(“ptr: %x, %x, %x %x \n”, &ptr, &ptr[0], &ptr[1]);

  return 0;

  }

  Note 1&2等价定义,其结构如下:

  a b c \0

  [__] [__] [__] [__]

  12fed4 +1 +2 +3

  Note 3结构如下

  42703c A B C \0

  [__] [__] [__] [__] [__]

  12fec8 42703c +1 +2 +3

  Note 4复习一下Section 1.显然的错误,因为p+1首先需要知道p的值(右值),只有在运行时刻才能得到,编译时刻就希望对其所在的地址进行赋值显然错误。

  Note 5验证Note1和3,运行结果如下:

  arr: 12fed4, 12fed4, 12fed5

  ptr: 12fec8, 42703c, 42703d

  可以发现,arr的地址(左值)的结果与数组中首元素的地址一致,而ptr的变量值(右值)与数组的首元素地址一致。

  因此对一个数组中的元素进行引用,c=arr[i]和c=ptr[i]都能够取出相应数组中的第i个元素。但要注意这两个操作的过程完全不同:

  c = arr[i]; c = ptr[i];

  1:取地址12fec8的内容,即42703c

  1 取出i的值与12fed4相加 2:取出i的值与42703c相加

  2 取地址(12fed4+ i)的内容 3:取地址(42703c+i)的内容

  得到结论:尽管c=arr[i]和c=ptr[i]用同样的形式完成了同样的功能,但绝不可以混用。注意数组原始的声明方式,如果原始声明为数组式的,那么对其元素的引用要使用数组形式,反之亦然。

  文件1中:

  int array[100];

  文件2中:

  extern int *array;

  array[50] = 3; //知道这句为什么错了吧?

  Section 3 数组与指针的相同

  传说有三种情况下,数组名会被当作指针。

  1 “表达式中的数组名”就是指针

  int a[10], *p, i;

  p = a; //here

  2 数组下标就是指针的偏移量

  以下语句功能一致,但需注意实现的过程不一样(Section 2):

  a[i] = 0;

  p[i] = 0;

  *(p+i) = 0;

  3 函数形参中的数组名被当作指向第一个元素的指针

  以下三种函数声明的形式是等同的:

  my_function(int *p) {…}

  my_function(int p[]) {…}

  my_function(int p[100]) {…}

  对my_function函数的调用,无论实参是数组还是指针,都是合法的。

  Section 4 多维数组

  首先理解一个简单的多维数组:

  int main()

  {

  int apricot[2][3][5];

  int (*p)[3][5] = apricot; // 别忘记“表达式中的数组名”就是指针

  int (*r)[5] = apricot[1];

  int *t = apricot[1][2];

  int u = apricot[1][2][3];

  return 0;

  }

  根据数组下标规则不难理解,apricot[i][j][k]将被编译器解析为(*(*(apricot+i)+j)+k)。而且多维数组在内存中的布局是线性形式的,所以可以得到apricot[i][j][k]可以通过计算*(&apricot + i*3*5 + j*5 + k)得到。

  另一种实现方法是使用指针数组或指针的指针。这种方式的特点是灵活,并且可以实现动态分配多维数组。

  char *pea[4];

  char **pea;

  仍然可以通过pea[i][j]来引用其中的变量以及上述的内存位置计算方法(注意是否满足连续线性内存布局)。但这时需要注意的是对这种变量的初始化工作有一点技巧性,因为需要保证指针在后续的使用过程中都是合法的。常用方法是循环malloc

  for(j = 0; j < 4; j++)

  pea[j] = malloc[6];

  或一次性malloc一整块数据,然后用循环将指针指向各个区域:

  malloc(row * column * sizeof(char));

  最后来两个我的yuvlib里的子程序,看懂了指针就过关了。

  ************************************************************************

  * \brief

  * Allocate 2D memory array -> unsigned char array2D[rows][columns]

  *

  * \par Output:

  * memory size in bytes

  ************************************************************************/

  int get_mem2D(byte ***array2D, int rows, int columns)

  {

  int i;

  if((*array2D = (byte**)malloc(rows*sizeof(byte*))) == NULL)

  exit(2);

  if(((*array2D)[0] = (byte* )malloc(columns*rows*sizeof(byte ))) == NULL)

  exit(2);

  for(i=1;i

  (*array2D)[i] = (*array2D)[i-1] + columns ;

  return rows*columns;

  }

  /*!

  ************************************************************************

  * \brief

  * free 2D memory array

  * which was alocated with get_mem2D()

  ************************************************************************

  */

  void free_mem2D(byte **array2D)

  {

  if (array2D)

  {

  if (array2D[0])

  free (array2D[0]);

  else exit(6);

  free (array2D);

  } else

  {

  exit(6);

  }

  }

    本文来源:比特网

 

    相关文章推荐:C程序开发:两个循环 四个步骤

分享到:
阅读:1617次
推荐阅读:

版权所有 © 2011-2016 南京云创大数据科技股份有限公司(股票代码:835305), 保留一切权利。(苏ICP备11060547号-1)  
云创大数据-领先的云存储、大数据、云计算产品供应商