Chinaunix

标题: 今天遇到的一个极品指针问题 [打印本页]

作者: cdphp    时间: 2007-12-27 15:56
标题: 今天遇到的一个极品指针问题
俺 比较菜,见笑了。
先来代码吧:

#include <stdio.h>

int main(void)
{

     char* string = "something";

     while(*string)
     {
       *string++ = 'd';
     }

     printf("The result is: %s\n",test);
}

问题, 编译通过,但运行到while 里面的时候出错了。。

错误提示(vs2005.net):Unhandled exception at 0x00413604 in contact.exe: 0xC0000005: Access violation writing location 0x0041565c.

谢谢!!
作者: lgfang    时间: 2007-12-27 16:05
你这能编译过?

典型错误。
搞清楚
char* string = "something";

char string[] = "something";
的区别就知道为什么出错了。
作者: -耗子-    时间: 2007-12-27 16:09
string指向的是字符串常量"something"
存放在进程的“只读”内存区域,往里面写东西当然要出错了
作者: cdphp    时间: 2007-12-27 16:43
非常感谢上面几个热心的大哥,问题已经解决。。现在贴上代码。
char* concat(char* string1,char* string2)
{
        static char original[30] = {0};
        int index=0;
        while(*string1)
        {
                original[index++] = *string1++;
        }

        while(*string2)
        {
                original[index++] = *string2++;
        }
        original[index] = '\0';
        return original;
}

这个代码肯定写得很不好,无耐学艺不精,只能慢慢学习了。
作者: anthony1983    时间: 2007-12-27 16:44
没看出哪里极品
作者: scutan    时间: 2007-12-27 17:23
这问题以前讨论过很多次的. 而且还讨论过不同编译器的情况.
作者: fallshuang    时间: 2007-12-27 23:34
原帖由 lgfang 于 2007-12-27 16:05 发表
你这能编译过?

典型错误。
搞清楚
char* string = "something";

char string[] = "something";
的区别就知道为什么出错了。


不要误导别人,虽然这个代码执行会崩溃,但是编译是绝对可以通过的。 你要搞清楚什么是compiler , 什么是linker and loader 。这个代码之所以崩溃不是因为不符合C99的语义,是因为linker和loader让它崩溃。

在linux kernel里,如果你没开一个RODATA的选项(缺省一般都没开)

创建一个简单的函数,其代码只有下面2行:
char *str= "hello";
*str = 'H' ;
这样编译运行都没问题的。
作者: 印随    时间: 2007-12-28 09:42
c语言程序组成
编译时:如数组,变量名,语法等,complier完成
运行时:逻辑运算,赋值运算等,linker and loader完成
作者: westgarden    时间: 2007-12-28 13:11
"极品指针"第一次听到,
楼主还是做广告策划比较合适。
作者: Jass    时间: 2007-12-28 13:30
原帖由 lgfang 于 2007-12-27 16:05 发表
你这能编译过?

典型错误。
搞清楚
char* string = "something";

char string[] = "something";
的区别就知道为什么出错了。


换成char string[]是编不过的吧。string不能作为左值的。
作者: baohuaihuai    时间: 2007-12-28 13:37
这个问题太不极品了
作者: agaric    时间: 2007-12-28 15:15
這玩意兒也極品嗎?
作者: soulskylove    时间: 2007-12-28 15:22
想喷人
作者: whyglinux    时间: 2007-12-29 20:52
>> 这个代码之所以崩溃不是因为不符合C99的语义,是因为linker和loader让它崩溃。

不是这样的。

在程序运行之前,作为静态对象的 "something" 字符串字面量和程序代码已经被 load 进来了。Access violation 是因为发生了向只读内存进行写操作,是程序运行时发生的。
作者: lgfang    时间: 2007-12-29 21:03
原帖由 fallshuang 于 2007-12-27 23:34 发表


不要误导别人,虽然这个代码执行会崩溃,但是编译是绝对可以通过的。 你要搞清楚什么是compiler , 什么是linker and loader 。这个代码之所以崩溃不是因为不符合C99的语义,是因为linker和loader让它崩溃。 ...


我是说LZ 先是定义“string“

     char* string = "something";
然后打印 ”test“
     printf("The result is: %s\n",test);

test 根本就没定义,怎么可能编译过。当然,这应该是LZ的笔误,真正的问题楼上已经指出了。

另外,我对compiler, linker, loader 的基本概念和作用还是明白的,倒是你需要搞清楚什么是编译期,什么是运行时。

[ 本帖最后由 lgfang 于 2007-12-29 22:37 编辑 ]
作者: chenzhanyiczy    时间: 2007-12-30 01:25
其实这是跟编译器有关的
作者: flw    时间: 2007-12-30 01:39
鄙视 7 楼,同情 15 楼。
作者: fallshuang    时间: 2007-12-30 02:30
原帖由 whyglinux 于 2007-12-29 20:52 发表
>> 这个代码之所以崩溃不是因为不符合C99的语义,是因为linker和loader让它崩溃。

