1 strerror
char * strerror ( int errnum )
从语言的库函数在运行的时候,如果发生错误,就会将错误码放在一个变量中,这个变量是errnor.
//strerror(errno)
// fopen
//FILE * fopen ( const char * filename, const char * mode );
//如果打开文件成功,就返回一个有效的指针,如果打开失败,就返回空指针。
int main()
{//打开该文件
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
printf("打开文件失败\n");
return 1;
}
//读文件
//关闭文件
fclose(pf);
return 0;
}
在这边我们就可以使用strerror来打印报错信息
这边显示没有该文件,我们在工程里面创建一个该文件。
另外还有一个报错的函数是perror函数
void perror ( cconst char*string )
直接打印错误信息,在打印错误信息前,会先打印自定义的信息,perror=printf+strerror
int main()
{//打开该文件
FILE* pf = fopen("test.txt", "r");
if (pf == NULL)
{
perror("fopen");
return 1;
}
//读文件
//关闭文件
fclose(pf);
pf = NULL;
return 0;
}
以上就是字符串函数介绍。
但是在代码中常常有其他类型的函数,如结构体,整型,浮点型等等,我们就需要学习一些内存函数。如memcpy,memmove,memcpy,memset等等
2. 内存函数的拷贝 memcpy
void * memcpy ( void * destination, const void * source, size_t num );
函数介绍:
memcpy函数是一个用于拷贝两个不相关的内存块的函数。memcpy函数会从src的位置开始向后复制count个字节的数据到dest的内存位置,并返回dest的首地址。
注意点:
函数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。这个函数在遇到 ‘\0’ 的时候并不会停下来。
如果source和destination有任何的重叠,复制的结果都是未定义的。
例如我要从arr1数组中打印3,4,5,6,7五个元素到arr2数组中去。
int main()
{
int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
memcpy(arr2,arr1+2,20);
return 0;
}
因为一个整型是4个字节,所以我们可以移动num=20个字节就可以移动5个元素了。
2.1 memcpy的模拟实现
void* my_memmcpy(void* dest, void* src, size_t num)
{
char* ret = dest;
while (num--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
int main()
{
int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
int arr2[10] = { 0 };
my_memcpy(arr2, arr1 + 2, 20);
return 0;
}
如图中就是要src打印的数字,我们把src强制类型转化成char*的类型,首先是3的地址解引用将src的值赋给dest。
然后指针+1,这样在就可以按照一个字节一个字节的进行打印,num为0时,为假就跳出循环。
3 memmove(字符串拷贝函数)
函数介绍以及与(memcpy的区别)
void * memmove ( void * destination, const void * source, size_t num );
我们发现memmove函数的参数和返回值与memcpy函数一模一样。没错,memmove函数和memcpy函数的功能一样,也是从src的位置开始向后复制count个字节的数据到dest的内存位置,并返回dest的首地址。
那么它们有什么不同呢?
memmove函数和memcpy函数的差别就是,memmove函数的源内存块和目标内存块是可以重叠的,而memcpy函数的源内存块和目标内存块是不可以重叠的。
如果我们要将arr数组中的1,2,3,4,5,6,7,8,9,10我们要打印1,2,3,4,5放到3,4,5,6,7上去,应该怎么打印呢?
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
memmove(arr+2,arr,20);
return 0;
}
但是使用memcpy的时候,会打印成1,2,1,1,1为啥会变成这样呢?原因是因为前面的3被覆盖成1,导致后面全变成1了。但是我们从后面打印呢?
就会变成7变5,6变4,5变3,4变2,3变1,最后打印就变成了1,2,1,2,3,4,5,8,9,10.
2 模拟实现
我们发现,当源内存块和目标内存块发生重叠的时候,我们不能像memcpy函数一样直接从前向后依次拷贝,我们需要分情况讨论。
在这里插入图片描述
通过画图,我们可以根据dest指针和src指针的相对位置将情况分为三类:
第一类:dest指针位于src内存块左边,采用从前向后拷贝。
第二类:dest指针位于src内存块内,采用从后向前拷贝。
第三类:dest指针位于src内存块右边,采用从前向后和从后向前均可以。
注:当dest指针与src指针位于同一位置时不用拷贝。
#include<assert.h>
void* my_memmove(void* dest,void* scr, size_t num)
{
assert(dest && scr);
char* ret = dest;
if (dest < scr)
{
while (num--)
{
*(char*)dest = *(char*)scr;
dest = (char*)dest + 1;
scr = (char*)scr + 1;
}
}
else
{
while (num--)
{
*((char*)dest + num) = *((char*)scr + num);
}
}
return ret;
}
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
my_memmove(arr+2, arr, 20);
return 0;
}
三
memcmp
int memcmp ( const void * ptr1,
const void * ptr2, size_t num );
memcmp函数是一个用于比较两个内存块大小的函数。它会比较从buf1和buf2指针开始的count个字节,当buf1大于buf2的时候返回一个大于0的数;当buf1等于buf2的时候返回0;当buf1小于buf2的时候返回一个小于0的数
int main()
{
int arr1[] = { 1,2,3,4,5,6,7 };
int arr2[] = { 1,2,3,5,6,7,8,9 };
int ret1=memcmp(arr1, arr2, 8);
int ret2=memcmp(arr1, arr2, 16);
printf("%d\n", ret1);
printf("%d\n", ret2);
return 0;
}
第四个 memset
void * memset ( void * ptr, int value, size_t num );
memset函数可以将内存块的某一部分设置为特定的字符。三个参数中,第一个参数是开始设置内存的起始位置,第二个参数是要将内存设置成的字符,第三个参数是从起始位置开始需要设置的内存的字节数。
int main()
{
char arr[] = "hello world";
memset(arr,'1',5);//将hello改成字符'1'
printf("%s\n", arr);
memset(arr + 6, '2', 5);//将world改成'2;
printf("%s\n", arr);
return 0;
}
如果用这个内存函数将整型数组arr中的元素全部改成1呢?
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};
memset(arr,'1',40);
return 0;
}
这样子可以吗?
我们可以调试一下看看。
我们会发现数字变得特别大。原因是memset函数是一个字节一个字节移动的·,每移动一个字节,就会改变成一个1。
调试到arr的地址,调试之后到memset就会变成