首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C语言 >

为什么char aa[5][7]不能赋值给char *pp,该怎么处理

2012-05-20 
为什么char aa[5][7]不能赋值给char **ppC/C++ codechar aa[5][7]char **sschar a[7]char *ss a/*

为什么char aa[5][7]不能赋值给char **pp

C/C++ code
char aa[5][7];char **ss;char a[7];char *s;s = a;         /*为什么这个可以*/ss = aa;    /*这个却不可以*/



s是一个指向字符的指针,a是一个有7个字符的数组,a[0]是a的第一个字符。s=a相当于s=a[0],就是让s指向a[0]的地址,这是可以的。也就是说,s可以等于a。

ss是一个指向字符指针的的指针,相当于ss是指向像s这样的指针的指针;
aa是一个有5个元素的数组,其中每个元素是1个有7个字符的数组,相当于aa含有5个像a这样的字符数组,aa[0]是其中第一个字符数组。
ss=aa相当于ss=aa[0],也就是让ss指向aa[0]的地址。

我纠结的是,既然s可以指向a,为啥ss不可以指向aa啊?

是不是因为ss是字符指针的指针,而aa[0]只能当做字符指针用啊?

二维数组char aa[5][7]的首地址是相当于字符指针还是相当于字符指针的指针呢?


[解决办法]
char aa[5][7]; 可以看作是一个一维数组,其中每个元素都是一个数组
也就是一个数组的数组,可以赋值给一个指向数组的指针
char aa[5][7];
char (*p)[7];
p = aa;

[解决办法]
探讨
ss是一个指向字符指针的的指针,相当于ss是指向像s这样的指针的指针;
aa是一个有5个元素的数组,其中每个元素是1个有7个字符的数组,相当于aa含有5个像a这样的字符数组,aa[0]是其中第一个字符数组。
ss=aa相当于ss=aa[0],也就是让ss指向aa[0]的地址。

[解决办法]
探讨
问题是,数组名不是可以赋值给指针的么?

[解决办法]
char a[5][7];
char (*b)[7];
char **p;
把a理解为一个数组的数组,它可以赋值给指向数组的指针b,但是不能赋值给指向指针的指针p
[解决办法]
关键是:
C/C++的多维数组也是一维数组

一维数组的首地址由一级指针指向,才能按照[]形式访问
C/C++ code
int a[5];int * pa = a;pa[3] = 100; //OK
[解决办法]
呵呵,祝贺楼主和brookmill终于大彻大悟。

说到C/C++的复杂性,我常常听到有人说C++的类很复杂,但其实C++的类只是庞大,不是复杂,由于规则很多,外延反而变小,灵活性受到了约束;而指针由于规则少,只有那么一两条,灵活性与复杂性反而更大。我从没有听到或看到过有人害怕写类,但却时常见到有人因为指针而抓狂、恐惧,甚至出现了拒绝使用指针的思潮。

解决了复杂指针的阅读,还有很多内容等着楼主去思考,例如:

1。数组名是一个常量吗?

2。我们常这样使用:
int a[10];
int *p = a + 1;
这是否意味着数组名是一个指针?

3。为什么a与&a的地址值相同?

4。在C中,有人说以下代码定义了一个常量:const int i = 10;但为什么如下代码不能通过编译?
int main( void )
{
const int i = 10; 
int a[i];
..........
}

5。const char ****const *****const **** const p,p是什么??

6。按照C/C++的规则,我们可以:
char *p1 = .......;
const char *p2 = p1;
但为什么const char **p2 = &p1;不行?

7。我们常常使用这样的指针:const char *p1;但对于下面的声明:
typedef char * T;
const T p2;
p1与p2也一样吗??为什么?重点在于为什么??


解决了以上几个问题,楼主对于数组与指针的理解,又会进入另一个境界了。
[解决办法]
1。数组名是一个常量吗?

是。
-----------------------------
不能说是。提示:数组名是一个不变的符号地址,但在C中,不变的量就是常量吗??



2。我们常这样使用:
int a[10];
int *p = a + 1;
这是否意味着数组名是一个指针?

当一个数组标识符出现在表达式中,这个标识符的类型就从“某种类型T的数组”转换成“指向类型T的指针”,而且它的值就等于数组第一个元素的地址。
---------------------------------------------
你没有回答是不是





3。为什么a与&a的地址值相同?