不是这样的。

在程序运行之前,作为静态对象的 "something" 字符串字面量和程序代码已经被 load 进来了。Access violati ...



我哪句话说错了? 你讲清楚点,不要顾左右而言他。

我的意思再明确重说一遍:
static void foo(void)
{
    char *str = "aaaaaaa";

    printf("%s\n", str);   
    *str = 'b';
    printf("%s\n", str );
}

任何一个支持C99的编译器无论是编译还是链接这段代码,都不应该报error!!!!!


在程序运行之前,作为静态对象的 "something" 字符串字面量和程序代码已经被 load 进来了。

程序运行之前,只是存在于硬盘上的一个elf文件而已。你说load,是什么把字符串常量给load了? load到哪里了 ? 编译链接过程只有relocation, 知道relocation和load的区别么?

"somthing" 这个玩意儿只是被编译器放在elf的rodata段而已,在程序执行前我不知道你说的load操作是谁做的。我倒想听你说清楚。

而且,在执行类似这个函数的代码时,程序完全可以不崩溃!!!!!!! 是否崩溃完全取决于loader

你随便找个2.6 kernel的linux机器,insmod下面这个module, 你看看程序可会崩溃或者出错!!!!  知道内核为什么不报错么? 你能解释么?

#include <linux/module.h>

static void foo(void)
{
    char *str = "aaaaaaa";

    printk("%s\n", str);
    *str = 'b';
    printk("%s\n", str );
}


static int
test_init (void)
{
    printk("Enter test module\n");
    foo();
    return 0;
}

static void
test_exit (void)
{
    printk ("Goodbye, test\n");
}

module_init (test_init);
module_exit (test_exit);

MODULE_LICENSE ("Dual BSD/GPL");


这样修改字符串常量的地方,在内核里我见过不止一处了。知道如何编译内核,使这个模块insmod会出错么? 你可以好好研究下,一个小问题,细挖下去,内涵很多的。

[ 本帖最后由 fallshuang 于 2007-12-30 02:46 编辑 ]
作者: fallshuang    时间: 2007-12-30 02:45
原帖由 flw 于 2007-12-30 01:39 发表
鄙视 7 楼,同情 15 楼。


年轻人啊,  too young , too simple ,  too naive
作者: xfly_t    时间: 2008-01-02 10:38
原帖由 fallshuang 于 2007-12-30 02:30 发表



我哪句话说错了? 你讲清楚点,不要顾左右而言他。

