【问题标题】:memcpy(), what should the value of the size parameter be?memcpy(),size参数的值应该是多少?
【发布时间】:2011-02-10 11:36:59
【问题描述】:

我想将一个int 数组复制到另一个int 数组。它们对长度使用相同的定义,因此它们将始终具有相同的长度。

memcpy()memcpy() 的以下两种尺寸参数替代方案的优缺点是什么?

memcpy(dst, src, ARRAY_LENGTH*sizeof(int));

memcpy(dst, src, sizeof(dst));

第二个选项总是有效吗?不管内容如何?

对最后一个有利的一件事是,如果要更改数组,则更新memcpy() 将是一些内务。

【问题讨论】:

  • 这完全取决于你如何声明dst(在一定程度上,src 是否等于或大于dst)。

标签: c arrays sizeof memcpy


【解决方案1】:

只要将dst 声明为具有大小的数组,sizeof 就会返回该数组的大小(以字节为单位):

int dst[ARRAY_LENGTH];

memcpy( dst, src, sizeof(dst) ); // Good, sizeof(dst) returns sizeof(int) * ARRAY_LENGTH

如果dst 恰好是指向此类数组的第一个元素的指针(与数组本身的类型相同),它将不起作用:

int buffer[ARRAY_LENGTH];
int* dst = &buffer[0];

memcpy( dst, src, sizeof(dst) ); // Bad, sizeof(dst) returns sizeof(int*)

