[精华] 出现频率最高的笔试题strcpy写法


http://www.chinaunix.net 作者:HopeCao  发表于:2008-10-30 15:49:14
【发表评论】 【查看原文】 【C/C++讨论区】【关闭】

题目: 
    已知strcpy函数的原型是: 
        char * strcpy(char * strDest,const char * strSrc); 
    1.不调用库函数,实现strcpy函数。 
    2.解释为什么要返回char *。

解说: 
    1.strcpy的实现代码

        char * strcpy(char * strDest,const char * strSrc){if ((strDest==NULL)||(strSrc==NULL)) //[1]throw "Invalid argument(s)"; //[2]char * strDestCopy=strDest;  //[3]while ((*strDest++=*strSrc++)!='\0'); //[4]return strDestCopy;}

错误的做法: 
    [1] 
    (A)不检查指针的有效性,说明答题者不注重代码的健壮性。 
    (B)检查指针的有效性时使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),说明答题者对C语言中类型的隐式转换没有深刻认识。在本例中char *转换为bool即是类型隐式转换,这种功能虽然灵活,但更多的是导致出错概率增大和维护成本升高。所以C++专门增加了bool、true、false三个关键字以提供更安全的条件表达式。 
    (C)检查指针的有效性时使用((strDest==0)||(strSrc==0)),说明答题者不知道使用常量的好处。直接使用字面常量(如本例中的0)会减少程序的可维护性。0虽然简单,但程序中可能出现很多处对指针的检查,万一出现笔误,编译器不能发现,生成的程序内含逻辑错误,很难排除。而使用NULL代替0,如果出现拼写错误,编译器就会检查出来。 
    [2] 
    (A)return new string("Invalid argument(s)");,说明答题者根本不知道返回值的用途,并且他对内存泄漏也没有警惕心。从函数中返回函数体内分配的内存是十分危险的做法,他把释放内存的义务抛给不知情的调用者,绝大多数情况下,调用者不会释放内存,这导致内存泄漏。 
    (B)return 0;,说明答题者没有掌握异常机制。调用者有可能忘记检查返回值,调用者还可能无法检查返回值(见后面的链式表达式)。妄想让返回值肩负返回正确值和异常值的双重功能,其结果往往是两种功能都失效。应该以抛出异常来代替返回值,这样可以减轻调用者的负担、使错误不会被忽略、增强程序的可维护性。 
    [3] 
    (A)忘记保存原始的strDest值,说明答题者逻辑思维不严密。 
    [4] 
    (A)循环写成while (*strDest++=*strSrc++);,同[1](B)。 
    (B)循环写成while (*strSrc!='\0') *strDest++=*strSrc++;,说明答题者对边界条件的检查不力。循环体结束后,strDest字符串的末尾没有正确地加上'\0'。

2.返回strDest的原始值使函数能够支持链式表达式,增加了函数的“附加值”。同样功能的函数,如果能合理地提高的可用性,自然就更加理想。 
    链式表达式的形式如: 
        int iLength=strlen(strcpy(strA,strB)); 
    又如: 
        char * strA=strcpy(new char[10],strB); 
    返回strSrc的原始值是错误的。其一,源字符串肯定是已知的,返回它没有意义。其二,不能支持形如第二例的表达式。其三,为了保护源字符串,形参用const限定strSrc所指的内容,把const char *作为char *返回,类型不符,编译报错。


 beggar 回复于:2003-03-02 21:30:01

高.


 seawolf1979 回复于:2003-03-03 00:00:35

高质量c_c++编程 里的,呼呼


 shanhan 回复于:2003-03-03 08:58:10

确实! 
好利害哦!


 lucky123 回复于:2003-03-04 20:37:24

不错,不错! 
佩服,佩服!


 Lanyd 回复于:2003-03-06 08:58:54

[color=red]好,收藏![/color]


 pcerma 回复于:2003-03-06 11:25:42

好,珍藏!

--------------- 
革命尚未成功,同志还需努力!


 liuyibing 回复于:2003-03-27 14:49:36

经典


 liuyibing 回复于:2003-03-27 14:53:44

经典


 likec 回复于:2003-08-12 00:19:09

是否仍然没有进行边界检查?如果strdest不足以容纳strsrc的话。 
算是up一下。


 Joran 回复于:2003-08-13 19:55:03

呵呵,CSDN上的

/* the emplementation in VC++ */char* strcpy(char* dest, const char* src){char* tmp = dest;while (*tmp++ = *src++);return dest;}/* the emplementation in Linux */char* strcpy(char* dest, const char* src){char* tmp = dest;while ((*tmp++ = *src++) != '\0');return dest;}

 ldzyg 回复于:2003-08-22 00:46:15

引用:原帖由 "Joran"][color=red][/color][size=18][/size 发表:
这样应该没什么错误吧?


 quanliking 回复于:2003-08-23 01:23:49

引用:原帖由 "Joran"]发表:
     
引用有错误,贴这种教课书式的代码,还是得严谨些。 
Linux 下的定义是这样的: 
/usr/lib/string.h

 string.h:char *strcpy (char *__restrict __dest, __const char *__restrict __src)__THROW;

/usr/src/linux-2.6.0-test3/lib/string.c

/*** strcpy - Copy a %NUL terminated string* @dest: Where to copy the string to* @src: Where to copy the string from*/char * strcpy(char * dest,const char *src){char *tmp = dest;while ((*dest++ = *src++) != '\0')/* nothing */;return tmp;}

