zoukankan      html  css  js  c++  java
  • 我要好offer之 字符串相关大总结

    1. str*系列手写代码

    a. 一定要注意末尾''的处理,切记切记

    b. 一定要对输入做有效性判断,多用断言就是了

    int Strlen(const char* str) {
        assert(str != NULL);
        const char* tmp = str;
        while (*tmp != '') {
            ++tmp;
        }
        return tmp - str;
    }
    
    char* Strcpy(char* dst, const char* src) {
        assert(dst != NULL && src != NULL);
        char* tmp = dst;
        while (*src != '') {
            *tmp++ = *src++;
        }
        *tmp = '';
        return dst;
    }
    
    char* Strncpy(char* dst, const char* src, int len) {
        assert(dst != NULL && src != NULL && len >= 0);
        char* tmp = dst;
        for (; len > 0 && *src != ''; --len) {
            *tmp++ = *src++;
        }
        for (; len > 0; --len) {
            *tmp ++ = '';
        }
        return dst;
    }
    
    char* Strcat(char* dst, const char* src) {
        assert(dst != NULL && src != NULL);
        char* tmp = dst;
        while (*tmp != '') {
            ++tmp;
        }
        while (*src != '') {
            *tmp++ = *src++;
        }
        *tmp = '';
        return dst;
    }
    
    char* Strncat(char* dst, const char* src, int len) {
        assert(dst != NULL && src != NULL && n >= 0);
        char* tmp = dst;
        while (*tmp != '') {
            ++tmp;
        }
        for (; len > 0 && *src != ''; --len) {
            *tmp++ = *src++;
        }
        *tmp = '';
        return dst;
    }
    
    int Strcmp(const char* str1, const char* str2) {
        assert(str1 != NULL && str2 != NULL);
        for (; *str1 == *str2; ++str1, ++str2) {
            if (*str1 == '') {
                return 0;
            }
        }
        if (*(unsigned char*)str1 < *(unsigned char*)str2) {
            return -1;
        } else {
            return 1;
        }
    }
    
    int Strncmp(const char* str1, const char* str2, int len) {
        assert(str1 != NULL && str2 != NULL && len >= 0);
        for (; len > 0; ++str1, ++str2) {
            if (*str1 != *str2) {
                return ((*(unsigned char*)str1) < (*(unsigned char*)str2) ? -1 : 1);
            } else if (*str1 == '') {
                return 0;
            }
        }
        return 0;
    }
    
    char* Strchr(const char* str, int c) {
        assert(str != NULL);
        const char* tmp = str;
        const char ch = (const char)c;
        for (; *tmp != ch; ++tmp) {
            if (*tmp == '') {
                return NULL;
            }
        }
        return (char*)tmp;
    }
    
    char* Strstr(const char* str1, const char* str2) {
        assert(str1 != NULL && str2 != NULL);
        if (*str2 == '') return str1;
        const char* tmp1 = str1;
        const char* tmp2 = str2;
        int len1 = Strlen(str1);
        int len2 = Strlen(str2);
        int i = 0;
        for (tmp1 = str1; i <= len1 - len2 && *tmp1 != ''; ++tmp1, ++i) {
            if (*tmp1 != *tmp2)
                continue;
            const char* cur1 = tmp1;
            const char* cur2 = tmp2;
            
            while (*cur1 == *cur2) {
                ++cur1;
                ++cur2;
                if (*cur2 == '') {
                    return (char*)tmp1;
                }
            }
        }
        return NULL;
    }

    2. mem*系列手写代码

    一定要对输入做有效性判断,多用断言就是了

    void* Memchr(const void* src, int c, int len) {
        assert(src != NULL && len >= 0);
        const unsigned char ch = c;
        const unsigned char* tmp = (const unsigned char*)src;
        for (; len > 0; --len, ++tmp) {
            if (*tmp == ch) {
                return (void*)tmp;
            }
        }
        return NULL;
    }
    
    int Memcmp(const void* s1, const void* s2, int len) {
        assert(s1 != NULL && s2 != NULL);
        const unsigned char* tmp1 = (const unsigned char*)s1;
        const unsigned char* tmp2 = (const unsigned char*)s2;
        for (; len > 0; --len, tmp1++, tmp2++) {
            if (*tmp1 != *tmp2) {
                return ((*tmp1 < *tmp2) ? -1 : 1);
            }
        }
        return 0;
    }
    
    void* Memcpy(void* dst, const void* src, int len) {
        assert(dst != NULL && src != NULL && len >= 0);
        char* dstTmp = (char*)dst;
        const char* srcTmp = (const char*)src;
        for (; len > 0; --len, ++dstTmp, ++srcTmp) {
            *dstTmp = *srcTmp;
        }
        return dst;
    }
    
    void* Memmove(void* dst, const void* src, int len) {
        assert(dst != NULL && src != NULL && len >= 0);
        char* dstTmp = (char*)dst;
        const char* srcTmp = (const char*)src;
        if (dstTmp > srcTmp && dstTmp < srcTmp + len) {
            for (srcTmp += n, dstTmp += n; len > 0 ; --len, --srcTmp, --dstTmp) {
                *dstTmp = *srcTmp;
            }
        } else {
            for (; len > 0; --len, ++srcTmp, ++dstTmp) {
                *dstTmp = *srcTmp;
            }
        }
        return dst;
    }

    3. atoi函数

    atoi函数的实现

    class Solution {
    public:
        int atoi(const char *str) {
            assert(str != NULL);
            const char* curr = str;
            const int maxRange = 10;
            int tmp = 0;
            int num = 0;
            while(isspace(*curr)) {
                ++curr;
            }
            const char* start = nullptr;
            char sign;
            if (*curr == '-' || *curr == '+') {
                sign = *curr;
                ++curr;
                start = curr;
            }  else {
                start = curr;
            }
            
            while (isdigit(*curr)) {
                tmp = num;
                num = num * 10 + (*curr - '0');
                ++curr;
            }
            int len = 0;
            if (!isdigit(*curr)) {
                len = curr - start;
            }
            --curr;
            if (len > maxRange || num < num - *curr) {
                if (sign == '-') {
                    return INT_MIN;
                } else {
                    return INT_MAX;
                }
            }
            if (sign == '-')  num = -num;
            return num;
        }
    };

    4. std::string实现

    c++ std::string的简易实现

    class Mystring {
        public:
            Mystring() : data_(new char[1]) {
                *data = '';
            }
    
            explicit Mystring(const char* str) : data_(new char[strlen(str) + 1]) {
                strcpy(data_, str);
            }
    
            explicit Mystring(const Mystring& str) : data_(new char[str.size() + 1]) {
                strcpy(data_, str.c_str());
            }
    
            ~Mystring() {
                delete[] data_;
            }
            
            // 重载赋值,采用copy and swap手法,旧式写法
            Mystring& operator=(const Mystring& str) {
                Mystring tmp(str);
                swap(tmp);
                return *this;
            }
    
            // 重载赋值,采用copy and swap手法,新式写法
            Mystring& operator=(Mystring& str) {
                swap(str);
                return *this;
            }
    
            int size() const {
                return (int)strlen(data_);
            }
            const char* c_str() const {
                return data_;
            }
    
            void swap(Mystring& str) {
                std::swap(data_, str.data_);
            }
        private:
            char* data_;
    };

    a. Mystring类能够类似内置类型int一样,可以定义变量,可以复制,赋值

    b. Mystring可以用作函数参数以及返回值类型

    c. Mystring可以用作标准库容器的元素类型,即 vector/list/deque 的 value_type

    d. 利用RAII正确管理资源,只在构造函数里调用 new char[],只在析构函数里调用 delete[]

    e. 重载赋值运算符使用copy and swap 手法

    5. Str进行大数计算

    博文:大数相乘

    class Solution {
    public:
        string multiply(string num1, string num2) {
            if (num1.size() == 0 || num2.size() == 0) return "";
            reverse(num1.begin(), num1.end());
            reverse(num2.begin(), num2.end());
            
            vector<int> result(num1.size() + num2.size(), 0);
            int count = 0;
            for (int i = 0; i < num1.size(); ++i) {
                for (int j = 0; j < num2.size(); ++j) {
                    result.at(i+j) += (num1.at(i) - '0') * (num2.at(j) - '0');
                }
            }
            for (int i = 0; i < result.size(); ++i) {
                int tmp = result.at(i) + count;
                result.at(i) = tmp % 10;
                count = tmp / 10;
            }
            
            int zeroPos = 0;
            for (zeroPos = result.size() - 1; zeroPos >= 0; --zeroPos) {
                if (result.at(zeroPos) != 0) break;
            }
            result.erase(result.begin() + zeroPos + 1, result.end());
            reverse(result.begin(), result.end());
            
            string res(result.size(), '0');
            for (int i = 0; i < result.size(); ++i) {
                res.at(i) += result.at(i);
            }
            
            
            if (res == "") {
                return "0";
            } else {
                return res;
            }
        }
    };

    6. 为什么要禁止 char* p = "hello"这种写法?

    学习C语言的同学肯定见过 char* p = "hello"这种写法的,现在我想说的是:千万不要这样写

    int main() {
        char* p1 = "hello";
        char* p2 = "hello";
        char p3[] = "hello";
        char p4[] = "hello";
        fprintf(stdout, "%p:%p
    ", p1, p2);
        fprintf(stdout, "%p:%p
    ", p3, p4);
        return 0;
    }

    程序结果显示:p1等于p2,p3不等于p4

    p1等于p2:"hello"为字符串常量,位于全局的const区域段,第一,它是常量const,不能被修改  第二,它是全局的,即所有指向"hello"的指针的地址值全都是一样的

    p3不等于p4:p3和p4是字符数组,位于栈上,并且字符数组里的字符是可以被修改的

    小结一下:

        char* p1 = "hello";    
        char p3[] = "hello";

    p1:所指向内容不可修改(全局const),p1指针可以修改(可以更改指向)

    p3:所指元素可以修改(普通数组),p3不可修改(数组名作为指针时表示数组的首地址,肯定不能修改)

    回到  char* p = "hello" 

    前面我们解释了,p所指向的内容不可修改,即 p是一个指向const的指针

    const char* p1 = "hello";

    为什么要加上const呢?

    因为 char* p = "hello" 把 实际的 const char* 隐含转换为 char*,万恶的转型啊,且看一段代码:

        char* p1 = "this is wrong";
        char* p2 = "hello world";
        strcpy(p1, p2);

    编译通过了,运行呢? core dump,哈哈

    前面说过了,p1实际上 const char*, 你现在想通过p1来修改const,必须来一个core dump

    但是,如果我们这样写呢?

        const char* p1 = "this is wrong";
        const char* p2 = "hello world";
        strcpy(p1, p2);

    编译错误!!!  (注:我使用的是 g++ -Wall 编译)

    小结一下:

    char* p1 = "this is wrong"; // 将字符串常量的const特性隐式转型了,通过p1修改字符串时将产生 运行时错误
    const char* p1 = "this is right"; //加上const明确表示字符串的const特性,通过p1修改字符串时将产生 编译时错误

    既然上面用了 strcpy函数做例子,那就再说说strcpy的问题

    假如对上述所提的问题都理解了,那就是以下的代码:

    int main() {
        char dst[] = "this is right";
        const char* src = "hello world";
        strcpy(dst, src);
        fprintf(stdout, "%s", dst);
        return 0;
    }

    运行都挺好的,但是,我想说的是:不要使用strcpy这类函数

    我们知道C语言标准库有: strcpy、strcat、strcmp

    C标准库也有如下函数:strncpy、strncat、strncmp

    以 strcpy strncpy为例

    strcpy只有遇到src的''才结束复制,根本不管dst的空间是不是足以容纳src,非常容易造成缓冲区溢出,各种××攻击纷至沓来

    所以才有了 strcpy对应的“安全”版本--strncpy,strncpy原本想解决strcpy的不安全性,但是它的语义真是让人蛋疼

    strncpy仅仅复制 src的前n个字节,如果src的前n个字节不包括结束符'',问题就出来了,根本不复制src的结束符.....真让人无语

    使用strncpy一般是如下方式:

    strncpy(dst, src, strlen(dst));
  • 相关阅读:
    JAVA基础--JAVA API常见对象(包装类和正则)12
    编程中最没用的东西是源代码,最有用的东西是算法和数据结构(转载)
    angularjs calling order
    Service vs provider vs factory 转自:http://stackoverflow.com/questions/15666048/service-vs-provider-vs-factory
    构建自己的AngularJS,第一部分:作用域和digest 转摘:http://www.ituring.com.cn/article/39865
    angularjs 信息链接 转摘自:http://www.zhihu.com/question/27427447
    activity 生命周期 http://stackoverflow.com/questions/8515936/android-activity-life-cycle-what-are-all-these-methods-for
    google+ sign in and get the oauth token 转摘:https://gist.github.com/ianbarber/5170508
    谁动了我的内存:php内存泄露,系统缓存消耗? 转摘:http://blog.csdn.net/tao_627/article/details/9532497
    installing-sql-server-2012-error-prior-visual-studio-2010-instances-requiring 转摘
  • 原文地址:https://www.cnblogs.com/wwwjieo0/p/3908295.html
Copyright © 2011-2022 走看看