免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
12345下一页
最近访问板块 发新帖
查看: 16765 | 回复: 40
打印 上一主题 下一主题

[C] [原创]对于C语言中指针和数组的认识和看法,欢迎讨论 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-07-11 16:49 |只看该作者 |倒序浏览
本版讨论很多对于C语言中指针和数组的认识和看法,
下面是我自己对指针的一些粗浅认识,写的有点乱, 希望对新手有所帮助
请大家批评指正。也欢迎大家积极讨论。

1. 指针变量也只是普通的变量


很多C语言的初学者都将指针变量看的很神秘,实际上,就像其他的普通变量(比如int类型的),指针变量也是一种普通变量,他具有其他变量的一切特征。

例如:
int main()
{
int q=10;
int *pi=0;
pi = &q;
printf ("%d\n", *pi);
}

main中声明并定义了一个自动变量p,他的类型是pointer-to-int.一旦定义了p,编译器就要给p分配内存空间。main结束后,p被自动释放。pi和q在这些方面没有丝毫不同。

结论:指针变量没有那么神秘,指针变量只是一个普通变量


2. 指针变量与其他变量的关系

那么,指针的特殊性表现在什么地方呢?

指针特殊就特殊在对他所存储的值的解释上和对他的使用上。

在上例中,pi的值会被解释成内存中的一个地址,以这个地址开始的一块内存则表示一个int型的数.

但是,即使在执行了pi = &q之后,pi和q也没有什么直接的关系:改变pi的值不会影响q,改变q的值也不会影响pi,他俩是两个独立的变量,有各自的存储空间。

C语言赋予了指针特殊的本领就是:可以存储别的变量的内存地址,也可以利用指针变量本身的值去间接的操作别的变量.这两种能力是通过两个操作符来完成的 : &和*。

pi = &q;                        //利用q的内存地址对pi进行赋值
printf ("%d\n", *pi);     //利用pi的值去间接的读q的值

sizeof(pi)跟sizeof(q)根本就是两码事. pi不会自动根据自己去寻找q的,只有你显示的使用*pi才可以.

结论:假设指针pi存储的是q的地址。pi和q没有任何直接的关系。只有*pi才和q有直接的关系。


3. C语言中的函数参数传递方式

很多人的另外一种误解是,C语言中有两种函数传递方式:按值传递和按地址传递。

造成这种误解的原因就是对上面所说的两点理解的不够。

void swap1( int a, int b)
{
int temp;
temp = a;
a = b;
b = temp;
}

void swap2( int* pa, int* pb)
{
int temp;
temp = *pa;
*pa = *pb;
*pb = temp;
}

int main()
{
int i=10, j=5;
int *pi, *pj;
pi = &i;
pj = &j;
swap1(i, j);
printf("i=%d, j=%d\n", i, j);
swap2(pi, pj);
printf("i=%d, j=%d\n", i, j);
}

很多人会认为swap1是按值传递,而swap2是按指针传递。其实,C语言只有一种函数参数传递方式:按值传递。

swap1大家都明白,我说一下swap2。

实际上,我在第一点中已经指出,指针只是一个普通变量而已。在对swap2的调用swap2(pi, pj)中, pi和pj的值分别被copy到swap2中的pa和pb中。现在,i,pi和pa是3个完全不同的变量,但pi和pa的值相同,他们存的都是i的地址。在swap2中,利用*pa就可以读写i了,同样利用*pb就可以读写j了,所以swap2就可以完成i和j的交换了。但是,pi和pj的值通过swap2是无法改变的。

也就是说, swap2使用的参数传递方式仍然是按值传递,只不过传递的是指针的值,然后利用指针有可以间接访问其他变量而已.

结论:C语言只有一种函数参数传递方式:按值传递.

4. 指针与数组

4.1 数组名的类型


在C语言中,指针与数组千丝万缕的联系.看下面的例子:

int a[5];
int b[3][5];
int *pa1[5];
int (*pa2)[5];

那么a,b,pa1,pa2的类型到底是什么呢?

很多人将a的类型误解成为一级指针,即const pointer to int,

而将b的类型误解成为二级指针,即const pointer to the pointer to int;

a不是const pointer to int类型的,我们是可以从下面这个事实推出来的:
sizeof(a)跟sizeof(int*)是不同的.

