鱼C论坛

 找回密码
 立即注册
查看: 5710|回复: 31

[技术交流] 关于数组名不能++,不能--问题,驳倒C语言书上的说法

  [复制链接]
发表于 2012-6-16 13:47:19 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
      我看到过很多讲C语言的书上在说到数组跟指针那一块,对于数组 int a[10]; a++;这样  编译无法通过,解释是因为数组名是一个地址常量,所以不能++不能--。

个人理解这是完全错误的说法,
例子eg:

struct Number_t
{
    int numone;
   char numbertwo;
};
void main()
{
    Number_t  Number;                       //定义一个结构体变量 Number;
   Number++;                     编译错误。
   Number--;
}

为什么?这也是个变量啊,为什么不能++,不能--?
所以对于数组名不能++,不能--,是因为在这里数组名解释为一个 int [10] 类型的变量,代表整个数组,sizeof(a) == 40,大家可以去试试。 在这个对于int [10] 类型的变量a 跟 Number_t类型(结构体类型)的变量Number是同一个道理,都是个大变量,内部包含了很多元素,无法++,--。。结构体类型就是存数组类型(前面我发过一贴专门解释过数组类型) 派生出来,能包含不同类型的元素,而数组只能包含类型完全相同的元素。。。但是道理还是一个道理。数组用a[0]  去访问,结构体用Number.numone去访问。

当然对于数组int a[10];int *p =a;的问题,跟我变量论会产生矛盾,这个大家可以去看看另外一个帖子指针的奥秘什么的,讲的很详细,分成两种情况去解释了。我曾试图用一种说法去解释,却造成了前面那个《关于数组指针,指针数组,数组名》那篇帖子里面严重的错误。。。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 15:07:34 | 显示全部楼层
这有啥好奇怪的 结构体是自定义类型 如果编译器自动给你实现一些基本运算符 那不乱套了
还用你自己编程? 具体参考运算符重载
这跟数组完全两码事 数组也是内置类型
不能++就是因为常量原因 比const还常的常量

想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
 楼主| 发表于 2012-6-16 15:41:35 | 显示全部楼层

楼上你说数组名是常量,那你给我解释下   对于int a[10]    sizeof(a) == 40  为什么?  &a  何解?  
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
 楼主| 发表于 2012-6-16 15:42:55 | 显示全部楼层
ccqiji 发表于 2012-6-16 15:07
这有啥好奇怪的 结构体是自定义类型 如果编译器自动给你实现一些基本运算符 那不乱套了
还用你自己编程?  ...

在驳倒别人理论的时候,要回答完整。。。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 16:46:16 | 显示全部楼层
楼主你这样只会越陷越深 数组名当sizeof时 当成个整体 整体就是数组这种类型 求数组大小
这也没什么奇怪的吧

我主要是说用户自定义类型 不能执行一些操作符 是因为你没重载而已 自定义类型和数组这个内置类型 本身就是没有任何联系的 这点你要明白
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 16:47:51 | 显示全部楼层
我认为书上写的一点都没错
int a[10]; a++;这样  编译无法通过,解释是因为数组名是一个地址常量,所以不能++不能--。
就这句  不要死扣了  
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 18:55:44 | 显示全部楼层
1. 首先,你说数组名是一个int [10]类型的变量。 int [10]类型没错。但是,不是变量。而是常量。
至于你说的sizeof(a) ==  40 ; 很正常, 常量也能sizeof; 比如sizeof(10) == 4; sizeof(10.6) == 8
所以sizeof(s) == 40 就等于是求int [10]类型的常量的大小,是40无可厚非。

2. 其次, 你说结构体不能++,--是因为它是大变量?什么叫大变量?占用的内存大就叫大变量?那结构体定义一个char 成员。占用内存大小是1, 为什么还是不能++? 至于为什么不能++,--,楼上这位兄弟 ,已经说的很明白了。因为结构体是自定义类型,编译器不知道你的++应该进行怎么样的操作。而要想++ 也是可以的。你得自己告诉编译器,你自己的类型++的时候,应该做什么操作。这就是运算符的重载。

3. 从第一条结论得出数组名是int [10]的常量,那么常量不能++,很正常。 其实这个你可以从编译器的报错中看出来,你给一个数组名++,错误报告是:“++操作符需要一个左值”。  而常量是个右值,当然不能++。这和直接写8++报告的错误一模一样。关于左值和右值的概念,可自行查看相关概念。

