[讨论]很有趣的一个程序

五号智能 2008-06-10 12:25:15

#include <iostream>
using namespace std;

int main()
{
char p[]="abcdefghijklmnopqrst";

for (char *ptr1=(char *)(&p+1)-1; ptr1!=p-1; ptr1--)
{
cout <<*ptr1<<" ";
}

cout <<endl;

cin.get();

}


这个程序的关键在于for循环里的 &p+1
p本来是地址用&取址运算符对它取址。这代表了什么呢,

对,是一个二级针。对这个二级指针加上1,代表的是这个指针的地址,将跨越整个p数组到达数组最后一个元素地址的下一个地址;

这很好理解,本来二级针就是这样特性。

但令人费解的是这个在数组建立之后,临时产生的这个二级指针,是怎么知道这个数组的长度?

对于数组,书上好像是这么说的,数组名实际上是一个指针常量。指向的是数组的首地址。也就是说它只是一个地址,

一个地址是不能代表数组的长度的,如果,只是如果上面的数组长度只有3.那&p+1,肯定是"p[4]"的地址,如果这个p[4]存在的

话。有趣,这个二级针真的很神奇。为什么编译器都不知道的东西,它怎么会知道?









...全文
464 47 打赏 收藏 转发到动态 举报
写回复
用AI写文章
47 条回复
切换为时间正序
请发表友善的回复…
发表回复
五号智能 2008-06-12
  • 打赏
  • 举报
回复
[Quote=引用 41 楼 linlan999 的回复:]
引用 34 楼 XSign 的回复:
#include <stdio.h>

