Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1954391
  • 博文数量: 185
  • 博客积分: 10707
  • 博客等级: 上将
  • 技术积分: 1777
  • 用 户 组: 普通用户
  • 注册时间: 2008-09-19 17:31
文章分类

全部博文(185)

文章存档

2014年(1)

2012年(6)

2011年(27)

2010年(13)

2009年(75)

2008年(63)

分类:

2011-05-25 21:03:31

指针和数组是非常容易混淆的两种数据类型,K&R和C专家编程里面的叙述都比较杂乱,没办法,因为C对于这两者的一些规定本身就很混乱。这里把我最近的学习成果整理一下。

1.声明与定义
指针的定义格式如下:

  1. type_t *p = ?;
  2. /*
  3. 这里的type_t可以任何C语言内嵌的类型,如int,char,也可以是结构体、函数等等;
  4. ?代表的可以是一个地址常数、一个&取址表达式、或者另一个指针、如果考虑强制类型转换它可以是任意的表达式或者变量。
  5. 定义产生的符号p即为指针型变量,他和unsigned long型的变量其实没有什么区别。一般来说就是一个32位的无符号整数,表示虚拟空间的某个地址值。
  6. */
数组的定义格式如下:
  1. type_t a[100] = "mario love lulu";
  2. /*
  3. type_t 如前面所述;a即为数组名,其实作用于a的操作可以说是混乱的最大来源之一。
  4. */
Note1:数组在声明时可以不指定长度,因为声明时连接器还不需要决定为符号分配多少空间;
Note2:新的编译器允许动态指定数组长度,即数据定义时可以用一个变量来制定长度。
Note3:字符串"abc"的长度为4,它其实是'a' 'b' 'c' '\0'
Note4:a[] = "mario love lulu"这种初始化方法只允许与用于定义中,在表达式中不允许这样用,实际上,这种定义形式所包含的操作并没有被翻译成某些指令实现,而是通编译器直接组织数据段的方式来实现。

2.前面说的比较基础,大家可能觉得太简单了,下面来个真正的考题:
写出下面代码的输出:
  1. #include <stdio.h>

  2. char ga[] = "abcdefg";

  3. void my_array_func(char ca[]){
  4.     printf("addr of array in routine = %#x\n",&ca);
  5.     printf("what is in this addr? %#x\n",ca);
  6.     printf("addr of first element of array in routine = %#x\n",&ca[0]);
  7.     printf("sizeof this array is %d\n",sizeof(ca));
  8. }

  9. main(){
  10.     printf("value of array symbol in invoker = %#x\n",ga);
  11.     printf("addr of array in invoker = %#x\n",&ga);
  12.     printf("addr of first elemnet of array in invoker = %#x\n",&ga[0]);
  13.     printf("sizeof this array is %d\n",sizeof(ga));
  14.     my_array_func(ga);
  15. }
  16. ~
怎么样,结果是啥呢,这就来揭晓:

  1. value of array symbol in invoker = 0x804a014
  2. addr of array in invoker = 0x804a014
  3. addr of first elemnet of array in invoker = 0x804a014
  4. sizeof this array is 8
  5. addr of array in routine = 0xbf93cb10
  6. what is in this addr? 0x804a014
  7. addr of first element of array in routine = 0x804a014
  8. sizeof this array is 4
可以看到,同样一个数组,在传递给函数之前和在子函数内部,其行为特性完全不同了。
很明显,在调用函数之间,编译器对于表达式&ga,直接给出ga的值,也就是该数组的地址;
同时g[0]表示数组的第一个元素,它的地址也就是ga的值即0x804a014。调用sizeof时,程序从符号表里查到ga的大小,并且返回;

另一方面,在子函数中,由于C参数传递永远是传值,也就是传递一份拷贝,即使是传递地址是也是拷贝一个指针变量;同时编译器将所有数组实参全部转化成指向数组第一个元素的指针。在这两条规则的联合作用下,当我们调用my_array_func(ga)时,一个指向数组ga的指针产生了,这个时候,函数内部的参数ca就不再是一个“数组对象”,而是一个赤果果的指针,当然了根据编译器的设定,只要这个指针指向ga的首地址,那么用ga[i]和ca[i]将获得同样的访问效果。此时&ca这个表达式的值是存储ca这个指针变量的地址,也就是0xbf93cb10。而ca的值即为0x804a014。代码中sizeof(ca)等于4也验证了这一点。

综上所述,指针和数组之间的混乱,完全是由数组单方面引起的,指针的行为非常规范,且不存在歧义,而数组由于它流露出“面向对象”的特质,在C语言中的行为表现的无所适从。但是,无论如何,只要掌握几种基本机理,在编程时就能避免混乱的发生。
阅读(1448) | 评论(0) | 转发(0) |
0

上一篇:段错误bug的调试

下一篇:FFT初解

给主人留下些什么吧!~~