有关char指针的文章一篇

[文章] 有关char指针的文章一篇

作者:未知 来源:月光软件站 加入时间:2005-2-28 月光软件站

[文章] 有关char指针的文章一篇(转自:http://www.moon-soft.com/doc/9040.htm)

先看以下代码: 
char *p; 
p="abc"; 
你认为是对的吗? 

答案:语法上是对的,但不提倡这种写法。 

误区1:没有给p分配内存空间就赋值,怎么会是对的呢? 
正解:不少人第一眼将这里的p="abc"看成了*p="abc",然后就做出了以上的论断。这是比较笨笨的错误咯:) 
看清楚就好啦,其实赋给p的是"abc"的地址。再说,*p="abc"也不对呀,字符串可不能这么赋值。 

误区2:这"abcd"哪来的地址,怎么能直接赋给p呢? 
正解:先自己试试吧。在2K/XP + VC下运行这段代码,是不会出错的,说明这段代码并无问题。晕吧?猜想的话呢,就是"abcd"不知道被放在了什么地方,然后弄来了一个地址,给了p。 

这到底是怎么回事呢? 

要知道,这两个语句和char *p="abc"是完全一样的,所以其中的道理也一样。 
char *p="abc"曾经迷惑了不少人呀。问问你:p到底是什么类型的?char *?错,是const char *! 
也就是说,它所指向的内容是不可改变的。不过要补充的是,a的指向是可以改变的。 
所以为了不再引起误会,char *p="abc这种写法是不提倡的。 
既然char *p="abcd"被建议写成const char *p="abcd",那么char *p; p="abcd";也应该写成const char *p; p="abcd"; 

讲来讲去,最后来得看看汇编代码。看完就明白是怎么回事了。(我才发现汇编代码原来这么爽看!VC下,没开编译器优化): 
我们重点先看const char *p="abc"和char p[]="abc"有什么不同(都放在main()中声明): 

PHP源码:

void main
()
{
  const 
char *p="abc";
}

3:        const char *p="abc";
00401028   mov         dword ptr [ebp-4],offset string "abc" (0041f01c)

void main()
{
  
char p[]="abc";
}

3:        char p[]="abc";
00401028   mov         eax,[string "abc" (0041f01c)]
0040102D   mov         dword ptr [ebp-4],eax


看出差别了吗?上一段ASM用offset取"abc"的地址,然后赋给[ebp-4],也就是p(下同)。 
而下一段ASM却转了一个弯,先把"abc"的地址转到寄存器eax,然后再转赋给p。 

有疑问了:为什么不和上面的一样,直接用offset?VC是很聪明的(废话,M$的东西呀),不用offset,恐怕就是用不了了。 
offset是一条伪指令,在编译的时候就已经把偏移量算好了。offset是无法执行间接寻址的计算的。 

说明了什么? 

const char *p="abc"中的"abc",在编译期间就已经处理好,要了一块内存,存起来了!在把地址赋给p的时候,就可以直接用offset计算。 
而char p[]="abc"中的"abc",是在运行期间动态分配内存给"abc",然后再算出地址,赋给p。hehe,这同时也说明了数组和指针的等价性。 

我们再做一次实验,这一次我们把char p[]="abc"放在main()外,并在main()内用一个指针再指向p看看。 
PHP源码:

char p
[]="abc";

void main()
{
  
char *t=p;
}

5:      char *t=p;
00401028   mov         dword ptr [ebp-4],offset p (00421adc)


这回p[]的声明放在main()外,变成了全局变量。结果main()内的t取p的指针的时候,直接用offset可以计算出来。 

据小石头所说,字面值,const,static,inline,全局变量都是放在静态数据区的。(但我感觉似乎不是如此,只是全局变量和字符串在编译时就处理好放在一起) 

不难发现,p[]被声明成全局变量后,就可以直接用offset计算地址,说明静态数据区是编译时就已经处理好的。
再对照const char *p="abc"的ASM,我们马上想到:"abc"就是被C/C++存在静态数据区中的!它的地址就是"abc"在静态数据区的地址! 

弄清了这个,有些问题也就可以想得通了。下面这种用法,看来不能说是错误的了,因为"abc"是在静态数据区的,生存期可以说是整个程序: 
PHP源码:

#include "stdio.h"

const char *fun()
{
  const 
char *p="abc";
  return 
p;
}

void main()
{
  const 
char *t=fun();
  
printf ("%s",t);
}



