笔试题strcpy写法



[精华] 出现频率最高的笔试题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
转载请注明作者名及原文出处
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在MATLAB中,NURBS(非均匀有理B样条)是一种强大的数学工具,用于表示和处理复杂的曲线和曲面。NURBS在计算机图形学、CAD(计算机辅助设计)、CAM(计算机辅助制造)等领域有着广泛的应用。下面将详细探讨MATLAB中NURBS的绘制方法以及相关知识点。 我们需要理解NURBS的基本概念。NURBS是B样条(B-Spline)的一种扩展,其特殊之处在于引入了权重因子,使得曲线和曲面可以在不均匀的参数空间中进行平滑插值。这种灵活性使得NURBS在处理非均匀数据时尤为有效。 在MATLAB中,可以使用`nurbs`函数创建NURBS对象,它接受控制点、权值、 knot向量等参数。控制点定义了NURBS曲线的基本形状,而knot向量决定了曲线的平滑度和分布。权值则影响曲线通过控制点的方式,大的权值会使曲线更靠近该点。 例如,我们可以使用以下代码创建一个简单的NURBS曲线: ```matlab % 定义控制点 controlPoints = [1 1; 2 2; 3 1; 4 2]; % 定义knot向量 knotVector = [0 0 0 1 1 1]; % 定义权值(默认为1,如果未指定) weights = ones(size(controlPoints,1),1); % 创建NURBS对象 nurbsObj = nurbs(controlPoints, weights, knotVector); ``` 然后,我们可以用`plot`函数来绘制NURBS曲线: ```matlab plot(nurbsObj); grid on; ``` `data_example.mat`可能包含了一个示例的NURBS数据集,其中可能包含了控制点坐标、权值和knot向量。我们可以通过加载这个数据文件来进一步研究NURBS的绘制: ```matlab load('data_example.mat'); % 加载数据 nurbsData = struct2cell(data_example); % 转换为cell数组 % 解析数据 controlPoints = nurbsData{1}; weights = nurbsData{2}; knotVector = nurbsData{3}; % 创建并绘制NURBS曲线 nurbsObj = nurbs(controlPoints, weights, knotVector); plot(nurbsObj); grid on; ``` MATLAB还提供了其他与NURBS相关的函数,如`evalnurbs`用于评估NURBS曲线上的点,`isoparm`用于生成NURBS曲面上的等参线,以及`isocurve`用于在NURBS曲面上提取特定参数值的曲线。这些工具对于分析和操作NURBS对象非常有用。 MATLAB中的NURBS功能允许用户方便地创建、编辑和可视化复杂的曲线和曲面。通过对控制点、knot向量和权值的调整,可以精确地控制NURBS的形状和行为,从而满足各种工程和设计需求。通过深入理解和熟练掌握这些工具,可以在MATLAB环境中实现高效的NURBS建模和分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值