• 8148阅读
  • 7回复

[提问]QT中char * str = "hello"问题 [复制链接]

上一主题 下一主题
 
只看楼主 倒序阅读 楼主  发表于: 2011-01-21
— 本帖被 XChinux 设置为精华(2011-01-22) —
先上代码,后描述问题,代码如下:
  1. #include <QtCore/QCoreApplication>
  2. #include <stdio.h>
  3. int main(int argc, char *argv[])
  4. {
  5.     QCoreApplication a(argc, argv);
  6.     char * str = "hello";
  7.     str[2] = 'b';
  8.     printf("%s",str);
  9.     return a.exec();
  10. }

这段代码在windows的QT Creator下编译运行不会出错,这让我很疑惑。
按我的理解,str指向一个字符串常量,改变其值是会出现段错误之类的问题,可是在QT Creator下运行起来却没问题。

我写了另一段普通的C++代码,如下:
  1. #include<stdio.h>
  2. int main()
  3. {
  4.     char * str = "hello";
  5.     str[2] = 'b';
  6.     printf("%s",str);
  7.     return 0;
  8. }

window下用mingw的g++编译,再运行就会出现错误。
难道说QT对
char * str = "hello";
的处理不同于普通C++代码?或者说用QT来编译时添加了不同的编译参数,所以避免了这个错误?请教各位大虾。
离线XChinux

只看该作者 1楼 发表于: 2011-01-21
你看看生成的汇编代码是什么。
二笔 openSUSE Vim N9 BB10 XChinux@163.com 网易博客 腾讯微博
承接C++/Qt、Qt UI界面、PHP及预算报销系统开发业务
只看该作者 2楼 发表于: 2011-01-21
为了排除printf出问题的可能,我把代码中的printf都去掉,但现象依旧。
下面是对两个版本代码对应的汇编,首先上QT版无错误的汇编代码:
  1. #include
  2. #include
  3. int main(int argc, char *argv[])
  4. {
  5.   4013b0:    55                       push   %ebp
  6.   4013b1:    89 e5                    mov    %esp,%ebp
  7.   4013b3:    83 e4 f0                 and    $0xfffffff0,%esp
  8.   4013b6:    56                       push   %esi
  9.   4013b7:    53                       push   %ebx
  10.   4013b8:    83 ec 28                 sub    $0x28,%esp
  11.   4013bb:    e8 00 40 00 00           call   4053c0 <___main>
  12.     QCoreApplication a(argc, argv);
  13.   4013c0:    8b 45 0c                 mov    0xc(%ebp),%eax
  14.   4013c3:    89 44 24 08              mov    %eax,0x8(%esp)
  15.   4013c7:    8d 45 08                 lea    0x8(%ebp),%eax
  16.   4013ca:    89 44 24 04              mov    %eax,0x4(%esp)
  17.   4013ce:    8d 44 24 14              lea    0x14(%esp),%eax
  18.   4013d2:    89 04 24                 mov    %eax,(%esp)
  19.   4013d5:    a1 a8 12 41 00           mov    0x4112a8,%eax
  20.   4013da:    ff d0                    call   *%eax
  21.     char * str = "hello";
  22.   4013dc:    c7 44 24 1c a8 ca 40     movl   $0x40caa8,0x1c(%esp)
  23.   4013e3:    00
  24.     str[2] = 'b';
  25.   4013e4:    8b 44 24 1c              mov    0x1c(%esp),%eax
  26.   4013e8:    83 c0 02                 add    $0x2,%eax
  27.   4013eb:    c6 00 62                 movb   $0x62,(%eax)
  28.     return a.exec();
  29.   4013ee:    a1 a4 12 41 00           mov    0x4112a4,%eax
  30.   4013f3:    ff d0                    call   *%eax
  31.   4013f5:    89 c3                    mov    %eax,%ebx
  32.   4013f7:    8d 44 24 14              lea    0x14(%esp),%eax
  33.   4013fb:    89 04 24                 mov    %eax,(%esp)
  34.   4013fe:    a1 ac 12 41 00           mov    0x4112ac,%eax
  35.   401403:    ff d0                    call   *%eax
  36.   401405:    89 d8                    mov    %ebx,%eax
  37. }

可以注意到
char * str = "hello";
  4013dc:    c7 44 24 1c a8 ca 40     movl   $0x40caa8,0x1c(%esp)
  4013e3:    00
    str[2] = 'b';
  4013e4:    8b 44 24 1c              mov    0x1c(%esp),%eax
  4013e8:    83 c0 02                 add    $0x2,%eax
  4013eb:    c6 00 62                 movb   $0x62,(%eax)

是其关键,地址
0x40caa8
应该就是"hello"的地址
找到这个地址的汇编代码如下:
  1. 0040caa8 <.rdata>:
  2.   40caa8:    68 65 6c 6c 6f           push   $0x6f6c6c65
  3.   40caad:    00 00                    add    %al,(%eax)

对照68 65 6c 6c 6f 就是hello对应的16进制表示,注意到这里是.rdata,属于只读数据段。后面对只读数据段进行写操作,应该会出现错误才对,可是并没有出现。。。。

