内存函数

在 string.h 库中除了操作字符串的函数外还有操作内存的函数,它们都以 mem 开头,这些函数不判断是否是合法的字符串(以 NULL 结尾),因此需要传入 size 作为参数

void memchr(const void str, int c, size_t n)
Searches for the first occurrence of the character c (an unsigned char) in the first n bytes of the string pointed to, by the argument str.
int memcmp(const void str1, const void str2, size_t n)
Compares the first n bytes of str1 and str2.
void memcpy(void dest, const void *src, size_t n)
Copies n characters from src to dest.
void memmove(void dest, const void *src, size_t n)
Another function to copy n characters from str2 to str1.
void memset(void str, int c, size_t n)
Copies the character c (an unsigned char) to the first n characters of the string pointed to, by the argument str.

restrict

C99 之后在源码声明中多出了 restrict 或者 restrict 关键字,目前不是 C++ 标准,但是被很多编译器支持。

restrict 的含义是由编程者向编译器声明,在这个指针的生命周期中,只有这个指针本身或者直接由它产生的指针(如 ptr + 1)能够用来访问该指针指向的对象。他的作用是限制指针别名,帮助编译器做优化.如果该指针与另外一个指针指向同一对象,那么结果是未定义的.

  1. int add (int* a, int* b)
  2. {
  3. *a = 10;
  4. *b = 12;
  5. return *a + *b;
  6. }

这个函数直观的作用便是将 a 和 b 指向的 int 对象赋值为 10 和 12, 然后返回他们两个的和,可以看出最后 return a + b 这条语句中是不需要的访问内存单元的,a 中一定是 10, b 中一定是 12, 所以编译器可以在这里做优化直接返回 22。但是如果 a 和 b 指向的是同一个 int 对象呢? 这时编译器就不应该做优化,实际上编译器的优化策略是保守的,当无法判断这里能不能优化时,便不做优化,所以可得如下汇编代码,这已经是编译器在已知信息下所能做的最好的优化,优化等级是 -O3

但是如果加上 restrict 修饰(编译器 GCC),编译器已经得知 a, b 两指针不会指向同一单元(此点应由编程者保证),所以在这里会减少一次内存的访问

C11 的安全函数

  1. _CRTIMP errno_t __cdecl strcpy_s(char *_Dst, rsize_t _SizeInBytes, const char *_Src);

这些函数在名字后面加了 _s 并且函数原型发生了改变,返回了一个错误码,说明这些函数会对不符合预期的使用进行报错。

定义 STDC_WANT_LIB _EXIT1 来开启标准库补充库,这样才可以使用这些函数,宏判断 _STDC_LIB _EXIT1 来判断有没有定义_

memcpy的内存覆盖

内存操作函数 - 图1

当 memcpy 源字符串给目标字符串赋值时,上面情况会出现问题,当src给dest赋值直到dest的开始时,即不超过dest的偏移量时都没有什么问题,但是一旦超过偏移量,由于前面的赋值操作已经将字符串给改变了,然后再用改变过后的字符串再给dest赋值,这就导致了前面内容复制重复了。

为了解决这个问题,我们有 memmove 函数,如果目标区域与源区域没有重叠,则和 memcpy() 函数功能相同。有上面冲突的话,先将内容复制到类似缓冲区的地方,再用缓冲区中的内容覆盖 dest 指向的内存。

memmove 实现上会比 memcpy 较慢一点。