您的位置:首页 > 其它

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

2009-09-11 15:43 363 查看
转自:http://blog.csdn.net/lzpaul/archive/2008/09/05/2884095.aspx

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

代码如下:

#include <iostream>

using namespace std;

void deletechar(char *str, char ch)
{//从字符串str中删掉所有字符ch
char *p = str, *q;
while (*p)
{
if (*p != ch)
{
p++;
}
else
{
q = p;
for (; *(p + 1); ++p)
{

*p = *(p + 1); //Stop!

}//for(;*(p+1);++p)

*p = '/0';
p = q;
}//if-else
}//while
}//void deletechar(char *str,char ch)

int main()
{
char *str = "abccd";
char ch = 'c';
deletechar(str, ch);
cout << str << endl;

return 0;
}//int main()


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

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

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

定一下,进入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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: