字符函数
顾名思义,字符函数就是作用于字符的函数,而字符函数主要分为字符分类函数
以及字符转换函数
。
字符分类函数
字符分类函数,用于判断一个字符是否属于某一个类
函数 | 参数符合条件 |
isntrl | 任何字符 |
isspace | 空白字符,空格'' ,换页符'\f' ,换行符'\n' ,回车符'\r' ,制表符'\t' ,垂直制表符'\v' |
isdigit | 十进制数字0 - 9 |
isxdigit | 十六进制数字,包括0 - 9 ,a - f ,A - F |
islower | 小写字母a - z |
isupper | 大写字母A - Z |
isalpha | 字母a - z ,A - Z |
isalnum | 字母或数字,a - z ,A - Z ,0 - 9 |
ispunct | 标点符号,任何不属于字母或数字的可打印字符 |
isgraph | 任何图形字符 |
isprint | 任何可打印字符 |
对于以上函数,只要传入的字符符合要求,返回非0数字;否则返回0;
字符转换函数
tolower
将传入的大写字母变成小写字母
toupper
将传入的小写字母变成大写字母
字符串函数
strlen
strlen
函数用于求出字符串的长度,其判定结束的标志为\0
。
大部分情况下,\0
出现在字符串的结尾,但是如果\0
出现在字符串的中间,那么我们就无法得到字符串的正确长度。
strcpy
strcpy
函数用于拷贝字符串,其参数如下:
destination
:拷贝后字符串存放的空间
source
:字符串的来源
返回值为char*
,指向拷贝后的destination
,方便链式访问。
- 对于
source
来说,其结束的标志依然为'\0'
,其默认带有的'\0'
也会被拷贝到指定字符串的末尾。 destination
必须指向可以修改的空间,并且足以放置source
的字符个数。
strncpy
是一个与strcpy
功能一致的函数,但是其可以限制拷贝字符的个数。
对于strcpy
,其拷贝字符的个数就是source
的'\0'
之前字符个数。strncpy
可以传入第三个参数,用于规定拷贝字符的个数。
示例:
char arr[100] = { 0 }; strncpy(arr, "hello", 3);
以上代码把"hello"
的前三个字符”hel“
拷贝进了arr
中。
另外,如果字符串的长度不足n
,那么少的位置会用\0
补充。
strcat
strcat
函数用于对字符串进行追加,其会把source
追加到destination
的末尾。所以destination
和source
都必须由\0
结尾。
示例:
char arr[100] = "hello"; strcat(arr, "world");
追加后,arr
内部存储的就是helloworld
了。
注意事项:
- C语言标准没有要求此函数可以自追加,这取决于编译器的实现
destination
必须是可以修改的
同样的,这个函数也有限制长度的版本strncat
。第三个参数用于限定追加字符串的长度:当字符串长度超过n,则只追加n个字符;当字符串长度小于n,那么追加完这个字符串就不再追加。
strcmp
此函数用于比较两个字符串的大小
- 当
str1
大于str2
,返回大于0的值 - 当
str1
等于str2
,返回0 - 当
str1
小于str2
,返回小于0的值
比较的是两个字符串的字典序,而非长度。
同样的,该函数存在一个限制长度的版本strncmp
,比较两个字符串的前n个字符。
strstr
此函数用于进行字符串查找,即在str1
中查找str2
。
如果找到了,返回
str1
中指向str2
的指针如果没找到,返回
NULL
strtok
strtok
函数用于对字符串进行分割,其有两个参数:
str
:被分割的字符串
delimiters
:分隔符组成的字符串
比如delimiters
如果是“@#!
”,那么这个字符串遇到#
,@
,!
时就会将字符串进行分隔。
当在字符串中找到了对应的分隔符:
- 将这个分隔符改为
\0
- 返回指向这个字符的指针
如果没找到字符,或者遇到末尾,此时返回NULL
。
另外地,strtok
如果对str
传入了NULL
,下一次会从上一次更改的地方开始查找。
示例:
char arr[100] = "hello!world?cs@dn"; const char* p = "!?@"; for (char* r = strtok(arr, p); r != NULL; r = strtok(NULL, p)) { printf("%s\n", r); }
以上代码中,第一次调用strtok
传入了arr
,后续都传入NULL
,从而完成整个字符串的分隔:
输出结果:
hello world cs dn
内存函数
C语言中的内存函数用于对内存进行操控,主要包括内存的拷贝,初始化,以及内存数据的比较。
memcpy
函数用于对内存进行拷贝,此函数包含三个参数:
destination
:目的地,即拷贝后的数据存入的地方
source
:源,即被拷贝的内存
num
:字节数
示例:
int arr1[] = { 1,2,3,4,5 }; int arr2[5] = { 0 }; memcpy(arr2, arr1, sizeof(int) * 5);
以上代码,完成了两个数组之间的内存拷贝,拷贝方向为:从arr1
拷贝到arr2
,共拷贝了sizeof(int) * 5 = 20
个字节。
现在我们模拟实现一个memecpy
:
void* my_memcpy(void* destination, void* source, size_t num) { void* ret = destination; assert(destination && source); while (num--) { *(char*)destination = *(char*)source; destination = (char*)destination + 1; source = (char*)source + 1; } return ret; }
可以看到,memcpy
的拷贝是以字节为单位,将传入的void*
指针转为char*
指针,然后一个一个字节进行拷贝。这样这个函数就可以处理任何类型的内存拷贝。
但是这个函数存在一个问题,无法拷贝内存发生重叠的函数。
看到以下过程:
int arr[6] = { 1,2,3,4 }; memcpy(arr + 2, arr, sizeof(int) * 4 ;
此代码希望把arr
的前四个元素拷贝到后四位去,即:
1 2 1 2 3 4
实际输出结果:
1 2 1 2 1 2
为什么会这样?
看到以下过程:
我们要把蓝色区域的数值拷贝到红色区域,从左往右拷贝,第一次进行拷贝,由于内存重叠,1 2
会把3 4
覆盖,导致后续拷贝3 4
时拷贝到的还是1 2
。
C语言标准库没有要求memcpy
对重叠的内存进行处理,但是有的编译器可以处理这种情况,有的不可以。
为处理这个情况,C语言有专门的函数memmove
用于处理重叠内存的情况。
memmove
memmove
函数也用于进行内存拷贝,与memcpy
不同的是,C标准规定其可以处理内存的重叠。
示例:
int arr[6] = { 1,2,3,4 }; memmove(arr + 2, arr, sizeof(int) * 4 ;
同样的代码,输出结果为:
1 2 1 2 3 4
这是如何做到的?
其实就是一个简单的拷贝方向的问题:
当我们把以上的重叠拷贝,从右向左拷贝:
可以看到,虽然发生了内存的重叠,但是我们先把会被覆盖的数据拷贝好,然后再覆盖重叠区域的数据,此时我们的拷贝就正常运行了。
所以memmove
的实现中,我们要根据内存的重叠情况,来控制内存的拷贝方向,以保证重叠的数据先被拷贝。
代码如下:
void* my_memmove(void* destination, void* source, size_t num) { void* ret = destination; assert(destination && source); if (destination < source) { while (num--) { *(char*)destination = *(char*)source; destination = (char*)destination + 1; source = (char*)source + 1; } } else { while (num--) { *((char*)destination + num) = *((char*)source + num); } } return ret; }
当destination
指针小于source
,我们就从低地址向高地址拷贝
当
destination
指针大于source
,我们就从高地址向低地址拷贝
memset
memset
函数用于对内存进行初始化,其包含三个参数:
ptr
:指向待初始化内存的指针
value
:希望内存被初始化的值
num
:想要初始化内存的字节数
此函数只能按照字节初始化内存,比如以下代码:
int arr[5] = { 0 }; memset(arr, 1, sizeof(int) * 5);
以上代码并不是把数组中的五个int
元素初始化为1,而是把每个字节都初始化为00000001
。
memcmp
此函数用于比较内存中的数据,ptr1
与ptr2
是被比较的内存。
其按照字节进行比较,从前往后,一个一个字节比较ASCII码值,当某一对字节的ASCII码值不同,此时该字节ASCII码值大的内存,就是比较大的。