8.2 字符转换
转换函数白大写字母转换为小写字母或者把小写字母转换为大写字母。有两个函数可供调用。toupper
函数返回其参数对应的大写形式,tolower
函数返回其参数对应的小写形式。
int tolower(int ch); int toupper(int ch);
举个实际的例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> int main() { char temp1[] = "To carry things with great virtue"; char temp2[] = "To carry things with great virtue"; //全转换为大写 for (int i = 0; i < strlen(temp1); i++) { if (islower(temp1[i])) temp1[i] = toupper(temp1[i]); printf("%c",temp1[i]); } printf("\n-----------------------------\n"); //全转换为小写 for (int i = 0; i < strlen(temp2); i++) { if (isupper(temp2[i])) temp2[i] = tolower(temp2[i]); printf("%c", temp2[i]); } printf("\n"); system("pause"); return 0; }
打印输出:
可以看到,我们可以按照自己的意愿去调整字符串中字母的大小写。
9 内存操作
字符串一般以NUL结尾,但是如果我们想要处理中间包含NUL的字符串,或者任意长度的字节序列的时候,前面的函数就显得比较乏力,或者说根本没法用。不过我们可以有另外一组函数,供我们使用,去完成实际开发中的一些需求。下面是它们的原型。
注意:这些函数能够处理的不仅仅是字符串,还可以是结构体或数组等数据类型,具体可以处理的数据类型要根据具体函数来定。
void *memcpy(void *dst, void const *src, size_t length); void *memmove(void *dst, void const *src, size_t length); void *memcmp(void const *a, void const *b, size_t length); void *memchr(void const *a, int ch, size_t length); void *memset(void *a, int ch, size_t length);
举个例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 10 int main() { char temp1[] = "hello world"; char temp2[] = "hello world"; char temp3[] = "hello world"; char temp4[] = "hello world"; unsigned int int_array[SIZE]; char *p = NULL; //复制字符串 memcpy(temp1 + 2, temp1, 5); memmove(temp2 + 2, temp2, 5); printf("temp1 = %s\n", temp1); printf("temp2 = %s\n", temp2); printf("---------------------------------------\n"); //比较字符串 if(!memcmp(temp1, temp2, 6)) printf("temp1 = temp2\n"); else printf("temp1 != temp2\n"); printf("---------------------------------------\n"); //查找字符 p = (char *)memchr(temp3, 'e', strlen(temp3)); if(p != NULL) printf("字符e在temp3中的位置是:%d\n", p - &temp3[0]); printf("---------------------------------------\n"); //初始化数组 memset(int_array, 0, sizeof(int_array)); for (int i = 0; i < SIZE; i++) printf("int_array[%d]的值为:%d\t", i, int_array[i]); printf("\n", sizeof(int)); printf("---------------------------------------\n"); //初始化数组 memset(temp4, 'a', sizeof(temp4) - 1); printf("字符串temp4为:%s\n", temp4); system("pause"); return 0; }
打印输出:
9.1 memcpy和memmove真的不一样吗?
有一个很值得探讨的问题:
memcpy和memmove函数真的一样吗?《C和指针》以及网上的很多说法都是:两者不一样,如果src和dst出现了重叠,则memcpy会出现问题,而memmove总能按照理想的情况去运行,但我们的程序运行结果却是,这两个函数都可以按照理想情况运行,这是为什么呢?
唯一的解释就是,软件的运行环境不一样,程序的底层库出现了差异,所以会出现这样的情况。但这并不影响我们对以前版本的(也就是两者不同)的memcpy和memmove进行一番研究!
先来看看所谓的重叠是什么意思,为什么重叠的时候,字符串复制会出现问题。
以上就是我们程序中复制字符串操作的示意图。可以看到src子串和dst子串有三个字母出现了重叠,如果我们按照常规的操作方法,复制之后就会出现下面这样的结果。
若区域重叠,就会导致复制出错,也就是说,想要取的值被新值所覆盖,导致无法顺利取值,复制后temp1
变成了hehehehorld
。这就是之前memcpy
字符串的复制方法。
然后让我们来看看memmove
(以及优化后的memcpy
)是如何巧妙地解决这个问题的。
可以看到,复制的顺序出现了变化,这次是从后往前复制的,很好地避免了这个问题。
那么问题来了,这次是要复制的目的位置在后面,如果在前面又该如何处理呢?答案是复制顺序也反过来。看看执行过程:
这个时候,也不会出现将要取的值被原来的值覆盖的情况,会按照预想的结果去执行,结果是:llo w world
。
9.2 memcmp:简单的比较
memcmp
比较内存区域a和的前length个字节。比较方法,返回值都和strcmp
基本一致,详情可以参考本文的strcmp
部分。
9.3 memchr:简单的查找
memchr从a的起始位置开始查找字符ch的第一次出现的位置,并返回一个指向该位置的指针。查找方法,返回值都和strchr
基本一致,详情可以参考本文的strchr
部分。
9.4 memset:初始化的值只能是0和-1?
从该函数的介绍来看,该函数可以对某连续的内存区域设置相同的任何值(理论上),但在实际的开发中,基本都设置为0
或者-1
,这是为什么呢?
这是因为,这个函数对于内存的赋值,是以字节为单位的,一般用来对字符串进行赋值没有任何问题,因为字符串的元素只占一个字节,而数组则不同,常见的short,int,long类型的数组元素都不止一个字节,所以初始化才不会出现我们预想的结果。当我们设置为0的时候,各个字节都为0(若为-1,则各个位都为1),所以每个元素无论几个字节都会初始化为0,但是为其他值,结果就不一样了。参考以下程序:
#include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 10 int main() { unsigned int int_array[SIZE]; //初始化数组 printf("--------------初始化值设为0-------------------------\n"); memset(int_array, 0, sizeof(int_array)); for (int i = 0; i < SIZE; i++) printf("int_array[%d]的值为:%d\t", i, int_array[i]); printf("\n"); printf("--------------初始化值设为1-------------------------\n"); memset(int_array, 1, sizeof(int_array)); for (int i = 0; i < SIZE; i++) printf("int_array[%d]的值为:%d\t", i, int_array[i]); printf("\n"); system("pause"); return 0; }
打印输出:
这是为什么呢?
这是因为,memset
是按字节赋值的,而int
类型的数据在内存中占了4
个字节,所以该值应该以4
个字节为单位,即:0x01010101
,转换成十进制正好是16843009
。
所以,一般如果想用memse
对一段内存区域设定相同的值,初始化为0
或者-1
是最好的选择。
10 总结
字符串本身并不是很复杂,为了方便开发,提供了很多的库函数,所以我们只需要掌握C语言的那些库函数即可。尤其是要注意,有的函数返回的并不是数值,而是指针;有的函数使用起来比较特殊,比方说strtok
等。
------------------------------------------------------------------------end-------------------------------------------------------------------------