看ASM: 
PHP源码:

5
:      const char *p="abc";
00401038   mov         dword ptr [ebp-4],offset string "%d" (0042001c)


一样也是使用offset计算地址。 

C/C++给字符串的待遇真是太好了。为了一个字符串,几乎可以打破所有的指针规则。晕~~~~(完) 

附:第一次写这么长的文章,写得挺晕的。本来我的C/C++也不是很纯熟的,ASM也是一知半解,今天在CSDN上为这个问题郁闷了半天,和几个人讨论了一下,最后就写了这么一篇文章。希望大家赏脸看看~~~有错一定要指正啊!最后特别感谢小石头(想飞的菜鸟,骄傲的石头,菜菜,都是他咯),还有小阿哥(就是Kingzeus咯)的帮忙~~~~~~谢谢咯~~~~~~

 

 

__________________
小菜虎 -> 菜菜的老虎

骄傲的石头回复:

堆几盘积木,心情好些了,所以再重新写一遍。 
关于字符串的这个问题,我一直在心里困惑着。所以呢,昨天就看了一下。 
以前回答别人的时候,总是很简单的回答,字符串就是const char *指针,指向它的入口地址。现在想来真是惭愧,虽然这个事实好象已经为大家所接受,甚至没有人探讨过这个问题!所以我相信我的发现对大家大多数是有好处的。 
首先请看以下代码 

PHP源码:

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    const 
char *0;
    
char *p2 p;
    return 
0;
}



以上代码有问题吗? 如果你说没有,请你试一下。很明显,这是有问题的。const是为了保证不变性,而你把他变成non const,肯定有错误或者警告,要么就要用const_cast转换。 
所以上面的代码不能通过编译。 

那么这就很明显的在lee的post里出现了问题,当然我以前也一直是这么认为,甚至很多人都是这么认为。难道这是编译器对字符串的特殊处理吗? 还是其他的原因? 

于是我想看看究竟。就动用了RTTI,我飞快的键入了以下代码。 

PHP源码:

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
   
cout << typeid("abc").name() << endl;
   return 
0;
}



你说结果是什么? 
是char [4]!而不是const char *; 
好,这个结果解决了我心中的疑点,原来是这样!这可以很简单的解释char *p = "abc"这个问题。 数组是一个char *const 指针,当然可以赋给char *指针而不会影响其常量性。所以这是完全正确的赋值。 
其实这想起来也很平常,指针是没有分配空间的地址而已,而数组是一种容器,占用连续的储存空间。想想字符串就该知道它是一个数组!而不是指针!真正意义上的指针只能是地址,而它在分配了连续的空间后可以作为数组来使用,这是由于他们的共性而决定的。 

哈哈!心情愉快,所以也接着看了下lee上面所做的探讨。字符串是放在静态储存区没错,毫无疑问。至于位置~ 偶不想多说,lee在上面分析了很多。所以我只对它进行了一下简单的测试。 
我手头上只有dev c++ 和 vc70编译器,所以就只用他们进行了测试。(打开了全部优化) 

PHP源码:

#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    
char *"abc"
    
char *p2 "abc";
    
p[1] = 'k';
    
p2[1] = 'j'
    
cout << p1 << endl;
    
cout << p2 << endl;
    return 
0;
}



结果呢?在dev c++下报错, 原因我想可能是因为在dev c++在对静态储存区进行了保护。而vc70下,通过,并且两个输出是不同的。所以偶去看vc70产生的msil码,原来vc70每次处理字符串都在静态区分配了空间,而且这两个"abc"是连续分配的!这里就没有出现Solmyr说的那种情况。我想一般比较好的编译器也应该这么说。至于vc60,偶没有测试,但是可能Solmyr说的情况会出现吧。但这样是不好的,相信这样会出现很多微妙的情况。 
所以,引用字符串的最佳格式是const char *指针,但char *指针是完全没错的。于情于理,也说的过去吧  

如果你还要问,那用指针接受数组呢? 呵呵,你该仔细看看前面了。相信这会给你帮助。

 

 

__________________
不可一日无酒无肉无女人

posted on 2011-05-03 10:50 MrRightLeft 阅读(419) 评论(0)  编辑 收藏 引用 所属分类: C/C++


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2011年5月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

导航

统计

随笔分类

随笔档案

文章分类

文章档案

搜索

最新评论

阅读排行榜

评论排行榜