下面是普通C++版,会出现错误。
汇编代码与上面类似,代码如下:

  1. 004013b0 <_main>:
  2. #include<stdio.h>
  3. int main()
  4. {
  5.   4013b0:    55                       push   %ebp
  6.   4013b1:    89 e5                    mov    %esp,%ebp
  7.   4013b3:    83 e4 f0                 and    $0xfffffff0,%esp
  8.   4013b6:    83 ec 10                 sub    $0x10,%esp
  9.   4013b9:    e8 b2 00 00 00           call   401470 <___main>
  10.     char * str = "hello";
  11.   4013be:    c7 44 24 0c 64 30 40     movl   $0x403064,0xc(%esp)
  12.   4013c5:    00
  13.     str[2] = 'b';
  14.   4013c6:    8b 44 24 0c              mov    0xc(%esp),%eax
  15.   4013ca:    83 c0 02                 add    $0x2,%eax
  16.   4013cd:    c6 00 62                 movb   $0x62,(%eax)
  17.     return 0;
  18.   4013d0:    b8 00 00 00 00           mov    $0x0,%eax
  19.   4013d5:    c9                       leave  
  20.   4013d6:    c3                       ret    
  21.   4013d7:    90                       nop    


char * str = "hello";
  4013be:    c7 44 24 0c 64 30 40     movl   $0x403064,0xc(%esp)
  4013c5:    00
    str[2] = 'b';
  4013c6:    8b 44 24 0c              mov    0xc(%esp),%eax
  4013ca:    83 c0 02                 add    $0x2,%eax
  4013cd:    c6 00 62                 movb   $0x62,(%eax)

可得"hello"地址,其汇编代码如下:
  1. 00403064 <.rdata>:
  2.   403064:    68 65 6c 6c 6f           push   $0x6f6c6c65
  3.   403069:    00 00                    add    %al,(%eax)

大虾指点,指点,谢谢。
离线XChinux

只看该作者 3楼 发表于: 2011-01-21
对应的汇编代码几乎一样。
你的 qtcreator那个,用的什么编译器?
看看编译过程。
二笔 openSUSE Vim N9 BB10 XChinux@163.com 网易博客 腾讯微博
承接C++/Qt、Qt UI界面、PHP及预算报销系统开发业务
只看该作者 4楼 发表于: 2011-01-22
恩,找到关键点了。
QT Creator下的编译过程如下:
Running build steps for project test...
Configuration unchanged, skipping qmake step.
Starting: "C:/Qt/2010.04/mingw/bin/mingw32-make.exe" -w
mingw32-make: Entering directory `D:/desktop/test/test-build-desktop'
C:/Qt/2010.04/mingw/bin/mingw32-make -f Makefile.Debug
mingw32-make[1]: Entering directory `D:/desktop/test/test-build-desktop'
g++ -c -g -frtti -fexceptions -mthreads -Wall -DUNICODE -DQT_LARGEFILE_SUPPORT -DQT_DLL -DQT_CORE_LIB -DQT_THREAD_SUPPORT -I"c:\Qt\2010.04\qt\include\QtCore" -I"c:\Qt\2010.04\qt\include" -I"c:\Qt\2010.04\qt\include\ActiveQt" -I"debug" -I"..\test" -I"." -I"c:\Qt\2010.04\qt\mkspecs\win32-g++" -o debug\main.o ..\test\main.cpp
..\test\main.cpp: In function 'int main(int, char**)':
..\test\main.cpp:6: warning: deprecated conversion from string constant to 'char*'
g++ -enable-stdcall-fixup -Wl,-enable-auto-import -Wl,-enable-runtime-pseudo-reloc -Wl,-subsystem,console -mthreads -Wl -o debug\test.exe debug/main.o -L"c:\Qt\2010.04\qt\lib" -lQtCored4
mingw32-make[1]: Leaving directory `D:/desktop/test/test-build-desktop'
mingw32-make: Leaving directory `D:/desktop/test/test-build-desktop'
The process "C:/Qt/2010.04/mingw/bin/mingw32-make.exe" exited normally.


其中有个参数很重要
-Wl,-enable-auto-import

当我把这个参数用来编译普通C++版本的代码时,编译出的程序能无错误运行了。
编译命令如下:
  1. g++ -Wl,-enable-auto-import test.cpp -o test.exe

我查看了下-enable-auto-import选项的文档http://sources.redhat.com/binutils/docs-2.16/ld/Options.html#Options
发现里面有这么一句话:
Note: Use of the 'auto-import' extension will cause the text section of the image file to be made writable.

不过这里说的是会导致text section writable,不是rdata section。
.text与.rdata的关系是什么呢?这个问题似乎涉及windows下的PE格式~~知识体系太大了,工作平台不在windows下,就不深究了。。。。。。
这里谢谢斑竹大哥热心的指导撒。
[ 此帖被我要下载代码在2011-01-22 09:50重新编辑 ]
离线XChinux

只看该作者 5楼 发表于: 2011-01-22
学习了。
二笔 openSUSE Vim N9 BB10 XChinux@163.com 网易博客 腾讯微博
承接C++/Qt、Qt UI界面、PHP及预算报销系统开发业务
离线lejcey

只看该作者 6楼 发表于: 2011-01-22
学习了,尤其是楼主舍得专研的精神!
离线leozh
只看该作者 7楼 发表于: 2011-05-26
虽然知道是错的,但是没有LZ这种钻研精神。
快速回复
限100 字节
 
上一个 下一个