上面说了,a出现在表达式中时,它作为指针,其值是数组第一个元素的地址;而当a作为取址(&)操作的操作数时,取址操作返回的是指向数组的指针。由于数组的地址和数组第一个元素的地址相同,所以a与&a的地址值相同。
--------------------------------------------
这只是表面现象,有没有思考过&a是否合法?一般来说,&运算符要求操作数是对象,但,数组名是对象吗?
先给你解释什么是对象,对象是执行环境中其内容表示为某个值的存储数据的区域。



4。在C中,有人说以下代码定义了一个常量:const int i = 10;但为什么如下代码不能通过编译?
int main( void )
{
const int i = 10;
int a[i];
..........
}

我在VC里可以通过编译,并正常使用啊?
-----------------------------------------
不要使用VC6,VC6对标准的支持很差,用对标准支持很好的gcc再试试看



5。const char ****const *****const **** const p,p是什么??



p是一个四级常量指针,该指针指向另外一个五级指针常量A,常量A又指向一个四级指针常量B,常量B最终指向一个常量字符。
----------------------------------------
p是一个13重指针,有多少个*,就是多少重指针,其中第四重、第九重及p本身,都是const指针。阅读方法:

const与左边最后一个声明说明符之间有多少个*号,那么就是多少级指针是const的。



6。按照C/C++的规则,我们可以:
char *p1 = .......;
const char *p2 = p1;
但为什么const char **p2 = &p1;不行?

第一个赋值语句中,p2指向的是char,p1指向的也是char,所以p2可以等于p1。
第二个赋值语句中,p2指向的是const char *, &p1指向的是char *,这两种指针类型不匹配。
--------------------------
对。




7。我们常常使用这样的指针:const char *p1;但对于下面的声明:
typedef char * T;
const T p2;
p1与p2也一样吗??为什么?重点在于为什么??

p1是指向const char的指针;p2是常量,其类型是字符指针。原因是指针修饰符是从右向左结合的,typedef的定义打乱了结合的次序。如果如下定义,则p2与p1相同。
C/C++ codetypedefconstchar T;
T*p2;
------------------------------
解释基本上可以。从标准的角度来讲,指针说明符跟标识符原本是组合在一起的,标准是这样说明的:

6.7.5.1 Pointer declarators

Semantics

If, in the declaration ‘‘T D1’’, D1 has the form

* type-qualifier-listopt D

D就是指标识符

typedef char * T把指针说明符从declarator中硬生生分开了,打乱了次序,才造成这么奇怪的现象。本来是一个不良的声明,但由于typedef对于ADT是一个不可缺少的东西,ADT一条重要原则是数据隐藏,要用户直接使用*来使用ADT是违背ADT的原则的,同时所造成的问题看起来也并不大,只部分影响了阅读习惯而已,因此就无所谓了。
[解决办法]
4。在C中,有人说以下代码定义了一个常量:const int i = 10;但为什么如下代码不能通过编译?
int main( void )
{
const int i = 10;
int a[i];
..........
}

我在VC里可以通过编译,并正常使用啊?
-----------------------------------------
不要使用VC6,VC6对标准的支持很差,用对标准支持很好的gcc再试试看 

============
在vc中注意要用.c扩展名,不要用.cpp的。
这样的语法在C++中是合法的,在C中是错误的。
[解决办法]
1。数组名是一个常量吗?

是。
-----------------------------
不能说是。提示:数组名是一个不变的符号地址,但在C中,不变的量就是常量吗??

愿闻其详。
---------------------------------------------------
在C中,常量表达式必须是编译期的,运行期的不是常量表达式。因此一个自动数组的数组名不是常量表达式,静态数组的数组名才是一个常量表达式。所以说,数组名只是一个符号地址,是否常量不一定,要视其如何定义的。

但在C++中,由于不再规定常量表达式必须是编译期,因此在C++中,数组名才是一个符号地址常量。




2。我们常这样使用:
    int a[10];
    int *p = a + 1;
  这是否意味着数组名是一个指针?

当一个数组标识符出现在表达式中,这个标识符的类型就从“某种类型T的数组”转换成“指向类型T的指针”,而且它的值就等于数组第一个元素的地址。
---------------------------------------------
你没有回答是不是

当数组标识符出现在表达式中,编译器把它当做指针。这样回答可以么?
--------------------
数组名无论何时何地都不是指针,但一定条件下数组名可以隐式转换为指针,把a放在表达式a + 1中的时候,
a已经不是数组名了,而是隐式转换为一个右值指针了。




3。为什么a与&a的地址值相同?

