数组与指针

1、一维数组

int array[10];
int *ap = arrary + 2;

ap这个很容易,你只要阅读它的初始化表达式就能得到答案:array+2.另外,&array[2]也是与它对等的表达式。
*ap这个也很容易,间接访问跟随指针访问它所指向的位置,也就是array[2]。你也可以这样写:* (array+2) .

 ap[0]“你不能这样做,ap不是一个数组!”如果你是这样想的,你就陷入了“其他语言不能这样做“,这个惯性思维中了。记住C的下标引用和间接访问表达式是一样的。其对等的表达式是*(ap+(0)),除去括号,其结果与前一个表达式相等,因此,它的答案和上一题相同:array[2].

 ap+6 如果ap指向array[2],这个加法运算产生的指针所指向的元素是array[2]向后移动6个整数位置的元素。与它对等的表达式是arrary +8或&array[8].

 *ap+6 小心!这里有两个操作符,哪一个先执行呢?是间接访问。间接访问的结果再与6相加,所以这个表达式相当于表达式array[2}+6 .
 

*(ap+6) 括号迫使加法运算首先执行,所以我们这次得到的值是array[8]。注意这里的间接访问操作和下标引用操作的形式是完全一样的。

ap[-1] 怎么又是它?负值的下标!下标引用就是间接访问表达式,你只要把它转换为那种形式并对它进行求值。ap指向第3个元素(就是那个下标值为2的元素),所以使用偏移量一1使我们得到它的前一个元素,也就是array[1].


不完整的初始化

int vector[5] = {1,2,3,4,5,6 };

 int vector[5] = {1,2,3,4};

    在这两种情况下,初始化值的数目和数组元素的数目并不匹配。第1个声明是错误的,我们没办法把6个整型值装到5个整型变量中。但是,第2个声明却是合法的,它为数组的前4个元素提供了初始值,最后一个元素则初始化为0.

     那么,我们可不可以省略列表中间的那些值呢?
int      vector[5]  = {1,5};
编译器只知道初始值不够,但它无法知道缺少的是哪些值。所以,只允许省略最后几个初始值。

2、多维数组

int  a;
1nt  b[10];
int c[6][10];
1nt d[3][6][10];

   a是个简单的整数。接下来的那个声明增加了一个维数,所以b就是一个向量,它包含10个整型元素。

 C只是在b的基础上再增加一维,所以我们可以把C看作是一个包含6个元素的向量,只不过它的每个元素本身是一个包含10个整型元素的向量。换句话说,C是个一维数组的一维数组。                                           

   d也是如此:它是一个包含3个元素的数组,每个元素都是包含6个元素的数组,而这6个元素中的每一个又都是包含10个整型元素的数组。简洁地说,d是一个3排6行10列的整型三维数组。
    理解这个视点是非常重要的,因为它正是C实现多维数组的基础。为了加强这个概念,让我们先来讨论数组元素在内存中的存储顺序。

   考虑下面这个数组:
   int array[3];它包含3个元素,如下图所示:

但现在假定你被告知这3个元素中的每一个实际上都是包含6个元素的数组,情况又将如何呢?下面是这个新的声明:
int  array[3][6];下面是它在内存中的存储形式:

 实线方框表示第1维的面每个元素的下标值分别是3个元素,虚线用于划分第2维的6个元素。按照从左到右的顺序,上面每个元素的下标分别是:

0,0 0,1 0,2 0,3 0,4 0,5  1,0 0,1 1,2 1,3 1,4 1,5  2,0 2,1 2,2 2,3 2,4 2,5

下面的代码段将会打印出什么样的值呢?
int matrix[6][10];
int *mp;
.....                                                                                           
mp =&matrix[3][8];                                                                       

printf("frisr value is %d\n",*mp);                                                   

 printf("second value is %d\n",*++mp);                                      