在 string.h 中有 __THROW 这个宏,我们来查看一下在哪里定义的: 
$ grep __THROW /usr/include/*.h |grep define

...usr/include/malloc.h:#  define __THROW throw ()...

而且几乎每个预处理指令都由 __THROW 来处理,可以这样查看: 
$ grep -R __THROW /usr/include/* | grep "#"

linux 里的和高质量C/C++ 里的其实是一样的,除了异常处理哪里稍不同而已。


 hangne 回复于:2003-09-11 11:04:03

引用:原帖由 "HopeCao" 发表:
); 
    返回strSrc的原始值是错误的。其一,源字符串肯定是已知的,返回它没有意义。其二,不能支持形如第二例的表达式。其三,为了保护源字符串,形参用const限定strSrc所指的内容,把const char *作为char *返回?.........


 zhqer 回复于:2008-09-27 11:12:06

很好,很强大:)


 zhqer 回复于:2008-09-27 11:12:38

很好,很强大:)


 fera 回复于:2008-09-27 11:31:55

lz還忘了一種情況:destination和source的內存區有重疊怎么辦?

[本帖最后由 fera 于 2008-9-27 12:24 编辑]


 lgfang 回复于:2008-09-27 11:43:02

引用:    (C)检查指针的有效性时使用((strDest==0)||(strSrc==0)),说明答题者不知道使用常量的好处。直接使用字面常量(如本例中的 0)会减少程序的可维护性。0虽然简单,但程序中可能出现很多处对指针的检查,万一出现笔误,编译器不能发现,生成的程序内含逻辑错误,很难排除。而使用 NULL代替0,如果出现拼写错误,编译器就会检查出来。 

Bjarne Stroustrup特意强调在c++里应当用0,而非NULL。

5.8 Advice [ptr.advice] 
[3] Use0 rather than N U L L ; §5.1.1.


 guile 回复于:2008-09-27 11:56:57

[table=95%][tr][td][font=FixedSys][color=#000000][color=#FF9900]/** 
* strcpy - Copy a %NUL terminated string 
* @dest: Where to copy the string to 
* @src: Where to copy the string from 
*/[/color] 
[color=#0000FF]char[/color] [color=#0000CC]*[/color] [color=#FF0000]strcpy[/color][color=#0000CC]([/color][color=#0000FF]char[/color] [color=#0000CC]*[/color] dest[color=#0000CC],[/color][color=#0000FF]const[/color] [color=#0000FF]char[/color] [color=#0000CC]*[/color]src[color=#0000CC])[/color] 
[color=#0000CC]{[/color] 
        [color=#0000FF]char[/color] [color=#0000CC]*[/color]tmp [color=#0000CC]=[/color] dest[color=#0000CC];[/color]

        [color=#0000FF]while[/color] [color=#0000CC]([/color][color=#0000CC]([/color][color=#0000CC]*[/color]dest[color=#0000CC]+[/color][color=#0000CC]+[/color] [color=#0000CC]=[/color] [color=#0000CC]*[/color]src[color=#0000CC]+[/color][color=#0000CC]+[/color][color=#0000CC])[/color] [color=#0000CC]![/color][color=#0000CC]=[/color] [color=#FF00FF]'\0'[/color][color=#0000CC])[/color] 
                [color=#FF9900]/* nothing */[/color][color=#0000CC];[/color] 
        [color=#0000FF]return[/color] tmp[color=#0000CC];[/color] 
[color=#0000CC]}[/color][/color][/font][/td][/tr][/table]

这个贴子也能反映出,写搞质量C/C++的人在一知半解的情况下,自我感觉却非常良好。吐血的是还要出来误人子弟


 azl 回复于:2008-09-27 12:01:08

谢谢了啊!


 benjiam 回复于:2008-09-27 12:14:53

char * __cdecl strcpy(char * dst, const char * src) 

        char * cp = dst;

while( *cp++ = *src++ ) 
                ;               /* Copy src over dst */

return( dst ); 
}

from strcat 的代码

微软的代码。 的确是的 strcpy 根本没有考虑异常, 错误,失败的情况。

套用这里某位老兄原来的说法 就是让他直接崩溃,然后找到bug.

但是应该说 你如果面试敢写 上面的代码 就意味着你面试的结束。


 lipingtababa 回复于:2008-09-27 12:26:18

两字符串重叠的话,用你这个函数的程序就直接崩溃了


 guile 回复于:2008-09-27 12:32:31

引用:原帖由 benjiam 于 2008-9-27 12:14 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9353786&ptid=25356] 
from strcat 的代码

微软的代码。 的确是的 strcpy 根本没有考虑异常, 错误,失败的情况。

套用这里某位老兄原来的说法 就是让他直接崩溃,然后找到bug.

但是应该说 你如果面试敢写 上面的代码 就意味着你面试的结束。 

面试技术题的至少是公司里技术上有点地位的人,应该知道这样的正确写法的概率还是很大的。到底这是比较基础的东西,我觉得我接触过的技术好点的人大多明白,特别是搞过linux源代码的。

但是如果面试官真的就像那写一楼那个例子的人,那直接回家也罢。如果进了的话,跟别人还好,有可能就跟了这种人干活那就太危险了


 思一克 回复于:2008-09-27 13:12:26

不应该在strcpy这样的底层的广泛使用的FUNCTION中设置多的检查.

比如GCC 库的strcpy没有参数的检查.

为什么? 因为影响速度和效率. 不检查,让程序CRASH掉, 使用者自己找自己的错误.


 fera 回复于:2008-09-27 13:34:28

引用:原帖由 思一克 于 2008-9-27 13:12 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354105&ptid=25356] 
不应该在strcpy这样的底层的广泛使用的FUNCTION中设置多的检查.

比如GCC 库的strcpy没有参数的检查.

为什么? 因为影响速度和效率. 不检查,让程序CRASH掉, 使用者自己找自己的错误. 

你已經深受unix的毒害了:mrgreen:


 思一克 回复于:2008-09-27 13:39:09

是的.

程序如果有错误, 最好的结果应该是CRASH掉.

最坏的结果是程序继续运行.

因为,CRASH掉, 业务停止, 找错误,然后恢复工作. 
继续运行, 如果是银行业务, 你取存一千, 给你帐户1000万. 公安就抓你,判刑的. 有例子.

引用:原帖由 fera 于 2008-9-27 13:34 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354298&ptid=25356]

你已經深受unix的毒害了:mrgreen: 


 benjiam 回复于:2008-09-27 13:41:02

引用:原帖由 思一克 于 2008-9-27 13:12 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354105&ptid=25356] 
不应该在strcpy这样的底层的广泛使用的FUNCTION中设置多的检查.

比如GCC 库的strcpy没有参数的检查.

为什么? 因为影响速度和效率. 不检查,让程序CRASH掉, 使用者自己找自己的错误. 

又回到当时那个话题, 是让他崩溃掉还是让他返回错误。

如果外部调用的错误,内核可以用崩溃来让外部知道。那么内核的bug,它用什么方式让人知道呢?


 思一克 回复于:2008-09-27 13:42:49

内核的bug, 更应该立即DOWN掉, 同时尽所能写些信息出来.


 benjiam 回复于:2008-09-27 13:45:01

引用:原帖由 思一克 于 2008-9-27 13:39 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354333&ptid=25356] 
是的.

程序如果有错误, 最好的结果应该是CRASH掉.

最坏的结果是程序继续运行.

因为,CRASH掉, 业务停止, 找错误,然后恢复工作. 
继续运行, 如果是银行业务, 你取存一千, 给你帐户1000万. 公安就抓你,判 ... 

毫无意义的例子, 神州7号,运行过程中发现有问题,不是返回错误,启动应急方案。 而是让他crash 掉,让他直接掉下来。

返回错误 也是可以找到错误 恢复工作的。


 思一克 回复于:2008-09-27 13:49:19

可是大部分应用不是这种.

飞机上的系统还有备份的手工操作系统. 
以免错误的程序将飞机自己CRASH掉.

引用:原帖由 benjiam 于 2008-9-27 13:45 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354374&ptid=25356]

毫无意义的例子, 神州7号,运行过程中发现有问题,不是返回错误,启动应急方案。 而是让他crash 掉,让他直接掉下来。

返回错误 也是可以找到错误 恢复工作的。 


 benjiam 回复于:2008-09-27 13:55:30

引用:原帖由 思一克 于 2008-9-27 13:49 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354404&ptid=25356] 
可是大部分应用不是这种.

飞机上的系统还有备份的手工操作系统. 
以免错误的程序将飞机自己CRASH掉.

其实我也一直在反思这个问题。  发现参数错误了 到底是应该善意的返回一个错误码 还是应该不检查 crash 掉。

好像根本没有一个可循的案例。也没什么标准。因为在不同的层面上似乎有不同的处理方式。 内核, 外部,以及对项目要求上。

但是从软件的强壮性 上看,无疑应该是第一个。 随着cpu 更快,大家对稳定 规范要求更高, 我相信 所有的代码都会向第一种看齐。第一种最大的问题是 长尾情况, 就是调用者可能看到长长各种错误原因,也许调用者无法理解这些原因。


 system888net 回复于:2008-09-27 14:00:31

大家讨论的不错! 
我的做法是把"benjiam"和"思一克"两位大拿的思想都拿过来放在一起互补, 根据实际情况决定在什么情况下用"benjiam"方法和什么情况下用"思一克"方法. 使两中方法相容起来(实际上目的是一样的,都是为了更好的解决问题).


 思一克 回复于:2008-09-27 14:04:11

实际上,直接CRASH是一个追求的最高目标. 可惜由于硬件软件,体系结构等限制, 远达不到.

比如,strcpy(d, s), 如果d是一个被free(d)的地址, 系统往往也能工作. 但这里实际是一个巨大的恶劣的BUG. 无法CRASH的原因是硬件对内存的保护是按页的, 无法按字节.

有BUG就直接CRASH应用是一个目标. 不能完全达到.

我原来是修理电视的. 最好的故障是什么? 是子系统全坏掉. 最恶劣的故障是系统神经病似地故障---时断时续,时大时小,变化莫测.---- 对于软件说也一样.

[本帖最后由 思一克 于 2008-9-27 14:15 编辑]


 yuanchengjun 回复于:2008-09-27 14:17:02

strcpy是C标准库函数,怎么可以throw?

[本帖最后由 yuanchengjun 于 2008-9-27 14:19 编辑]


 system888net 回复于:2008-09-27 14:34:19

引用:原帖由 思一克 于 2008-9-27 14:04 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354518&ptid=25356] 
实际上,直接CRASH是一个追求的最高目标. 可惜由于硬件软件,体系结构等限制, 远达不到.

比如,strcpy(d, s), 如果d是一个被free(d)的地址, 系统往往也能工作. 但这里实际是一个巨大的恶劣的BUG. 无法CRASH的原 ... 

理解,容易复现的故障好找,不容易复现的鼓掌难找一些. 
实际上就是如何找出问题,计算机的应用到了各种领域,其中出现了各种方式的解决问题的思想和方式,都是基于实际情况中的一些特性提出的,并经过反复的应用改进,直道相对满足实际的应用. 
这写不同的思想的产生,都有他一定的道理在里面,甚至有些思想看上去会互相矛盾,但一落实到实际的场合中往往又是有效的.


 system888net 回复于:2008-09-27 14:39:11