只能说,a是array of 5 ints类型的

而b则是array of 3 arrays of 5 ints类型的

这里之所以把pa1和pa2列出来,主要是给大家区别一下:
pa1是array of 5 int pointers类型的,
而pa2是pointer to array of 5 ints类型的

4.2 数组名的运算

大家经常会遇到关于数组名的运算问题,比如

int a[5]={1, 2, 3, 4, 5};
int b[3][5]={{1,2,3,4,5}, {6,7,8,9,10}, {11,12,13,14,15}};
printf("%d", *(a+2));
printf("%d, %d\n", **(b+2), *(*b+2));


在进行上面的运算时,有下面的似非而是的结论:

&a可以看作是pointer to array of 5 ints类型的
所以&a+1,这里的“1”是指5*sizeof(int)

a是array of 5 ints类型的,但是a的值是什么呢?a的值其实是一个
pointer to int

*a的值则是一个int,即a[0];

&b可以看作是pointer to array of 3 arrays of 5 ints类型的
所以&b+1,这里的“1”是指3*5*sizeof(int)

b是array of 3 arrays of 5 ints类型的,但是b的值却是一个
pointer to array of 5 ints

*b是array of 5 ints类型的,但是*b的值却是pointer to int类型的

**b的值则是int类型的,即b[0][0];

推而广之,则对于 int c[n1][n2]...[nm]的m维数组(m后面的数字为下标),有下面的结论(或者说计算方法):
先将c扩展为int c[n1][n2]...[nm][1]; (最后一个是数字1,不是字母l)

1.&c的单位“1”为n1*n2*...*nm*1*sizeof(int)
sizeof(&c)等于存储一个指针的大小,其值与sizeof(int*)相同;

2.令c1代表*...*c,共有i(0<i<=m-1)个*,则他的单位“1”为n(i+2)*...*nm*1*sizeof(int),其中(i+2)等是下标
sizeof(c1)=n(i+1)*...*nm*sizeof(int)

3.令c2代表*...*c,共有m个*,表示数组中第一个整数
sizeof(c2)=sizeof(int)



例如:int c[3][4][5][6];
先转换成int c[3][4][5][6][1];
&c+1相当于地址加3*4*5*6*1*sizeof(int), sizeof(&c)的值等于sizeof(int*);
c+1相当于地址加4*5*6*1*sizeof(int), sizeof(c)=3*4*5*6*1*sizeof(int);
**c+1相当于地址加6*sizeof(int), sizeof(**c)=5*6*1*sizeof(int);
****c就是数组中第一个整数

4.3 一种常见错误

int b[3][5];
int **p=b;

有些人误认为p+1的值和b+1是相同的,其实不然

类似于

T1 b1;
T2* b2=(T2*)(&b1); //T1,T2为两种不同的类型

这样的话,b2+1是按sizeof(T2)进行增加的
而&b1+1是按照sizeof(T1)进行增加的
指针进行类型转换后,其运算跟他自身的类型有关,而与他指向的东东无关