printf("thrid value is %d\n",*++mp);

    很显然,第1个被打印的值将是matrix[3] [8]的内容,但下一个被打印的又是什么呢?存储顺序可以回答这个问题—--下一个元素将是最右边下标首先变化的那个,也就是matrix[3][9]。再接下去又轮到谁呢?第9列可是一行中的最后一列啦。不过,根据存储顺序规定,一行存满后就轮到下一行,所以下一个被打印的元素将是matrix[4] [0].
    这里有一个相关的问题。matrix到底是6行10列还是10行6列?答案可能会令你大吃一惊—在某些上下文环境中,两种答案都对。
    两种都对?怎么可能有两个不同的答案呢?这个简单,如果你根据下标把数据存放于数组中并在以后根据下标查找数组中的值,那么不管你把第1个下标解释为行还是列,都不会有什么区别。
    只要你每次都坚持使用同一种方法,这两种解释方法都是可行的。
    但是,把第1个下标解释为行或列并不会改变数组的存储顺序。如果你把第2个下标解释为行,把第2个下标解释为列,那么当你按照存储顺序逐个访问数组元素时,你所获得的元素是按行排列的。

   另一方面,如果把第1个下标作为列,那么当你按前面的顺序访问数组元素时,你所得到的元素是按列排列的。你可以在你的程序中选择更加合理的解释方法。但是,你不能修改内存中数组元素的实际存储方式。这个顺序是由标准定义的。

   指向数组的指针
   下面这些声明合法吗?
   int vector[10],*vp=vector;
   int matrix[3 ][10],*mp=matrix;

    第1个声明是合法的。它为一个整型数组分配内存,并把vp声明为一个指向整型的指针,并把它初始化为指向vector数组的第1个元素。vector和vP具有相同的类型:指向整型的指针.
    第2个声明是非法的。它正确地创建了matrix数组,并把mp声明为一个指向整型的指针。但是,mp的初始化是不正确的,因为matrix并不是一个指向整型的指针,而是一个指向整型数组的指针。

   数组指针
   我们应该怎样声明一个指向整型数组的指针的呢?

    int(*P)[10];
    这个声明比我们以前见过的所有声明更为复杂,但它事实上并不是很难。你只要假定它是一个表达式并对它求值。下标引用的优先级高于间接访问,但由于括号的存,首先执行的还是间接访问。所以,P是个指针,但它指向什么呢?
    接下来执行的是下标引用,所以P指向某种类型的数组。这个声明表达式中并没有更多的操作符,所以数组的每个元素都是整数。
    声明并没有直接告诉你P是什么,但推断它的类型并不困难—当我们对它执行间接访问操作时,我们得到的是个数组,对该数组进行下标引用操作得到的是一个整型值。所以P是一个指向整型数组的指针。

  在声明中加上初始化后是下面这个样子:

   int  (*p) [10] = matrix;

  (*p)[10] 是一个数组指针,它使p指向matrix的第一行。

   

   指针数组

    int *api[10]是什么?首先下标的优先级高于间接访问(指针),因此它是一个指针数组。

    作为函数参数的数组名
    当一个数组名作为参数传递给一个函数时会发生什么情况呢?你现在己经知道数组名的值就是一个指向数组第1个元素的指针,所以很容易明白此时传递给函数的是一份该指针的拷贝。                                             

    函数如果执行了下标引用,实际上是对这个指针执行间接访问操作,并且通过这种间接访问,函数可以访问和修改调用程序的数组元素。
    现在我可以解释c关于参数传递的表面上的矛盾之处.所有传递给函数的参数都是通过传值方式进行的,但数组名参数的行为却仿佛它是通过传址调用传递的。传址调用是通过传递一个指向所需元素的指针,然后在函数中对该指针执行间接访问操作实现对数据的访问。作为参数的数组名是个指针,下标引用实际执行的就是间接访问。
    那么数组的传值调用行为又是表现在什么地方呢?传递给函数的是参数的一份拷贝(指向数组起始位置的指针的拷贝),所以函数可以自由地操作它的指针形参,而不必担心会修改对应的作为实参的指针。
    所以,此处并不存在矛盾:所有的参数都是通过传值方式传递的。当然,如果你传递了一个指向某个变量的指针,而函数对该指针执行了间接访问操作,那么函数就可以修改那个变量。尽管初看上去并不明显,但数组名作为参数时所发生的正是这种情况。这个参数(指针)实际上是通过传值方式传递的,函数得到的是该指针的一份拷贝,它可以被修改,但调用程序所传递的实参并不受影响。

     如下程序是一个简单的函数,用于说明这些观点。它把第2个参数中的字符串复制到第1个参
数所指向的缓冲区。调用程序的缓冲区将被修改,因为函数对参数执行了间接访问操作。但是,无论函数对参数(指针)如何进行修改,都不会修改调用程序的指针实参本身(但可能修改它所指向的内容)。
    注意whie语句中的*string++表达式。它取得suing所指向的那个字符,并且产生一个副作用,就是修改string,使它指向下一个字符。用这种方式修改形参并不会影响调用程序的实参,因为只有传递给函数的那份拷贝进行了修改。

 

    在绝大多数表达式中,数组名的值是指向数组第1个元素的指针。这个规则只有两个例外。sizeof返回整个数组所占用的字节而不是一个指针所占用的字节。单目操作符&返回一个指向数组的指针,而不是一个指向数组第1个元素的指针的指针。
    除了优先级不同以外,下标表达式array[value]和间接访问表达式*(array+(value))是一样的。因此,下标不仅可以用于数组名,也可以用于指针表达式中。不过这样一来,编译器就很难检查下标的有效性。指针表达式可能比下标表达式效率更高,但下标表达式绝不可能比指针表达式效率更高。
    指针和数组并不相等。数组的属性和指针的属性大相径庭。当我们声明一个数组时,它同时也分配了一些内存空间,用于容纳数组元素。但是当我们声明一个指针时,它只分配了用于容纳指针本身的空间。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值