• C语言 字符串


    引言

    C语言中对字符和字符串的处理很频繁,但 C语言本身是没有字符串类型的,所以字符串通常放在常量字符串中或者字符数组中。

    一、字符串的创建方式

    程序清单:

    #include 
    #include 
    
    int main()
    {
    	char arr1[] = "abcdef";
    	char arr2[] = "abc\0def";
    	char arr3[] = { 'a', 'b', 'c', 'd', 'e', 'f' };
    	char arr4[] = { 'a', 'b', 'c', 'd', 'e', 'f', '\0'};
    
    	printf("%s\n", arr1);
    	printf("%s\n", arr2);
    	printf("%s\n", arr3);
    	printf("%s\n\n", arr4);
    
    	printf("%d\n", strlen(arr1));
    	printf("%d\n", strlen(arr2));
    	printf("%d\n", strlen(arr3)); // 随机值
    	printf("%d\n", strlen(arr4));
    
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    输出结果:

    1-1

    调试窗口:

    1-2

    注意事项:

    ① 在 C语言中,被双引号括起来是字符串字面值,简称为字符串。同样地,字符串也只能被双引号括起来。

    ② 字符串的结束标志是一个 ’ \0 ’ 的转义字符。在使用格式化输出时,’ \0 ’ 的作用相当于告诉了编译器,它是一个停止的标志。在使用 strlen 这个库函数计算字符串的长度时,也是一样的道理,它只计算 ’ \0 ’ 之前的长度。

    ③ 对比 arr1 和 arr3 这两个创建字符数组的方式,可以发现,由于在 C语言 中,内存具有连续性,所以如果没有 ’ \0 ’ 作为结束标志,就会导致我们使用 printf / strlen 的时候,一直向后找 ’ \0 ’ 这个结束标志。

    1-3

    二、字符串函数

    1. strlen 函数

    strlen - string length - 计算字符串长度

    size_t strlen ( const char* str );
    
    // 返回值:字符串中出现的字符个数(不包含 '\0')
    // 参数:需要计算的起始位置的指针
    
    • 1
    • 2
    • 3
    • 4

    这里应该注意 strlen 返回的类型为无符号数。

    使用示例1

    #include 
    #include 
    
    int main() {
    
    	char arr[] = "abcdef";
    	char arr2[] = "abc\0def";
    	printf("%d\n", strlen(arr)); // arr 表示数组名,即数组的起始地址
    	printf("%d\n", strlen(arr2));
    
    	return 0;
    }
    
    // 输出结果:
    // 6
    // 3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    使用示例2

    #include 
    #include 
    
    int main(){
    	const char* str1 = "abcdef";  // 6
    	const char* str2 = "bbb";	  // 3
    
    	if (strlen(str2) - strlen(str1) < 0){
    		printf("正确\n");
    	}else{
    		printf("错误\n");
    	}
    
    	return 0;
    }
    
    // 输出结果:错误
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    注意: 由于 strlen 函数的返回值是无符号类型的,所以我们所理解的 " -3 ",其实输出的是一个很大的正整数。

    模拟 strlen 函数

    #include 
    #include 
    
    size_t my_strlen(const char* start) {
    	assert(start != NULL);
    
    	int count = 0;
    	while (*start != '\0') {
    		start++;
    		count++;
    	}
    	return count;
    }
    
    int main() {
    
    	char arr[] = "abcdef";
    	char arr2[] = "abc\0def";
    	printf("%d\n", my_strlen(arr));
    	printf("%d\n", my_strlen(arr2));
    
    	return 0;
    }
    
    // 输出结果:
    // 6
    // 3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    2. strcpy 函数

    strcpy - string copy - 字符串拷贝

    char* strcpy(char* destination, const char* source );
    
    // 返回值:返回 destination 的副本
    // 参数:destination 为粘贴的起始指针,source 为字符串复制的起始指针
    
    • 1
    • 2
    • 3
    • 4

    注意事项:

    ① 源字符串必须以 ‘\0’ 结束。
    ② 拷贝操作会将源字符串中的 ‘\0’ 拷贝到目标空间。
    ③ 目标空间必须足够大,以确保能存放源字符串。
    ④ 目标空间必须为一个可修改的字符串,而不是字符串常量值。

    使用示例

    #include 
    #include 
    
    int main() {
    
    	char arr1[] = "hello world";
    	char arr2[] = "xxxxxxxxxxxxxx";
    
    	printf("%s\n", arr2);
    	strcpy(arr2, arr1);
    
    	printf("%s\n", arr2);
    
    	return 0;
    }
    
    // 输出结果:
    // xxxxxxxxxxxxxx
    // hello world
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    调试过程:

    1-4

    注意事项:

    在调试窗口中,我们可以看到 arr2 数组中也被存入了 ‘\0’ 字符。由于 printf 打印的时候,是将 ‘\0’ 之前的字符作为输出内容的,所以后面的 XX 便不会被打印出来。

    模拟 strcpy 函数

    模拟思想:将目标空间的字符,进行逐个替换。

    #include  
    #include 
    
    char* my_strcpy(char* end, char* start) {
    
    	assert(start != NULL);		// 断言
    	assert(end != NULL);
    
    	char* ret = end;			// 返回粘贴数组的起始地址
    
    	while (*start != '\0') {
    		*end = *start;			// 解引用替换值
    		start++;
    		end++;
    	}
    	*end = *start;				// 把 '\0' 也进行替换
    
    	return ret;
    }
    
    int main() {
    
    	char arr1[] = "hello world";
    	char arr2[] = "xxxxxxxxxxxxxx";
    
    	printf("%s\n", arr2);
    	printf("%s\n", my_strcpy(arr2, arr1)); // 函数的链式访问
    
    	return 0;
    }
    
    // 输出结果:
    // xxxxxxxxxxxxxx
    // hello world
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    3. strcat 函数

    strcat - 字符串追踪

    char* strcat ( char* destination, const char* source );
    
    // 返回值:返回 destination 的副本
    // 参数:destination 追踪目标的指针,source 为字符串的源头指针
    
    • 1
    • 2
    • 3
    • 4

    注意事项:

    ① 源字符串必须以 ‘\0’ 结束。
    ② 追踪操作会将源字符串中的 ‘\0’ 放到目标空间。
    ② 目标空间必须有足够的大,能容纳下源字符串的内容。
    ③ 目标空间必须为一个可修改的字符串,而不是字符串常量值。

    使用示例

    #include 
    #include 
    
    int main() {
    
    	char arr1[20] = "hello";
    	char arr2[] = "world";
    	printf("%s\n", arr1);
    
    	strcat(arr1, arr2);
    	printf("%s\n", arr1);
    
    	return 0;
    }
    
    // 输出结果:
    // hello
    // helloworld
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    模拟 strcat 函数

    模拟思想:

    1. 找到目标空间的 ‘\0’.
    2. 对目标空间 ‘\0’ 之后的字符,进行字符串替换。

    #include 
    #include 
    #include 
    
    char* my_strcat(char* dest, const char* source) {
    	assert(dest != NULL);
    	assert(source != NULL);
    	char* ret = dest;
    
    	// 1. 找到目标空间的 '\0'
    	while (*dest != '\0') {
    		dest++;
    	}
    
    	// 2. 进行字符拷贝
    	while (*source != '\0') {
    		*dest = *source;
    		dest++;
    		source++;
    	}
    	*dest = *source;
    	return ret;
    }
    
    int main() {
    
    	char arr1[20] = "hello";
    	char arr2[] = "world";
    	printf("%s\n", arr1);
    	printf("%s\n", my_strcat(arr1, arr2));
    
    	return 0;
    }
    
    // 输出结果:
    // hello
    // helloworld
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    4. strcmp 函数

    strcmp - string compare - 字符串比较

    int strcmp ( const char * str1, const char * str2 );
    
    // 参数:两个待比较字符串的起始指针
    // 返回值:
    // 第一个字符串大于第二个字符串,则返回大于 0 的数字
    // 第一个字符串等于第二个字符串,则返回 0
    // 第一个字符串小于第二个字符串,则返回小于 0 的数字
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注意事项:

    strcmp 函数比较不是字符串的长度,而是比较字符串中对应位置上的字符大小,如果相同就比较下一对,直到两个比较的字符不同或者都遇到了 ‘\0’ 才会停止。实际上,在 C语言 中,比较的也就是字符对应的 ASCII 码值。

    使用示例

    #include 
    #include 
    
    int main() {
    
    	char arr1[] = "abcd";
    	char arr2[] = "abc";
    	char arr3[] = "abcz";
    	char arr4[] = "abcd";
    	char arr5[] = "abz";
    
    	printf("%d\n", strcmp(arr1, arr2));
    	printf("%d\n", strcmp(arr1, arr3));
    	printf("%d\n", strcmp(arr1, arr4));
    	printf("%d\n", strcmp(arr1, arr5));
    
    	return 0;
    }
    
    // 输出结果:
    // 1
    // -1
    // 0
    // -1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    注意事项: 虽然我们看到在 VS 编译器底下输出的 1,-1,0. 但在官方的标准文档中,输出规定的是大于0,小于0,或者为0.

    模拟 strcmp 函数

    模拟思想:挨个比较字符之间的 ASCII 码值,相同的字符就直接往后跳,如果遇到两个不同的字符,则开始比较。直至遇到 ‘\0’ 结束。

    #include 
    #include 
    
    int my_strcmp(const char* str1, const char* str2) {
    	assert(str1 != NULL);
    	assert(str2 != NULL);
    	
    	int ret = 0;
    	
    	while (*str1 == *str2) {
    		if (*str1 == '\0' || *str2 == '\0') {
    			break;
    		}
    		str1++;
    		str2++;
    	}
    
    	if (*str1 < *str2) {
    		ret = -1;
    	}else if (*str1 > *str2) {
    		ret = 1;
    	}
    
    	return ret;
    }
    
    int main() {
    
    	char arr1[] = "abcd";
    	char arr2[] = "abc";
    	char arr3[] = "abcz";
    	char arr4[] = "abcd";
    	char arr5[] = "abz";
    	
    	printf("%d\n", my_strcmp(arr1, arr2));
    	printf("%d\n", my_strcmp(arr1, arr3));
    	printf("%d\n", my_strcmp(arr1, arr4));
    	printf("%d\n", my_strcmp(arr1, arr5));
    
    	return 0;
    }
    
    // 输出结果:
    // 1
    // -1
    // 0
    // -1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    5. strncpy、strncat、strncmp

    strcpy、strcat、strcmp 是一组长度不受限制的字符串函数。
    而 strncpy、strncat、strncmp 是一组长度受限制的字符串函数。

    所谓长度受限制,就是相比于前者,多了一个 size_t 类型的参数。例如:strncpy 就可以指定拷贝字符的数量、strncat 就可以指定追踪字符的个数、strncmp 就可以指定比较的字符个数。

    6. strstr 函数

    strstr - string string - 找子串

    char* strstr ( const char* str1, const char* str2);
    
    // 返回值:返回 str1 中的子串起始指针
    // 参数:str1 为主串,str2 为子串
    
    • 1
    • 2
    • 3
    • 4

    使用示例

    #include 
    #include 
    
    int main() {
    
    	char arr1[] = "abcdefxyxyzwe";
    	char arr2[] = "bcd";
    	char arr3[] = "xyz";
    	char arr4[] = "xyw";
    
    	printf("%s\n", strstr(arr1, arr2));
    	printf("%s\n", strstr(arr1, arr3));
    	printf("%s\n", strstr(arr1, arr4));
    
    	return 0;
    }
    
    // 输出结果:
    // bcdefxyxyzwe
    // xyzwe
    // (null)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    模拟 strstr 函数

    模拟思路:额外创建三个指针,s1, s2, flag.
    flag 指针作为标志主串中子串的起始位置,s2 指针用来重置子串的开头,s1 指针用来和 s2 进行比较是否为相同字符。

    模拟的三个场景:

    1-5

    #include 
    #include 
    
    char* my_strstr(const char* str1,  const char* str2) {
    
    	assert(str1 != NULL);
    	assert(str2 != NULL);
    
    	const char* s1 = str1;			
    	const char* s2 = str2;			// str2 用来重置 s2
    	char* flag = str1;				// flag 指针用来记录需要返回的位置
    
    	while (*flag != '\0') {
    		s1 = flag;
    		s2 = str2;
    		while ( (*s1 == *s2) && (*s1 != '\0') && (*s2 != '\0') ) {
    			s1++;
    			s2++;
    		}
    		if (*s2 == '\0') {
    			// s2 走到字符串末尾,说明子串已经被找到
    			return flag;
    		}
    		flag++;
    	}
    
    	return NULL;
    }
    
    int main() {
    
    	char arr1[] = "abcdefxyxyzwe";
    	char arr2[] = "bcd";
    	char arr3[] = "xyz";
    	char arr4[] = "xyw";
    
    	printf("%s\n", my_strstr(arr1, arr2));
    	printf("%s\n", my_strstr(arr1, arr3));
    	printf("%s\n", my_strstr(arr1, arr4));
    
    	return 0;
    }
    
    // 输出结果:
    // bcdefxyxyzwe
    // xyzwe
    // (null)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    7. strtok 函数

    strtok - 字符串分割函数

    char* strtok ( char* str, const char* sep );
    
    // 返回值:返回被分割的标记指针,若字符串被分割没有更多的标记,则返回 NULL
    // 参数:str 为目标字符串,sep 为标记分隔符
    
    • 1
    • 2
    • 3
    • 4

    使用示例1

    1-6

    经过上面的程序,可以得出结论:

    ① 字符串的内容经 strtok 函数分割后,会被改变,标记分隔符处会被改变为 ‘\0’.
    ② 当 strtok 函数的第一个参数不为 NULL 时,则会找到字符串的第一个起始指针;当 strtok 函数的第一个参数为 NULL 时,则表示从分隔符后继续寻找。

    分割过程如下:

    hello | world , 你好 | 世界
    
    hello \0 world , 你好 | 世界			-> return 'h' 的地址
    hello \0 world \0 你好 | 世界		-> return 'w' 的地址
    hello \0 world \0 你好 \0 世界		-> return '你' 的地址
    									-> return NULL
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用示例2

    #include 
    #include 
    
    int main() {
    
    	char arr[] = "hello|world,你好|世界";
    	const char* sep = ",|";
    	char* str = NULL;
    
    	for (str = strtok(arr, sep); str != NULL; str = strtok(NULL, sep)) {
    		printf("%s\n", str);
    	}
    
    	printf("\n%s\n", arr);
    
    	return 0;
    }
    
    // 输出结果:
    // hello
    // world
    // 你好
    // 世界
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    三、字符函数

    1-7

    使用示例

    #include 
    #include 
    
    int main() {
    
    	char ch = 'A';
    	int ret = islower(ch);
    	printf("%d\n", ret); 			// 0
    
    	printf("%c\n", tolower(ch)); 	// a
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    四、内存操作函数

    注意事项:

    内存操作函数,顾名思义,它是对内存操作的,换言之,它就是以字节为单位来操作数据的,明白这一点很重要。单位既不是字符、也不是整型…

    1. memset 函数

    memset - memory set - 内存设置函数 - 以字节为单位设置数据

    void* memset( void* dest, int ch, size_t count );
    
    // 返回值:dest 的副本
    // 参数:dest 表示需要操作的起始指针位, ch 表示需要填充的数据, count 表示需要设置字节的数量
    
    • 1
    • 2
    • 3
    • 4

    使用示例

    将 arr 数组的前 8 个字节分别设置成 1;千万不要理解成将前两个元素设置成 1.

    1-8

    2. memcmp

    memcmp - memory compare - 内存比较函数 - 以字节为单位比较两个数据

    int memcmp( const void* str1, const void* str2, size_t count );
    
    // 返回值:
    // 第一个数据大于第二个数据,则返回大于 0 的数字
    // 第一个数据等于第二个数据,则返回 0
    // 第一个数据小于第二个数据,则返回小于 0 的数字
    
    // 参数:str1 为第一个数据的起始指针,str2 为第二个数据的起始指针, count 表示需要比较的字节数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    使用示例

    #include 
    #include 
    
    int main() {
    
    	int arr1[] = { 1,2,3,4,5 };
    	int arr2[] = { 1,2,3,4,0x11223305 };
    	int ret1 = memcmp(arr1, arr2, 16);
    	int ret2 = memcmp(arr1, arr2, 18);
    	
    	printf("%d, %d\n", ret1, ret2);
    
    	return 0;
    }
    
    // 输出结果: 0, -1
    
    // 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00 (十六进制 arr1)
    // 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 33 22 11 (十六进制 arr2)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3. memcpy 函数

    memcpy - memory copy - 内存拷贝函数 - 以字节为单位拷贝数据

    void* memcpy ( void* destination, const void* source, size_t count );
    
    // 返回值:destination 的副本
    // 参数:destination 为目标空间、source 为源数据、count 表示需要拷贝的字节数
    
    • 1
    • 2
    • 3
    • 4

    memcpy 函数的使用思想和 strncpy 基本一致,但 memcpy 更加强大,它能够针对各种数据类型进行拷贝,只要涉及内存的,都可以。

    使用示例

    1-9

    4. memmove 函数

    memmove 函数和 memcpy 使用的思想基本相同,但早期的 memcpy 在使用时,若源空间和目标空间重叠,就可能出现数据覆盖的情况,所以才有 memmove 的诞生。然而,现在两者几乎没有区别,因为主流的 VS 编译器已经将两者实现差不多了。

    使用示例

    1-10

    2-1

  • 相关阅读:
    OPENWIFI实践1:下载并编译SDRPi的HDL源码
    如何处理数据集内的缺失值?
    python3:len()返回对象的长度(元素数量) 2023-11-17
    guzzlehttp5.3 and guzzlehttp6.7 , 同时共存。同时安装2个版本guzzlehttp
    Ansys Maxwell三相变压器制作方法教程
    LeetCode 每日一题 2023/10/2-2023/10/8
    iOS开发app置灰功能添加
    U8二开成果汇总
    GitHub提交代码步骤
    Note_First:Hadoop安装部署与测试
  • 原文地址:https://blog.csdn.net/lfm1010123/article/details/128077779