a. 必定要注意末尾'\0'的處理,切記切記html
b. 必定要對輸入作有效性判斷,多用斷言就是了c++
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 >= 0); char* tmp = dst; for (; len > 0 && *src != '\0'; --len) { *tmp++ = *src++; } for (; len > 0; --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 >= 0); char* tmp = dst; while (*tmp != '\0') { ++tmp; } for (; len > 0 && *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 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 == '\0') { 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 == '\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 = 0; 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; }
必定要對輸入作有效性判斷,多用斷言就是了git
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; }
atoi函數的實現shell
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; } };
class Mystring { public: Mystring() : data_(new char[1]) { *data = '\0'; } 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[]this
e. 重載賦值運算符使用copy and swap 手法spa
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; } } };
學習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 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的'\0'才結束複製,根本無論dst的空間是否是足以容納src,很是容易形成緩衝區溢出,各類××攻擊紛至沓來
因此纔有了 strcpy對應的「安全」版本--strncpy,strncpy本來想解決strcpy的不安全性,可是它的語義真是讓人蛋疼
strncpy僅僅複製 src的前n個字節,若是src的前n個字節不包括結束符'\0',問題就出來了,根本不復制src的結束符.....真讓人無語
使用strncpy通常是以下方式:
strncpy(dst, src, strlen(dst));