c语言的数组与指针

c语言的数组与指针

更新:2013-07-24

//


  环境gcc (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2
 
  因为数组类型在作为很多运算符的操作数时,会被转换为指针类型,所以数组
  和指针的关系相当密切。在这里主要是想对比去理解它们。
 
  0)数组
 
  在c99标准6.2.5.20中有说到:
  An array type describes a contiguously allocated nonempty set of
  objects with a particular member object type, called the element
  type.36) Array types are characterized by their element type and
  by the number of elements in the array. An array type is said to
  be derived from its element type, and if its element type is T ,
  the array type is sometimes called ‘‘array of T ’’. The
  construction of an array type from an element type is called
  ‘‘array type derivation’’.
 
  数组被称为派生类(derived type)。衍生类会有许多与基本类不同的性质,
  数组成员(element type)线性地址连续。一个数组的特性是由组成其的元素
  类型和元素个数表现出来。
  数组名转换成指针后,该指针指向第一个成员的地址。
 
  c99:6.2.5.21:
  Arithmetic types and pointer types are collectively called scalar
  types. Array and structure types are collectively called aggregate
  types.37)
 
  数组和结构体也被称为聚集类(aggregate type)。什么是聚集类,没能
  理解清楚。


 

  1)指针
 
  指针==地址?如果按照标准文档对pointer的用法,应该指针和地址不是
  同一个概念,指针是一种类型,而地址只是内存单元位置的线性编号。
  类型的指针(pointer to type)的值解释为类型实例的地址(address of
  type)。在本文中说到的指针都不是指地址。
  指针变量可以指向完整类型和不完整类型。


 

  2)数组运算和指针运算
 
  c的指针变量常常仅是被人简单地解释为一个地址变量,
  但需记住c的指针是有着类型分别和有特定的运算规则的。
  因为数组在运算时常常被转换成指针,所以有时候数组运算
  其实是指针运算。
 
  c99:6.3.2.1:
  Except when it is the operand of the sizeof operator or the unary
  & operator, or is a string literal used to initialize an array, an
  expression that has type ‘‘array of type’’ is converted to an
  expression with type ‘‘pointer to type’’ that points to the initial
  element of the array object and is not an lvalue.
 
  这段话我的理解是只有对于sizeof,&,和初始化时,数组类型(数组名)理
  解为某个类型的数组,否则数该组类型会被转换降维后的类型的指针,即指向
  某类型的指针,并且该指针不能作为左值。这个转换很常见。


 

  2.0)数组的[]运算和指针的加减法运算
 
  c99:6.5.2.1.2:
  The definition of the subscript operator []
  is that E1[E2] is identical to (*((E1)+(E2))).  
 
  a[b]等价与*(a+b),由c语言的加法交换率知其a和b的位置是完全可以交换的,
  在[]这个运算中,数组类型会因为*运算而发生类型转换,进而变成指针类型。
  为了增加题目中的陷阱,它们有可能被交换出现在c语言考题中,这样会使题
  目看来很怪异,在程序的实现中为了增加可读性一般是不将它们交换的。
 
  由2)知数组会被转换成指针,现在需要的是理解指针的加法运算。指针在被加法

  运算时,其值的改变相当于指针的指向移动。


  type a[N],b;
  int c = (int)(&b + 1) - (int)(&b); // c的值是sizeof type
  int d = (int)(a + 1) - (int)(a); // b的值是sizeof type
  int e = (int)(&a + 1) - (int)(&a); // e的值是(sizeof type) * N
 
  这里可看到,指针的移动距离是它所指向的类型的长度(以字节算)。
  当数组被转换成指针后,其指针指向原数组降维后的类型(数组元素类型)。
  这里说的降维是指某类型数组(array of type)降维后是某类型(type)。
 
  c99:6.5.2.1:
  Successive subscript operators designate an element of a multidimensional
  array object.If E is an n-dimensional array (n ≥ 2) with dimensions i × j
  × . . . × k, then E (used as other than an lvalue) is converted to a
  pointer to an (n − 1)-dimensional array with dimensions j × . . . × k. If
  the unary * operator is applied to this pointer explicitly, or implicitly
  as a result of subscripting, the result is the pointed-to (n − 1)-dimensional
  array, which itself is converted into a pointer if used as other than an
  lvalue. It follows from this that arrays are stored in row-major order
  (last subscript varies fastest).
 
  数组转换成指针后,指针的移动距离是它降维后的类型的长度(以字节算),
  这里也可以看到这时指针的类型。
 
  数组与指针的减法运算,和加法类似,只是指针移动的方向不一样。
   
 
  2.1)指针之间的减法运算
 
  指针的减法是要相同的类型的指针之间才可以进行
  两某类型指针(pointer to type)相减的结果是两指针地址之间有多少个元素
  (type)。
 
  若运算有误(通常是发生在指针类型强制转换之后,地址距离无法被单
  元长度整除)则会返回错误的结果,例如下:

 
  char a[6];
  int b = (int*)(a + 1) - (int*)a;
  int c = (int*)(a + 5) - (int*)a;
 
  3)数组与指针的&运算和*运算
 
  对指针进行&运算,是指针类型对象的地址值,对指针*运算是pointer
  to type算成type。
 
  对数组类型进行*运算,会使类型变为低一维的类型。而对数组进行&运

  算呢?


  这里用到sizeof,提醒一下其的作用:计算类型所占的内存空间以字节为
  单位的长度(更多内容可以看本博的《c语言的sizeof》),由此可推导
  其类型。
 
  例如:

   
  char a[2][2][2];
  b = (sizeof a); // b为2*2*2*sizeof(char)
  c = (sizeof *a); // c为2*2*sizeof(char)
  d = (sizeof &(*a)); // d为地址长度
  e = (sizeof *(&(*a))); // e为2*2*sizeof(char)
  f = (sizeof &(**a))); // f为地址长度
  g = (sizeof *(&(**a))); // g为2*sizeof(char)
 
  c99:6.5.3.2:
  Thus, &*E is equivalent to E (even if E is a null pointer),
  and &(E1[E2]) to ((E1)+(E2)).
 
  看到b与d,c与f,应注意数组在被*号运算后,其再取址就不是数组了。

  &对被*运算后的数组已不能恢复。数组在一次连续运算中(没有显式强制

  转换的情况下)被*运算后,类型不可逆。

 
  那么c99说的是&*E和E是同等的,是不是违反了标准?以上的说法是针对指
  针,而不是数组,要记得前面提过,数组在运算时已被转换成相应的指针
  类型。

 
  4)数组的赋值
 
  数组在初始化时,可以整体初始化,但在赋值时为什么就不能整体赋值呢?
 
  如下:

 
  int a[2], b[2];
  a = b;
 
  在编译后提示错误:
 
  error: incompatible types when assigning to type ‘int[2]’ from
  type ‘int *’
 
  这里可以看到b已被转换成指针类型,在2)中提到的c99:6.3.2.1,这是
  gcc的‘=’运算对用作右值的数组的转换处理。
 
  而如下:

 
  typedef struct A{
    char *a;    
  }A;
 
  typedef struct B{
    char b[5];    
  }B;
 
  A a = {"hello"};
  A b = {"world"};
  B c = {"you"};
  B d = {"me"};
 
  printf("A a = %p\n", a.a);
  printf("A b = %p\n", b.a);
  printf("B c = %p\n", c.b);
  printf("B d = %p\n", d.b);
  a = b;
  c = d;
 
  printf("A a = %p\n", a.a);
  printf("A b = %p\n", b.a);
  printf("B c = %p\n", c.b);
  printf("B d = %p\n", d.b);
 
  以上的输出可以看到a和b的指向在赋值后改变了,而c和d的地址没有变。
  这里结构体中的数组整体赋值了。
 
  我的理解是:c99:6.2.5.20中说到数组定义为派生类,因此在数组的很多操作
  中,都具有其元素类的特性。数组想要改变其自身的值时,其所表现的是其元

  素类的特性而不是数组整体的特性,c99:6.3.2.1也对数组的运算进行了说明。


本博客版权声明点击打开链接


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值