引用:原帖由 benjiam 于 2008-9-27 13:55 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354450&ptid=25356]

其实我也一直在反思这个问题。  发现参数错误了 到底是应该善意的返回一个错误码 还是应该不检查 crash 掉。

好像根本没有一个可循的案例。也没什么标准。因为在不同的层面上似乎有不同的处理方式。 内核 ... 

debug阶段可以没有顾虑的crash, 但在一些crash会产生灾难性后果的场合(当然另外很多场合允许crash),就不能随意crash了.


 思一克 回复于:2008-09-27 15:00:30

对与楼主具体的strcpy(d, s),

检查s, d意义的确不大. 否则标准库也会做检查的.

因为有BUG的s, d数值千万个(比如free后的s, d等). d == NULL是特殊的一个, 而且还是可以CRASH程序的一个(非常容易DEBUG的).


 benjiam 回复于:2008-09-27 15:17:42

引用:原帖由 思一克 于 2008-9-27 15:00 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354876&ptid=25356] 
对与楼主具体的strcpy(d, s),

检查s, d意义的确不大. 否则标准库也会做检查的.

因为有BUG的s, d数值千万个(比如free后的s, d等). d == NULL是特殊的一个, 而且还是可以CRASH程序的一个(非常容易DEBUG的). 

关键是无法对参数是否正确作出判断。比如 d s 是否合法,显然 NULL 是非法的,而其他的 1, 2 算不算非法。 要做到完全合法检查代价非常大。

这里就有一个 因为无法对参数作出判断,我们是应该完全放弃呢? 还是能判断出多少算多少?


 思一克 回复于:2008-09-27 15:48:53

实际上, 编译系统和OS配合, 故意将地址NULL(0)留出来并设置为内存保护段. 当程序使用NULL中内容时候, 让程序CRASH掉. 目的就是为了DEBUG, 而且是非常容易地DEBUG.

所以可以说, strcpy和许多库FUNCTION看似没有检查参数指针是否是NULL, 其实是系统替你检查了. 好处是, 不影响速度. 同时有可以给用户报了错误. 你想呀, strcpy是多么底层的被大量调用的, 检查那东西浪费了效率. 程序中的废话在高层地方出现对效率影响很小, 在底层出现影响很大.

BTW, strcpy的库绝大多数不是LZ那样用C编的, 而是用汇编指令(许多CPU都用专门移动,COPY串的指令). C编的效率要低许多许多.


 思一克 回复于:2008-09-27 16:11:58

还有, 
不要将应用程序的CRASH(Segmentation fault后推出运行)理解成汽车撞树,飞机CRASH地面.

实际上, 这里发生的一切都在OS 的掌控之下,检查出问题了,让你的程序停止下来, 温柔地停下来. 
有点相当与你靠驾驶执照时候, 交通警察是考官, 你犯了错误后的情况.

而撞树相当与OS没有检查,错误的程序还在继续,直到系统不动了,或错误数据产生了.DOS原来许多地方就如此.


 naihe2010 回复于:2008-09-27 17:26:58

其实这就是一种吃饱了撑的的编程态度。

程序的正确,是靠逻辑来保证的。作为strcpy的调用者,你必须对你自己的行为负责,即保证传入参数的正确性。

想象一下,这个strcpy是在一个循环中的情况。如果每次都进行这些判断的话,要浪费多少CPU资源?

所以,既然strcpy是由用户来提供目的地址,当然由用户来保证目的地址有效、正确、不溢出。

出这个题目的公司,出题者有问题。


 ytl 回复于:2008-09-27 20:38:12

引用:原帖由 benjiam 于 2008-9-27 13:45 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354374&ptid=25356]

毫无意义的例子, 神州7号,运行过程中发现有问题,不是返回错误,启动应急方案。 而是让他crash 掉,让他直接掉下来。

返回错误 也是可以找到错误 恢复工作的。 

crash 掉正是为了通知外界立即启动应急方案。如果带着异常继续运行才会导致某个时刻悄无声息的掉下来


 ytl 回复于:2008-09-27 20:42:58

引用:原帖由 思一克 于 2008-9-27 16:11 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9355385&ptid=25356] 
还有, 
不要将应用程序的CRASH(Segmentation fault后推出运行)理解成汽车撞树,飞机CRASH地面.

实际上, 这里发生的一切都在OS 的掌控之下,检查出问题了,让你的程序停止下来, 温柔地停下来. 
有点相当与你靠驾 ... 

对!


 ytl 回复于:2008-09-27 20:48:15

引用:原帖由 benjiam 于 2008-9-27 13:55 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354450&ptid=25356]

其实我也一直在反思这个问题。  发现参数错误了 到底是应该善意的返回一个错误码 还是应该不检查 crash 掉。

其实2中方式都是合理的,问题的关键是接口的定义:接口约定为按第一种方式实现,那就返回错误码;接口约定为按第二种方式实现,那就由调用者保证参数的正确性。 
作为几乎最底层api的strcpy, 显然应该采取第二种方式


 system888net 回复于:2008-09-27 20:52:29

引用:原帖由 ytl 于 2008-9-27 20:48 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9356749&ptid=25356]

其实2中方式都是合理的,问题的关键是接口的定义:接口约定为按第一种方式实现,那就返回错误码;接口约定为按第二种方式实现,那就由调用者保证参数的正确性。 
作为几乎最底层api的strcpy, 显然应该采取第 ... 

你的这句话很有道理: 
引用: 
其实2中方式都是合理的,问题的关键是接口的定义:接口约定为按第一种方式实现,那就返回错误码;接口约定为按第二种方式实现,那就由调用者保证参数的正确性。


 system888net 回复于:2008-09-27 20:56:11

而且两者是可以结合起来的.也就是CRASH和返回值可以在不同情况下结合起来用. 取其长而避其短.


 ytl 回复于:2008-09-27 20:56:11

引用:原帖由 ytl 于 2008-9-27 20:48 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9356749&ptid=25356]

其实2中方式都是合理的,问题的关键是接口的定义:接口约定为按第一种方式实现,那就返回错误码;接口约定为按第二种方式实现,那就由调用者保证参数的正确性。 
作为几乎最底层api的strcpy, 显然应该采取第 ... 

可以这样简单的总结:对于一个系统的内部,应该尽可能由调用者保证参数的正确性;而对于系统与系统之间的接口,则实现者必须检查参数的有效性,应该力求作到对于任何输入,自己都不会crash。strcpy()是前者的一个例子。对于后一种情况,考虑系统调用:无论任何参数,os内核绝对没理由crash;再有web server的例子,无论浏览器向server发送了多么不合理的请求,web server都不应该crash


 system888net 回复于:2008-09-27 21:01:04

引用:原帖由 ytl 于 2008-9-27 20:56 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9356791&ptid=25356]

可以这样简单的总结:对于一个系统的内部,应该尽可能由调用者保证参数的正确性;而对于系统与系统之间的接口,则实现者必须检查参数的有效性,应该力求作到对于任何输入,自己都不会crash。strcpy()是前 ... 

good, 说的非常客观!


 hanke1985 回复于:2008-09-27 21:16:34

楼主真的很高!!!


 shan_ghost 回复于:2008-09-28 09:23:13

我来补充一点:

内存访问违例,在linux下会导致OS给你发一个sign 11

如果你捕获了这个sign 11并内置机制妥善处理了它(比如,可以使用setjmp/longjmp,或者干脆用C++提供的现成品try-catch),那么程序显然是不会崩溃的:这是正确且唯一正确的做法。

请用GDB打开所有因为内存访问违例导致crash的core,你会发现程序崩溃的原因不是因为内存访问违例,而是没有处理sign 11,从而导致处理流程跑进系统内置的core dump例程之中

sign 11并非唯一可用的手段——如果深入了解过操作系统的话。

如果感觉不能理解的话,请去查查资料,搞明白CPU执行某指令,发现它访问无效内存或除零错、溢出等等发生后,继而发生的一切细节。

我们一直强调程序有bug就让它crash,原因就在于我们有无数的后续手段,可用以保证代码 [color=red]总体[/color] 的稳定。

隐藏错误去严防crash,代码 局部 看来是稳定了——但 总体 质量必定会是一坨屎。

[本帖最后由 shan_ghost 于 2008-9-28 09:31 编辑]


 piaoyizu 回复于:2008-09-28 09:25:08

