1. str*系列手写代码

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

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

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

2. mem*系列手写代码

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

void* Memchr(const void* src, int c, int len) {
assert(src != NULL && len >= );
const unsigned char ch = c;
const unsigned char* tmp = (const unsigned char*)src;
for (; len > ; --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 > ; --len, tmp1++, tmp2++) {
if (*tmp1 != *tmp2) {
return ((*tmp1 < *tmp2) ? - : );
}
}
return ;
} void* Memcpy(void* dst, const void* src, int len) {
assert(dst != NULL && src != NULL && len >= );
char* dstTmp = (char*)dst;
const char* srcTmp = (const char*)src;
for (; len > ; --len, ++dstTmp, ++srcTmp) {
*dstTmp = *srcTmp;
}
return dst;
} void* Memmove(void* dst, const void* src, int len) {
assert(dst != NULL && src != NULL && len >= );
char* dstTmp = (char*)dst;
const char* srcTmp = (const char*)src;
if (dstTmp > srcTmp && dstTmp < srcTmp + len) {
for (srcTmp += n, dstTmp += n; len > ; --len, --srcTmp, --dstTmp) {
*dstTmp = *srcTmp;
}
} else {
for (; len > ; --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 = ;
int num = ;
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 * + (*curr - '');
++curr;
}
int len = ;
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[]) {
*data = '\0';
} explicit Mystring(const char* str) : data_(new char[strlen(str) + ]) {
strcpy(data_, str);
} explicit Mystring(const Mystring& str) : data_(new char[str.size() + ]) {
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() == || num2.size() == ) return "";
reverse(num1.begin(), num1.end());
reverse(num2.begin(), num2.end()); vector<int> result(num1.size() + num2.size(), );
int count = ;
for (int i = ; i < num1.size(); ++i) {
for (int j = ; j < num2.size(); ++j) {
result.at(i+j) += (num1.at(i) - '') * (num2.at(j) - '');
}
}
for (int i = ; i < result.size(); ++i) {
int tmp = result.at(i) + count;
result.at(i) = tmp % ;
count = tmp / ;
} int zeroPos = ;
for (zeroPos = result.size() - ; zeroPos >= ; --zeroPos) {
if (result.at(zeroPos) != ) break;
}
result.erase(result.begin() + zeroPos + , result.end());
reverse(result.begin(), result.end()); string res(result.size(), '');
for (int i = ; i < result.size(); ++i) {
res.at(i) += result.at(i);
} if (res == "") {
return "";
} 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\n", p1, p2);
fprintf(stdout, "%p:%p\n", p3, p4);
return ;
}

程序结果显示: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 ;
}

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

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

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

以 strcpy strncpy为例

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

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

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

使用strncpy一般是如下方式:

strncpy(dst, src, strlen(dst));

我要好offer之 字符串相关大总结的更多相关文章

  1. 我要好offer之 系统基础大总结

    1. APUE Unix环境高级编程 (1) Unix基础知识: 内核->系统调用->shell和库函数->应用软件 (2) 文件I/O:read函数返回值.进程的文件描述符表.文件 ...

  2. 我要好offer之 概率题大总结

    1. 利用等概率Rand5生成等概率Rand3 Rand5生成等概率Rand3 这个题目可以扩展为:利用等概率RandM生成等概率RandN (M > N) 这里,我们首先明白一个简单的知识点: ...

  3. 我要好offer之 排序算法大总结

    1. 插入排序 (1) 直接插入排序 void StraightInsertionSort(std::vector<int>& num) { || num.size() == ) ...

  4. 【Todo】字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树

    另开一文分析字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树. 先来一个汇总, 算法: 本文中提到的字符串匹配算法有:KMP, BM, Horspool, Sunday, BF, ...

  5. Java数据结构和算法总结-字符串相关高频面试题算法

    前言:周末闲来无事,看了看字符串相关算法的讲解视频,收货颇丰,跟着视频讲解简单做了一下笔记,方便以后翻阅复习同时也很乐意分享给大家.什么字符串在算法中有多重要之类的大路边上的客套话就不多说了,直接上笔 ...

  6. PHP基础系列(一) PHP字符串相关的函数分类整理

    PHP提供了非常丰富的自带函数,有人说PHP是一个大的函数库,在某种程度上我是非常认同这种观点的,这个也是PHP非常容易上手的原因之一.在使用PHP编程的时候,需要实现某一功能的时候,如果说php自带 ...

  7. python字符串、字符串处理函数及字符串相关操作

    python字符串.字符串处理函数及字符串相关操作 字符串介绍 python字符串表示 Python除处理数字外还可以处理字符串,字符串用单撇号或双撇号包裹: >>> 'spam e ...

  8. java常用类详细介绍及总结:字符串相关类、日期时间API、比较器接口、System、Math、BigInteger与BigDecimal

    一.字符串相关的类 1.String及常用方法 1.1 String的特性 String:字符串,使用一对""引起来表示. String声明为final的,不可被继承 String ...

  9. 常用linux 命令 -字符串相关

    参考网络文章,个人工作总结 题记:一般对字符串的操作有以下几种:求长度,截取字符串,拼接字符串,找字符串中某个字符的索引 1 expr 命令 1.1 定义 man 手册 Print the value ...

随机推荐

  1. 第一单元OO总结

  2. python-下拉框处理

    在自动化中python对下拉框的处理网上相对实例比较少,其它前辈写的教程中对下拉也仅仅是相对与教程来说的,比如下面: m=driver.find_element_by_id("Shippin ...

  3. 使用的是html5的canvas将文字转换成图片

    当前功能的运用场景是:用户需要传文件给他人,在用户选择文件之后需要显示一个文件图标和所选文件的名称. 当前代码部分是摘自网上,但是已经忘记在什么地方获取的,如有侵权联系小弟后自当删除. 注意:必须在h ...

  4. 如何在vue项目中引用Iview

    iview 安装 npm install iview --save 引入iview import Vue from 'vue' import App from './App' import route ...

  5. NowCoder 9.9 模拟赛

    T1.中位数 二分答案x,原序列大于x的置为1,小于x的置为-1,判断是否存在长度大于m的区间和大于0(也就是大于x的数多于小于x的数),若有,则ans>=x,否则ans<x #inclu ...

  6. RabbitMQ学习(二):Java使用RabbitMQ要点知识

    转  https://blog.csdn.net/leixiaotao_java/article/details/78924863 1.maven依赖 <dependency> <g ...

  7. Ubuntu Server 18.04 LTS安装

    Please choose your preferred language. 选择您喜欢的语言 这里直接选择[English] Keyboard configuration 键盘配置 Please s ...

  8. jmeter操作mysql数据库

    1.导入jdbc的jar包,因为jmeter本身不能直接连接mysql,所以需要导入第三方的jar包,来连接mysql 2.创建数据库连接配置,mysql的url.端口号.账号.密码 在JDBC Co ...

  9. read design into DC memory

  10. DeepFaceLab: SSE,AVX, OpenCL 等版本说明!

    Deep Fake Lab早期只有两个版本,一个是专门正对NVIDIA显卡的CUDA9的版本,另一个是支持CPU的版本. 三月初该项目作者对tenserFlow,Cuda的版本进行了升级,预编译的软件 ...