【讨论】:

    【解决方案2】:

    sizeof(dst) 仅当dst 是一个在编译时知道其大小的数组时才是正确的:例如int arr[ARRAY_LENGTH] 或C99 可变长度数组;否则返回指针的大小,而不是目标数组的长度。

    为避免将来出现错误,请保持一致并首选第一种形式:类型的大小 * 长度。

    【讨论】:

    • 真的吗?我做了 int arr[10]; cout
    • 啊是的,因为它是静态分配的
    • 答案仍然不正确,因为在 C99 中 sizeof 将返回一个可变长度数组的大小,该大小只有在运行时才知道(通常)。
    • dreamlax > 我把它添加到了答案中
    • 如果声明为 int dst[10] , sizeof(dst) 返回数组的大小
    【解决方案3】:

    如果你有一个数组(真正的),你可以使用sizeof(array) 技巧,但请注意,如果你重构代码并将其推送到数组已衰减为指针的地方(或者如果内存最初是在指针(malloc/new)中分配,您需要传递一个已知大小。

    忽略源和目标的相对大小,也就是说,假设它们在其余讨论中是相同的,如果你使用 C++,我会推荐一个元编程技巧,它会给你一个类型安全的数组大小计数和如果您尝试将其与指针一起使用,将无法编译:

    template <typename T, int N>
    inline int array_memory_size( T (&a)[N] ) { return sizeof a; }
    

    这样:

    int main() {
       int array[10];
       int *ptr = array;
       int orig[10] = { 0 };
       memcpy( array, orig, array_memory_size(array) ); // ok
       //memcpy( ptr, orig, array_memory_size(ptr) ); // compilation error
    }
    

    如果您在任何时候重构并且代码移动到数组已衰减的位置(或者您将静态数组替换为动态分配的数组),编译器会告诉您需要更正大小计算。

    【讨论】:

    • 我什至没有注意到这个问题被标记为c++,+1!
    • 非常好,但是return sizeof a; 就足够了。
    【解决方案4】:

    如果你使用 malloc 分配,你必须说明数组的大小

    int * src = malloc(ARRAY_LENGTH*sizeof(*src));
    int * dst1 = malloc(ARRAY_LENGTH*sizeof(*dst1));
    memcpy(dst1,src,ARRAY_LENGTH*sizeof(*dst1));
    

    如果你已经分配了一个静态数组,你可以使用sizeof

    int dst2[ARRAY_LENGTH];
    memcpy(dst2,src,sizeof(dst2));
    

    【讨论】:

      【解决方案5】:

      第二个选项总是有效吗?不看内容?

      仅当您添加回缺少的) 时,第二个选项才有效并且 dst 是一个静态数组(即int[123] 类型)。

      如果dst 的大小未知(即int[]),则sizeof dst 只返回指针大小,因为dst 已衰减为指针。在这种情况下,您需要使用sizeof(*dst)*ARRAY_LENGTH

      【讨论】:

      • +1,但我不认为“静态”是数组类型变量的正确术语。它可能是自动的,并且“静态”在 C 尤其是 C++ 中已经有很多含义。你的“ie”更像它。
      【解决方案6】:

      假设 dst 是 int* 类型,sizeof(dst) 将返回指针本身的大小(即 32 位系统上为 4,64 位系统上为 8),因此您的第二个示例将只复制这么多字节,而第一个将正确使用内容的实际大小。

      【讨论】:

        【解决方案7】:

        第二个选项总是有效吗?不看内容?

        只有同时满足两个条件才会起作用:

        • dst 是常规数组,不是指针
        • srcdst 大小相同

        【讨论】:

        • 或者src大于dst
        【解决方案8】:

        sizeof(X) 总是给你“X”的字节数 如果 X 是 10 的 uint16_t 数组,则 sizeof(X) 将返回 20

        uint16_t X[10]={0};
        cout<<"sizeof x: "<<sizeof(X);
        
        $> sizeof x: 20
        

        如果你想要元素的数量,你必须做一些字节算术:
        8bit = 1byte
        16 位 = 2 字节
        32 位 = 4 字节
        64 位 = 8 个字节

        所以要获得你可以做的元素数量:

         numb_of_elements = ( sizeof(X)/sizeof(X[0]) );
        

        导致:

        uint32_t source[100]={0};
        memcpy((void*) dest, (void*) source, ( sizeof(source)/sizeof(source[0]) ));
        

        当然,您可能希望将 ( sizeof(X)/sizeof(X[0]) ) 设为常量/变量,这样您就不必每次都进行计算。( 我不知道编译器是否会一直优化这个)

        【讨论】:

          【解决方案9】:

          memcpy(),size参数的值应该是多少?

          它应该是源缓冲区大小和目标缓冲区大小之间的最小值。

          传统上,使用源缓冲区的大小。有时会溢出目标缓冲区...因此最好使用该函数的“更安全”版本:同时指定源缓冲区和目标缓冲区大小的函数。

          您可以通过ISO/IEC TR24731 获得“更安全”的功能。它还有很多其他功能,例如一致的返回值和一致的字符串处理行为。

          “更安全”的函数现在是 C 标准的一部分,因此它应该在任何地方都可用。所以你应该使用memcpy_s

          您不能在 Linux 上使用它,因为它不提供这些功能(不要相信关于符合标准的营销炒作)。在 Linux 上,您应该“推出自己的”包装器。

          并不是每个人都喜欢更安全的功能。例如,参见Do you use the TR 24731 'safe' functions?。关于这一点我只能说:Multiple libunp buffer overflows。数以百万计的路由器和网关受到多个漏洞的影响,并且许多仍未打补丁。它们是由于更安全的功能可以阻止的错误造成的。向所有说“不要使用这个微软废话”的人 +1。

          【讨论】:

            【解决方案10】:

            这取决于。 arr 和 pointer 都是数组,但 sizeof() 只返回正确的 arr 大小,这是在编译时声明的。

            int main() {
                    int arr[10];
                    int * pointer;
                    pointer = (int *) malloc(10 * sizeof(int));
                    printf("%d\n", sizeof(arr)); // 40
                    printf("%d\n", sizeof(pointer)); // 4 or 8
                    free(pointer);
            }
            

            【讨论】:

            • 不要忘记 C99 可变长度数组。它们的长度在运行时确定,但sizeof 仍然有效。
            【解决方案11】:

            如果 dst 是从堆中分配的(例如使用 malloc),则第二种解决方案将不起作用。 sizeof(dst) 只有在编译器知道时才会起作用。例如,以下示例将失败,因为 sizeof(dst) 将等于指针的大小(4-8 字节)。

            #define ARRAY_LENGTH 10
            int *dst;
            
            dst = malloc(ARRAY_LENGTH*sizeof(int));
            memcpy(dst, src, sizeof(dst)); // sizeof dst in this case would be 4 bytes on 32 bit system
            

            此代码段每次都会起作用:

            #define ARRAY_LENGTH 10
            int *dst;
            
            dst = malloc(ARRAY_LENGTH*sizeof(int));
            memcpy(dst, src, ARRAY_LENGTH*sizeof(int)); // sizeof would be 40 bytes
            

            【讨论】:

              【解决方案12】:

              怎么样?

              memcpy(dst, src, &src[ARRAY_LENGTH] - &src[0]);
              

              即使单个元素的大小小于实际数组中每个项目的大小,这也应该有效。

              【讨论】:

              • 我不清楚您认为这会在哪些情况下有效,但原始表格不会。
              • @Dennis:你说得对,我可能在考虑解压structs,其中连续的元素可能不会立即相互跟随......在C数组中,你可以假设&amp;a[n+1] == &amp;a[n] + sizeof (a[0]) ,或&amp;a[n] == a + n * sizeof (a[0]),所以原始表单可以正常工作。很抱歉造成混乱。
              猜你喜欢
              • 2018-11-27
              • 2018-02-07
              • 2019-02-16
              • 2018-12-11
              • 2022-01-13
              • 2020-12-13
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多