【C语言】第十章-字符串及内存函数

第十章 字符串及内存操作函数

  在这一章我们重点讲解几个字符串及内存的标准库函数,这些函数都是标准库中提前准备好了的,但是我们有必要了解它们的功能,并且模拟它们的实现,这对我们将来的程序编写有很多指导性意义。

字符串函数

strlen()

  这个函数我相信大家再熟悉不过了,就是普通的计算字符串长度的函数,不过我们要注意这个函数传入的参数必须是一个以空字符结尾的字符串。不然就会出现未定义行为。这个函数返回的长度是字符串不包含末尾空字符的长度。这点很重要,很多同学都会在这点上出错。接下来我们对这个函数进行基本实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
size_t Strlen(const char* str)
{
assert(str != NULL);//有效性检验
size_t i = 0;
while(str[i] != '\0')
{
++i;
}
return i;
}
int main()
{
char str[] = "123";
printf("%d\n", Strlen(str));
}



[misaki@localhost test]$ ./Main
3

  我们在实现函数的时候一定要注意有效性检验,这里只做了最基本的检验,在实际开发中要注意的远远要多余此。

strcpy()

  这个函数是字符串复制函数,可以将两个参数中第二个字符串的值完全赋值给第一个字符串,但是要注意第一个字符串一定要有足够大小的空间。接下来提供一种实现思路。

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
char* Strcpy(char* destination, const char* source)
{
assert(destination != NULL);
assert(source != NULL);
size_t i;
for(i = 0; i < strlen(source); i++)
{
destination[i] = source[i];

}
destination[i] = '\0';
return destination;
}
int main()
{
char str[10] = {0};
char str2[] = "12314524";
Strcpy(str, str2);
printf("str2 = %s\n", str2);
printf("str = %s\n", str);
}



[misaki@localhost test]$ ./Main
str2 = 12314524
str = 12314524

strcat()

  这个是字符串拼接函数,功能是可以将第二个字符串拼接到第一个字符串的末尾,不过要注意第一个字符串要有足够大的空间。

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
char* Strcat(char* destination, const char* source )
{
size_t a = strlen(destination);
size_t b = strlen(source) + 1;
for(size_t i = 0; i < b; i++)
{
destination[a] = source[i];
a++;
}
return destination;
}
int main()
{
char str[1024] = "abc";
char str2[] = "12314524";
printf("str = %s, str2 = %s\n", str, str2);
Strcat(str, str2);
printf("拼接后:str = %s, str2 = %s\n", str, str2);
}

[misaki@localhost test]$ ./Main
str = abc, str2 = 12314524
拼接后:str = abc12314524, str2 = 12314524

strcmp()

  这个是字符串比较函数,比较前后两个字符串的大小,如果前一个大于后一个则返回正数,相等返回0,小于返回负数。然而比较字符串的时候则是通过两个字符串从前致后每个字符串的字符的ASCII码的大小来进行比较的,如何相同则比较下一个字符知道出现第一个不相等的字符位置,然后再返回相应的结果。这点从下面的实现代码上可以明确看出。

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
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>                            
#include <stdlib.h>
#include <string.h>
#include <assert.h>
int Strcmp(const char* str1, const char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
while(*str1 != '\0' && *str2 != '\0')
{
if(*str1 < *str2)
{
return -1;
}
else if(*str1 > *str2)
{
return 1;
}
else
{
++str1;
++str2;
}
}
if(*str1 == '\0' && *str2 != '\0')
{
return -1;
}
if(*str1 != '\0' && *str2 == '\0')
{
return 1;
}
if(*str1 == '\0' && *str2 == '\0')
{
return 0;
}
}
int main()
{
char str[1024] = "abc";
char str2[] = "12314524";
if(Strcmp(str, str2) > 0)
{
printf("str > str2\n");
}
else if(Strcmp(str, str2) == 0)
{
printf("str == str2\n");
}
else
{
printf("str < str2\n");
}
}

[misaki@localhost test]$ ./Main
str > str2