上面说了,a出现在表达式中时,它作为指针,其值是数组第一个元素的地址;而当a作为取址(&)操作的操作数时,取址操作返回的是指向数组的指针。由于数组的地址和数组第一个元素的地址相同,所以a与&a的地址值相同。
--------------------------------------------
这只是表面现象,有没有思考过&a是否合法?一般来说,&运算符要求操作数是对象,但,数组名是对象吗?
先给你解释什么是对象,对象是执行环境中其内容表示为某个值的存储数据的区域。

当数组名出现在&之后时,编译器把它理解为a[0],而a[0]是合法的可用于&运算符的对象。
----------------------------------
如果定义int i; i是一个对象,&i成立是很自然的,但数组名不是对象(数组是对象),因为内存中并不存在一个地方存储数组名的值,数组名本身就代表一个值,没有指针对象那样的取值操作,因此按照&的本意,&a是非法的,但是由于早期有些编译器已经允许了&a这样的东西的存在,因此C标准委员会鉴于这种结构并没有害处而且对象的概念已经有了扩展,因此也规定了&a的合法性,但&a并不表示对数组名取地址,而是对数组对象取地址,也正因为这个原因,&a的值跟a是一样的



4。在C中,有人说以下代码定义了一个常量:const int i = 10;但为什么如下代码不能通过编译?
    int main( void )
  {
        const int i = 10;
        int a[i];
        ..........
  }

我在VC里可以通过编译,并正常使用啊?
-----------------------------------------
不要使用VC6,VC6对标准的支持很差,用对标准支持很好的gcc再试试看。

这是不是又和编译器对常量的理解有关?愿闻其详。
-----------------------------------------------------
是的,在C中,作为自动变量的i运行期内才建立,因此不是常量表达式,虽然被const修饰,但这里的const的确切意义,只不过表示不能直接通过i这个标识符去修改i的值而已。数组声明器内部的操作数被规定为必须是常量表达式。



在C++中,同样由于不再规定必须是编译期,因此在C++中上述代码成立。





7。我们常常使用这样的指针:const char *p1;但对于下面的声明:
    typedef char * T;
    const T p2; 
    p1与p2也一样吗??为什么?重点在于为什么??

p1是指向const char的指针;p2是常量,其类型是字符指针。原因是指针修饰符是从右向左结合的,typedef的定义打乱了结合的次序。如果如下定义,则p2与p1相同。
C/C++ codetypedefconstchar T;
T*p2;
------------------------------
解释基本上可以。从标准的角度来讲,指针说明符跟标识符原本是组合在一起的,标准是这样说明的:

6.7.5.1 Pointer declarators

Semantics

If, in the declaration ‘‘T D1’’, D1 has the form

* type-qualifier-listopt D

D就是指标识符

typedef char * T把指针说明符从declarator中硬生生分开了,打乱了次序,才造成这么奇怪的现象。本来是一个不良的声明,但由于typedef对于ADT是一个不可缺少的东西,ADT一条重要原则是数据隐藏,要用户直接使用*来使用ADT是违背ADT的原则的,同时所造成的问题看起来也并不大,只部分影响了阅读习惯而已,因此就无所谓了。

弱弱地问,ADT是啥?另外,你引的这段英文没理解的说。
--------------------
ADT叫抽象数据类型,是C++中的类的前身,C++的类使用了OOP的思想,同时由C的ADT发展而来的

T表示类型,D1表示declarator,type-qualifier-listopt 表示可选类型修饰符列表,opt表示可选,例如:

int * const p;

T就是那个int,* const p就是D1,const就是type-qualifier-listopt 。
[解决办法]

探讨
我的问题:第一个赋值语句中实际上p2指向的是const char,p1指向的是char。既然char *不能自动转换为const char *,那为何char能自动转换为const char?

[解决办法]
探讨
谢谢supermegaboy的耐心回复。学习了一小圈之后,还有一些迷惑,继续请教.

前面提到按照C/C++的规则,我们可以:
C/C++ codechar*p1= .......;constchar*p2= p1;//但下述语句不行:constchar**p2=&p1;
因为,第一个赋值语句中,p2指向的是char,p1指向的也是char,所以p2可以等于p1。
第二个赋值语句中,p2指向的是const char *, &p1指向的是char *,这两种指针类型不匹配。
--------------------------
我的问题:第一个赋值语句中实际上p2指向的是const char,p1指向的是char。既然char *不能自动转换为const char *,那为何char能自动转换为const char?