[ 本帖最后由 ypxing 于 2007-8-9 11:17 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2007-07-11 17:18 |只看该作者
顶~~~~

论坛徽章:
0
3 [报告]
发表于 2007-07-11 17:24 |只看该作者
8错

论坛徽章:
0
4 [报告]
发表于 2007-07-11 19:19 |只看该作者
经验表明: 不要作定义性的论述为宜

论坛徽章:
0
5 [报告]
发表于 2007-07-11 19:26 |只看该作者
把任何语句都想成机器指令.....................就OK了..........................阿弥陀佛............

论坛徽章:
0
6 [报告]
发表于 2007-07-11 19:38 |只看该作者
实际上,我在第一点中已经指出,指针只是一个普通变量而已。在对swap2的调用swap2(pi, pj)中, pi和pj的值分别被copy到swap2中的pa和pb中。现在,i,pi和pa是3个完全不同的变量,但pi和pa的值相同,他们存的都是i的地址。
==========================
pi和pa的值不相同, pi有pi的地址值,pa有pa的地址值

论坛徽章:
0
7 [报告]
发表于 2007-07-11 20:33 |只看该作者
你的理解很有问题呀

“pi有pi的地址值,pa有pa的地址值”是不错,因为他们是不同的变量
但是,他们的值是一样的,因为要按值传递参数


原帖由 fcloudf 于 2007-7-11 19:38 发表
pi和pa的值不相同, pi有pi的地址值,pa有pa的地址值

论坛徽章:
0
8 [报告]
发表于 2007-07-11 21:00 |只看该作者
原帖由 mik 于 2007-7-11 19:19 发表
经验表明: 不要作定义性的论述为宜

同意,定义性讨论由于个人理解不同引来无数争论,争论到最后还是自己坚持自己的,结果把看贴的人看晕。
通常写底层程序的人脑中只有内存,各种变量、结构、定义不过是对内存的解释方法而已,久而久之反而忘了定义,明白实质就行了。
不过对新手这样的文章还是很有帮助的,赞lz的精神

论坛徽章:
0
9 [报告]
发表于 2007-07-11 21:17 |只看该作者
>> 指针只是一个普通变量

根据不同的上下文环境,单独的“指针(Pointer)”可以指的是指针类型、指针对象、指针变量(变量是有名字的对象)、指针值中的任意一种,而不是象你说的那样仅仅指的是一个变量。

比如程序中的表达式 &q 的结果是一个指向 int 的指针。此处的“指针”指的其实是一个指针值。

>> 指针特殊就特殊在对他所存储的值的解释上和对他的使用上。

其实就是对指针类型规定的行为。

>> C语言只有一种函数参数传递方式:按值传递

站在函数参数的角度来看确实如此,只有值传递。不过,如果站在指针指向的对象的角度来看,那就是此对象的地址通过指针参数传递到了函数内,即传址。很多时候转换一下看问题的角度可以带来更大的方便性。

>> a不是const pointer to int类型的,我们是可以从下面这个事实推出来的:
>> sizeof(a)跟sizeof(int*)是不同的.

a 的定义类型是一个数组,不是指针,这是没有问题的;但是,数组对象的一个重要特点就是它的值是数组第一个元素的地址(即指向第一个元素的指针),这一点比前者更为常用。

其实,数组对象在表达式中作为数组类型还是指针类型来使用取决于其上下文环境:
  • 在左值语义下是数组类型

    例如 &a 以及 sizeof(a) 中的 a 都处于左值语义下,所以 a 仍然是数组类型。
  • 在右值语义下是指针类型

    在对 a 进行求值的情况下它转换为一个指针。例如:

    int a[10];
    int* p = a;

    上面的 a 应该看作是指向 int 的指针(pointer to int)。如果仍然把 a 作为数组类型,在理解上会带来困难。

    同时还请注意:sizeof(a) 的结果不能说明 a 在右值语义环境中的情况,因为 sizeof(a) 中的 a 始终处于左值语义环境。


>> int a[5]={1, 2, 3, 4, 5};
>> a是array of 5 ints类型的,但是a的值是什么呢?a的值存储的是一个 pointer to int

a 的值不是“存储”在数组对象中,而是来自于数组对象 a 的第一个元素的地址,是数组对象在右值语义环境下而产生的数组转换为指针的结果。

论坛徽章:
0
10 [报告]
发表于 2007-07-11 21:35 |只看该作者
学习了,不过,有几点进行说明


>>根据不同的上下文环境,单独的“指针(Pointer)”可以指的是指针类型、指针对象、指针变量(变量是有名字的对象)、指针值中的任意一种,而不是象你说的那样仅仅指的是一个变量。

>>比如程序中的表达式 &q 的结果是一个指向 int 的指针。此处的“指针”指的其实是一个指针值。

" 指针只是一个普通变量", 这里说的是指针变量,我的例子就是比较了q和pi,只是说明"pi和q在这些方面没有丝毫不同"


>>a 的值不是“存储”在数组对象中,而是来自于数组对象 a 的第一个元素的地址,是数组对象在右值语义环境下而产生的数组转换为指针的结果。

我的原话是"a的值存储的是一个 pointer to int",而不是说"a 的值“存储”在数组对象中", 当然这里用“存储”是不恰当的


>>其实,数组对象在表达式中作为数组类型还是指针类型来使用取决于其上下文环境:

说实话,这一点(我是指C语言的这个语言点)无论如何都确实很难理解

另外,请问,“左值语义”和“右值语义”是如何定义和区分的?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP