我要好offer之 字符串相关大总结
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函数
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实现
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之 字符串相关大总结的更多相关文章
- 我要好offer之 系统基础大总结
1. APUE Unix环境高级编程 (1) Unix基础知识: 内核->系统调用->shell和库函数->应用软件 (2) 文件I/O:read函数返回值.进程的文件描述符表.文件 ...
- 我要好offer之 概率题大总结
1. 利用等概率Rand5生成等概率Rand3 Rand5生成等概率Rand3 这个题目可以扩展为:利用等概率RandM生成等概率RandN (M > N) 这里,我们首先明白一个简单的知识点: ...
- 我要好offer之 排序算法大总结
1. 插入排序 (1) 直接插入排序 void StraightInsertionSort(std::vector<int>& num) { || num.size() == ) ...
- 【Todo】字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树
另开一文分析字符串相关的各种算法,以及用到的各种数据结构,包括前缀树后缀树等各种树. 先来一个汇总, 算法: 本文中提到的字符串匹配算法有:KMP, BM, Horspool, Sunday, BF, ...
- Java数据结构和算法总结-字符串相关高频面试题算法
前言:周末闲来无事,看了看字符串相关算法的讲解视频,收货颇丰,跟着视频讲解简单做了一下笔记,方便以后翻阅复习同时也很乐意分享给大家.什么字符串在算法中有多重要之类的大路边上的客套话就不多说了,直接上笔 ...
- PHP基础系列(一) PHP字符串相关的函数分类整理
PHP提供了非常丰富的自带函数,有人说PHP是一个大的函数库,在某种程度上我是非常认同这种观点的,这个也是PHP非常容易上手的原因之一.在使用PHP编程的时候,需要实现某一功能的时候,如果说php自带 ...
- python字符串、字符串处理函数及字符串相关操作
python字符串.字符串处理函数及字符串相关操作 字符串介绍 python字符串表示 Python除处理数字外还可以处理字符串,字符串用单撇号或双撇号包裹: >>> 'spam e ...
- java常用类详细介绍及总结:字符串相关类、日期时间API、比较器接口、System、Math、BigInteger与BigDecimal
一.字符串相关的类 1.String及常用方法 1.1 String的特性 String:字符串,使用一对""引起来表示. String声明为final的,不可被继承 String ...
- 常用linux 命令 -字符串相关
参考网络文章,个人工作总结 题记:一般对字符串的操作有以下几种:求长度,截取字符串,拼接字符串,找字符串中某个字符的索引 1 expr 命令 1.1 定义 man 手册 Print the value ...
随机推荐
- overloading and overriding
What is the difference between method overloading and method overriding in Java? Differences between ...
- centos7中文显示为小方块~~啊啊啊 求大佬们解答
这个问题困扰我很久了,刚好前几天注册了博客园,就想问问大佬们是怎么解决中文显示小方块的? 我试了很多办法,包括但不限于修改i18n配置文件,locale.conf,添加中文字体库等等等... 但都没有 ...
- SSH程序框架之Spring与HIbernate整合
spring整合hibernate 有两种方式 1.注解方式 2.xml方式实现 Spring整合Hibernate有什么好处? 1.由IOC容器来管理Hibernate的SessionFactory ...
- oracle系統表、數據字典介紹與日常問題診斷
oracle系統表.數據字典介紹與日常問題診斷 數據字典是由唯讀的table和view組成的,產生於$oracle_home\rdbms\admin\catalog.sql.裡面儲存Oracle資料庫 ...
- Unity3d 中键值监听方法
unity3d的api中没有负责监听键值的方法,不过unity的input类是通过c#类获取各类监听事件,所以我们可以通过c#类监听,方法如下: void OnGUI() { Event e = Ev ...
- POP简单动画简单使用 (入门级别)
动画可以让APP“更友好”的与用户交互,苹果提供很多的好看的动画供开发者使用,不过简单的平移.旋转.缩放.......使用起来很简单,但是想要进行一些比较复杂的动画效果,使用起来就比较难以实现,俗话说 ...
- 为什么要在函数内部声明 var that = this 呢
看一个例子 $('#conten').click(function(){ //this是被点击的#conten var that =this; $('.conten').each(function() ...
- 【SAM】bzoj5084: hashit
做得心 力 憔 悴 Description 你有一个字符串S,一开始为空串,要求支持两种操作 在S后面加入字母C 删除S最后一个字母 问每次操作后S有多少个两两不同的连续子串 Input 一行一个字符 ...
- centos7无法切换startx
centos光盘安装后,显示命令行模式,通过startx无法进入图形界面? 解决方法:1.使用yum grouplist查看,根据显示的结果采用不同的界面格式,我用的是 yum groupinstal ...
- IAR生成bin,HEX文件
1.生成bin,hex文件 options->output converter->output format binary:.bin文件:intel extended:hex文件. 生成的 ...