指针和数组名的区别 char *str 和 char str[]的不同之处

 转自:http://blog.csdn.net/lzpaul/archive/2008/09/05/2884095.aspx

 

一个简单的字符串相关程序,运行时崩溃,罪魁祸首 char *str  和 char str[]的不同之处

代码如下:

 

 

上述代码编译链接后,运行时程序崩溃,崩溃??!!

仔细分析了这两个函数,没发现有什么不对。

疯了,我也快蓝屏了,: (

 

定一下,进入debug

当运行到上面 //Stop!处出错   Unhandled exception in test.exe:0xC0000005:Access Violation

停在汇编指令 mov     byte ptr [edx],cl  处,说明执行该指令出错,(将寄存器值写内存时出错,也即将* (p+1)的值写到*p 时候出错,该指令的上一指令是 mov     cl,byte ptr [eax+1] ,将* (p+1)写入寄存器)

 

用WinDbg进一步分析

同样执行到上述那行代码后停止(Access violation)

(在指令mov     byte ptr [edx],cl  )

写内存错!!??奇怪!

看下内存中的内容:

0046f02e    63 63 64 00    ccd 

在偏移地址0046f02e处开始四个字节分别是63 63 64 00 就是字母ccd的ASCII值,就是主函数字符串"abccd"中内容

没有问题啊?

 

用windbg的命令查看下这块内存信息,如下

 !address edx
    00400000 : 0046f000 - 00008000
                    Type     01000000 MEM_IMAGE
                    Protect  00000002 PAGE_READONLY
                    State    00001000 MEM_COMMIT
                    Usage    RegionUsageImage
                    FullPath test.exe

显示Protect  00000002 PAGE_READONLY,更奇怪了!? 内存只读!

*p = * (p+1); //p所指向的内存位置只读!

 

把这个问题拿到群里跟别人讨论,出现同样的错误,对方给了另外个版本:

在deletechar()函数中用malloc动态分配一块内存将str所指向的字符串复制到这块内存中,运行OK

 

这样改动是个办法,但,是不是成本太高了???

说到动态分配,突然想到(就是突然想到了)  [ ],改成这个:char str[]试试看,编译,链接,运行,OK!

 

怪了,怎么这样就好了??!!

有点意思,看来还得往里走走,char *str,char str[ ] 差别还挺大

 

反汇编!看看汇编怎么做的

于是在main()里就写了两行代码

 char *s="abc";
 char str[]="abccd";

看看怎么回事

 

char *str="abc";   的汇编代码

mov         dword ptr [ebp-4],offset string "abc" (0046f034)

 

char str[ ]="abccd";  的汇编代码

mov         eax,[string "abccd" (0046f02c)]
mov         dword ptr [ebp-0Ch],eax
mov         cx,word ptr [string "abccd"+4 (0046f030)]
mov         word ptr [ebp-8],cx

 


乍一看,不一样!行数不一样,嘿嘿。开个玩笑

有意思,继续分析

看着这几行代码半天,没看明白不一样在哪里(行数除外 :D )

 

mov         dword ptr [ebp-4],offset string "abc" (0046f034)     是把"abc"的偏移量(指针)给了指针变量str,即str中的值是0046f034

下面这几行干嘛了?

 

看看内存,寄存器怎么变化的

mov         eax,[string "abccd" (0046f02c)]

 

把abcc四个字符存到寄存器eax中,然后写到内存[ebp-0ch](我的机器上[ebp-0ch]是0013ff74(偏移量),str是偏移量为0013ff74的内存开始处)。

 

再看看上面的mov指令,是取偏移量(mov         dword ptr [ebp-4],offset string "abc" (0046f034)),这里是把串"abccd"所在内存中的值复制到str开始的内存处。

 

 

 

到这里,问题开始清楚了:

char *s="abc";
char str[]="abccd";

 

1、char *s="abc";

     看这个赋值:

     右边,是"abc",是个字符串常量,存在于内存某处(我的机器上是ds:0x0046f034),程序员不知道,编译器安排的,也没必要知道(当然,这个赋值之后,程序员就知道并能控制这个串了)。字符串常量所在内存是只读的。

 

     左边,字符指针s,赋值时候,把地址ds:0x0046f034的偏移地址("abc"所在),存放到指针变量s(其地址为 ds:0x0046f034)中。程序员能完全控制的内存只是指针变量所占据的这四个字节内存,只能改变该指针的指向,至于其指向的内存能不能写,那就看程序了,本程序是指向的只读内存,不能写!

 

2、char str[]="abccd";

     再看这个赋值:

     右边,和上面类似,是"abccd",也是个字符串常量,存在于内存某处(是ds:0x0046f02c),程序员不知道,编译器安排的(这个赋值之后,程序员还是不知道这个常量在哪里,因为并没有用指针指向这块内存,这和上面不同)。该字符串常量所在内存也是只读的。

 

     左边,字符数组str,赋值时候,把地址ds:0x0046f02c("abccd"所在)所指内存中的内容,复制到字符数组str开始(其地址为 ds:0x0013ff74)的内存中,每复制一个字符都会开辟一个字节(char型变量占1字节)内存来存放这个字符(这也是实现了数组元素个数的动态确定)。从字符数组str开始的这部分存放这些字符的内存是程序员可以完全控制的,可读写,因此在这些内存写当然是没有问题的!

 

      到这里,突然想到多年前看过的一本书《C和指针》(Kenneth A. Reek著,徐波 译,现在有第二版了¥65,第一版¥55,不便宜,但是是好书,值得研读,是研读哦,不是翻翻,当然翻翻也会有收获的)里面好像提到过这个问题,赶快拿来看看(字符数组的初始化一节),果然这样。  o(∩_∩)o

 

       

       另附, 数组名和指针的区别与大家分享下

       原文http://dmacy.bokee.com/4728674.html                              

       许多程序员对数组名和指针的区别不甚明了,他们认为数组名就是指针,而实际上数组名和指针有很大区别,在使用时要进行正确区分,其区分规则如下:


规则1 数组名指代一种数据结构,这种数据结构就是数组;
                    char str[10];
                    char *pStr = str;
                    cout << sizeof(str) << endl;
                    cout << sizeof(pStr) << endl;

                    输出结果为:
                    10
                    4
  这说明数组名str指代数据结构char[10]。


规则2 数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改;
                    char str[10];
                    char *pStr = str;
                    str++; //编译出错,提示str不是左值 
                    pStr++; //编译正确

规则3 指向数组的指针则是另外一种变量类型(在WIN32平台下,长度为4),仅仅意味着数组的存放地址;

规则4 数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅只是一个指针;很遗憾,在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。


例如:
void arrayTest(char str[])
{
cout << sizeof(str) << endl;   //输出指针长度
    str++; //编译正确
}


int main(int argc, char* argv[])
{
 char str1[10] = "I Love U";
 arrayTest(str1);
    return 0;
}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值