• char指针与数组(转载)


    先看以下代码:
    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 *指针而不会影响其常量性。所以这是完全正确的赋值。
    其实这想起来也很平常,指针是没有分配空间的地址而已,而数组是一种容器,占用连续的储存空间。想想字符串就该知道它是一个数组!而不是指针!真正意义上的指针只能是地址,而它在分配了连续的空间后可以作为数组来使用,这是由于他们的共性而决定的。

  • 相关阅读:
    CI框架用cookie实现用户自动登录
    php预定义常量
    ubuntu root密码
    jQuery的form中ajaxSubmit参数
    优酷视屏播放去广告链接id_XXX.html内容替换红色
    linux 重启apache:apachectl -k graceful
    python深copy与浅copy的区别
    python3中pathlib库的Path类方法汇总
    unittest使用discover加载不同目录下的case失败,提示Path must be within the project
    自定义HTMLTestRunner报告case名称
  • 原文地址:https://www.cnblogs.com/samurail/p/2802350.html
Copyright © 2020-2023  润新知