4. c标准里面,在提到指针常量的时候,都有提到,数组名可以看作一个特殊的指针常量。为什么说特殊呢? 因为它本身严格说来是int[10]类型。支持大多数指针的特性。但却也有很多特性和指针相排斥。 而正常,我们一般不会说int[10]类型。所以,大多时候,人们喜欢将数组名划入指针常量的行列。 这个没什么错,只不过它是指针家族的一个异类而已。所以,后来很多书上都会提到数组名是指针常量,或者地址常量。个人觉得没什么。不要觉得用自己发现了这点“特殊”就把书贬的一文不值。只要这本书上面提到“可以看作”二字。都是我能接受的。

5. 至于int  [10] 这个类型 ,可能很多人觉得是自己意淫出来的。其实不然,你可以在编译器下写代码将一个整型数组的首地址赋值给一个char*. 从编译器的错误报告中就能得到:cannot convert from 'int [10]' to 'char *'。不能将一个int[10]类型赋值给一个char*.  也就是说本身是存在这种类型的。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 20:00:08 | 显示全部楼层
其实你这个问题应该问编译器,顺便说一句,vs2008下(其他编译器我不知道)sizeof()是直接由编译器计算出来的。语言的规范是由语言发明者写出来的,但是语言特性的实现是由编译器实现的。你问的这个问题,说明你连c语言的语法都不清楚,c语言对这些问题都有相关规定的。如果你对这些规定有异议,你可以自己制定一套你自己的语言规范,然后自己编写相应编译器。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
 楼主| 发表于 2012-6-16 20:26:16 | 显示全部楼层
DebugLove 发表于 2012-6-16 18:55
1. 首先,你说数组名是一个int [10]类型的变量。 int [10]类型没错。但是,不是变量。而是常量。
至于你说 ...

受教了,真的受教了,我纠结了很长时间的问题。。。这样解释却是让我明白很多。我从来不知道 sizeof(8) == 4  
sizeof(5.5)==8;    这位兄弟有时候可否帮我解释下  int a[10] ;    &a;  这个问题,
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 21:31:37 | 显示全部楼层
Tzdner_C 发表于 2012-6-16 20:26
受教了,真的受教了,我纠结了很长时间的问题。。。这样解释却是让我明白很多。我从来不知道 sizeof(8) = ...

你是在疑惑“不是说 常量不能取地址么?为什么这里可以取地址?” 实际这个说法本来就有问题。某些情况下常量是可以取地址的。如:"hello world" 是个字符串常量 吧? 但是却是可以  &"hello world";
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
 楼主| 发表于 2012-6-16 21:33:31 | 显示全部楼层
DebugLove 发表于 2012-6-16 21:31
你是在疑惑“不是说 常量不能取地址么?为什么这里可以取地址?” 实际这个说法本来就有问题。某些情况下 ...

:funk:    &"hello.wold" 这样都可以?  我日。。。。我试一下,这要是真的,我今天真的受教了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 21:37:36 | 显示全部楼层
LZ要坚持自己的思想,我个人认为你原来理解得很不错了,只是缺少些火候。

首先,数组就是数组,指针就是指针,什么数组名是指针的说法都是扯淡,有本事从C标准里指出一句说数组名是指针的。

其次,我认为如int a[10];这样的数组,就是个变量,不是常量。只不过这个变量是包含了许多个值的集合。所以这个变量很特别,不能++(你对哪个值++呢?)也不能赋值(对哪个值赋值呢?);至于&a;太好理解了,a是个变量,你说&a是什么?

再次,为什么那么多书,那么多人老是认为数组名和指针有一些乱七八糟的关系呢?因为数组名在很多情况下会转化为指针(指向该数组的首元素)。
比如:
int a[10];int *p =a;为什么int *p =a;是正确的?因为数组名转化为指针常量了,转化后再赋值给p当然可以;
又如:数组名做函数的参数将转化为指针。
只有在两种情况下,数组名不会转化为指针:
1.sizeof运算符
sizeof作用在数组名上的时候,数组名不会转化为指针,这个可以回答LZ前面的疑问。
2.&运算符
如:
int a[10];int * p= a;
我们知道&p的类型是int**
而&a的类型int(*)[10],就是因为在这里a没有转化为指针

最后,再次声明。只有以上说的两种情况下数组名不会自动转化为指针,其它情况下,一律自动转化为指针。(转化知道吗?就像变态。一个男人变态成女人后当然只具有女人的特性不具有男人的特性了,所以也只有在前面两种不转化的情况下才能让我们看清数组原本的特性)
附赠一个N多人都搞错的问题。[]运算符的左操作数是什么类型?A.数组类型 B.指针类型 C.数组或者指针类型

