与字符有关的较复杂库函数:
前言:当我们只是简单的了解C语言,我们会认识和掌握strcpy,strcmp等简单的库函数,如果想更进一步,我们或许需要掌握更加复杂的库函数。本篇文章就介绍了strtok、strerror、字符判断函数、memcpy、memcmp以及memset函数。
1、strtok函数
在函数定义中的str是目标字符串的地址,sep是分隔符的地址。 在使用这个函数之前我们需要知道这个隐藏的使用规则。 strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。) strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。 strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。 如果字符串中不存在更多的标记,则返回 NULL 指针。 有以上规则我们可以敲一个例子:
int main()
{
const char* p = "@.";
char arr[] = "123456789@qq.com";
char buf[50] = { 0 };
strcpy(buf, arr);
char* str = NULL;
str = strtok(arr, p);
printf("%s\n", str);
str = strtok(NULL, p);
printf("%s\n", str);
str = strtok(NULL, p);
printf("%s\n", str);
for (str = strtok(buf, p); str != NULL; str = strtok(NULL, p))
{
printf("%s\n", str);
}
return 0;
}
注:当我们知道字符串的时候我们可以确定使用strtok的此时,当未知字符串我们可使用for循环来完美实现。(第二种方法可以包含第一种) 运行结果:
2、strerror函数
我们先来实现报错:
#include <errno.h>
int main()
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%s\n", strerror(i));
}
return 0;
}
运行结果: 同时strerror函数有一个头文件与之对应—#include <errno.h> 然后我们再举一个现实的例子:
#include <errno.h>
int main()
{
FILE* p = fopen("test.txt", "r");
if (NULL == p)
{
printf("%s\n", strerror(errno));
return 0;
}
fclose(p);
p = NULL;
return 0;
}
此时我的工程内并没有这个test.txt文件。我们当然读取不到,所以就会报错,我们可以用strerror这个函数来找到报错原因。 运行结果:
3、字符串分类函数
这个函数也有自己的头文件—#include <ctype.h>。 例子:
int main()
{
printf("%d\n", isspace('!'));
printf("%d\n", isspace(' '));
char ch = '0';
if (ch >= '0' && ch <= '9')
{
printf("1\n");
}
if (isdigit(ch))
{
printf("2\n");
}
return 0;
}
还有例子:
int main()
{
char ch = 0;
ch = getchar();
if (islower(ch))
{
ch = toupper(ch);
}
else
{
ch = tolower(ch);
}
printf("%c\n", ch);
return 0;
}
4、memcpy
memcpy类似与strcpy,但是strcpy只能作用在字符串,而memcpy是利用内存而复制,所以可以作用于所有的数据类型。 他的函数定义: 分别是目标地址,源地址以及复制内容的字节大小。
void * memcpy ( void * destination, const void * source, size_t num );
例子:
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
int arr3[] = { 1,2,3,4,5,6,7,8,9 };
int arr4[5] = { 0 };
memcpy(arr4, arr3, 5*sizeof(arr3[0]));
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d\n", arr4[i]);
}
return 0;
}
运行结果: 因为原理比较明显,所以我们可以尝试进行memcpy的模拟实现。
void* my_memcpy(void* dest,const void* src, size_t num)
{
void* ret = dest;
assert(dest && src);
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
void* my_memcpy(void* dest, const void* src, size_t num);
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
int arr3[] = { 1,2,3,4,5,6,7,8,9 };
int arr4[5] = { 0 };
my_memcpy(arr4, arr3, 5 * sizeof(arr3[0]));
my_memcpy(arr2, arr1, 6*sizeof(arr1[0]));
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%d ", arr4[i]);
if (4 == i)
{
printf("\n");
}
}
for (i = 0; i < 6; i++)
{
printf("%c ", arr2[i]);
}
return 0;
}
运行结果:
5、memmove
和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。 如果源空间和目标空间出现重叠,就得使用memmove函数处理。
void * memmove ( void * destination, const void * source, size_t num )
两者对比例子:
void test1()
{
int arr3[] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(arr3 + 2, arr3, 5 * sizeof(arr3[0]));
int arr4[] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr4 + 2, arr4, 5 * sizeof(arr3[0]));
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr3[i]);
if (i == 9)
{
printf("\n");
}
}
for (i = 0; i < 10; i++)
{
printf("%d ", arr4[i]);
}
}
int main()
{
test1();
return 0;
}
运行结果; 我们看到两者一样,其实并不是因为两者没有差别,而是经过时间的流逝,在最新的vs里,memcpy的功能已经被优化了,达到了和memmove一样的效果,但是两者的实现方式还是有着根本性的不同的。
memmove的模拟实现:
void* my_memmove(void* dest,const void* src,size_t num)
{
void* ret = dest;
assert(dest && src);
if (dest < src)
{
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
while (num--)
{
*((char*)dest + num) = *((char*)src+ num);
}
}
return ret;
}
我们要根据内存中数据向前移动还是向后移动的情况下进行相反方向的拷贝。 运行结果:
6、memcmp
函数定义:
int memcmp ( const void * ptr1, const void * ptr2, size_t num );
int main()
{
int arr1[] = { 1,2,3,4,5 };
int arr2[] = { 1,2,3,4,5 };
int ret = memcmp(arr1, arr2, 8);
printf("%d\n", ret);
return 0;
}
运行结果: 其模拟实现可以参考:
int my_memcmp(const void *p1, const void *p2, size_t count)
{
assert(p1);
assert(p2);
char *dest = (char *)p1;
char *src = (char *)p2;
while (count && *dest == *src)
{
count--;
dest++;
src++;
}
if (count == 0)
{
return 0;
}
else
{
return *dest - *src - ‘\0’;
}
}
7、memset
它的功能是将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值, 块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作, 其返回值为指向S的指针。memset的作用是在一段内存块中填充某个给定的值,它是对较大的结构体或数组进行清零操作的一种最快方法。 函数定义:
void * memset ( void * ptr, int value, size_t num )
其中ptr是起始设置地址、value是要是设置的值、num是字节长度。
例子:
int main()
{
char arr[20] = { 0 };
memset(arr, 'x', 10);
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
memset(arr1, 0, 20);
printf(arr);
printf("\n");
for (int i = 0; i < 10; i++)
{
printf("%d ", arr1[i]);
}
return 0;
}
运行结果: 模拟实现:
void* my_memset(void* buf, int set, int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
*((char*)buf + i) = set;
}
return buf;
}
int main()
{
char buf[5];
int i = 0;
my_memset(buf, 0, 5);
for (i = 0; i < 5; i++)
{
printf("%s\n", buf[i]);
}
system("pause");
return 0;
}
其中的set 是ASCII值。
今天函数的介绍到此为止???
|