强, 顶起收藏..


 shan_ghost 回复于:2008-09-28 09:26:26

嗯,总结一句话:

事实上,从sign 11的意义上说,无论是gcc还是ms的实现,其实都是检查过空指针的。

既然操作系统已经替你做过了,在每一细部再做一次,有何必要? 
(当然,大局上,如果你的函数还将调用其他函数,那么确实是应该检查参数以避免错误流窜——但是,还是那句话:一旦检查到错误而你又无法准确界定根本原因,那么请动用sign 11)


 太平绅士 回复于:2008-09-28 09:35:24

C函数里写throw, 
对兼容性的敏感度不够,不予任用。。。:mrgreen:


 shan_ghost 回复于:2008-09-28 09:48:17

一个重要的系统,认真搞好sign(或其它OS提供的机制)的处理才是正道。

sign(或与之相当的其他东西)传来,就代表着一个未知的故障发生了。 
正因为“未知”,所以通用处理机制才特别好设计(甚至已经被封装为标准的try-catch机制了)

所以,这种机制一旦实现一次(即使很不熟悉相关方面,但只要人员资质足以理解sign机制,那么投入1人月也已经相当宽裕了),今后公司的所有项目都可以从中受益。

另外,一段代码被重用越多,它就越不可能存在未知的bug。

即使从短期来看,这样做的好处也是非常明显的。


 shan_ghost 回复于:2008-09-28 10:00:19

引用:原帖由 思一克 于 2008-9-27 14:04 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354518&ptid=25356] 
我原来是修理电视的 

握个手。我以前也是死修电视的:mrgreen:


 tonera 回复于:2008-09-28 10:58:13

楼主和思一克的观点是从两个不同角度来考虑的,都非常有道理。

我们从代码的健壮性考虑的话,楼主的分析相当精辟。但从程序效率和C语言的本质来看,思一克大大的观点也相当的有道理。对比python,在python中你不用担心什么内存管理的事情,,但C你却要自己照顾好你对内存的使用。在业务中,你使用哪种语言也取决于你需要高性能还是开发的高效率。


 思一克 回复于:2008-09-28 11:00:33

哈. 同行呀. 我还爬过几个星期电线杆呢. 比你强的多了.

这贴很好. 因为涉及到编程的一些思想. 会影响人的程序设计. 也让人明白了为什么那么成熟的库函数中竟然没有参数检查.

引用:原帖由 shan_ghost 于 2008-9-28 10:00 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9358135&ptid=25356]

握个手。我以前也是死修电视的:mrgreen: 


 system888net 回复于:2008-09-28 12:54:27

我的理解是LZ的例子里并没有排斥crash, 就算做了判断也会有crash的情况发生,比如对于一个非法内存(它!=NULL),那么程序也无法判断其合法性. 感觉两种方法是"你中有我,我中有你",当然针对实际的具体情况时会有所侧重。


 以泪洗面 回复于:2008-09-28 16:34:49

很好很强大。


 liu1061 回复于:2008-09-28 21:27:45

引用:原帖由 seawolf1979 于 2003-3-3 00:00 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=192655&ptid=25356] 
高质量c_c++编程 里的,呼呼 

这好像是MS出的面试题!


 nbkjbo 回复于:2008-09-28 21:50:59

过于上纲上线了吧 
不过作为招聘的题目,你的解答确实足够了,,,多谢


 思一克 回复于:2008-10-28 10:08:27

TO SEE


 newbie1984 回复于:2008-10-28 15:09:50

切, 出这个问题的动机不纯正, 吃饱没事干的, 呵呵~ 运行时的检查的越多往往是伴随着效率的运行的, 所以函数已经说明的清楚了, 需要调用者自己负责内存块检查的就不应该让函数来考虑, 什么都检查, 什么都放到一个函数内考虑的话, 那么我反过来说, 我就是要让它崩溃不检查呢? 代码要精简要高效才行. 加上我觉得1楼的说到的面试官的想法的话, 如果面试官正的是那样认为的话, 我觉得这个面试官不足以作为考察技术的人才, 太肤浅的想法了


 system888net 回复于:2008-10-28 18:29:19

1.  大家说的挺有道理. 
2.  建议不要用"非左即右"的方式来看问题!


 Tanacore 回复于:2008-10-29 09:01:45

又被翻出来了。。。。 
我前些天去一公司面试,也让写这个代码,我就按照楼主的写法写的,但面试官还说有问题,说,这样写,拷贝后,可能会导致覆盖,我郁闷了半天也没想出来。。。。

谁给我解释一下,是不是栈溢出了?


 benjiam 回复于:2008-10-29 11:12:45

引用:原帖由 Tanacore 于 2008-10-29 09:01 发表 [url=http://bbs3.chinaunix.net/redirect.php?goto=findpost&pid=9530014&ptid=25356] 
又被翻出来了。。。。 
我前些天去一公司面试,也让写这个代码,我就按照楼主的写法写的,但面试官还说有问题,说,这样写,拷贝后,可能会导致覆盖,我郁闷了半天也没想出来。。。。

谁给我解释一下,是不是 ... 

导致覆盖的说法是对的!

就是 2个 区域有相交的时候 就要考虑了。


 wblyfnj 回复于:2008-10-29 11:14:30

林博士把这个问题讲的很仔细,可是面试靠这些真不知道能起到多大的作用。


 xbzjackey 回复于:2008-10-29 16:05:21

对于strdest不足以容纳strsrc的问题就要看调用者的水平啦。


 hanliu2008 回复于:2008-10-30 15:06:26

k&r里边的经典例子, 
while(*to++ = *from++),怎么会有毛病呢,看不出来!!


 mz198424 回复于:2008-10-30 15:10:28

好利害


 chary8088 回复于:2008-10-30 15:49:14

不错


原文链接:http://bbs.chinaunix.net/viewthread.php?tid=25356
转载请注明作者名及原文出处

[精华] 出现频率最高的笔试题strcpy写法


http://www.chinaunix.net 作者:HopeCao  发表于:2008-10-30 15:49:14
【发表评论】 【查看原文】 【C/C++讨论区】【关闭】

题目: 
    已知strcpy函数的原型是: 
        char * strcpy(char * strDest,const char * strSrc); 
    1.不调用库函数,实现strcpy函数。 
    2.解释为什么要返回char *。

解说: 
    1.strcpy的实现代码

char * strcpy(char * strDest,const char * strSrc){if ((strDest==NULL)||(strSrc==NULL)) //[1]throw "Invalid argument(s)"; //[2]char * strDestCopy=strDest;  //[3]while ((*strDest++=*strSrc++)!='\0'); //[4]return strDestCopy;}

错误的做法: 
    [1] 
    (A)不检查指针的有效性,说明答题者不注重代码的健壮性。 
    (B)检查指针的有效性时使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),说明答题者对C语言中类型的隐式转换没有深刻认识。在本例中char *转换为bool即是类型隐式转换,这种功能虽然灵活,但更多的是导致出错概率增大和维护成本升高。所以C++专门增加了bool、true、false三个关键字以提供更安全的条件表达式。 
    (C)检查指针的有效性时使用((strDest==0)||(strSrc==0)),说明答题者不知道使用常量的好处。直接使用字面常量(如本例中的0)会减少程序的可维护性。0虽然简单,但程序中可能出现很多处对指针的检查,万一出现笔误,编译器不能发现,生成的程序内含逻辑错误,很难排除。而使用NULL代替0,如果出现拼写错误,编译器就会检查出来。 
    [2] 
    (A)return new string("Invalid argument(s)");,说明答题者根本不知道返回值的用途,并且他对内存泄漏也没有警惕心。从函数中返回函数体内分配的内存是十分危险的做法,他把释放内存的义务抛给不知情的调用者,绝大多数情况下,调用者不会释放内存,这导致内存泄漏。 
    (B)return 0;,说明答题者没有掌握异常机制。调用者有可能忘记检查返回值,调用者还可能无法检查返回值(见后面的链式表达式)。妄想让返回值肩负返回正确值和异常值的双重功能,其结果往往是两种功能都失效。应该以抛出异常来代替返回值,这样可以减轻调用者的负担、使错误不会被忽略、增强程序的可维护性。 
    [3] 
    (A)忘记保存原始的strDest值,说明答题者逻辑思维不严密。 
    [4] 
    (A)循环写成while (*strDest++=*strSrc++);,同[1](B)。 
    (B)循环写成while (*strSrc!='\0') *strDest++=*strSrc++;,说明答题者对边界条件的检查不力。循环体结束后,strDest字符串的末尾没有正确地加上'\0'。

2.返回strDest的原始值使函数能够支持链式表达式,增加了函数的“附加值”。同样功能的函数,如果能合理地提高的可用性,自然就更加理想。 
    链式表达式的形式如: 
        int iLength=strlen(strcpy(strA,strB)); 
    又如: 
        char * strA=strcpy(new char[10],strB); 
    返回strSrc的原始值是错误的。其一,源字符串肯定是已知的,返回它没有意义。其二,不能支持形如第二例的表达式。其三,为了保护源字符串,形参用const限定strSrc所指的内容,把const char *作为char *返回,类型不符,编译报错。


 beggar 回复于:2003-03-02 21:30:01

高.


 seawolf1979 回复于:2003-03-03 00:00:35

高质量c_c++编程 里的,呼呼


 shanhan 回复于:2003-03-03 08:58:10

确实! 
好利害哦!


 lucky123 回复于:2003-03-04 20:37:24

不错,不错! 
佩服,佩服!


 Lanyd 回复于:2003-03-06 08:58:54

[color=red]好,收藏![/color]


 pcerma 回复于:2003-03-06 11:25:42

好,珍藏!

--------------- 
革命尚未成功,同志还需努力!


 liuyibing 回复于:2003-03-27 14:49:36

经典


 liuyibing 回复于:2003-03-27 14:53:44

经典


 likec 回复于:2003-08-12 00:19:09

是否仍然没有进行边界检查?如果strdest不足以容纳strsrc的话。 
算是up一下。


 Joran 回复于:2003-08-13 19:55:03

呵呵,CSDN上的

/* the emplementation in VC++ */char* strcpy(char* dest, const char* src){char* tmp = dest;while (*tmp++ = *src++);return dest;}/* the emplementation in Linux */char* strcpy(char* dest, const char* src){char* tmp = dest;while ((*tmp++ = *src++) != '\0');return dest;}

 ldzyg 回复于:2003-08-22 00:46:15

引用:原帖由 "Joran"][color=red][/color][size=18][/size 发表:
这样应该没什么错误吧?


 quanliking 回复于:2003-08-23 01:23:49

引用:原帖由 "Joran"]发表:
     