int main()
{
char *a= "hello";
printf("%x\n",a);
printf("%x\n",(&a+1)-2);
printf("%c\n",(char *)(&a+1)-2);
return 0;

}
请教下上面的输出是什么?
应该是输出a的首地址,然后是“o”的地址,最后是字符o吧?
[hacksign@XSign code]$ gcc test.c -o t
[hacksign@XSign code]$ ./t
80484d0
bfe7a52c
2
[hacksign@XSign…



C/C++ code#include<…
[/Quote]

仔细一下中间所有我回的帖子。
五号智能 2008-06-12
  • 打赏
  • 举报
回复
是的,对于数组这么理解结果是对的。

但对指针就不行了。所以你必须把数组名解释成是首地址的引用,

这样理解才能正确解释为什么指针进行这个运算不好使
唐巧 2008-06-12
  • 打赏
  • 举报
回复
说说我的看法:
1、char p[] = "abc";在编译的时候会等价换成:char p[4] = "abc"; (最后一个字符是\0) 所以,其实编译时编译器就知道p的大小是4,所以在取p的地址时:&p
实际上就是被变成了 (char (*)[4])p ,这一点楼主可以通过以下方式验证:
把你的for (char *ptr1=(char *)(&p+1)-1; ptr1!=p-1; ptr1--)
换成 for (char *ptr1=(char *) ( (char (*)[21])p+1 )-1; ptr1!=p-1; ptr1--)
你会发现输出同样的内容。
这也引出了C编译器的特性,即取地址&符,实际上就是强制的指针类型转换而已。上面的例子,p被转换成一个列指针,列元素的大小是21,这个21是编译器在最初就知道的。

2、为什么把 char p[]换成 char *p 就不行了呢?因为这时候,&p被解释成了:(char **)p,即指向指针的指针。这和上面的列指针是不一样的。列指针+1会移动整个列的地址长度,而指针的指针+1只移动一个指针的指针的地址大小(即4),所以结果完全不一样。

3、楼主因为这个例子想到编译器既然能够知道数组大小,为什么不重载=号操作符来实现数组的直接赋值。
我觉得原因有四点:
1、这种赋值是直接可以通过memcpy来完成的,都只需要写一行,没有必要多加一个功能。
2、C语言本来就没有完整封装好的数组类型,直接用=号来赋值不利于大家对于数据内部的理解。
3、这种赋值必须两边数组元素个数刚好相等,实际当中这种情况相当少,而且编译器还需要增加验证=号两边长度的功能。实在是没有多大必要。
4、实际实现中还会有许多问题需要考虑,不知道楼主有没有试过,把p[]数组当参数传递到函数中,然后在里面用sizeof(p)会得不到数组的大小。因为传递时数组的长度信息已经丢失,所以要实现直接赋值还需要考虑各种情况,增加编译器的复杂性。


namespace_fox 2008-06-11
  • 打赏
  • 举报
回复
mark
五号智能 2008-06-11
  • 打赏
  • 举报
回复

/*[Quote=引用 34 楼 XSign 的回复:]
#include <stdio.h>

int main()
{
char *a= "hello";
printf("%x\n",a);
printf("%x\n",(&a+1)-2);
printf("%c\n",(char *)(&a+1)-2);
return 0;

}
请教下上面的输出是什么?
应该是输出a的首地址,然后是“o”的地址,最后是字符o吧?
[hacksign@XSign code]$ gcc test.c -o t
[hacksign@XSign code]$ ./t
80484d0
bfe7a52c
2
[hacksign@XSign code]$
根本就不对~~
[/Quote]


字符指针 char *a= "hello";

数组 char b[]="hello";

因为 a是指针,你对它加1,是指针地址本身加了1,而不是它指向的数据地址加了1

数组是不一样的,数组b,可以看成b是b指向的数据的首地址引用,对它进行操作,实际是对它指向的数据地址进行操作
所以用数组名(char*)(&b+1)-1能正确指向数据位置。

而用(char *)(&a+1)-1则是对指针自身地址进行了运算,它把指针自身的位置移了向移了4,然后把原本在那个地址的指针给强行转成了char*。然后再给这个char*指向的数据地址减了1。
结果就出来了,因为不知道char*-1之后指向的是哪里,这个显示的数据也是不一定的。



而现在我思考的是,即然可以算出数组长度,为什么不支持 同类型的数组之间的赋值操作。

进而想到,即然知道了数组长度,怎么才可以把数组之间的赋值变得更简单一些,

效率再高一些

代码可读性更强一些。

如果能重载=运算符就更好了。

希望高手能给出代码,供俺们学习一下^_^
*/

五号智能 2008-06-11
  • 打赏
  • 举报
回复
[新加了100分]

不能让这个帖子刚加分就沉下去。。

kimir 2008-06-11
  • 打赏
  • 举报
回复
char p[]="abcdefghijklmnopqrst"; //创建长度为21字节的1维数组
&p //动态产生了2维数组地址,其首地址和p的地址一样,2维数组的第一个元素就是"abcdefghijklmnopqrst";
&p+1 //相当于2维数组第1维的下标+1,那指针就跑到"abcdefghijklmnopqrst"之后了
(char *)(&p+1) //这一步又把刚才的2维数组强制转换成一维数组了
(char *)(&p+1)-1 //转换后元素就是类型就是单字节了,指针只能以单字节移动,结果就移动到
//"abcdefghijklmnopqrst"的最后一个空间'\0'这个位置.

老c菜兄:我这么理解行不?给个评语.
xhd3767 2008-06-11
  • 打赏
  • 举报
回复
[Quote=引用 40 楼 xgywd 的回复:]
引用 39 楼 HelloDan 的回复:
其实最重要的不是找到多强的人来教你,而是你自学成多强的人。


我可以自学,但,自学是相当的浪费时间的,一个小问题,你的一时没想到,就可能浪费你几天的时间,
在5月18日的那天,我在学循环判断,只是一个小小的问题,让我对了GDB调试了两天。而我刚把问题发到这个论坛上,就找到了解决的方式。这是我经常遇到 的事,每当这个时候,我就想,唉缺个老师啊,不然这两天得学多少东西。
[/Quote]
都是这么过来的....


倒序输出....地址的地址类比指针的指针.....
linlan999 2008-06-11
  • 打赏
  • 举报
回复
#include <stdio.h>

int main()
{
char a[]= "hello";
printf("%x\n",a);
printf("%x\n",(&a+1)-2);
printf("%c\n",*((char *)(&a+1)-2));
return 0;

}
linlan999 2008-06-11
  • 打赏
  • 举报
回复
[Quote=引用 34 楼 XSign 的回复:]
#include <stdio.h>

int main()
{
char *a= "hello";
printf("%x\n",a);
printf("%x\n",(&a+1)-2);
printf("%c\n",(char *)(&a+1)-2);
return 0;

}
请教下上面的输出是什么?
应该是输出a的首地址,然后是“o”的地址,最后是字符o吧?
[hacksign@XSign code]$ gcc test.c -o t
[hacksign@XSign code]$ ./t
80484d0
bfe7a52c
2
[hacksign@XSign…
[/Quote]



#include  <stdio.h> 

int main()
{
char a[]= "hello";
printf("%x\n",a);
printf("%x\n",(&a+1)-2);
printf("%c\n",*((char *)(&a+1)-2));
return 0;

}
五号智能 2008-06-11
  • 打赏
  • 举报
回复
[Quote=引用 39 楼 HelloDan 的回复:]
其实最重要的不是找到多强的人来教你,而是你自学成多强的人。
[/Quote]

我可以自学,但,自学是相当的浪费时间的,一个小问题,你的一时没想到,就可能浪费你几天的时间,
在5月18日的那天,我在学循环判断,只是一个小小的问题,让我对了GDB调试了两天。而我刚把问题发到这个论坛上,就找到了解决的方式。这是我经常遇到 的事,每当这个时候,我就想,唉缺个老师啊,不然这两天得学多少东西。
lc19890326 2008-06-10
  • 打赏
  • 举报
回复
(char *)(&p+1)-1应该指向的字符串最后的'\0'吧

所以说,长度为3的数组的话,&p+1是p[4]的地址没错
fox000002 2008-06-10
  • 打赏
  • 举报
回复
这种没有实用价值的代码

也就玩玩下
lc19890326 2008-06-10
  • 打赏
  • 举报
回复
帮顶
五号智能 2008-06-10
  • 打赏
  • 举报
回复
[改错]
是面有一个地方写错了,是p[3]的地址,小学数学没学好,给算差了
hong8292 2008-06-10
  • 打赏
  • 举报
回复
注意你的这句话:
  对于数组,书上好像是这么说的,数组名实际上是一个指针常量。
可不一定完全正确哦。数组在某些时候不完全等价于一个指针常量。那么到底是在什么情况下不完全等于呢?请阁下看看《expert c》吧,那里有非常详尽的解释。
GoAssemblyNow 2008-06-10
  • 打赏
  • 举报
回复
&p是指向数组p 的指针,&p+1指向数组最后一个元素的下一个地址,恩,理解了。谢谢各位高手的解释,受益匪浅啊。
lin_style 2008-06-10
  • 打赏
  • 举报
回复
你那天理解有点误

char p[]="abcdefghijklmnopqrst";

&p+1
(int)p+1

这个加1,要看前面的单位变量
比如 int+1,跳四个字节
char+1,跳一个字节

而&p指的是一个数组的单位量,加上的长度自然就是sizeof(p)
而(int)p,是化成一个普通的数字,再加上1
lin_style 2008-06-10
  • 打赏
  • 举报
回复
[Quote=引用 16 楼 axing0308008341 的回复:]
不懂,关注~~~指针常量就分配在静态数据区..&p+1是什么意思呢,为什么能知道数组的长度?
如果换成char *p="abcdefghijklmnopqrst";动态数组在动态分配中的堆栈分配,&p+1就不知道指向哪了~~
[/Quote]

&p+1就是p的地址加上一个四字节的长度
lin_style 2008-06-10
  • 打赏
  • 举报
回复
你那天理解有点误

char p[]="abcdefghijklmnopqrst";

&p+1
(int)p+1

这个加1,要看前面的单位变量
比如 int+1,跳四个字节
char+1,跳一个字节

而&p指的是一个数组的单位量,加上的长度自然就是sizeof(p)
而(int)p,是化成一个普通的数字,再加上1
加载更多回复(27)

64,637

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