zoukankan      html  css  js  c++  java
  • <C语言> C99 Restrict memcpy 内存重叠

    https://my.oschina.net/zidanzzg/blog/812887

    https://www.cnblogs.com/dylancao/p/9951838.html

    C语言关键字,编译器优化时使用,不要对编译器撒谎,如果把一个指针定义成Restrict , 编译器会相信你,并对程序进行优化,如果出现内存重叠的问题,

    编译器不会替你排查。memcpy()会有内存重叠的问题,memove()会提前帮你检查是否有内存重叠的问题。

    visual studio 把函数和变量定义成 Restrict 的方法不一样。

    变量 : int * __restrict

    函数: __declspec(restrict) pointer_return_type function();

    __declspec(restrict) float * ma(int size) { float * retval; retval = memptr; memptr += size; return retval; }

    c99中新增加了一个类型定义,就是restrict。

    概括的说,关键字restrict只用于限定指针;该关键字用于告知编译器,所有修改该指针所指向内容的操作全部都是基于(base on)该指针的,即不存在其它进行修改操作的途径;这样的后果是帮助编译器进行更好的代码优化,生成更有效率的汇编代码。

    举个简单的例子

    int foo (int* x, int* y) {
        *x = 0;
        *y = 1;
        return *x;
    }

    很显然函数foo()的返回值是0,除非参数x和y的值相同。可以想象,99%的情况下该函数都会返回0而不是1。然而编译起必须保证生成100%正确的代码,因此,编译器不能将原有代码替换成下面的更优版本

    int f (int* x, int* y) {
        *x = 0;
        *y = 1;
        return 0;
    }

    现在我们有了restrict这个关键字,就可以利用它来帮助编译器安全的进行代码优化了

    int f (int *restrict x, int *restrict y) {
        *x = 0;
        *y = 1;
        return *x;
    }

    此时,由于指针 x 是修改 *x的唯一途径,编译器可以确认 “*y=1; ”这行代码不会修改 *x的内容,因此可以安全的优化为

    int f (int *restrict x, int *restrict y) {
        *x = 0;
        *y = 1;
        return 0;
    }

    最后注意一点,restrict是C99中定义的关键字,C++目前并未引入;在GCC可通过使用参数” -std=c99” 
    来开启对C99的支持

    下面是我从C语言核心技术一书上摘的:

    void *memcpy( void * restrict dest , const void * restrict src, size_t n) 

    这是一个很有用的内存复制函数,由于两个参数都加了restrict限定,所以两块区域不能重叠,即 dest指针所指的区域,不能让别的指针来修改,即src的指针不能修改. 相对应的别一个函数 memmove(void *dest, const void *src, size_t)则可以重叠。

    概念:

      restrict,C语言中的一种类型限定符(Type Qualifiers),用于告诉编译器,对象已经被指针所引用,不能通过除该指针外所有其他直接或间接的方式修改该对象的内容。

      渊源:

      restrict是c99标准引入的,它只可以用于限定和约束指针,并表明指针是访问一个数据对象的唯一且初始的方式.即它告诉编译器,所有修改该指针所指向内存中内容的操作都必须通过该指针来修改,而不能通过其它途径(其它变量或指针)来修改;这样做的好处是,能帮助编译器进行更好的优化代码,生成更有效率的汇编代码.如 int *restrict ptr, ptr 指向的内存单元只能被 ptr访问到,任何同样指向这个内存单元的其他指针都是未定义的,直白点就是无效指针。restrict 的出现是因为 C 语言本身固有的缺陷,C 程序员应当主动地规避这个缺陷,而编译器也会很配合地优化你的代码.

      使用场景:

    • 非常需要性能。
    • 需要改写指针的所指物。
    • 明确知道某两个指针在业务逻辑上不会、也不能重叠

      例子:

    复制代码
     1 #include <stdio.h>
     2 
     3 int foo(int *a, int *b)
     4 {
     5     *a = 5;
     6     *b = 6;
     7     return *a + *b;
     8 }
     9  
    10 int rfoo(int *restrict a, int *restrict b)
    11 {
    12     *a = 5;
    13     *b = 6;
    14     return *a + *b;
    15 }
    16 
    17 int main()
    18 {
    19     int i =0;
    20     int *a = &i;
    21     int *b = &i;
    22     
    23     printf("%d ",foo(a,b));
    24     printf("%d ", rfoo(a,b));
    25 
    26 }
    复制代码

      在gcc 8.1 下的运行结果:

      

      不过,我有一点是疑惑的,暂时没有想清楚,就是我在自己的ubuntu 16.04上编译,一直是不会运行出来11的结果,感觉是这个关键字没有起作用,网上查了一下没有查到原因,请知道答案的朋友解释一下,多谢.

    参考文档:

    1 https://en.cppreference.com/w/c/language/restrict

    memcpy是C语言中的库函数,在头文件string.h中,作用是拷贝一定长度的内存的内容,原型分别如下:

    void *memcpy(void *dest, const void *src, size_t count)

    使用memcpy时,有可能会遇到内存重叠的问题:

    第一种情况下,拷贝重叠的区域不会出现问题,内容均可以正确的被拷贝。
    第二种情况下,问题出现在右边的两个字节,这两个字节的原来的内容首先就被覆盖了,而且没有保存。所以接下来拷贝的时候,拷贝的是已经被覆盖的内容,显然这是有问题的。

    通过memmove可以避免这一问题。memmove和memcpy实现一样的功能:内存拷贝。原型如下:

    void *memmove(void *dest, const void *src, size_t count)

    以下几点你需要了解:

    1. memove可以避免内存拷贝时的重叠问题。
    2. 实际上,memcpy只是memmove的一个子集。
    3. memcpy比memmove的速度要快一些。

    有兴趣的,可以看看linux的源码,实现很简单,一看就明白。

    /**
     * memcpy - Copy one area of memory to another
     * @dest: Where to copy to
     * @src: Where to copy from
     * @count: The size of the area.
     *
     * You should not use this function to access IO space, use memcpy_toio()
     * or memcpy_fromio() instead.
     */
    void *memcpy(void *dest, const void *src, size_t count)
    {
    	char *tmp = dest;
    	const char *s = src;
    
    	while (count--)
    		*tmp++ = *s++;
    	return dest;
    }
    
    /**
     * memmove - Copy one area of memory to another
     * @dest: Where to copy to
     * @src: Where to copy from
     * @count: The size of the area.
     *
     * Unlike memcpy(), memmove() copes with overlapping areas.
     */
    void *memmove(void *dest, const void *src, size_t count)
    {
    	char *tmp;
    	const char *s;
    
    	if (dest <= src) {
    		tmp = dest;
    		s = src;
    		while (count--)
    			*tmp++ = *s++;
    	} else {
    		tmp = dest;
    		tmp += count;
    		s = src;
    		s += count;
    		while (count--)
    			*--tmp = *--s;
    	}
    	return dest;
    }

     

  • 相关阅读:
    Windows XP下 Android开发环境 搭建
    Android程序的入口点
    在eclipse里 新建android项目时 提示找不到proguard.cfg
    64位WIN7系统 下 搭建Android开发环境
    在eclipse里 新建android项目时 提示找不到proguard.cfg
    This Android SDK requires Android Developer Toolkit version 20.0.0 or above
    This Android SDK requires Android Developer Toolkit version 20.0.0 or above
    Android requires compiler compliance level 5.0 or 6.0. Found '1.4' instead
    Windows XP下 Android开发环境 搭建
    Android程序的入口点
  • 原文地址:https://www.cnblogs.com/focus-z/p/11337001.html
Copyright © 2011-2022 走看看