目录
字符串函数
strlen
size_t strlen ( const char * str );
- 字符串以'\0'作为结束标志,strlen函数返回的是字符串中'\0'前面出现的字符个数(不包括'\0')。
- 参数指向的字符串必须以'\0'结束。
- 函数的返回值是size_t,是无符号的。
模拟实现strlen
#include<assert.h> #include<stdio.h> #include<string.h> //指针-指针 size_t my_strlen1(const char* str) { const char* start = str; assert(str != NULL); while (*str) str++; return str - start; } //递归 size_t my_strlen2(const char* str) { assert(str != NULL); if (*str != '\0') return 1 + my_strlen2(str + 1); else return 0; } size_t my_strlen3(const char* str) { assert(str != NULL); size_t count = 0; while (*str != '\0') { count++; str++; } return count; } int main() { char arr[] = "abcdef"; size_t len1 = my_strlen1(arr); printf("%u\n", len1); size_t len2 = my_strlen2(arr); printf("%u\n", len2); size_t len3 = my_strlen3(arr); printf("%u\n", len3); return 0; }
strcpy
char* strcpy(char * destination, const char * source );
- 将source字符串拷贝到destination字符串中。
- 源字符串必须以 '\0' 结束。
- 会将源字符串中的 '\0' 拷贝到目标空间。
- 目标空间必须足够大,以确保能存放源字符串。
- 目标空间必须可变。
模拟实现strcpy
char* my_strcpy(char* dest, const char* src) { assert(dest && src); char* ret = dest; while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = ""; char arr2[] = "hello world"; my_strcpy(arr1, arr2); printf("%s\n", arr1); return 0; }
strcat
char * strcat ( char * destination, const char * source );
- 在destination之后追加一个字符串source。
- 源字符串必须以 '\0' 结束。
- 目标空间必须足够大,能容纳下源字符串的内容。
- 目标空间必须可修改。
模拟实现strcat
char* my_strcat(char* dest, const char* src) { assert(dest && src); char* ret = dest; //找目标空间的\0 while (*dest != '\0') { dest++; } //追加 while (*dest++ = *src++) { ; } return ret; } int main() { char arr1[20] = "hello"; char arr2[] = " world"; my_strcat(arr1, arr2); printf("%s\n", arr1); return 0; }
strcmp
int strcmp ( const char * str1, const char * str2 );比较str1和str2,比较的是对应位置的字符大小。规定:第一个字符串大于第二个字符串,则返回大于 0 的数字第一个字符串等于第二个字符串,则返回 0第一个字符串小于第二个字符串,则返回小于 0 的数字
模拟实现strcmp
int my_strcmp(const char* str1,const char* str2) { assert(str1 && str2); while (*str1 == *str2) { if (*str1 == '\0') return 0; str1++; str2++; } if (*str1 > *str2) return 1; else return -1; }
上面的三个函数由于字符串的长度不受限制,所以被认为不安全,因此对应再给出三个较安全的函数:strncpy,strncat,strncmp
strncpy
char * strncpy ( char * destination, const char * source, size_t num );这里相比strcpy只增加了一个参数num,指定拷贝多少个字符,如果指定的num超过source则超出的部分补\0
strncat
char * strncat ( char * destination, const char * source, size_t num );这里相比strcat也是只增加了一个参数num,指定追加多少个字符
strncmp
int strncmp ( const char * str1, const char * str2, size_t num );num指定要比较多少个字符
strstr
char * strstr ( const char *str1, const char * str2);字符串查找,在str1中找str2找到了则返回第一次出现的字符串,如果没找到则返回NULL
模拟实现strstr
const char* my_strstr(const char* str1, const char* str2) { assert(str1 && str2); if (*str2 == '\0') return str1; const char* s1 = str1; const char* s2 = str2; const char* cp = str1; while (*cp) { s1 = cp; s2 = str2; while (*s1 != '\0' && *s2 != '\0' && * s1 == *s2) { s1++; s2++; } if (*s2 == '\0') return cp; cp++; } return NULL; }
这里关于字符串查找,有想要继续了解的,可以看我的另一篇文章,带你搞懂字符串查找算法:KMP算法
strtok
char * strtok ( char * str, const char * sep );按照sep提供的字符集合对str字符串进行分割
- sep参数是个字符串,定义了用作分隔符的字符集合。
- 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
- strtok函数找到str中的下一个标记,并将其用\0 结尾,返回一个指向这个标记的指针。比如:“abcdef@ghi”如果以@分割该字符串,那么strtok会找到@位置,将其改成'\0',然后返回a的位置。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
strtok 函数的第一个参数不为 NULL ,函数将找到 str 中第一个标记, strtok 函数将保存它在字符串中的位置。(依然是上面的那个例子,它不仅将@改成\0,而且记录了这个修改过的位置) strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。(如果在上边的例子的基础上变成了“abcdef@ghi#jkl”,按照上边的操作已经从@处分割好了,那么再次使用strtok切割#处时,第一个参数就需要传NULL,因为strtok在切割@时记录了这个位置) 如果字符串中不存在更多的标记,则返回 NULL 指针。用例子看一下该函数的用法:
当然上边的写法是为了更好的观察,实际当中这样写太繁琐,我们使用for循环:
内存函数
memcpy
void * memcpy ( void * destination, const void * source, size_t num );memcpy相比于strcpy适用于任意类型,所以它的参数也是void*,num表示拷贝多少字节
- 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。
- 这个函数在遇到 '\0' 的时候并不会停下来。
- 如果source和destination有任何的重叠,复制的结果都是未定义的。(什么意思:假设我要将“1,2,3,4,5,6,7,8,9”中的“1,2,3,4,5”拷贝到“3,4,5,6,7”位置,最终得到的结果是“1,2,1,2,1,2,1,8,9”,因为存在覆盖的问题)
模拟实现memcpy
void* my_memcpy(void* dest, const void* src, size_t count) { void* ret = dest; assert(dest); assert(src); while (count--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } return(ret); }
考虑到memcpy不能实现存在重叠情况的字符串的拷贝,所以我们提供另一个内存函数memmove
memmove
void * memmove ( void * destination, const void * source, size_t num );和 memcpy 的差别就是 memmove 函数处理的源内存块和目标内存块是可以重叠的。如果源空间和目标空间出现重叠,就得使用 memmove 函数处理。
模拟实现memmove
void* my_memmove(void* dest, const void* src, size_t count) { void* ret = dest; if (dest <= src || (char*)dest >= ((char*)src + count)) { while (count--) { *(char*)dest = *(char*)src; dest = (char*)dest + 1; src = (char*)src + 1; } } else { dest = (char*)dest + count - 1; src = (char*)src + count - 1; while (count--) { *(char*)dest = *(char*)src; dest = (char*)dest - 1; src = (char*)src - 1; } } return(ret); }
memcmp
int memcmp ( const void * ptr1, const void * ptr2, size_t num );规则同strcmp
memset
void* memset ( void* ptr , int value , size_t num );
内存设置函数
ptr指向要设置的空间
value代表要设置的值
num代表要设置的字节数