strncpy()

  这个函数与strcpy()类似,不过它的功能增加了可以将指定数量的字符复制到目标字符串中,同时这个函数在一些处理上有一些变化。当我们给出的数字大于原字符串的长度,则目标字符串要用空字符补充多出来长度,如果小于或原字符串的长度则末尾不会自动添加空字符,因此我们尤其要除以这一点,然而这些规则都是C的标准中进行规定的。

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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
char* Strncpy(char* destination, const char* source, size_t num)
{
assert(destination != NULL);
assert(source != NULL);
size_t i = 0;
while(i < num && source[i] != '\0')
{
destination[i] = source[i];
i++;
}
if(i == num)
{
return destination;
}
if(source[i] == '\0')
{
for(; i < num; i++)
{
destination[i] = '\0';
}
return destination;
}
}
int main()
{
char str[1024] = {0};
char str2[] = "12314524";
printf("str2 = %s\n", str2);
Strncpy(str, str2, 3);
printf("复制三个字符后str = %s\n", str);
}



[misaki@localhost test]$ ./Main
str2 = 12314524
复制三个字符后str = 123

strncat()

  strncat()函数与strncpy()函数类似,也是同样加入了可以指定字符数量地将源字符串拼接致目标字符串的末尾。至于其中的特殊情况,比如num参数大于小于或等于原字符串的处理在官方文档上有明确要求和规定,这里不再赘述。

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
#include <stdio.h>                                              
#include <stdlib.h>
#include <string.h>
#include <assert.h>
char* Strncat(char* destination, const char* source, size_t num)
{
assert(destination != NULL);
assert(source != NULL);
size_t i = 0;
while(destination[i] != '\0')
{
i++;
}
size_t j = 0;
while(j < num && source[j] != '\0')
{
destination[i] = source[j];
j++;
i++;
}
destination[i] = '\0';
return destination;
}
int main()
{
char str[1024] = "abc";
char str2[] = "12314524";
printf("str = %s\n", str);
printf("str2 = %s\n", str2);
Strncat(str, str2, 3);
printf("拼接三个字符后:str = %s\n", str);
}



[misaki@localhost test]$ ./Main
str = abc
str2 = 12314524
拼接三个字符后:str = abc123

strncmp()

  strncmp()strcmp()类似,也是字符串比较函数,同样的也是将第一个字符串中的指定数量的字符与第二个字符串进行比较,返回规则也是完全一致。

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
48
49
#include <stdio.h>                                                          
#include <stdlib.h>
#include <string.h>
#include <assert.h>
int Strncmp(const char* str1, const char* str2, size_t num)
{
assert(str1 != NULL);
assert(str2 != NULL);
size_t i = 0;
while(str1[i] == str2[i] && i < num && str1[i] != '\0' && str2[i] != '\0')
{
i++;
}
if(i == num)
{
return 0;
}
if(str1[i] == '\0' && str2[i] == '\0')
{
return 0;
}
if(str1[i] > str2[i])
{
return 1;
}
if(str1[i] < str2[i])
{
return -1;
}
}
int main()
{
char str[1024] = "abc";
char str2[] = "12314524";
int a = Strncmp(str, str2, 3);
int b = Strncmp(str, str2, 0);
printf("str = %s\n", str);
printf("str2 = %s\n", str2);
printf("比较前三个字符串的结果是:%d\n", a);
printf("比较前零个字符串的结果是:%d\n", b);
}



[misaki@localhost test]$ ./Main
str = abc
str2 = 12314524
比较前三个字符串的结果是:1
比较前零个字符串的结果是:0

strstr()

  这个函数的功能相比之前的就较为复杂,是在str1查找为str2的子串。这里的内容与数据结构略有相关,简单来说就是在第一个字符串中找是否存在一串和第二个字符串完全一致的串,如果存在则返回第一次出现的首字符的指针,如果不存在返回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
48
#include <stdio.h>                                                       
#include <stdlib.h>
#include <string.h>
#include <assert.h>
const char* Strstr(const char* str1, const char* str2)
{
assert(str1 != NULL);
assert(str2 != NULL);
if(*str1 == '\0' || *str2 == '\0')
{
return NULL;
}
const char* black_ptr = str1;
while(*black_ptr != '\0')
{
const char* red_ptr = black_ptr;
const char* sub_ptr = str2;
while(*red_ptr != '\0' && *sub_ptr != '\0' && (*red_ptr == *sub_ptr))
{
++red_ptr;
++sub_ptr;
}
if(*sub_ptr == '\0')
{
//找到了
return black_ptr;
}
++black_ptr;
}
//没找到
return NULL;
}
int main()
{
char str[1024] = "123456";
char str2[] = "234";
const char* str3 = Strstr(str, str2);
printf("str = %s\n", str);
printf("str2 = %s\n", str2);
printf("str2中存在str并打印:%s\n",str3);
}