我的意思再明确重说一遍:
static void foo(void)
{
    char *str = "aaaaaaa";

    printf("%s\n", str);   
    *str = 'b';
    print ...

你写的程序试过没?

Snap1.jpg (19.01 KB, 下载次数: 28)

Snap1.jpg

作者: fallshuang    时间: 2008-01-02 21:40
原帖由 xfly_t 于 2008-1-2 10:38 发表

你写的程序试过没?



请把我的话看仔细点, 我说编译链接都不应该有问题

我没说在linux-x86上运行不会有问题!

你知道编译链接和运行有什么区别么? 仔细想想


你执行  gcc a.c 没出任何错,就说明编译链接没有问题。

但是在linux-x86上运行这个应用程序就会崩溃, 知道为什么么?
我写了2个示范代码,一个是应用程序的,一个是内核模块的。 你试过我写的内核模块没有? 年轻人,请看话看仔细点
作者: beilian    时间: 2008-01-02 21:53
oops... what a man...
作者: fallshuang    时间: 2008-01-02 22:05
原帖由 beilian 于 2008-1-2 21:53 发表
oops... what a man...


These kids are all amateurish and I can't draw myself up......
作者: scutan    时间: 2008-01-02 22:08
标题: 回复 #18 fallshuang 的帖子
我真想骂你一句裘千仞.
你真的在2.6内核中试了你那个内核模块吗?
char *str = "aaaa";
*str = 'b';
这个语句能将str[0]的值变为'b'吗?
你试试再说话.

很简单的一个问题, 就是一个进程的地址空间的权限问题, 你非要弄个什么内核模块出来. 还骂这骂那的, 看不惯不同的意见.
作者: beilian    时间: 2008-01-02 22:22
原帖由 scutan 于 2008-1-2 22:08 发表
我真想骂你一句裘千仞.
你真的在2.6内核中试了你那个内核模块吗?
char *str = "aaaa";
*str = 'b';
这个语句能将str[0]的值变为'b'吗?
你试试再说话.

很简单的一个问题, 就是一个进程的地址空间的权 ...

agree...
From C99 - 6.4.5 String literals
6   It is unspecified whether these arrays are distinct provided their elements have the appropriate values.  If the program attempts to modify such an array, the behavior is undefined.

作者: fallshuang    时间: 2008-01-03 09:27
原帖由 scutan 于 2008-1-2 22:08 发表
我真想骂你一句裘千仞.
你真的在2.6内核中试了你那个内核模块吗?
char *str = "aaaa";
*str = 'b';
这个语句能将str[0]的值变为'b'吗?
你试试再说话.

很简单的一个问题, 就是一个进程的地址空间的权 ...



你看话请看仔细点,年轻人不要激动。我只说执行那个内核模块不会出错,我没说缺省的内核配置

char *str = "aaaa";
*str = 'b';

这个语句能将str[0]的值变为'b!

但是,如果你经常debug kernel的话,你就会很清楚在什么样的kernel上这2段代码会将str[0]的值变位'b' 。  这是debug kerenl的基本常识之一。

==================================
只要修改kernel的配置,使rodata可写
char *str = "aaaa";
*str = 'b';
这2句代码就可以在内核里修改字符串常量了。 知道该改内核哪个地方么?


我的意思就一个:
char *str = "aaaa";
*str = 'b';

这样的代码是符合ISO C99语法规范的。

这个问题其实很简单,就是rodata的权限问题,但是2楼的那位居然说这2句代码编译应该报错,这是不对的。 我主要是反对2楼的观点,但是很多人都不仔细看我说的话。
最好仔细看完别人说的话再发表意见。

btw: 我很想知道我那句话是骂人的脏话,谢谢赐教。

[ 本帖最后由 fallshuang 于 2008-1-3 09:49 编辑 ]
作者: lgfang    时间: 2008-01-03 09:55
这个问题其实很简单,就是rodata的权限问题,但是2楼的那位居然说这2句代码编译应该报错,这是不对的。 我主要是反对2楼的观点,但是很多人都不仔细看我说的话。
最好仔细看完别人说的话再发表意见。


也不知道你是没有“最好仔细看完别人说的话再发表意见。”,还是故意装作没看见。我已经解释过了我之所以说一楼的例子编译会报错是因为里面用到了未定义的变量 test。当然,这肯定是楼主没有把他的原始代码copy上来的,而是重新输入了一份,导致了这个笔误。因此我还特意加了个

至于说真正的错误,以及什么时候会报错相信大家包括楼主自己都知道吧,毕竟这也不是什么高深问题。犯不着牛皮哄哄的又是“规范”,又是“内核”的吧。

ps. LZ 千万别改一楼的帖子呀,要不然我就说不清了

[ 本帖最后由 lgfang 于 2008-1-3 09:57 编辑 ]
作者: fallshuang    时间: 2008-01-03 10:16
原帖由 lgfang 于 2008-1-3 09:55 发表


也不知道你是没有“最好仔细看完别人说的话再发表意见。”,还是故意装作没看见。我已经解释过了我之所以说一楼的例子编译会报错是因为里面用到了未定义的变量 test。当然,这肯定是楼主没有把他的原始代码 ...


回头看了下,没注意到你re的帖子,呵呵。  

主要去re 14和24楼的帖子了
作者: lgfang    时间: 2008-01-03 11:00
标题: 回复 #28 fallshuang 的帖子


不管怎么样,还是从你那学了一招
作者: zx_wing    时间: 2008-01-03 13:47
原帖由 fallshuang 于 2008-1-3 09:27 发表



你看话请看仔细点,年轻人不要激动。我只说执行那个内核模块不会出错,我没说在缺省的内核配置上

char *str = "aaaa";
*str = 'b';

这个语句能将str[0]的值变为'b!

但是,如果你经常debug ker ...

本来都没看这个帖子的,觉得这个问题被说了无数次,没有什么新意,但没想到还会引出争论。
看了兄台的发言和各位的回复,觉得大家说的都没错,但有几点疑惑想请教一下。
首先,这个程序符合语法,不会有编译错误,那肯定是对的,兄台和大家说的都没错。
其次,str被放在了.rodata段,也是对的,没有异议。

再其次, whyglinux说的也是正确的,确是运行时发生的错误。至于后来兄台回复的一大段话,我就补充一句,应用程序编译后就已确定加载的位置,不会发生relocation,所以兄台提到relocation和load的区别,感觉和主题扯远了。当然,我相信这个你是知道的,我只是补充。

最后,来看看兄台提到的.rodata段在内核中的问题。
兄台提到某种情况下编译那段代码,运行可以不出错。虽然我不知道如何编译,但理论上当然是可以做到的,只要让.rodata在页表里有写权限就可以了。请教了朋友,也看了kernel的config文件,我还是没找到打开的选项。后来朋友指点了我一下,我想你说的应该是这个东西:CONFIG_DEBUG_RODATA, 不知对否。

在x86平台下(其它平台下该函数不一定被实现了),init/main.c中init函数中,调用了mark_rodata_ro()函数,该函数的作用是将.rodata段在页表中的属性设置成只读,去掉它,.rodata段就可写了。它受CONFIG_DEBUG_RODATA宏控制。

我知道兄台是想论述,修改常量字符串不是在任何情况下都会出错,但不管怎样,这种用法除了在某些特定环境下(例如debug)外,都是不对的。无论内核还是应用程序。

[ 本帖最后由 zx_wing 于 2008-1-3 13:49 编辑 ]
作者: scutan    时间: 2008-01-03 13:54
标题: 回复 #30 zx_wing 的帖子
嗯. 明白了. 谢谢指教!
作者: gosapphire    时间: 2008-01-03 17:36
不管怎样,我感觉我学到些知识。
作者: whyglinux    时间: 2008-01-04 12:18
To fallshuang

>> 我哪句话说错了? 你讲清楚点,不要顾左右而言他。

很明显,我已经引用了你的“这个代码之所以崩溃不是因为不符合C99的语义,是因为linker和loader让它崩溃。”这句话,显然是这句话有错。而且稍微留意一下就会注意到:我反驳的是你这句话的后半部分,对于前半句并没有触及。

我也没有“顾左右而言他”,只是指出了你话中的错误,然后说明了原因。如果说给人“顾左右而言他”之感觉的,恰恰是我批驳你的这句话。

再详细地说一下你的这句话为什么不对:程序错误不是在link时产生的,linker不是一个运行时概念;loader算是一个运行时概念,但是程序的错误不是在load时发生的,而是在load之后的运行时发生的。至于程序产生运行错误的真正原因是操作系统的内存保护机制在起作用。

>> 是因为linker和loader让它崩溃。
>> 程序完全可以不崩溃!!!!!!! 是否崩溃完全取决于loader

我想用铅笔画一个图形,比如圆形和正方形。按照你上面的逻辑,那么最终画出的是圆形还是正方形完全取决于所用的铅笔。这个结论是不是感到有点荒谬?

其实我也可以用钢笔,甚至不用笔也可以画图。loader的作用很象这个例子中的铅笔。

>> "somthing" 这个玩意儿只是被编译器放在elf的rodata段而已

然后loader只不过按照设定装载数据和程序代码而已。因此,又不是程序转载过程出错,搂主程序出现的运行错误与loader何干?
作者: cdphp    时间: 2008-01-05 00:39
小弟在这里再次跪拜一次,偶是初学者,不好意思让大家卷入激烈争论,这次果然学到了不少。这里高手很多,很和谐
作者: lysde    时间: 2008-01-05 21:36
呵呵呵,都很牛,学习了!!1
作者: flw    时间: 2008-01-05 21:58
呵呵,不知道是什么来头,见人就呼一声“年轻人”,倒是挺和我胃口的,呵呵,我喜欢!有性格!
水平应该也是值得我注意一下下的。只是对 linker 和 loader 的理解有相当的偏差。

[ 本帖最后由 flw 于 2008-1-5 21:59 编辑 ]
作者: xfly_t    时间: 2008-01-07 11:09
原帖由 fallshuang 于 2008-1-2 21:40 发表



请把我的话看仔细点, 我说编译链接都不应该有问题

我没说在linux-x86上运行不会有问题!

你知道编译链接和运行有什么区别么? 仔细想想


你执行  gcc a.c 没出任何错,就说明编译链接没有问题。 ...


还是你强,一个简单的VS2005问题非给你从内核到应用层解释的这么清楚

不过还是建义还是要跳出程序员的思维教人会更好一点

[ 本帖最后由 xfly_t 于 2008-1-7 11:21 编辑 ]
作者: leemj_226    时间: 2008-01-08 17:22
几天涨见识了,知道什么是极品指针了,呵呵
作者: srdrm    时间: 2008-01-08 23:51
跟, fallshuang及其他朋友学了很多, 感谢楼主这贴, 虽对你们来讲是老调重弹, 但我确实学到了新的知识, 感谢你们.

我希望大家不要再在这个上面争了

给 fallshuang: 你说的也没错,大家说的也没错,因此,我觉得你没必要动上肝火,你对内核的深入了解我很佩服,提出有不同的地方确实很有意思,虽然有不同的,更有意思的意见,我想是不是可以更友好一点提出来.没有必要把讨论弄成争执吧.

我说多了, 我只是个菜鸟,我学到了很多,感谢大家,无意冒犯任何人,我刚来宝地, chinaunix 久已闻名,最近打算长居此地混迹此地,望大家多多照顾
作者: fangzheng    时间: 2008-01-18 12:26
标题: 回复 #4 cdphp 的帖子
does't it have a problem?
if strlen(tring1)>30,how does it work?

may be there is a agument fault...




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2