引用有错误,贴这种教课书式的代码,还是得严谨些。 
Linux 下的定义是这样的: 
/usr/lib/string.h

string.h:char *strcpy (char *__restrict __dest, __const char *__restrict __src)__THROW;

/usr/src/linux-2.6.0-test3/lib/string.c

/*** strcpy - Copy a %NUL terminated string* @dest: Where to copy the string to* @src: Where to copy the string from*/char * strcpy(char * dest,const char *src){char *tmp = dest;while ((*dest++ = *src++) != '\0')/* nothing */;return tmp;}

在 string.h 中有 __THROW 这个宏,我们来查看一下在哪里定义的: 
$ grep __THROW /usr/include/*.h |grep define

...usr/include/malloc.h:#  define __THROW throw ()...

而且几乎每个预处理指令都由 __THROW 来处理,可以这样查看: 
$ grep -R __THROW /usr/include/* | grep "#"

linux 里的和高质量C/C++ 里的其实是一样的,除了异常处理哪里稍不同而已。


 hangne 回复于:2003-09-11 11:04:03

引用:原帖由 "HopeCao" 发表:
); 
    返回strSrc的原始值是错误的。其一,源字符串肯定是已知的,返回它没有意义。其二,不能支持形如第二例的表达式。其三,为了保护源字符串,形参用const限定strSrc所指的内容,把const char *作为char *返回?.........


 zhqer 回复于:2008-09-27 11:12:06

很好,很强大:)


 zhqer 回复于:2008-09-27 11:12:38

很好,很强大:)


 fera 回复于:2008-09-27 11:31:55

lz還忘了一種情況:destination和source的內存區有重疊怎么辦?

[本帖最后由 fera 于 2008-9-27 12:24 编辑]


 lgfang 回复于:2008-09-27 11:43:02

引用:    (C)检查指针的有效性时使用((strDest==0)||(strSrc==0)),说明答题者不知道使用常量的好处。直接使用字面常量(如本例中的 0)会减少程序的可维护性。0虽然简单,但程序中可能出现很多处对指针的检查,万一出现笔误,编译器不能发现,生成的程序内含逻辑错误,很难排除。而使用 NULL代替0,如果出现拼写错误,编译器就会检查出来。 

Bjarne Stroustrup特意强调在c++里应当用0,而非NULL。

5.8 Advice [ptr.advice] 
[3] Use0 rather than N U L L ; §5.1.1.


 guile 回复于:2008-09-27 11:56:57

[table=95%][tr][td][font=FixedSys][color=#000000][color=#FF9900]/** 
* strcpy - Copy a %NUL terminated string 
* @dest: Where to copy the string to 
* @src: Where to copy the string from 
*/[/color] 
[color=#0000FF]char[/color] [color=#0000CC]*[/color] [color=#FF0000]strcpy[/color][color=#0000CC]([/color][color=#0000FF]char[/color] [color=#0000CC]*[/color] dest[color=#0000CC],[/color][color=#0000FF]const[/color] [color=#0000FF]char[/color] [color=#0000CC]*[/color]src[color=#0000CC])[/color] 
[color=#0000CC]{[/color] 
        [color=#0000FF]char[/color] [color=#0000CC]*[/color]tmp [color=#0000CC]=[/color] dest[color=#0000CC];[/color]

        [color=#0000FF]while[/color] [color=#0000CC]([/color][color=#0000CC]([/color][color=#0000CC]*[/color]dest[color=#0000CC]+[/color][color=#0000CC]+[/color] [color=#0000CC]=[/color] [color=#0000CC]*[/color]src[color=#0000CC]+[/color][color=#0000CC]+[/color][color=#0000CC])[/color] [color=#0000CC]![/color][color=#0000CC]=[/color] [color=#FF00FF]'\0'[/color][color=#0000CC])[/color] 
                [color=#FF9900]/* nothing */[/color][color=#0000CC];[/color] 
        [color=#0000FF]return[/color] tmp[color=#0000CC];[/color] 
[color=#0000CC]}[/color][/color][/font][/td][/tr][/table]

这个贴子也能反映出,写搞质量C/C++的人在一知半解的情况下,自我感觉却非常良好。吐血的是还要出来误人子弟


 azl 回复于:2008-09-27 12:01:08

谢谢了啊!


 benjiam 回复于:2008-09-27 12:14:53

char * __cdecl strcpy(char * dst, const char * src) 

        char * cp = dst;

while( *cp++ = *src++ ) 
                ;               /* Copy src over dst */

return( dst ); 
}

from strcat 的代码

微软的代码。 的确是的 strcpy 根本没有考虑异常, 错误,失败的情况。

套用这里某位老兄原来的说法 就是让他直接崩溃,然后找到bug.

但是应该说 你如果面试敢写 上面的代码 就意味着你面试的结束。


 lipingtababa 回复于:2008-09-27 12:26:18

两字符串重叠的话,用你这个函数的程序就直接崩溃了


 guile 回复于:2008-09-27 12:32:31

引用:原帖由 benjiam 于 2008-9-27 12:14 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9353786&ptid=25356] 
from strcat 的代码

微软的代码。 的确是的 strcpy 根本没有考虑异常, 错误,失败的情况。

套用这里某位老兄原来的说法 就是让他直接崩溃,然后找到bug.

但是应该说 你如果面试敢写 上面的代码 就意味着你面试的结束。 

面试技术题的至少是公司里技术上有点地位的人,应该知道这样的正确写法的概率还是很大的。到底这是比较基础的东西,我觉得我接触过的技术好点的人大多明白,特别是搞过linux源代码的。

但是如果面试官真的就像那写一楼那个例子的人,那直接回家也罢。如果进了的话,跟别人还好,有可能就跟了这种人干活那就太危险了


 思一克 回复于:2008-09-27 13:12:26

不应该在strcpy这样的底层的广泛使用的FUNCTION中设置多的检查.

比如GCC 库的strcpy没有参数的检查.

为什么? 因为影响速度和效率. 不检查,让程序CRASH掉, 使用者自己找自己的错误.


 fera 回复于:2008-09-27 13:34:28

引用:原帖由 思一克 于 2008-9-27 13:12 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354105&ptid=25356] 
不应该在strcpy这样的底层的广泛使用的FUNCTION中设置多的检查.

比如GCC 库的strcpy没有参数的检查.

为什么? 因为影响速度和效率. 不检查,让程序CRASH掉, 使用者自己找自己的错误. 

你已經深受unix的毒害了:mrgreen:


 思一克 回复于:2008-09-27 13:39:09

是的.

程序如果有错误, 最好的结果应该是CRASH掉.

最坏的结果是程序继续运行.

因为,CRASH掉, 业务停止, 找错误,然后恢复工作. 
继续运行, 如果是银行业务, 你取存一千, 给你帐户1000万. 公安就抓你,判刑的. 有例子.

引用:原帖由 fera 于 2008-9-27 13:34 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354298&ptid=25356]

你已經深受unix的毒害了:mrgreen: 


 benjiam 回复于:2008-09-27 13:41:02

引用:原帖由 思一克 于 2008-9-27 13:12 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354105&ptid=25356] 
不应该在strcpy这样的底层的广泛使用的FUNCTION中设置多的检查.

比如GCC 库的strcpy没有参数的检查.

为什么? 因为影响速度和效率. 不检查,让程序CRASH掉, 使用者自己找自己的错误. 

又回到当时那个话题, 是让他崩溃掉还是让他返回错误。

如果外部调用的错误,内核可以用崩溃来让外部知道。那么内核的bug,它用什么方式让人知道呢?


 思一克 回复于:2008-09-27 13:42:49

内核的bug, 更应该立即DOWN掉, 同时尽所能写些信息出来.


 benjiam 回复于:2008-09-27 13:45:01

引用:原帖由 思一克 于 2008-9-27 13:39 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354333&ptid=25356] 
是的.

程序如果有错误, 最好的结果应该是CRASH掉.

最坏的结果是程序继续运行.

因为,CRASH掉, 业务停止, 找错误,然后恢复工作. 
继续运行, 如果是银行业务, 你取存一千, 给你帐户1000万. 公安就抓你,判 ... 

毫无意义的例子, 神州7号,运行过程中发现有问题,不是返回错误,启动应急方案。 而是让他crash 掉,让他直接掉下来。

返回错误 也是可以找到错误 恢复工作的。


 思一克 回复于:2008-09-27 13:49:19

可是大部分应用不是这种.

飞机上的系统还有备份的手工操作系统. 
以免错误的程序将飞机自己CRASH掉.

引用:原帖由 benjiam 于 2008-9-27 13:45 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354374&ptid=25356]

