strcpy 面试题 高质量C++

 

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

    解说:
    1.strcpy的实现代码 

 

 

  错误的做法:
    [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 *返回,类型不符,编译报错。

 

 

 


char * __cdecl strcpy(char * dst, const char * src)
{
        char * cp = dst;

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

        return( dst );
}

from strcat 的代码

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

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


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

 

 

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

 

 

 

我来补充一点:

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

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


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



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

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

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

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

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

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

 

 


 

你看过c的标准吗?
6.3.2.3 Pointers
3 An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant.55) If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal
to a pointer to any object or function.

55) The macro NULL is defined in <stddef.h> (and other headers) as a null pointer constant; see 7.17.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值