字符串函数
1. strlen
size_t strlen (const char* str);
(1) 它是用来求字符串的长度
(2)使用strlen必须是字符串,并且他必须进行参数校验
(3)用代码来实现它,如下:
size_t strlen(const char* str) {
//两种校验方式
//此处校验只能校验空指针,不能校验野指针
/*if (str == NULL) {
return 0;
}*/
//assert是"断言",是一个宏.
assert(str != NULL);
size_t size = 0;
while (str[size] != '\0') {
size++;
}
}
注意:此处参数校验可以使用两种方式一种是if,另一种是assert,这两种比较if比较温和,assert比较激烈.使用assert时必须包含头文件"#include <assert.h>";
上面代码中的size_t一般都是8个字节的无符号整数
2. strcpy
char* strcpy(char* destination,const char* sourse);
(1) 它是用来拷贝字符串的
(2) 用代码来实现它,如下:
char* myStrcpy(char* dest, const char* src) {
assert(dest != NULL);
assert(src != NULL);
int i = 0;
while (src[i] != '\0') {
dest[i] = src[i];
i++;
}
dest[i] = '\0';
return dest;
}
3. strcat
char* strcat (char* destination,const char* sourse);
(1) 它是用来字符串拼接的
(2) 用代码来实现它,如下:
char* myStrcat(char* dest , const char* src ) {
assert(dest != NULL);
assert(src != NULL);
//先找dest的结束位置
int destTail = 0;
while (dest[destTail] != '\0') {
destTail++;
}
```c
//当循环结束的时候,destTail就指向\0的位置
//mystrcpy(dest + destTail, src);
int i = 0;
while (src[i] != '\0') {
dest[destTail + i] = src[i];
i++;
}
//最后把dest最后的位置设为\0
dest[destTail + i] = '\0';
return dest;
}
4. strcmp
int strcmp ( const char * str1, const char * str2 );
(1) 它是用来比较字符串是否相等
(2)比较规则就是字典序(谁在前谁小)
(3) 用代码来实现它,如下:
int myStrcmp(const char* str1, const char* str2) {
assert(str1 != NULL);
assert(str2 != NULL);
const char* p1 = str1;
const char* p2 = str2;
while (*p1 != '\0' && *p2 != '\0') {
if (*p1 < *p2) {
return -1;
}
else if (*p1 > *p2) {
return 1;
}
else {//相等就是比较下一个字符
p1++;
p2++;
}
}
//两个字符串不一样长
if (*p1 < *p2) {
return -1;
}
else if (*p1 > *p2) {
return 1;
}
else {
return 0;
}
}
注意:大部分编程语言都是根据==来比较的,但C和Java例外
5. strncpy
char* strncpy (char* destination,const char* source,size_t num);
(1) 他也是拷贝字符串,但是它多了个num,是为了避免出现dest空间不够的情况,它限制长度,确实能够防止内存越界,但很可能只拷贝了一般(这很可能成为一个bug)
(2) 用代码实现它,如下:
char* myStrncpy(char* dest, const char* src, size_t num) {
assert(dest != NULL);
assert(src != NULL);
assert(num != 0);
int i = 0;
while (src[i] != '\0' && i < num) {
dest[i] = src[i];
i++;
}
//有两种情况导致循环结束
//1)src遇到\0,后续把dest剩余部分都设成\0
//2)i=nmu,函数直接结束即可(这种情况会被下面的while给包含)
/*if (i == num) {
return dest;
}*/
while (i < num) {
dest[i] = '\0';
i++;
}
return dest;
}
注意: 1. 当num比src小的时候,会在后面补充上\0
2. 当num比src小的时候,只拷贝了num个字符,这样不会拷贝\0
3. num的数值,必须要保证dest能够容纳的下(要考虑\0的情况)
6. strncat
char* strncat (char* destination,const char* sourse,size_t num);
(1) 拼接前num个字符串
(2) 用代码来实现它,如下:
char* myStrncat(char* dest, char* src, size_t num) {
assert(dest != NULL);
assert(src != NULL);
assert(num != 0);
//先找dest末尾
size_t destTail = 0;
while (dest[destTail] != '\0') {
destTail++;
}
size_t i = 0;
while (src[i] != '\0' && i < num) {
dest[destTail + i] = src[i];
i++;
}
//循环结束有两种情况
//1)src[i]=>\0,需要给dest末尾(dest[destTail+i])设为\0
//2)i==num,也需要给dest末尾加\0
dest[destTail + i] = '\0';
return dest;
}
7. strncmp
int strncmp (const char* destination, const char* sourse, size_t num);
(1) 只比较前num个字符(比较规则任为字典序)
(2) 用代码来实现它,如下:
int myStrncmp(const char* str1, const char* str2, size_t num) {
assert(str1 != NULL);
assert(str2 != NULL);
assert(num != 0);
size_t i = 0;
while (str1[i] != '\0' && str2[i] != '\0' && i < num) {
if (str1[i] < str2[i]) {
return -1;
}
else if (str1[i] > str2[i]) {
return 1;
}
else {
i++;
}
}
//循环结束有三种情况
//1)str1[i]=='\0'
//2)str2[i]=='\0'
// 这两种情况视为一种,谁先结束,谁就更小
//3)i==num
if (i == num) {
return 0;
}
return str1[i] - str2[i];
}
8. strstr
const char * strstr ( const char * str1, const char * str2 );
(1) 它是用来判定一个字符串是否包含另一个
(2) 用代码来实现,如下:
char* mystrstr(const char* str1, const char* str2) {
assert(str1 != null);
assert(str2 != null);
assert(*str1 != '\0');
assert(*str2 != '\0');
const char* black = str1;
//外层循环是从black从str1的哪个位置开始找子串
while (*black != '\0') {
char* red = black;
char* sub = str2;
//里层循环就是从black开始判定当前这个子串是否和str2相等
while (*red!='\0'&&*sub!='\0'
&&*red == *sub) {
red++;
sub++;
}
//循环结束有三种可能
//1.*red==\0
//2.*sub==\0
//3.*red!=*sub
/*if (*red == '\0' || *red == *sub) {
black++;
continue;
}*/
if (*sub == '\0') {
return black;
}
black++;
}
return null;
}
9. strtok
char* strtok(char* str, const char* delimiters);
(1) 它是用来切分字符串的
(2) strtok的缺点:1. 需要多次调用才能完成功能,使用起来比较复杂
2. 多次调用过程中的参数不同
3. 调用过程中会调用原来的字符串
4. strtok内部使用静态变量,记录上次调用的位置,这会导致线程不安全.
10. memcpy
void* memcpy(void* destination,const void* source,size_t num);
(1) 他是把一块内存中的数据拷贝到另一个内存中
(2) 用代码来实现它,如下:
void* myMemcpy(void* dest, const void* src, size_t num) {
assert(dest != NULL);
assert(src != NULL);
assert(num != 0);
//按照字节为单位进行拷贝
char* cdest = (char*)dest;
const char* csrc = (const char*)src;
for (size_t i = 0; i < num; i++) {
cdest[i] = csrc[i];
}
return dest;
}
注意:void* 是一种特殊的指针,只知道地址,不知道大小(各种类型的变量都可以赋值给void*),mem系列的函数也是在"#include <string.h>的头文件下的.
11. memmove
void* memmove(void* destination,const void* source,size_t num);
(1) 在字符串出现重叠的时候,字符串拷贝用这个函数
(2) 用代码来实现它,如下:
void* myMemmove(void* dest, const void* src, size_t num) {
char* cdest = (char*)dest;
const char* csrc = (const char*)src;
if (csrc <= cdest && cdest <= csrc + num) {
//内存重叠,反着拷贝
for (size_t i = num; i > 0; i--) {
cdest[i - 1] = csrc[i - 1];
}
return dest;
}
else {
//不重叠就正着拷贝
for (size_t i = 0; i < num; i++) {
cdest[i] = csrc[i];
}
return dest;
}
}
12. memcmp
int memcmp (voidptr1,const void ptr2,size_t num);
(1)它是用来比较两个内存中内容大小关系(他没有什么意义)
(2) 用代码来实现它,如下:
int myMemcmp(const void* ptr1, const void* ptr2, size_t num) {
assert(ptr1 != NULL);
assert(ptr2 != NULL);
assert(num != 0);
const char* cptr1 = (const char*)ptr1;
const char* cptr2 = (const char*)ptr2;
for (size_t i = 0; i < num; i++) {
if (cptr1[i] < cptr2[i]) {
return -1;
}
else if (cptr1[i] > cptr2[i]) {
return 1;
}
else {
continue;
}
}
}
13. memset
void* memset(void* ptr , int value,size_t num);
(1)它是用来将某块内存中的内容设定为指定值的
(2) 用代码来实现它,如下:
void* myMemset(void* ptr, int value, size_t num) {
assert(ptr != NULL);
assert(num != 0);
char* cptr = (char*)ptr;
for (size_t i = 0; i < num; i++) {
cptr[i] = (char)value;
}
return ptr;
}