毫无意义的例子, 神州7号,运行过程中发现有问题,不是返回错误,启动应急方案。 而是让他crash 掉,让他直接掉下来。

返回错误 也是可以找到错误 恢复工作的。 


 benjiam 回复于:2008-09-27 13:55:30

引用:原帖由 思一克 于 2008-9-27 13:49 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354404&ptid=25356] 
可是大部分应用不是这种.

飞机上的系统还有备份的手工操作系统. 
以免错误的程序将飞机自己CRASH掉.

其实我也一直在反思这个问题。  发现参数错误了 到底是应该善意的返回一个错误码 还是应该不检查 crash 掉。

好像根本没有一个可循的案例。也没什么标准。因为在不同的层面上似乎有不同的处理方式。 内核, 外部,以及对项目要求上。

但是从软件的强壮性 上看,无疑应该是第一个。 随着cpu 更快,大家对稳定 规范要求更高, 我相信 所有的代码都会向第一种看齐。第一种最大的问题是 长尾情况, 就是调用者可能看到长长各种错误原因,也许调用者无法理解这些原因。


 system888net 回复于:2008-09-27 14:00:31

大家讨论的不错! 
我的做法是把"benjiam"和"思一克"两位大拿的思想都拿过来放在一起互补, 根据实际情况决定在什么情况下用"benjiam"方法和什么情况下用"思一克"方法. 使两中方法相容起来(实际上目的是一样的,都是为了更好的解决问题).


 思一克 回复于:2008-09-27 14:04:11

实际上,直接CRASH是一个追求的最高目标. 可惜由于硬件软件,体系结构等限制, 远达不到.

比如,strcpy(d, s), 如果d是一个被free(d)的地址, 系统往往也能工作. 但这里实际是一个巨大的恶劣的BUG. 无法CRASH的原因是硬件对内存的保护是按页的, 无法按字节.

有BUG就直接CRASH应用是一个目标. 不能完全达到.

我原来是修理电视的. 最好的故障是什么? 是子系统全坏掉. 最恶劣的故障是系统神经病似地故障---时断时续,时大时小,变化莫测.---- 对于软件说也一样.

[本帖最后由 思一克 于 2008-9-27 14:15 编辑]


 yuanchengjun 回复于:2008-09-27 14:17:02

strcpy是C标准库函数,怎么可以throw?

[本帖最后由 yuanchengjun 于 2008-9-27 14:19 编辑]


 system888net 回复于:2008-09-27 14:34:19

引用:原帖由 思一克 于 2008-9-27 14:04 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354518&ptid=25356] 
实际上,直接CRASH是一个追求的最高目标. 可惜由于硬件软件,体系结构等限制, 远达不到.

比如,strcpy(d, s), 如果d是一个被free(d)的地址, 系统往往也能工作. 但这里实际是一个巨大的恶劣的BUG. 无法CRASH的原 ... 

理解,容易复现的故障好找,不容易复现的鼓掌难找一些. 
实际上就是如何找出问题,计算机的应用到了各种领域,其中出现了各种方式的解决问题的思想和方式,都是基于实际情况中的一些特性提出的,并经过反复的应用改进,直道相对满足实际的应用. 
这写不同的思想的产生,都有他一定的道理在里面,甚至有些思想看上去会互相矛盾,但一落实到实际的场合中往往又是有效的.


 system888net 回复于:2008-09-27 14:39:11

引用:原帖由 benjiam 于 2008-9-27 13:55 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354450&ptid=25356]

其实我也一直在反思这个问题。  发现参数错误了 到底是应该善意的返回一个错误码 还是应该不检查 crash 掉。

好像根本没有一个可循的案例。也没什么标准。因为在不同的层面上似乎有不同的处理方式。 内核 ... 

debug阶段可以没有顾虑的crash, 但在一些crash会产生灾难性后果的场合(当然另外很多场合允许crash),就不能随意crash了.


 思一克 回复于:2008-09-27 15:00:30

对与楼主具体的strcpy(d, s),

检查s, d意义的确不大. 否则标准库也会做检查的.

因为有BUG的s, d数值千万个(比如free后的s, d等). d == NULL是特殊的一个, 而且还是可以CRASH程序的一个(非常容易DEBUG的).


 benjiam 回复于:2008-09-27 15:17:42

引用:原帖由 思一克 于 2008-9-27 15:00 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354876&ptid=25356] 
对与楼主具体的strcpy(d, s),

检查s, d意义的确不大. 否则标准库也会做检查的.

因为有BUG的s, d数值千万个(比如free后的s, d等). d == NULL是特殊的一个, 而且还是可以CRASH程序的一个(非常容易DEBUG的). 

关键是无法对参数是否正确作出判断。比如 d s 是否合法,显然 NULL 是非法的,而其他的 1, 2 算不算非法。 要做到完全合法检查代价非常大。

这里就有一个 因为无法对参数作出判断,我们是应该完全放弃呢? 还是能判断出多少算多少?


 思一克 回复于:2008-09-27 15:48:53

实际上, 编译系统和OS配合, 故意将地址NULL(0)留出来并设置为内存保护段. 当程序使用NULL中内容时候, 让程序CRASH掉. 目的就是为了DEBUG, 而且是非常容易地DEBUG.

所以可以说, strcpy和许多库FUNCTION看似没有检查参数指针是否是NULL, 其实是系统替你检查了. 好处是, 不影响速度. 同时有可以给用户报了错误. 你想呀, strcpy是多么底层的被大量调用的, 检查那东西浪费了效率. 程序中的废话在高层地方出现对效率影响很小, 在底层出现影响很大.

BTW, strcpy的库绝大多数不是LZ那样用C编的, 而是用汇编指令(许多CPU都用专门移动,COPY串的指令). C编的效率要低许多许多.


 思一克 回复于:2008-09-27 16:11:58