[解决办法]
探讨
“数组名不是对象(数组是对象)”这个说法我觉得有点没意义(失敬之处请担待)。

这岂不是和说“变量名不是对象(变量是对象)”一个意思么?没错,变量名只是个标识符,它不是对象。可是变量作为一个对象,能代表它的除了变量名还有别的么?区别变量名和变量有意义么?

不知我这样理解是否偏颇?这几天看了些例子的结果,我感觉
(1)数组名arr就代表数组变量。因此&arr取得的是指向数组arr的指针;sizeof(arr)得到的是arr这个数组的大小。
(2)只不过在其他表达式中,数组名arr会被隐式地转换为指针,指针指向的类型同数组元素的类型。

所以,“数组名本身就代表一个值”这句话我其实没看懂。代表什么值?在什么情况下代表这个值?

[解决办法]
说实话,在看过标准相关部分以后,我对“数组名”一说相当深恶痛绝……

在所有类型的标识符里面,数组名可能是点名率最高的了,而从语法上看,它和一个变量的变量名究竟有什么区别?标准里面也没有关于“数组名”的特别说法。不过是因为数组可以转型为指针,就把它的名字与指针、地址混为一谈。虽说这样初期理解容易,但深究起来却成了一个很大的认识障碍。而且,国内相当多的教材都把数组作为一种数据类型的特征看得很低,这更使人以为数组名不过是一个常指针而已,忽视了数组在语法上的地位。

所以,我个人是一般不用“数组名”这个说法的~~在这点上我和西西 41 楼的相关立场基本一致~~
[解决办法]
探讨
说实话,在看过标准相关部分以后,我对“数组名”一说相当深恶痛绝……

在所有类型的标识符里面,数组名可能是点名率最高的了,而从语法上看,它和一个变量的变量名究竟有什么区别?标准里面也没有关于“数组名”的特别说法。不过是因为数组可以转型为指针,就把它的名字与指针、地址混为一谈。虽说这样初期理解容易,但深究起来却成了一个很大的认识障碍。而且,国内相当多的教材都把数组作为一种数据类型的特征看得很低,这更使人以为数组名不过是一个常指针而已,忽视了数组在语法上的地位。

所以,我个人是一般不用“数组名”这个说法的~~在这点上我和西西 41 楼的相关立场基本一致~~

[解决办法]
这岂不是和说“变量名不是对象(变量是对象)”一个意思么?没错,变量名只是个标识符,它不是对象。可是变量作为一个对象,能代表它的除了变量名还有别的么?区别变量名和变量有意义么? 
--------------------------------------
对于int i;来说,把i称作整数名与整数的确没有什么意义,因为无论在哪里,i都只有一种涵义,只代表一个整数对象。但数组名不同,作为一个特例化的实体,不同情况下扮演不同的角色,有时候代表数组对象本身,有时候又不是对象,所以必须加以区分。
[解决办法]
探讨
这岂不是和说“变量名不是对象(变量是对象)”一个意思么?没错,变量名只是个标识符,它不是对象。可是变量作为一个对象,能代表它的除了变量名还有别的么?区别变量名和变量有意义么?
--------------------------------------


对于int i;来说,把i称作整数名与整数的确没有什么意义,因为无论在哪里,i都只有一种涵义,只代表一个整数对象。但数组名不同,作为一个特例化的实体,不同情况下扮演不同的角色,有时候代表数组对象本身,有时候又不是对象,所以必须加以区分。


[解决办法]
对,数组名在经过数组到指针的转换后,就是个右值。否则,是一个不可修改的左值。
[解决办法]
探讨
谢谢。

我来较点真。你怎么知道,它是右值,而不是不可修改的左值呢?

[解决办法]
a如果不是对象,我如何能用*对它进行操作,并得到正确的结果呢?一元*操作符应该只能作用于对象吧?
----------------------------------------
间接运算符不要求操作数是对象,但要求它具有指针类型。

还有,指针不一定是对象,也不一定是变量。
[解决办法]
探讨
79楼是别人的话,请supermegaboy帮看看对不对?我感觉他和你意思相同。

[解决办法]
你把a[0]那地方当作存储了a的值,再想象一下就明白了

不知道你看过《C与指针》这本书没有?里面有个著名的例子:

在一个文件中定义

int a[10];

在另一个文件中声明

extern int *a;

然后*a = 10;

把表达式中的数组名理解成左值就会发生跟这类似的情况了。

热点排行