diff --git "a/\346\211\213\346\222\225\344\273\243\347\240\201.md" "b/\346\211\213\346\222\225\344\273\243\347\240\201.md" index d18965e..92dff67 100644 --- "a/\346\211\213\346\222\225\344\273\243\347\240\201.md" +++ "b/\346\211\213\346\222\225\344\273\243\347\240\201.md" @@ -671,9 +671,8 @@ String & String::operator =(const String &other) // 得分点:输入参数为c ```c++ void * my_memcpy(void *dst, const void *src, size_t count){ - if(dst==nullptr||src==nullptr){ - return nullptr; - } + assert(dst != nullptr); + assert(src != nullptr); char* temp_dst=(char*)dst; char* temp_src=(char *)src; if(temp_dst>temp_src&&temp_dst(dst); + static_cast(src); + while(times8--){ + *dst = *src; + dst++; + src++; + } + static_cast(dst); + static_cast(src); + while(times1--){ + *dst = *src; + dst++; + src++; + } + return dst; +} +``` + +在地址按8字节对齐的时候,上述算法的效率比单字节 memcpy 实现高很多,但如果地址没有按8字节对齐,则其效率并不高,有时甚至还比普通 memcpy 还低。这可能是因为,虽然上述算法减少了 cpu 的指令数,但内存的速度比 cpu 慢得多,速度的瓶颈还是在内存。 + # 手写strcpy +```c++ +//把src所指向的字符串复制到dest,注意:dest定义的空间应该⽐src⼤。 +char* strcpy(char *dest, const char *src) { + char *ret = dest; + assert(dest!=NULL);//优化点1:检查输⼊参数 + assert(src!=NULL); + while(*src != '\0'){ + *(dest++)=*(src++); + } + *dest = '\0';//优化点2:⼿动地将最后的'\0'补上 + return ret; +} + +//考虑内存重叠的字符串拷⻉函数优化的情况 +char* strcpy(char *dest,char *src) { + char *ret = dest; + assert(dest!=NULL); + assert(src!=NULL); + memmove(dest,src,strlen(src)+1); + return ret; +} + +//对于以上代码,我们可以看出来,它是存在隐患的 +//当源字符串的长度超出目标字符串时,会导致把数据写入到我们无法控制的地址中去,存在很大的风险,所以就有了strncpy +char *strncpy(char* dest, const char* src, size_t n) +{ + assert( (dest != NULL) && (src != NULL)); + char *address = dest; + while ( n-- && (*src++ != '\0')){ + *dest++ = *src++; + } + *dest = '\0'; + return address; +} +``` + + + +# 手写strcat + +```c++ +//把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。 +char* strcat(char *dest,const char *src) { + //1. 将目的字符串的起始位置先保存,最后要返回它的头指针 + //2. 先找到dest的结束位置,再把src拷⻉到dest中,记得在最后要加上'\0' + char *ret = dest; + assert(dest!= nullptr); + assert(src!= nullptr); + while(*dest!='\0') + dest++; + while(*src!='\0') + *(dest++)=*(src++); + *dest='\0'; + return ret; +} +``` + + + +# 手写strcmp + +```c++ +//把 str1 所指向的字符串和 str2 所指向的字符串进⾏⽐较。 +//该函数返回值如下: +//如果返回值 < 0,则表示 str1 ⼩于 str2。 +//如果返回值 > 0,则表示 str1 ⼤于 str2。 +//如果返回值 = 0,则表示 str1 等于 str2。 +//'\0'的ascii码是0 +int strcmp(const char* str1, const char*str2){ + assert(str1 != nullptr && str2 != nullptr); + while (*str1 != '\0' && *str2 != '\0' && *str1 == *str2){ + str1++; + str2++; + } + if(*(unsigned char*)str1 < *(unsigned char*)str2){ + return -1; + } + else if (*(unsigned char*)str1 > *(unsigned char*)str2){ + return 1; + } + return 0; + //return *str1 - *str2; +} +``` + + + +# 手写strlen + +```c++ +int strlen(const char *str) { + assert(str != NULL); + int len = 0; + while( (*str++) != '\0'){ + len++; + } + return len; +} +``` + + + +# 手写strfind + +```c++ +//在字符串 str1中查找第⼀次出现字符串 str2 的位置,不包含终⽌符 '\0'。 +char* strstr(const char *str1, const char *str2) { + char* tmp_s = str1; + assert(str1 != nullptr); + assert(str2!= nullptr); + //若str2为空,则直接返回空 + if(!str2){ + return nullptr; + } + //若不为空,则进⾏查询 + while(*tmp_s != '\0') { + char* s1 = tmp_s; + char* s2 = str2; + while(*s1 != '\0' && *s2!='\0' && *s1 == *s2){ + s1++; + s2++; + } + //若s2先结束 + if(*s2 == '\0'){ + return str2; + } + //若s1先结束⽽s2还没结束,则返回空 + if(*s2 != '\0' && *s1== '\0'){ + return nullptr; + } + tmp_s++; + } + return nullptr; +} +``` + @@ -864,6 +1028,8 @@ else // (*p == 0x12) printf("大端模式"); ``` + + # C++ 多线程打印奇偶数 [参考链接](https://blog.csdn.net/Deep___Learning/article/details/106013837) @@ -1183,3 +1349,422 @@ string convert_to_hex(int number) { +# 二分查找 + +## 左闭右闭[left, right] + +```c++ +int binary_search(vector& nums, int target){ + int left = 0; + int right = nums.size() - 1; + while(left <= right){ + int middle = left + (right - left) /2; + if(nums[middle] > target){ + right = middle -1; + } + else if(nums[middle] < target){ + left = middle + 1; + }else{ + return middle; + } + } + return -1; +} +``` + +## 左闭右开[left, right) + +```c++ +int search(vector& nums, int target) { + int left = 0; + int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right) + while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 < + int middle = left + ((right - left) >> 1); + if (nums[middle] > target) { + right = middle; // target 在左区间,在[left, middle)中 + } else if (nums[middle] < target) { + left = middle + 1; // target 在右区间,在[middle + 1, right)中 + } else { // nums[middle] == target + return middle; // 数组中找到目标值,直接返回下标 + } + } + // 未找到目标值 + return -1; + } +``` + +## 最左侧边界 + +```c++ +//左闭右开 +int left_bound(vector& nums, int target) { + if (nums.length == 0) return -1; + int left = 0; + int right = nums.length(); // 注意 + + while (left < right) { // 注意 + int mid = left + (right -left) / 2; + if (nums[mid] < target) { + left = mid + 1; //没问题 + } else{ + right = mid; //这个有点意思,找到target不返回而是继续缩小边界,不断锁定左侧,最终会出现left=right + } + } + //检查出界情况 + if (left >= nums.length || nums[left] != target) + return -1; + return left; +} + +//左闭右闭 +int left_bound(int[] nums, int target) { + int left = 0, right = nums.length - 1; + // 搜索区间为 [left, right] + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] < target) { + // 搜索区间变为 [mid+1, right] + left = mid + 1; + } else if (nums[mid] > target) { + // 搜索区间变为 [left, mid-1] + right = mid - 1; + } else if (nums[mid] == target) { + // 收缩右侧边界 + right = mid - 1; + } + } + // 检查出界情况 + if (left >= nums.length || nums[left] != target) + return -1; + return left; +} + +``` + +## 右侧边界 + +```c++ +//左闭右开 +int right_bound(vector& nums, int target) { + if (nums.length == 0) return -1; + int left = 0; + int right = nums.length(); // 注意 + + while (left < right) { // 注意 + int mid = left + (right -left) / 2; + if (nums[mid] < target) { + left = mid ; //没问题 + } else{ + right = mid - 1; + } + } + //检查出界情况 + if (right >= nums.length || nums[left] != target) + return -1; + return right; +} + +``` + + + +# C语言实现多态 + +```c++ +//虚函数表结构 +struct base_vtbl +{ + void(*dance)(void *); + void(*jump)(void *); +}; + +//基类 +struct base +{ + /*virtual table*/ + struct base_vtbl *vptr; +}; +//基类的构造函数 +struct base * new_base() +{ + struct base *temp = (struct base *)malloc(sizeof(struct base)); + //基类虚表结构中函数指针具体关联的函数名 + temp->vptr->dance = base_dance; + temp->vptr->jump = base_jump; + return temp; +} +//基类的成员函数 +void base_dance() +{ + printf("base dance\n"); +} +void base_jump() +{ + printf("base jump\n"); +} + + +//派生类 +struct derived1 +{ + struct base super; + int high; +}; +//派生类的构造函数 +struct derived1 * new_derived1(int h) +{ + struct derived1 * temp= (struct derived1 *)malloc(sizeof(struct derived1)); + //派生类虚表结构中函数指针具体关联的函数名 + temp->super->vptr->dance = derived1_table; + temp->super->vptr->jump = derived1_jump; + temp->high = h; + return temp; +} +//派生类对象成员函数 +void derived1_dance() +{ + printf("derived1 dance\n"); +} +void derived1_jump(void * this) +{ + struct derived1* temp = (struct derived1 *)this; + printf("derived1 jump:%d\n", temp->high); +} + +/*******实际调用***********/ + struct base * bas = new_base(); +//这里调用的是基类的成员函数 +bas->vptr->dance(); +bas->vptr->jump(); + +struct derived1 * child = new_derived1(100); +//基类指针指向派生类 +bas = (struct base *)child; + +//这里调用的其实是派生类的成员函数 +bas->vptr->dance(); +bas->vptr->jump((void *)bas); +``` + + + +# 不用sizeof如何获得int所占的字节数? + +```c++ +int main(){ + int i = 1; + int count = 0; + while(i) + i = i <<1;//一个循环,每次左移一位 + count++; + cout << count/8 << endl;//因为一个字节8位 + return 0; +} +``` + +# C++通过递归实现字符串反转 + +```c++ +#include +#include +using namespace std; +string f(string str ){ + int len=str.length() ; + if (len<=1) + return str; + return f(str.substr( 1)) + str.substr(0,1); //substr(0,1)表示从下标0开始取一个字符形成的串 +} +``` + + + +``` +#include +#include +#include +#include +#include + +std::string trim(const std::string& str) +{ + std::string::size_type pos = str.find_first_not_of(' '); //在字符串中查找第一个与str中的字符都不匹配的字符,返回它的位置 + if (pos == std::string::npos) { + return str; + } + + std::string::size_type pos2 = str.find_last_not_of(' '); + if (pos2 != std::string::npos) { + return str.substr(pos, pos2 - pos + 1); + } + + return str.substr(pos); +} + +void split(const std::string& str, std::vector* ret_, std::string sep = ",") +{ + if (str.empty()) { + return; + } + + std::string tmp; + std::string::size_type pos_begin = str.find_first_not_of(sep); + std::string::size_type comma_pos = 0; + while (pos_begin != std::string::npos) { + comma_pos = str.find(sep, pos_begin); + if (comma_pos != std::string::npos) { + tmp = str.substr(pos_begin, comma_pos - pos_begin); + pos_begin = comma_pos + sep.length(); + } else { + tmp = str.substr(pos_begin); + pos_begin = comma_pos; + } + + if (!tmp.empty()) { + ret_->push_back(tmp); + tmp.clear(); + } + } +} + +bool DateVerify(int year, int month, int day) +{ + // 这里限制了年份需要在2013-2020,可去掉 + if(year < 2013 || year > 2020 || month < 1 || month > 12 || day < 1 || day > 31) { + return false; + } + + switch (month) { + case 4: + case 6: + case 9: + case 11: + if (day > 30) { // 4.6.9.11月天数不能大于30 + return false; + } + break; + case 2: + { + bool bLeapYear = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); + if ((bLeapYear && day > 29) || (!bLeapYear && day > 28)) { + // 闰年2月不能大于29天;平年2月不能大于28天 + return false; + } + } + break; + default: + break; + } + + return true; +} + +// 校验yyyy/mm/dd +bool CheckDateValid(const std::string& strDate) +{ + std::string strPureDate = trim(strDate); + if(strPureDate.length() < 8 || strPureDate.length() > 10) { + return false; + } + + std::vector vecFields; + split(strPureDate, &vecFields, "/"); + + if(vecFields.size() != 3) { + return false; + } + + // TODO:这里最好再下判断字符转换是否成功 + int nYear = atoi(vecFields[0].c_str()); + int nMonth = atoi(vecFields[1].c_str()); + int nDay = atoi(vecFields[2].c_str()); + + return DateVerify(nYear, nMonth, nDay); +} + +// 校验HH:MM:SS +bool CheckTimeValid(const std::string& strTime) +{ + std::string strPureTime = trim(strTime); + if(strPureTime.length() < 5 || strPureTime.length() > 8) { + return false; + } + + std::vector vecFields; + split(strPureTime, &vecFields, ":"); + + if(vecFields.size() != 3) { + return false; + } + + int nHour = atoi(vecFields[0].c_str()); + int nMinute = atoi(vecFields[1].c_str()); + int nSecond = atoi(vecFields[2].c_str()); + + bool bValid = (nHour >= 0 && nHour <= 23); + bValid = bValid && (nMinute >=0 && nMinute <= 59); + bValid = bValid && (nSecond >= 0 && nSecond <= 59); + + return bValid; +} + +// 日期格式为: yyyy/mm/dd || yyyy/mm/dd HH:MM:SS +bool CheckDateTimeValid(const std::string& strDateTime) +{ + std::string strPureDateTime = trim(strDateTime); + + std::vector vecFields; + split(strPureDateTime, &vecFields, " "); + + if(vecFields.size() != 1 && vecFields.size() != 2) { + return false; + } + + // 仅有日期 + if(vecFields.size() == 1) { + return CheckDateValid(vecFields[0]); + } + + return CheckDateValid(vecFields[0]) && CheckTimeValid(vecFields[1]); +} + +int main() +{ + assert(CheckDateTimeValid("2013/8/1")); + assert(CheckDateTimeValid(" 2013/8/1 ")); + assert(CheckDateTimeValid("2013/8/01")); + assert(CheckDateTimeValid("2013/08/1")); + assert(CheckDateTimeValid("2013/08/01")); + assert(!CheckDateTimeValid("2013/ / ")); + assert(!CheckDateTimeValid("2013/ / ")); + assert(!CheckDateTimeValid("2013/ / ")); + assert(!CheckDateTimeValid("2013/13/01")); + assert(!CheckDateTimeValid("2013/-1/31")); + assert(CheckDateTimeValid("2013/01/31")); + assert(CheckDateTimeValid("2020/02/29")); + assert(!CheckDateTimeValid("2021/02/29")); + + assert(CheckDateTimeValid("2013/8/1 8:8:8")); + assert(CheckDateTimeValid(" 2013/8/1 8:8:8 ")); + assert(!CheckDateTimeValid("2013/8/1 18:8")); + assert(!CheckDateTimeValid("2013/8/1 18")); + assert(!CheckDateTimeValid("2013/8/1 18:8:60")); + assert(CheckDateTimeValid("2013/8/1 00:8:00")); + assert(CheckDateTimeValid("2013/8/1 00:00:00")); + assert(CheckDateTimeValid("2013/8/1 0:0:0")); + assert(!CheckDateTimeValid("2013/8/1 24:00:00")); + assert(CheckDateTimeValid("2013/8/1 23:59:59")); + assert(CheckDateTimeValid("2013/8/1 23:00:59")); + assert(!CheckDateTimeValid("2013/8/1 23: 00: 59")); + assert(CheckDateTimeValid(" 2013/8/1 23:59:59")); + assert(CheckDateTimeValid(" 2013/8/1 23:00:59")); + assert(!CheckDateTimeValid(" 2013/8/1 23: 00: 59")); + + assert(!CheckDateTimeValid("2013-8/1 8:8:8")); + return 0; +} +``` + + + + +