还有, 
不要将应用程序的CRASH(Segmentation fault后推出运行)理解成汽车撞树,飞机CRASH地面.

实际上, 这里发生的一切都在OS 的掌控之下,检查出问题了,让你的程序停止下来, 温柔地停下来. 
有点相当与你靠驾驶执照时候, 交通警察是考官, 你犯了错误后的情况.

而撞树相当与OS没有检查,错误的程序还在继续,直到系统不动了,或错误数据产生了.DOS原来许多地方就如此.


 naihe2010 回复于:2008-09-27 17:26:58

其实这就是一种吃饱了撑的的编程态度。

程序的正确,是靠逻辑来保证的。作为strcpy的调用者,你必须对你自己的行为负责,即保证传入参数的正确性。

想象一下,这个strcpy是在一个循环中的情况。如果每次都进行这些判断的话,要浪费多少CPU资源?

所以,既然strcpy是由用户来提供目的地址,当然由用户来保证目的地址有效、正确、不溢出。

出这个题目的公司,出题者有问题。


 ytl 回复于:2008-09-27 20:38:12

引用:原帖由 benjiam 于 2008-9-27 13:45 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354374&ptid=25356]

毫无意义的例子, 神州7号,运行过程中发现有问题,不是返回错误,启动应急方案。 而是让他crash 掉,让他直接掉下来。

返回错误 也是可以找到错误 恢复工作的。 

crash 掉正是为了通知外界立即启动应急方案。如果带着异常继续运行才会导致某个时刻悄无声息的掉下来


 ytl 回复于:2008-09-27 20:42:58

引用:原帖由 思一克 于 2008-9-27 16:11 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9355385&ptid=25356] 
还有, 
不要将应用程序的CRASH(Segmentation fault后推出运行)理解成汽车撞树,飞机CRASH地面.

实际上, 这里发生的一切都在OS 的掌控之下,检查出问题了,让你的程序停止下来, 温柔地停下来. 
有点相当与你靠驾 ... 

对!


 ytl 回复于:2008-09-27 20:48:15

引用:原帖由 benjiam 于 2008-9-27 13:55 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354450&ptid=25356]

其实我也一直在反思这个问题。  发现参数错误了 到底是应该善意的返回一个错误码 还是应该不检查 crash 掉。

其实2中方式都是合理的,问题的关键是接口的定义:接口约定为按第一种方式实现,那就返回错误码;接口约定为按第二种方式实现,那就由调用者保证参数的正确性。 
作为几乎最底层api的strcpy, 显然应该采取第二种方式


 system888net 回复于:2008-09-27 20:52:29

引用:原帖由 ytl 于 2008-9-27 20:48 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9356749&ptid=25356]

其实2中方式都是合理的,问题的关键是接口的定义:接口约定为按第一种方式实现,那就返回错误码;接口约定为按第二种方式实现,那就由调用者保证参数的正确性。 
作为几乎最底层api的strcpy, 显然应该采取第 ... 

你的这句话很有道理: 
引用: 
其实2中方式都是合理的,问题的关键是接口的定义:接口约定为按第一种方式实现,那就返回错误码;接口约定为按第二种方式实现,那就由调用者保证参数的正确性。


 system888net 回复于:2008-09-27 20:56:11

而且两者是可以结合起来的.也就是CRASH和返回值可以在不同情况下结合起来用. 取其长而避其短.


 ytl 回复于:2008-09-27 20:56:11

引用:原帖由 ytl 于 2008-9-27 20:48 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9356749&ptid=25356]

其实2中方式都是合理的,问题的关键是接口的定义:接口约定为按第一种方式实现,那就返回错误码;接口约定为按第二种方式实现,那就由调用者保证参数的正确性。 
作为几乎最底层api的strcpy, 显然应该采取第 ... 

可以这样简单的总结:对于一个系统的内部,应该尽可能由调用者保证参数的正确性;而对于系统与系统之间的接口,则实现者必须检查参数的有效性,应该力求作到对于任何输入,自己都不会crash。strcpy()是前者的一个例子。对于后一种情况,考虑系统调用:无论任何参数,os内核绝对没理由crash;再有web server的例子,无论浏览器向server发送了多么不合理的请求,web server都不应该crash


 system888net 回复于:2008-09-27 21:01:04

引用:原帖由 ytl 于 2008-9-27 20:56 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9356791&ptid=25356]

可以这样简单的总结:对于一个系统的内部,应该尽可能由调用者保证参数的正确性;而对于系统与系统之间的接口,则实现者必须检查参数的有效性,应该力求作到对于任何输入,自己都不会crash。strcpy()是前 ... 

good, 说的非常客观!


 hanke1985 回复于:2008-09-27 21:16:34

楼主真的很高!!!


 shan_ghost 回复于:2008-09-28 09:23:13

我来补充一点:

内存访问违例,在linux下会导致OS给你发一个sign 11

如果你捕获了这个sign 11并内置机制妥善处理了它(比如,可以使用setjmp/longjmp,或者干脆用C++提供的现成品try-catch),那么程序显然是不会崩溃的:这是正确且唯一正确的做法。

请用GDB打开所有因为内存访问违例导致crash的core,你会发现程序崩溃的原因不是因为内存访问违例,而是没有处理sign 11,从而导致处理流程跑进系统内置的core dump例程之中

sign 11并非唯一可用的手段——如果深入了解过操作系统的话。

如果感觉不能理解的话,请去查查资料,搞明白CPU执行某指令,发现它访问无效内存或除零错、溢出等等发生后,继而发生的一切细节。

我们一直强调程序有bug就让它crash,原因就在于我们有无数的后续手段,可用以保证代码 [color=red]总体[/color] 的稳定。

隐藏错误去严防crash,代码 局部 看来是稳定了——但 总体 质量必定会是一坨屎。

[本帖最后由 shan_ghost 于 2008-9-28 09:31 编辑]


 piaoyizu 回复于:2008-09-28 09:25:08

强, 顶起收藏..


 shan_ghost 回复于:2008-09-28 09:26:26

嗯,总结一句话:

事实上,从sign 11的意义上说,无论是gcc还是ms的实现,其实都是检查过空指针的。

既然操作系统已经替你做过了,在每一细部再做一次,有何必要? 
(当然,大局上,如果你的函数还将调用其他函数,那么确实是应该检查参数以避免错误流窜——但是,还是那句话:一旦检查到错误而你又无法准确界定根本原因,那么请动用sign 11)


 太平绅士 回复于:2008-09-28 09:35:24

C函数里写throw, 
对兼容性的敏感度不够,不予任用。。。:mrgreen:


 shan_ghost 回复于:2008-09-28 09:48:17

一个重要的系统,认真搞好sign(或其它OS提供的机制)的处理才是正道。

sign(或与之相当的其他东西)传来,就代表着一个未知的故障发生了。 
正因为“未知”,所以通用处理机制才特别好设计(甚至已经被封装为标准的try-catch机制了)

所以,这种机制一旦实现一次(即使很不熟悉相关方面,但只要人员资质足以理解sign机制,那么投入1人月也已经相当宽裕了),今后公司的所有项目都可以从中受益。

另外,一段代码被重用越多,它就越不可能存在未知的bug。

即使从短期来看,这样做的好处也是非常明显的。


 shan_ghost 回复于:2008-09-28 10:00:19

引用:原帖由 思一克 于 2008-9-27 14:04 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9354518&ptid=25356] 
我原来是修理电视的 

握个手。我以前也是死修电视的:mrgreen:


 tonera 回复于:2008-09-28 10:58:13

楼主和思一克的观点是从两个不同角度来考虑的,都非常有道理。

我们从代码的健壮性考虑的话,楼主的分析相当精辟。但从程序效率和C语言的本质来看,思一克大大的观点也相当的有道理。对比python,在python中你不用担心什么内存管理的事情,,但C你却要自己照顾好你对内存的使用。在业务中,你使用哪种语言也取决于你需要高性能还是开发的高效率。


 思一克 回复于:2008-09-28 11:00:33

哈. 同行呀. 我还爬过几个星期电线杆呢. 比你强的多了.

这贴很好. 因为涉及到编程的一些思想. 会影响人的程序设计. 也让人明白了为什么那么成熟的库函数中竟然没有参数检查.

引用:原帖由 shan_ghost 于 2008-9-28 10:00 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=9358135&ptid=25356]