[misaki@localhost test]$ ./Main
str = 123456
str2 = 234
str2中存在str并打印:23456

strtok()

  这个函数是字符串分割函数,它可以将指定的字符串以指定的字符进行分割,并且随着参数的该表可以记录上次分割结果并且继续上一次分割进度继续进行分割,这个字符串函数使用和实现较为复杂,这里不做细致讲解。

strerror()

  strerror()是错误信息报告函数,根据错误返回的错误码可以将其使用对应的错误信息并且打印。这里举个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>//要使用错误码我们需要引入这个头文件
#include <string.h>
int main()
{
FILE* file;
file = fopen("1.txt", "r");//打开这个文件实际上我的目录下并没有这个文件因此就会产生错误,
if(file == NULL)
{
printf("%s\n", strerror(errno));//打印最后一个错误码的错误信息
}
}




[misaki@localhost test]$ ./Main
No such file or directory

  不难发现打印的错误信息和我们编译错误信息十分相似,是的我们可以通过这种方式提醒用户哪里出现错误,但是目前这种报错方式十分落后。

内存操作函数

  常用的内存操作函数有很多,这里重点介绍两个,并且加以实现。

memcpy()

  这个函数与strncpy()十分类似,不过此时复制的已经不仅仅只能是字符串了,而是任何类型的数据都可以进行复制,最后一个参数num给出的则是目标复制的字节数。

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
#include <stdio.h>                                   
#include <stdlib.h>
#include <assert.h>
void* Memcpy(void* dest, const void* src, size_t num)
{
assert(dest != NULL);
assert(src != NULL);
char* pdest = (char*)dest;
char* psrc = (char*)src;
for(size_t i = 0; i < num; i++)
{
pdest[i] = psrc[i];
}
return dest;
}
int main()
{
int arr[] = {11,14,26};
int arr2[3];
for(int i = 0; i < 3; i++)
{
printf("%d\t", arr[i]);
}
printf("\n");
Memcpy(arr2, arr, sizeof(arr));
for(int i = 0; i < 3; i++)
{
printf("%d\t", arr2[i]);
}
printf("\n");
}



[misaki@localhost test]$ ./Main
11 14 26
11 14 26

memmove()

  这个函数与memcpy()类似,不过这个函数解决了memcpy()中两个缓冲区重叠可能会导致复制错误的问题,因此相比于memcpy()我更推荐于使用这个函数。

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
48
49
50
51
52
53
54
55
56
57
#include <stdio.h>                          
#include <stdlib.h>
#include <assert.h>
#include <stdint.h>
void* Memcpy(void* dest, const void* src, size_t num)
{
assert(dest != NULL);
assert(src != NULL);
char* pdest = (char*)dest;
char* psrc = (char*)src;
for(size_t i = 0; i < num; i++)
{
pdest[i] = psrc[i];
}
return dest;
}
void* Memmove(void* dest, const void* src, size_t num)
{
assert(dest != NULL);
assert(src != NULL);
char* pdest = (char*)dest;
char* psrc = (char*)src;
if(pdest > psrc && pdest < psrc + num)//缓冲区重叠,特殊处理
{
for(int64_t i = num - 1; i >= 0; i--)
{
pdest[i] = psrc[i];
}
}
else
{
Memcpy(dest, src, num);
}
return dest;
}
int main()
{
int arr[] = {11,14,26};
int arr2[3];
for(int i = 0; i < 3; i++)
{
printf("%d\t", arr[i]);
}
printf("\n");
Memmove(arr2, arr, sizeof(arr));
for(int i = 0; i < 3; i++)
{
printf("%d\t", arr2[i]);
}
printf("\n");
}



[misaki@localhost test]$ ./Main
11 14 26
11 14 26

  除了以上这些字符串函数和内存操作函数,在C语言中还有比较常用的字符处理及数学函数函数,都包含在标准库中,这些函数十分简单,容易上手,但是在开发中会给我们带来很多方便,这里不再详细介绍。

-------------本文结束感谢您的阅读!-------------
记录学习每一分,感谢您的赞助