zoukankan      html  css  js  c++  java
  • strcpy/strlen/strcat/strcmp面试总结

    《strcpy拷贝越界问题》

    一. 程序一

    
    

    #include<stdio.h>  
    #include<string.h>  
    void main()  
    {  
     char s[]="123456789";  
     char d[]="123";  
     strcpy(d,s);  
     printf("d=%s,
    s=%s",d,s);  
    } 
    执行结果:

    解释:

    首先要解释一下,char s[]="123456789"; char d[]="123"; 这样定义的数组和变量存放在栈内存中。
    栈内存是一个自顶向下分布的数据结构,那么越先定义的变量地址就越高,越后定义的地址就越低。
    s比d定义在前,那么s得到了高地址,而d得到了相对低的地址,那么内存中的存放形式就是
    d[] <- | -> s[]
    '1' '2' '3' '' | '1' '2' '3' '4' '5' '6' '7' '8' '9' ''
    字符串拷贝后:
    '1' '2' '3' '4 ' | '5' '6' '7' '8' '9' '' '7' '8' '9' ''
    中间的‘|’表示s[]的起始位置。
    所以此时输出的是d的值是 '1' '2' '3' '4' '5' '6' '7' '8' '9' '',s的值是 '5' '6' '7' '8' '9' ''

    二. 程序二

    
    

    #include<stdio.h>  
    #include<string.h>  
    void main()  
    {  
      char d[]="123";  
     
     char s[]="123456789";  
     strcpy(d,s);  
     printf("d=%s,
    s=%s",d,s);  
    }  
    运行结果:

    说明:

    虽然可以看到正确的输出结果d=123456789,s=123456789执,但是产生运行错误!

    这是因为字符串拷贝后,越过了目标字串的实际空间,访问到了不可预知的地址了。

    三. 字符串操作函数原型

    
    
    一、字符串拷贝strcpy

    函数strcpy的原型是char* strcpy(char* des , const char* src),des 和 src 所指内存区域不可以重叠且 des 必须有足够的空间来容纳 src 的字符串。

    #include <assert.h>
    #include <stdio.h>
    
    char* strcpy(char* des, const char* src)
    {
    	assert((des!=NULL) && (src!=NULL)); 
    	char *address = des;  
    	while((*des++ = *src++) != '')  
    		;  
    	return address;
    }

    要知道 strcpy 会拷贝’’,还有要注意:

    • 源指针所指的字符串内容是不能修改的,因此应该声明为 const 类型。

    • 要判断源指针和目的指针为空的情况,思维要严谨,这里使用assert(见文末)。

    • 要用一个临时变量保存目的串的首地址,最后返回这个首地址。

    • 函数返回 char* 的目的是为了支持链式表达式,即strcpy可以作为其他函数的实参。

    二、字符串长度strlen

    函数strlen的原型是size_t strlen(const char *s),其中 size_t 就是 unsigned int。

    #include <assert.h>
    #include <stdio.h>
    
    int strlen(const char* str)
    {
    	assert(str != NULL);
    	int len = 0;
    	while((*str++) != '')
    		++len;
    	return len;
    }

    strlen 与 sizeof 的区别:

    • sizeof是运算符,strlen是库函数。

    • sizeof可以用类型、变量做参数,而strlen只能用 char* 变量做参数,且必须以结尾。

    • sizeof是在编译的时候计算类型或变量所占内存的大小,而strlen的结果要在运行的时候才能计算出来,用来计算字符串的长度。

    • 数组做sizeof的参数不退化,传递给strlen就退化为指针了。

    三、字符串连接strcat

    函数strcat的原型是char* strcat(char* des, char* src),des 和 src 所指内存区域不可以重叠且 des 必须有足够的空间来容纳 src 的字符串。

    #include <assert.h>
    #include <stdio.h>
    
    char* strcat(char* des, const char* src)   // const表明为输入参数 
    {  
    	assert((des!=NULL) && (src!=NULL));
    	char* address = des;
    	while(*des != '')  // 移动到字符串末尾
    		++des;
    	while(*des++ = *src++)
    		;
    	return address;
    }
    四、字符串比较strcmp

    函数strcmp的原型是int strcmp(const char *s1,const char *s2)

    • 若s1==s2,返回零;
    • 若s1>s2,返回正数;
    • 若s1<s2,返回负数。

    即:两个字符串自左向右逐个字符相比(按ASCII值大小相比较),直到出现不同的字符或遇为止。

    #include <assert.h>
    #include <stdio.h>
    
    int strcmp(const char *s1,const char *s2)
    {
    	assert((s1!=NULL) && (s2!=NULL));
        while(*s1 == *s2)
        {
            if(*s1 == '')
                return 0;
             
            ++s1;
            ++s2;
        }
        return *s1 - *s2;
    }
    附:
    一.assert()断言

    assert是宏,而不是函数。它的原型定义在头文件 assert.h 中:

    1
    void assert( int expression );

    宏 assert 经常用于在函数开始处检验传入参数的合法性,可以将其看作是异常处理的一种高级形式。assert 的作用是先计算表达式expression,然后判断:

    • 如果表达式值为假,那么它先向stderr打印错误信息,然后通过调用 abort 来终止程序运行。

    • 如果表达式值为真,继续运行后面的程序。

    注意:assert只在 DEBUG 下生效,在调试结束后,可以通过在#include <assert.h>语句之前插入#define NDEBUG来禁用assert调用。

    二.函数面试时操作得分点总结

    //得2分 
    void strcpy( char *dest, char *src ) 
    { 
        while( (*dest++ = * src++) != '' ); 
    } 
      
    //得4分 
    void strcpy( char *dest, const char *src ) 
    { 
        //将源字符串加const,表明其为输入参数,加2分 
        while( (*dest++ = * src++) != '' ); 
    } 
      
    //得7分 
    void strcpy(char *dest, const char *src) 
    { 
        //对源地址和目的地址加非0断言,加3分 
        assert( (dest != NULL) && (src != NULL) ); 
        while( (*dest++ = * src++) != '' ); 
    } 
      
    //得9分 
    //为了实现链式操作,将目的地址返回,加2分! 
    char * strcpy( char *dest, const char *src ) 
    { 
        assert( (dest != NULL) && (src != NULL) ); 
        char *tmp = dest; 
        while( (*dest++ = * src++) != '' ); 
        return tmp; 
    } 
      
    //得10分,基本上所有的情况,都考虑到了 
    //如果有考虑到源目所指区域有重叠的情况,加1分! 
    char * strcpy( char *dest, const char *src ) 
    { 
        if(dest == src) { return dest; } 
        assert( (dest != NULL) && (src != NULL) ); 
        char *tmp = dest; 
        while( (*dest++ = * src++) != '' ); 
        return tmp; 
    }
  • 相关阅读:
    LA 2038 Strategic game(最小点覆盖,树形dp,二分匹配)
    UVA 10564 Paths through the Hourglass(背包)
    Codeforces Round #323 (Div. 2) D 582B Once Again...(快速幂)
    UVALive 3530 Martian Mining(贪心,dp)
    UVALive 4727 Jump(约瑟夫环,递推)
    UVALive 4731 Cellular Network(贪心,dp)
    UVA Mega Man's Mission(状压dp)
    Aizu 2456 Usoperanto (贪心)
    UVA 11404 Plalidromic Subsquence (回文子序列,LCS)
    Aizu 2304 Reverse Roads(无向流)
  • 原文地址:https://www.cnblogs.com/hdk1993/p/4932419.html
Copyright © 2011-2022 走看看