握个手。我以前也是死修电视的:mrgreen: 


 system888net 回复于:2008-09-28 12:54:27

我的理解是LZ的例子里并没有排斥crash, 就算做了判断也会有crash的情况发生,比如对于一个非法内存(它!=NULL),那么程序也无法判断其合法性. 感觉两种方法是"你中有我,我中有你",当然针对实际的具体情况时会有所侧重。


 以泪洗面 回复于:2008-09-28 16:34:49

很好很强大。


 liu1061 回复于:2008-09-28 21:27:45

引用:原帖由 seawolf1979 于 2003-3-3 00:00 发表 [url=http://bbs.chinaunix.net/redirect.php?goto=findpost&pid=192655&ptid=25356] 
高质量c_c++编程 里的,呼呼 

这好像是MS出的面试题!


 nbkjbo 回复于:2008-09-28 21:50:59

过于上纲上线了吧 
不过作为招聘的题目,你的解答确实足够了,,,多谢


 思一克 回复于:2008-10-28 10:08:27

TO SEE


 newbie1984 回复于:2008-10-28 15:09:50

切, 出这个问题的动机不纯正, 吃饱没事干的, 呵呵~ 运行时的检查的越多往往是伴随着效率的运行的, 所以函数已经说明的清楚了, 需要调用者自己负责内存块检查的就不应该让函数来考虑, 什么都检查, 什么都放到一个函数内考虑的话, 那么我反过来说, 我就是要让它崩溃不检查呢? 代码要精简要高效才行. 加上我觉得1楼的说到的面试官的想法的话, 如果面试官正的是那样认为的话, 我觉得这个面试官不足以作为考察技术的人才, 太肤浅的想法了


 system888net 回复于:2008-10-28 18:29:19

1.  大家说的挺有道理. 
2.  建议不要用"非左即右"的方式来看问题!


 Tanacore 回复于:2008-10-29 09:01:45

又被翻出来了。。。。 
我前些天去一公司面试,也让写这个代码,我就按照楼主的写法写的,但面试官还说有问题,说,这样写,拷贝后,可能会导致覆盖,我郁闷了半天也没想出来。。。。

谁给我解释一下,是不是栈溢出了?


 benjiam 回复于:2008-10-29 11:12:45

引用:原帖由 Tanacore 于 2008-10-29 09:01 发表 [url=http://bbs3.chinaunix.net/redirect.php?goto=findpost&pid=9530014&ptid=25356] 
又被翻出来了。。。。 
我前些天去一公司面试,也让写这个代码,我就按照楼主的写法写的,但面试官还说有问题,说,这样写,拷贝后,可能会导致覆盖,我郁闷了半天也没想出来。。。。

谁给我解释一下,是不是 ... 

导致覆盖的说法是对的!

就是 2个 区域有相交的时候 就要考虑了。


 wblyfnj 回复于:2008-10-29 11:14:30

林博士把这个问题讲的很仔细,可是面试靠这些真不知道能起到多大的作用。


 xbzjackey 回复于:2008-10-29 16:05:21

对于strdest不足以容纳strsrc的问题就要看调用者的水平啦。


 hanliu2008 回复于:2008-10-30 15:06:26

k&r里边的经典例子, 
while(*to++ = *from++),怎么会有毛病呢,看不出来!!


 mz198424 回复于:2008-10-30 15:10:28

好利害


 chary8088 回复于:2008-10-30 15:49:14

不错


原文链接:http://bbs.chinaunix.net/viewthread.php?tid=25356
转载请注明作者名及原文出处

笔试题strcpy写法相关推荐

  1. 2015阿里校园招聘笔试题(8.29 测试开发工程师)

    [注]因为换了博客,所以这篇是从我之前的博客里copy过来的. 这是8.29日阿里2015校招笔试题,题目来源于网络,所以题目可能略有问题,但大部分题目是正常的.这些题是网上各套题总结出的题库,稍后会 ...

  2. C++笔试题大全----下

    一.输入一个n ,然后在屏幕上打印出NxN的矩阵! 例如,输入一个3,则 1 23 8 94 7 65 输入一个4,则 1 2 3 4 1213 14 5 1116 15 6 10 98 7 参考答案 ...

  3. C++笔试题汇总(6)

    1.引言本文的写作目的并不在于提供C/C++程序员求职面试指导,而旨在从技术上分 析面试题的内涵.文中的大多数面试题来自各大论坛,部分试题解答也参考了网友的意见. 许多面试题看似简单,却需要深厚的基本 ...

  4. 数字IC笔试题---千题解,量大管饱,图文并茂

    前言 出笔试题汇总,是为了总结秋招可能遇到的问题,做题不是目的,在做题的过程中发现自己的漏洞,巩固基础才是目的. 所有题目结果和解释由笔者给出,答案主观性较强,若有错误欢迎评论区指出,资料整理来自于& ...

  5. 转]C,C++经典问题,及面试笔试题

    转]C,C++经典问题,及面试笔试题 1       编程基础 1.1    基本概念 1.  的理解:const char*, char const*, char*const的区别问题几乎是C++面 ...

  6. 程序员必看 c++笔试题汇总

    ①链表反转 单向链表的反转是一个经常被问到的一个面试题,也是一个非常基础的问题.比如一个链表是这样的: 1->2->3->4->5 通过反转后成为5->4->3-& ...

  7. IC/FPGA校招笔试题分析(四)再看Moore状态机实现序列检测器

    参加了几次笔试,发现序列检测器是常考的内容,而之前认为序列检测器真的很简单,但是这次X疆的笔试题做完之后,我怀疑自己了. 画状态转移图的时候,我开始犹豫了,我怕我会没考虑全,甚至有点晕. 人家又问: ...

  8. 【前端】2015阿里前端实习生在线笔试题

    网上找的题,自己做了做. ... 2015阿里巴巴前端实习生在线笔试题 1. (单项选择)对于下列程序运行结果,符合预期的是 function f1() { console.time('time sp ...

  9. C语言笔试题--从CSDN转发

    C语言笔试题--从CSDN转发 关键字: 工作,C语言 4.static有什么用途?(请至少说明两种) 1.限制变量的作用域 2.设置变量的存储域 7.引用与指针有什么区别? 1) 引用必须被初始化, ...

最新文章

  1. 十厂商发起成立软件自律联盟
  2. HDLBits答案(21)_Verilog有限状态机(8)
  3. redisserver是什么问题_面试官老是问:为什么采用单线程的Redis也会如此之快?...
  4. jq使用教程03_JQData说明书概要
  5. 设计鲁棒性的方法:输入一个链表的头结点,逆序遍历打印该链表出来
  6. SQL2005下载版本之区别
  7. 京东员工p级别分几级_一文揭秘字节跳动、华为、京东的薪资职级
  8. kaggle初探--泰坦尼克号生存预测
  9. Android常用控件-01
  10. phpmywind 调取导航
  11. python按照号段生成手机号接收验证码_django 发送手机验证码的示例代码
  12. 豆芽的生长过程观察日记-绿豆发芽观察日记7天-2021年
  13. css3自定义字体实现毛体输出沁园春雪以及font属性
  14. 用Electron开发的Windows快捷启动工具:Dawn Launcher
  15. android 键盘 自动消失,Android 系统键盘怎么也不消失
  16. linux 修改用户密码 报错,linux中修改用户密码报错 passwd:Authentication token manipulation error...
  17. 《缠中说禅108课》83:笔-线段与线段-最小中枢结构的不同心理意义 1
  18. 自己的双系统XP32 + UBUNTU11 500G硬盘分区方案
  19. 超算中心CentOS环境
  20. JS Worker多线程

热门文章

  1. MySQL初始化配置文件my.ini
  2. Android CV系列 图片选择 严大的Ablum
  3. mysql prepare 注入_实战1:如何用 PREPARE 防止 SQL 注入
  4. 【Vulnhub】搭建Vulnhub靶机
  5. 源泉书签,助您管理海量收藏。今日更新【里程碑】支持书签导出功能了,从此不怕网络书签丢失了,随时随地备份您的书签。
  6. CHOLAN:一种模块化实体链接方法
  7. 【原创】电脑为什么会慢?C盘空间越来越小?怎么清理?请看本文
  8. OpenCV 基础实战一图像的读取和显示
  9. 易维帮助台:为企业搭建高效服务平台的“实力派”专家
  10. mcp918使用教程(MineCraft 1.8.8反编译)