一个关于char型指针的危险认识(欢迎大家讨论)

topwork 2003-11-28 10:11:31
小弟最近在浏览论坛内的帖子的时候发现了一个对char型指针不太正确的认识:
首先请看一段代码:
char *stri = "usually";
strncpy(stri,"123",3);
(原贴见:http://expert.csdn.net/Expert/topic/2501/2501487.xml?temp=.2497522)
还不止这一个帖子里面有类似语句:
char *p = "string";
的。
我要指出的是这样做不是不可以,而是非常危险的,首先我们先看看这种语句会产生什么运行效果:
看看对应的汇编
mov dword ptr [ebp-4],offset string "abddddd" (0041fe50)
什么意思呢?就是在程序栈中申请四个字节的指针空间(ebp-4),然后(注意这是最关键的),把数据段里面的字符串首地址付给该指针。于是问题出现了,由于我们程序的数据段是写保护的,那么象我最上面举的例子就肯定不能运行成功了,因为它试图向写保护的内存段拷贝内容。就鉴于此我们才有了const关键字(当然const关键字不止干这一件事情),我们的变量可以这样定义:
const char *p = "string";
这样定义变量说明p指针指向的内容不可以修改,即如果程序这样写:
const char *stri = "usually";
strncpy(stri,"123",3);
那么编译的时候就会出错。
下面我再写一个例子,使各位大哥对我上面的叙述有更深入的了解:

#include "stdio.h"
int main(int argc, char* argv[])
{
DWORD dw;
char *p1 = "abddddd";
VirtualProtect(p1 , 8 , PAGE_READWRITE , &dw);
strncpy(p1 , "ABC" , 3);
VirtualProtect(p1 , 8 , dw , &dw);
printf(p1);
return 0;
}
大家看看这段程序会有什么运行结果?答案是运行不会出错,而且最后会打印"ABCdddd"这个结果,这说明我们也可以通过修改数据段的只读属性来达到修改自己的数据段内容的目的,但是我决不推荐这种用法,太危险了。
下面我们来看看几种正确的用法,这些用法所得到的字符串指针都可以随意操作的(不过可别越了界):

正确用法一:
char stri[] = "usually";
这段语句如何运行呢?见下:
mov eax,[string "usually" (0041f01c)]
mov dword ptr [ebp-8],eax
mov ecx,dword ptr [string "usually"+4 (0041f020)]
mov dword ptr [ebp-4],ecx
我们可以看到,程序首先把字符串"usually"的前四个BYTE,放到ebp-8位置,然后把后四个BYTE放到ebp-4位置,说句通俗的话就是程序在栈里面申请了一个8个字节长的缓冲区,然后把数据段中的内容分批复制到该缓冲区内,可能我们用的字符串刚好7个不太说明问题,下面我们看一个字符串稍长的例子:
char stri[] = "usuallyaabbcc";
对应代码:
mov eax,[string "usuallyaabbcc" (0041fe58)]
mov dword ptr [ebp-10h],eax
mov ecx,dword ptr [string "usuallyaabbcc"+4 (0041fe5c)]
mov dword ptr [ebp-0Ch],ecx
mov edx,dword ptr [string "usuallyaabbcc"+8 (0041fe60)]
mov dword ptr [ebp-8],edx
mov ax,[string "usuallyaabbcc"+0Ch (0041fe64)]
mov word ptr [ebp-4],ax
这段代码不用我解释了吧,总体说就是程序申请了一个16字节的缓冲区,然后把数据段的数据分四次复制过来,顺便说一句为什么申请16而不是14呢,因为我们现在用的32位系统对DWORD的操作最快(^_^,这是编译器的优化呀)。

正确用法二:
char stri[20] = "usuallyaabbcc";
看看代码:
mov eax,[string "usuallyaabbcc" (0041fe58)]
mov dword ptr [ebp-14h],eax
mov ecx,dword ptr [string "usuallyaabbcc"+4 (0041fe5c)]
mov dword ptr [ebp-10h],ecx
mov edx,dword ptr [string "usuallyaabbcc"+8 (0041fe60)]
mov dword ptr [ebp-0Ch],edx
mov ax,[string "usuallyaabbcc"+0Ch (0041fe64)]
mov word ptr [ebp-8],ax
这段代码与上面所述的第二个例子相似,所不同的是我们指定了缓冲区的大小(大小为20,0x14),其它地方一样的,这里不再叙述了。

正确用法三:
char *stri = new char[20];
strcpy(stri , "usuallyaabbcc");
这个例子我不贴汇编代码了,因为很好理解,就是首先在堆中申请空间,然后进行字符串复制。(不要忘记删除呀^_^)

我们前面叙述了多种字符串指针的申请方法都可以,但就是不能这样写:
char *p = "string";
如果非要这样写的话,也一定要写成这样:
const char *p = "string";
虽然第一种写法不会报错,而且还可以进行读操作,但是作为一名严谨的程序员,这种代码是不能出现的,即使知道自己在干什么也不可以,反正作为我个人的编码规则,程序中决不容许这种代码存在。

说到这里我的意思也表达清楚了,小弟拙文漏笔,实不入各位大哥高眼,但有错误之处,望多多指正。
...全文
237 69 打赏 收藏 转发到动态 举报
写回复
用AI写文章
69 条回复
切换为时间正序
请发表友善的回复…
发表回复
topwork 2003-12-03
  • 打赏
  • 举报
回复
能写清楚些么?标点符号太乱了,看不懂。
ISunwu 2003-12-02
  • 打赏
  • 举报
回复
还在阿。弓虽
modnar 2003-12-02
  • 打赏
  • 举报
回复
char *stri = "usually";
这句完全没错,"usually"默认为const char

strncpy(stri,"123",3);
更改一个const,我还说什么好呢?
魔芋 2003-12-02
  • 打赏
  • 举报
回复
对,没有良好的习惯
farfh 2003-12-02
  • 打赏
  • 举报
回复
写这种代码的人,多半都没写过几天程序,或者平时就没什么好习惯可言。
leozhou2002 2003-12-02
  • 打赏
  • 举报
回复
楼主讲的比较详细了,但是有一个问题“程序在编译debug版的时候把代码段(code seg)和数据段(data seg)放在了一起,而这时候这个段是受写保护的(因为代码段必须受到写保护)”是不准确的。
实际上应该是在debug版本的时候"usually"存放在.rdata段中是写保护的。而在release版本中该数据被放在了.data段中,也就是说可以被修改了。
魔芋 2003-12-02
  • 打赏
  • 举报
回复
本来就应该是常量的,为什么要对它操作呢?
魔芋 2003-12-02
  • 打赏
  • 举报
回复
这个东西不值得讨论吧
魔芋 2003-12-02
  • 打赏
  • 举报
回复
既然有问题,
char *str = "string";
strcpy(str,"abc",3);
那么"abc"又做何解释呢,是不是也要char abc[] = "abc"?
要LPCTSTR lpstr = "string";吧
broccoli 2003-12-02
  • 打赏
  • 举报
回复
有些人是不是都没有看过C语言的教材啊,char *stri = "usually";这个语句肯定是没有错的,根本就不存在分不分配的问题,stri是个指向字符的指针变量,里面存放的是第一个u的地址嘛,这能有什么问题。至于strncpy(stri,"123",3),肯定就不对了,因为stri只是指向了一个字符串,也可以说是指向了一个字符,并没有给stri分配那么多空间,虽然"usually"占有8个字节的空间,但是和stri没有关系,你怎么能够往stri里面复制东西呢?
wooingcn 2003-12-02
  • 打赏
  • 举报
回复
char *stri = "usually";
本身并没有错,看使用的人了,其实它可以演化出如下两种模式,这样更明确。

1、长度固定,内容可变。
char stri[]="usually"; //长度为8的储存空间

2、长度固定,内容不可改。
const char *lpszText="usually";
或者
LPCSTR lpszText=usually";
这种情况只能当作常量字符串使用。
jiujiuchong 2003-12-02
  • 打赏
  • 举报
回复
topwork (日光):
“”这个东西你怎么理解,为什么我在vc下得到的值始终是 4281356(int n = (int)"" ;
changlele 2003-12-02
  • 打赏
  • 举报
回复
高手就是高手。通过寥寥数语道破了一些最低层的问题,这样的人应该成为版主,
ayanamiwww 2003-12-02
  • 打赏
  • 举报
回复
to:microyzy(毛毛叉),当然方法有很多,但是这是个人写代码的习惯,我认为那样做安全,所以就按照那个格式写了,没说别的方法不行。
nonocast 2003-12-01
  • 打赏
  • 举报
回复
char *stri = "usually";
???
还没分配呢?!
shantian 2003-12-01
  • 打赏
  • 举报
回复
mark
topwork 2003-12-01
  • 打赏
  • 举报
回复
up
popyyb 2003-12-01
  • 打赏
  • 举报
回复
up
topwork 2003-12-01
  • 打赏
  • 举报
回复
今日晚些时候节贴,大家再讨论一天。
microyzy 2003-12-01
  • 打赏
  • 举报
回复
char* p = NULL;

p = new char[x];
.
.
.
if(p)
//do some work with *p;
这是现在写代码的规范,我认为都应该这样做。
============
我是菜鸟,不知道是不是这样,但是我觉得如果x不大的话,为什么不直接在栈内分配呢?
加载更多回复(49)

16,472

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • Web++
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

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