答案是B。之所以如int a[10];  a[5]这里[]的左边可以写数组,完全是因为数组名在这里又转化为指针了,它转化后已经不是数组了,可以查C语言标准运算符[]的两个操作数一个是指针类型,一个是整数类型,绝对不可能有数组类型。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 21:43:22 | 显示全部楼层
此外,很多书上的说法都是错的,不值得去驳倒,要驳倒只要Y引用C标准的相关条款即可。C标准是个好东西啊,在网络上吵架基本就靠它了,不仅能驳倒书本,就是编译器和它不 一致,也可以直接骂编译器“不符合标准”(这个学术用语就相当于生活中的骂娘,你下次可以对你的同事用用这个词)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
 楼主| 发表于 2012-6-16 21:47:54 | 显示全部楼层
仰望天上的光 发表于 2012-6-16 21:43
此外,很多书上的说法都是错的,不值得去驳倒,要驳倒只要Y引用C标准的相关条款即可。C标准是个好东西啊,在 ...

是啊,只有把数组名解释成变量,才能正确的解释sizeof(a)  和&a 的问题。。。  可是我的坚持处处碰壁,还不够成熟。。。N年后我应该有一套自己成熟的理论去解释这一切。。。  
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 21:57:30 | 显示全部楼层
Tzdner_C 发表于 2012-6-16 21:47
是啊,只有把数组名解释成变量,才能正确的解释sizeof(a)  和&a 的问题。。。  可是我的坚持处处碰壁,还 ...

不是自己成熟的理论,而是如果你有实力的话可以研读C语言标准(全是英文),标准里的理论是唯一正确的理论。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 22:14:49 | 显示全部楼层
仰望天上的光 发表于 2012-6-16 21:37
LZ要坚持自己的思想,我个人认为你原来理解得很不错了,只是缺少些火候。

首先,数组就是数组,指针就是 ...

两种? 那你觉得:
int a[10][10][10][10];
printf("%d", ***a);
这种时候, *还有他原来作用在指针上的时候的意义么?
虽然取了三次*号 ,却未有一次真正的解引用,取内存里面的值。只是单纯的类型变换。请问阁下?哪种指针常量有这种情况?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 22:22:41 | 显示全部楼层
我感觉楼主说得有道理,变量不能++,--也是有的,变量应该是运行时候内存中有存放位置的,常量应该是指在运算过程中一般存在于寄存器不会占用内存位置。变量是可以用&去取得他的地址作为一个指针的有效值。常量就不能了,&"hello.wold"这个能编译是编译器为这个字符串分配空间了。应该const类型,不是常量。数组不能++,--是因为现在cpu没有办法把不是基础变量类型的变量进行++,--,重载是你人为更改规则的事情不能套用。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 22:30:40 | 显示全部楼层
狂傲天 发表于 2012-6-16 22:22
我感觉楼主说得有道理,变量不能++,--也是有的,变量应该是运行时候内存中有存放位置的,常量应该是指在运 ...

常量不占用内存? 不占用内存。。难道程序刚开始就自动到寄存器里面了?
正常情况下。例如printf("%d", 10)中的10不是不占用内存。而是他所属的内存在代码段。 直接被转为 push 10指令了而已。 这个10是真正存在于内存中的。 而且,这个地址是可以拿出来的, 只是不能用&拿而已~
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 22:42:09 | 显示全部楼层
DebugLove 发表于 2012-6-16 22:14
两种? 那你觉得:
int a[10][10][10][10];
printf("%d", ***a);

*作用在指针上,或者解引用,就是取得该指针所指向的值。
int a[10][10][10];
printf("%d", ***a);
这里面printf里的a(类型为int[10][10][10])首先由数组名转化为指针(类型为int(*)[10][10]),接着*a得到一个数组,这个数组的类型是int[10][10],这个数组再次转化为指针,类型为int(*)[10];接着**a又得到一个数组,类型为int[10],这个数组转化为指针,类型为int *;最后***a得到整数类型int
以上就是按照C语言标准里的转换过程。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
发表于 2012-6-16 22:47:18 | 显示全部楼层
仰望天上的光 发表于 2012-6-16 22:42
*作用在指针上,或者解引用,就是取得该指针所指向的值。
int a[10][10][10];
printf("%d", ***a);

请问,这个过程中,何时从内存中取值了? 从头到尾都是类型的转换。哪个地址,取出了什么值?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-3-28 15:58

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表