十一、字符串操作函数与内存操作函数
C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在常量字符串中或者字符数组中。
字符串常量适用于那些对它不做修改的字符串函数.
strcpy, strncpy, strcat, strncat, strcmp, strncmp 这些操作的都是字符串,主要针对 '\0'操作。但是对于整型数组,浮点型数组,结构体数组的时候就不能用了,所以还设有内存函数
size_t strlen ( const char * str ); /* 1. 字符串已经'\0' 作为结束标志,strlen函数返回的是在字符串中'\0' 前面出现的字符个数(不包含'\0' )。 2. 参数指向的字符串必须要以'\0' 结束。 3. 注意函数的返回值为size_t,是无符号的( 易错) */#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<assert.h> int my_strlen(const char * str) { int count = 0; assert(str != NULL); while (*str != '\0') { count++; str++; } return count; } int main() { int len = strlen("abcdef"); char arr[] = { 'a', 'b', 'c', 'd' }; printf("%d\n", my_strlen("abcdef")); // 6 printf("%d\n", len); // 6 printf("%d\n", strlen(arr)); // 随机值,找不到 '\0' if (strlen("abc") - strlen("abcdef") > 0) { printf("haha\n"); //haha, 因为 strlen返回的值是 size_t(unsigned int)。 3 - 6 = -3.但是是无符号,所以转换为正数了 } else { printf("hehe\n"); } return 0; }
1.2 strcpy、strncpy(字符串拷贝)
char* strcpy(char * destination, const char * source ); /* 1. 源字符串必须以'\0' 结束。 char a[] = {'1', '2'}这样就不行了 2. 会将源字符串中的'\0' 拷贝到目标空间。 3. 目标空间必须足够大,以确保能存放源字符串。 4. 目标空间必须可变。 char *p = "abcdef";这样也不行,因为 p指向的是常量字符串,不能改变。 5. 学会模拟实现。 */#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<assert.h> char * my_strcpy(char* dest, const char* src) { assert(dest != NULL); assert(src != NULL); char* ret = dest; // 拷贝 src指向的字符串到dest指向的空间, 包含 '\0',返回目的空间的起地址 while (*src != '\0') { *dest++ = *src++; } *dest = *src; // 添加'\0' /* 下面这样写也行 while (*dest++ = *src++) { ; } */ return ret; } int main() { char arr1[] = "abcdefg"; char arr2[] = "wy"; char arr3[] = "wangyong"; char arr4[] = "wangy"; strcpy(arr1, arr2); printf("%s\n", arr1); // wy my_strcpy(arr3, arr4); printf("%s\n", arr3); // wangy return 0; }
char * strncpy ( char * destination, const char * source, size_t num ); /* 拷贝num个字符从源字符串到目标空间。 如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个。 */#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> int main() { char arr1[6] = "world"; char arr2[] = "abc"; strncpy(arr1, arr2, 4); printf("%s\n", arr1); my_strncpy(arr1, arr2, 4); return 0; }
1.3 strcat、strncat(字符串追加)
1.3.1 strcat
char * strcat ( char * destination, const char * source ); /* 1. 源字符串必须以'\0' 结束。 2. 目标空间必须有足够的大,能容纳下源字符串的内容。 3. 目标空间必须可修改。 4. 字符串自己给自己追加,如何? 报错 */#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<assert.h> char* my_strcat(char *dest, const char *src) { char *ret = dest; assert(dest != NULL); assert(src); while (*dest != '\0') // 找到目标字符串的末尾 { dest++; } while (*dest++ = *src++)// 追加 { ; } return ret; } int main() { char arr1[10] = "abc"; // 将arr2追加到arr1上, arr1的空间一定要足够大才行,这样才能追加成功,否则报错 char arr2[] = "def"; char arr3[15] = "hello"; char arr4[] = "world"; strcat(arr1, arr2); // abcdef printf("%s\n", arr1); my_strcat(arr3, arr4); printf("%s\n", arr3); // helloworld return 0; }
char * strncat ( char * destination, const char * source, size_t num );#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> int main() { char arr1[30] = "hello"; char arr2[] = "world"; strncat(arr1, arr2, 6); printf("%s\n", arr1); return 0; }
1.4 strcmp、strncmp(字符串比较)
1.4.1 strcmp
int strcmp ( const char * str1, const char * str2 ); /* 1. 第一个字符串大于第二个字符串,则返回大于0的数字 2. 第一个字符串等于第二个字符串,则返回0 3. 第一个字符串小于第二个字符串,则返回小于0的数字 */#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<assert.h> int my_strcmp(const char* str1, const char* str2) { assert(str1 && str1); int ret = 0; int i = 0; while (*str1 == *str2) { if (*str1 == '\0') { return 0; // 相等 } str1++; str2++; } return (*str1 - *str2); } int main() { char* p1 = "abcdef"; char* p2 = "abqwer"; int ret = 0; ret = strcmp(p1, p2); printf("ret = %d\n", ret); // <0 ret = my_strcmp(p1, p2); printf("ret = %d\n", ret); // ret = -14 return 0; }
int strncmp ( const char * str1, const char * str2, size_t num );#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> int main() { int ret = strncmp("abcd","abcf", 3); printf("ret = %d\n",ret); // ret = 0 return 0; }
char * strstr ( const char *str1, const char * str2); // 在字符串 str1 中查找第一次出现字符串 str2(不包含空结束字符)的位置。#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<assert.h> char* my_strstr(const char *p1, const char *p2) { assert(p1 != NULL); assert(p2 != NULL); char *s1 = NULL; char *s2 = NULL; char *cur = (char *)p1; if (*p2 == '\0') // 空字符串 { return (char *)p1; } while (*cur) { s1 = cur; s2 = (char *)p2; while ((*s1 == *s2) && *s1 && *s2) { s1++; s2++; } if (*s2 == '\0') { return cur; } if (*s1 == '\0') // 提前终止,可以没有这个 { return NULL; } cur++; } return NULL; } int main() { char *p1 = "abcdefgdef"; char *p2 = "def"; char *ret = strstr(p1, p2); // 从 p1 中找 p2, 输出的结果是从找到第一次子字符串的首地址一直往后的字符串 if (ret == NULL) { printf("不存在字符串\n"); } else { printf("%s\n", ret); // defgdef } ret = my_strstr(p1, p2); if (ret == NULL) { printf("不存在字符串\n"); } else { printf("%s\n", ret); // defgdef } return 0; }
char * strtok ( char * str, const char * sep ); /* 1. sep参数是个字符串,定义了用作分隔符的字符集合 2. 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。 3. strtok函数找到str中的下一个标记,并将其用\0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。) 4. strtok函数的第一个参数不为NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。 5. strtok函数的第一个参数为NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。 6. 如果字符串中不存在更多的标记,则返回NULL 指针。 */#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<assert.h> int main() { char arr[] = "abc.def@gh"; char *p = ".@"; char buff[1024] = { 0 }; strcpy(buff, arr); char *ret = strtok(buff, p); printf("ret = %s\n", ret); // ret = abc ret = strtok(NULL, p); printf("ret = %s\n", ret); // ret = def ret = strtok(NULL, p); printf("ret = %s\n", ret); // ret = gh return 0; }#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<assert.h> int main() { char arr[] = "abc.def@gh"; char *p = ".@"; char buff[1024] = { 0 }; char* ret = NULL; strcpy(buff, arr); for (ret = strtok(buff, p); ret != NULL;ret = strtok(NULL, p)) { printf("%s ", ret); // abc def gh } return 0; }
char * strerror ( int errnum ); // 返回错误码,所对应的错误信息。#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> int main() { char *str = strerror(0); printf("%s\n", str); // No error str = strerror(1); printf("%s\n", str); // Operation not permitted str = strerror(2); printf("%s\n", str); // No such file or directory // 0, 1, 2就是错误码 }#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<errno.h> int main() { // error 是一个全局的错误码变量,当C语言的库函数在执行过程中发生了错误,就会把对应的错误码复制到error中 // 打开文件 FILE* pf = fopen("test.txt", "r"); if (pf == NULL) { printf("%s\n", strerror(errno)); // No such file or directory } else { printf("open file syccess\n"); } return 0; }
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<ctype.h> int main() { char ch = 'w'; int ret = islower(ch); printf("%d\n", ret); // 2 非 0 的数 return 0; }
int tolower ( int c ); int toupper ( int c );#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<ctype.h> int main() { char ch = tolower('Q'); printf("%c\n", ch); // q return 0; }#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<ctype.h> int main() { char arr[] = "I Am Wang Yong"; int i = 0; while (arr[i]) { if (isupper(arr[i])) { arr[i] = tolower(arr[i]); } i++; } printf("%s\n", arr); // i am wang yong return 0; }
二、内存函数
void * memcpy ( void * destination, const void * source, size_t num ); /* 函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。 这个函数在遇到'\0' 的时候并不会停下来。 如果source和destination有任何的重叠,复制的结果都是未定义的。 */#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> struct S { char name[20]; int age; }; int main() { int arr1[] = { 1, 2, 3, 4, 5 }; int arr2[5] = { 0 }; struct S arr3[] = { { "张三", 20 }, {"李四",30} }; struct S arr4[3] = {0}; memcpy(arr2, arr1, sizeof(arr1)); memcpy(arr4, arr3, sizeof(arr3)); return 0; }
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<assert.h> struct S { char name[20]; int age; }; void* my_memcpy(void* dest, const void* src, size_t num) { void *ret = dest; assert(dest != NULL); assert(src != NULL); while (num) { *(char *)dest = *(char *)src; ++(char *)dest; ++(char *)src; num--; } return ret; } int main() { int arr1[] = { 1, 2, 3, 4, 5 }; int arr2[5] = { 0 }; struct S arr3[] = { { "张三", 20 }, {"李四",30} }; struct S arr4[3] = {0}; my_memcpy(arr2, arr1, sizeof(arr1)); my_memcpy(arr4, arr3, sizeof(arr3)); return 0; }
void * memmove ( void * destination, const void * source, size_t num ); /* 和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。 如果源空间和目标空间出现重叠,就得使用memmove函数处理。 */#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> int main() { int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int i; memmove(arr + 2, arr, 20); for (i = 0; i < 10; i++) { printf("%d ", arr[i]);// 1 2 1 2 3 4 5 8 9 10 } return 0; }#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> #include<assert.h> void* my_memmove(void* dest, const void* src, size_t count) { void *ret = dest; assert(dest != NULL); assert(src != NULL); if (dest < src) { // 从前向后 while (count--) { *(char *)dest = *(char *)src; ++(char *)dest; ++(char *)src; } } else { // 从后向前 while (count--) { *((char *)dest + count) = *((char *)src + count); } } return ret; } int main() { int arr1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; int arr2[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; my_memmove(arr1, arr1+2, 20); my_memmove(arr2+2, arr2, 20); return 0; }
int memcmp ( const void* ptr1, const void* ptr2, size_t num ); /* 比较从ptr1和ptr2指针开始的num个字节 <0 =0 >0 */#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> int main() { int arr1[] = { 1, 2, 3, 4, 5 }; int arr2[] = { 1, 2, 5, 4, 3 }; int ret = memcmp(arr1, arr2, 9); printf("%d\n", ret); // -1 return 0; }
#define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h> int main() { char arr[10] = ""; int i = 0; memset(arr, '#', 10); for (i = 0; i<10; i++) { printf("%c ", arr[i]); // # # # # # # # # # } return 0; }