strlen
size_t strlen ( const char * str );
//计算字符串长度,\0不计入。
三种方法模拟实现:
//递归
size_t my_strlen1(const char* p) {
assert(p != NULL);
if (*p != '\0')
return 1 + my_strlen1(p + 1);
else
return 0;
}
//指针-指针
size_t my_strlen2(const char* p) {
assert(p != NULL);
char* str = p;
while (*p != '\0') {
p++;
}
return p - str;
}
//迭代
size_t my_strlen3(const char* p) {
assert(p != NULL);
size_t count = 0;
while (*p++ != '\0') {
count++;
}
return count;
}
使用:
void strlen_test() {
char arr[20] = "hello world";
printf("%d\n", strlen1(arr));
}
strcpy和strncpy
char * strncpy ( char * destination, const char * source, size_t num );
//将source的n个字符复制到destination
如果source字符串长度小于num,在多出的地方复制\0;
如果source字符串长度大于等于num,复制后尾部无\0
char * strcpy ( char * destination, const char * source );
//将source复制到destination,包括\0
模拟实现:
char* my_strncpy(char* des, const char* source, size_t n)
{
assert(des != NULL && source != NULL);
char* ret = des;
while (n--) {
*des = *source;
if (*source) {
des++;
source++;
}
else {
des++;
}
}
return ret;
}
char* my_strcpy(char* des, const char* source)
{
int n = my_strlen1(source);
return my_strncpy(des, source, n+1); //尾部要有\0, n+1
//不使用strncpy的实现
/*assert(des && source);
char* ret = des;
while (*des++ = *source++){
;
}
return ret;*/
}
使用:
void strcpy_test() {
char dest[20] = { 0 };
char sorc1[] = "hello world";
char sorc2[] = "friend";
strcpy(dest, sorc1);
printf("%s\n", dest);
strncpy(dest + 6, sorc2, 6);
printf("%s\n", dest);
}
//结果:
//hello world
//hello friend
strcmp和strncmp
int strcmp ( const char * str1, const char * str2 );
//比较每个字符串的第一个字符。如果它们彼此相等,则继续以下对,直到字符不同或达到终止空字符。
int strncmp ( const char * str1, const char * str2, size_t num );
//比较每个字符串的第一个字符。如果它们彼此相等,则继续使用以下对,
//直到字符不同,直到达到终止的空字符,或者直到两个字符串中的 num 字符匹配,以先发生者为准。
模拟实现:
int my_strncmp(const char* p1, const char* p2, size_t n){
assert(p1 && p2);
while (n--) {
if (*p1 == *p2) {
if (*p1 == '\0')
return 0;
else {
p1++;
p2++;
}
}
else
return *p1 - *p2;
}
}
int my_strcmp(const char* p1, const char* p2){
size_t n1 = my_strlen1(p1);
size_t n2 = my_strlen1(p2);
size_t n = n1 > n2 ? n2 : n1;
return my_strncmp(p1, p2, n);
//不调用strncmp的模拟实现
/*assert(p1 && p2);
while (*p1 == *p2){
if (*p1 == '\0')
return 0;
else{
p1++;
p2++;
}
}
return *p1 - *p2;*/
}
使用:
void strcmp_test() {
char str1[] = "hello friend";
char str2[] = "hello world";
printf("%d\n", strcmp(str1, str2));
printf("%d\n", strncmp(str1, str2, 5));
}
strcat和strncat
char * strcat ( char * destination, const char * source );
//将source字符串的副本追加到destination字符串。
//destination中的终止空字符被源的第一个字符覆盖,并且在目标中由两者串联形成的新字符串的末尾包含一个空字符。
char * strncat ( char * destination, const char * source, size_t num );
//将源的第一个数字字符追加到目标,外加一个终止空字符。
//如果源中 C 字符串的长度小于 num,则仅复制终止空字符之前的内容。
模拟实现:
char* my_strncat(char* des, const char* source, size_t n){
assert(des && source);
char* ret = des;
//找到\0
while (*des){
des++;
}
//拼接
while (n--){
*des = *source;
if (*source){
des++;
source++;
}
else{
break;
}
}
//如果while结束函数未结束,最后一个字符不是\0,尾部加\0
*des = '\0';
return des;
}
char* my_strcat(char* des, const char* source){
/*assert(des && source);
char* ret = des;
while (*des){
des++;
}
while (*des++ = *source++){
;
}
return ret;*/
int n = my_strlen1(source);
return my_strncat(des, source, n);
}
使用:
void strcat_test() {
char str1[30] = { 0 };
char str2[] = "hello world";
char str3[] = "hello friend";
strncat(str1, str2, 6);
strcat(str1, str3+6);
printf(str1);
}
//结果:
//hello friend
strtok
char * strtok ( char * str, const char * delimiters );
//str是要拆分的字符串的地址,delimiters是分割符所组成字符串的地址
使用:
第一次调用时str直接传字符串地址即可,但是之后的调用str都是NULL
{
char arr[50] = "I.am.learning.strtok";
char div[] = ".";
char* ret = NULL;
for (ret = strtok(arr, div); ret != NULL;ret = strtok(NULL, div)) {
printf("%s ", ret);
}
}
//结果:
//I am learning strtok
strerror
使用此函数时,不仅需要引用头文件string.h,还要有errno.h
char * strerror ( int errnum );
//解释 errnum 的值,生成一个字符串,其中包含描述错误条件的消息
使用:
void strerror_test()
{
FILE* p = fopen("test.txt", "r");
if (p == NULL) {
printf("%s\n", strerror(errno));
}
}
//结果:
//No such file or directory
值得一提的是:perror也有类似的作用,而且使用起来可能更方便,perror在头文件stdio.h中。
void strerror_test()
{
FILE* p = fopen("test.txt", "r");
if (p == NULL) {
perror("fopen");
}
}
//结果:
//fopen: No such file or directory
strstr
char * strstr ( char * str1, const char * str2 );
//返回在str1中第一次出现str2的指针
模拟实现:
char* my_strstr(const char* str1, const char* str2) {
assert(str1 && str2);
char* cur1 = NULL;
char* cur2 = NULL;
char* ret = str1;
while (*ret) {
cur1 = ret;
cur2 = str2;
while (*cur1 && *cur2 && *cur1 == *cur2) {
cur1++;
cur2++;
}
if (*cur2 == '\0') {
return (char*)ret;
}
ret++;
}
return NULL;
}
使用:
void strstr_test() {
char arr1[] = "qqqqwertaa";
char arr2[] = "qwert";
printf(strstr(arr1, arr2));
}
memcpy和memmove
void * memcpy ( void * destination, const void * source, size_t num );
void * memmove ( void * destination, const void * source, size_t num );
二者都是将source的 num个字符复制到destination,但是当复制的内容存在空间重叠时,比如:
int arr[10] = {1,2,3,4,5};
memcpy(arr+1,arr,3);
这时对于memcpy,就会这样执行arr[0]->arr[1],arr[1]->arr[2],arr[2]->arr[3],这就会发生问题,
arr数组变成{1,1,1,1,5},与预期的结果{1,1,2,3,5}相差甚远。如果使用memmove就不会发生这个问题。
但是,现在的大多数编辑器对于两个函数的实现其实是相同的,在使用时也不会出现上述的问题。
模拟实现:
void* my_memcpy(void* dest, const void* sorc, size_t n){
assert(dest && sorc);
void* ret = dest;
while (n--) {
*(char*)dest = *(char*)sorc;
dest = (char*)dest+1;
sorc = (char*)sorc+1;
}
return ret;
}
void* my_memmove(void* dest, const void* sorc, size_t n) {
assert(dest && sorc);
void* ret = dest;
if (dest > sorc) {
//从后往前复制
while (n--) {
*((char*)dest + n) = *((char*)sorc + n);
}
}
else {
//从前向后复制,同memcpy
while (n--) {
*(char*)dest = *(char*)sorc;
dest = ++(char*)dest;
sorc = ++(char*)sorc;
}
}
return ret;
}
使用:
void memcpy_test() {
int arr1[10] = { 1,2,3,4,5 };
int arr2[5] = { 6,7,8,9,10 };
memcpy(arr1 + 5, arr2, 20);
memmove(arr1 + 5, arr1, 20);
for (int i = 0;i < 10;i++) {
printf("%d ", arr1[i]);
}
}
//结果:
//1 2 3 4 5 1 2 3 4 5
memcmp
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
//将ptr1的前num字节与ptr2的比较
模拟实现:
int my_memcmp(const void* str1, const void* str2, size_t n) {
assert(str1 && str2);
while (n--) {
if (*(char*)str1 == *(char*)str2) {
++(char*)str1;
++(char*)str2;
}
else
return *(char*)str1 - *(char*)str2;
}
return 0;
}
使用:
void memcmp_test()
{
int arr1[5] = { 1,2,3,4,6 };
int arr2[5] = { 1,2,3,4,5 };
printf("%d\n", my_memcmp(arr1, arr2, 16));
printf("%d\n", my_memcmp(arr1, arr2, 20));
printf("%d\n", my_memcmp(arr1, arr2+1, 20));
}
memset
void * memset ( void * ptr, int value, size_t num );
//将ptr开始的num个字节的值都修改为value
模拟实现:
void* my_memset(void* dest, int val, size_t n) {
assert(dest);
void* ret = dest;
while (n--){
*(char*)dest = (char)val;
++(char*)dest;
}
return ret;
}
使用:
void memset_test()
{
char arr[20] = "hello world";
my_memset(arr, 'x', 5);
my_memset(arr+6, 'y', 5);
printf(arr);
}
//结果:
//xxxxx yyyyy