Copy 2D Array Using Memcpy

C memcpy 2D array

The size is in bytes, so you copy only 3 bytes - not even one integer.

memcpy(arr_copy, arr, 3);

You can see how big your array is by printing its size in bytes:

printf("sizeof(arr) = %zu\n", sizeof(arr));

So the correct memcpy code should be:

memcpy(arr_copy, arr, sizeof(arr));

Same error here:

memcpy(arr_copy[i], arr[i], 3);

It has to be:

memcpy(arr_copy[i], arr[i], sizeof(arr[i]));

Copy 2D array using memcpy?

If you actually had a 2-D array, that memcpy call would work. But you don't, you have width separate discontiguous 1-D arrays, collected in an array of pointers.

It is possible to dynamically allocate a contiguous block where row and column count both vary at runtime, and preserve the two-subscript access. You'll have to change the allocation code as follows:

GridUnit** newGrid;
newGrid = new GridUnit*[width];
newGrid[0] = new GridUnit[width * height];
for (int i = 1; i < width; i++)
newGrid[i] = newGrid[i-1] + height;

Deallocation becomes simpler:

delete[] newGrid[0];
delete[] newGrid;

There's no delete[] for newGrid[i] with i > 0 because they don't have their own blocks, they just point into the single large block. Because everything is contiguous, you can think of newGrid[0] either as a pointer to the first row (height elements), or the entire 2-D array (width * height elements).

And you can then access all the data as a single contiguous block:

memcpy(newGrid[0], oldGrid[0], height * width * sizeof newGrid[0][0]);

Of course, one shouldn't use raw pointers for memory ownership. A smart pointer will ensure the memory is properly deleted even with exceptional flow control. It would look like this:

std::unique_ptr<GridUnit[]> newData;
std::unique_ptr<GridUnit*[]> newGrid;
// before C++14 use // newData.reset(new GridUnit[width * height]);
newData = std::make_unique<GridUnit[]>(width * height);
// before C++14 use // newGrid.reset(new GridUnit*[width]);
newGrid = std::make_unique<GridUnit*[]>(width);
for (int i = 0; i < width; i++)
newGrid[i] = &newData[i * height];

Is copying 2D arrays with memcpy technically undefined behaviour?

It's well-defined, even if you use memcpy(arr_cpy, arr, size) rather than

memcpy(&arr_cpy, &arr, size) (which @LanguageLawyer has finally explained is what they've been arguing for the whole time), for reasons explained by @HolyBlackCat and others.

The intended meaning of the standard is clear, and any language to the contrary is a defect in the standard, not something compiler devs are going to use to pull the rug out from under countless normal uses of memcpy (including 1D arrays) that don't cast int* to int (*)[N], especially since ISO C++ doesn't allow variable-length arrays.

Experimental evidence for how compiler-developers chose to interpret the standard as letting memcpy read from the whole outer object (array-of-array-of-int) which is pointed-to by the void* arg, even if that void* was obtained as a pointer to the first element (i.e. to the first array-of-int):

If you pass a size that's too large, you do get a warning, and for GCC the warning even spells out exactly what object and what size it sees being memcpyed:

#include <cstring>

int dst[2][2];
void foo(){
int arr[2][2] = {{1,1},{1,1}};
std::memcpy(dst, arr, sizeof(arr)); // compiles cleanly
}

void size_too_large(){
int arr[2][2] = {{1,1},{1,1}};
std::memcpy(dst, arr, sizeof(arr)+4);
}

Using &dst, &src makes no difference here to warnings or lack thereof.

Godbolt compiler explorer for GCC and clang -O2 -Wall -Wextra -pedantic -fsanitize=undefined, and MSVC -Wall.

GCC's warning for size_too_large() is:

warning: 'void* memcpy(void*, const void*, size_t)' forming offset [16, 19] is  \
out of the bounds [0, 16] of object 'dst' with type 'int [2][2]' [-Warray-bounds]
11 | std::memcpy(dst, arr, sizeof(arr)+4);
| ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
<source>:3:5: note: 'dst' declared here
3 | int dst[2][2];

clang's doesn't spell out the object type, but does still show sizes:

<source>:11:5: warning: 'memcpy' will always overflow; destination buffer has size 16, but size argument is 20 [-Wfortify-source]
std::memcpy(dst, arr, sizeof(arr)+4);
^

So it's clearly safe in practice with real compilers, a fact which we already knew. Both see the destination arg as being the whole 16-byte int [2][2] object.

However, GCC and clang are possibly less strict than the ISO C++ standard. Even with dst[0] as the destination (decaying to an int* rather than int (*)[2]), they both still report the destination size as 16 bytes with type int [2][2].

HolyBlackCat's answer points out that calling memcpy this way really only gives it the 2-element sub-array, not the whole 2D array, but compilers don't try to stop you from or warn about using a pointer to the first element to access any part of a larger object.

As I said, testing real compilers can only show us that this is well-defined on them currently; arguments about what they might do in future requires other reasoning (based on nobody wanting to break normal uses of memcpy, and the standard's intended meaning.)



ISO standard's exact wording: arguably a defect

The only question is whether there's any merit to the argument that there's a defect in the standard's wording for the way it explains which object is relevant for the language beyond the end of an object, whether that's limited to the single pointed-to object after array to pointer "decay" for passing an arg to memcpy. (And yes, that would be a defect in the standard; it's widely assumed that you don't need and shouldn't use &arr with an array type for memcpy, or basically ever AFAIK.)

To me, that sounds like a misinterpretation of the standard, but I may be biased because I of course want to read it as saying what we all know is true in practice. I still think that having it be well-defined is a valid interpretation of the wording in the standard, but the other interpretation may also be valid. (i.e. it could be ambiguous whether it's UB or not, which would be a defect.)

A void* pointing to the first element of an array can be cast back to an int (*)[2] to access the whole array object. That isn't how memcpy uses it, but it shows that the pointer hasn't lost its status as a pointer to the whole N-dimensional array. I think the authors of the standard are assuming this reasoning, that this void* can be considered a pointer to the whole object, not just the first element.

However, it's true that there's special language for how memcpy works, and a formal reading could argue that this doesn't let you rely on normal C assumptions about how memory works.

But the UB interpretation allowed by the standard is not how anyone wants it to work or thinks it should. And it would apply to 1D arrays, so this interpretation conflicts with standard examples of using memcpy that are well-known / universally assumed to work. So any argument that the wording in the standard doesn't quite match this is an argument that there's a defect in the wording, not that we need to change our code and avoid this.

There's also no motivation for compiler devs to try to declare this UB because there's very little optimization to be had here (unlike with signed overflow, type-based aliasing, or assumption of no NULL deref).

A compiler assuming that runtime-variable size must only affect at most the whole first element for the pointer type that got cast to void* wouldn't allow much optimization in real code. It's rare for later code to only access elements strictly after the first, which would let the compiler do constant-propagation or similar things past a memcpy that was intended to write it.

(As I said, everyone knows this isn't what the standard intended, unlike with clear statements about signed overflow being UB.)

memcpy for multidimensional array

Your code is correct. You should not expect the output to show you a value of 1. You should expect it to show you a value of 16843009, assuming a 4 byte int.

The reason is: you are filling array1 with bytes of value 1, not with ints of value 1. i.e. binary 00000001000000010000000100000001 (0x01010101) is being filled into all the int elements with your memset operation.

So regardless of the size of int on your machine (unless it's a single byte!) you should not expect to see the value 1.

I hope this helps.

Copying a 1D array to a row of 2D array using memcpy

The problem is in the condition in the inner loop (i<5).

Here’s a fix (with hardcodings removed).

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
int arr1[] = {34, 45, 89, 123, 56};
int arr2[2][sizeof arr1 / sizeof *arr1];
memcpy(arr2[0], arr1, sizeof arr1);
memcpy(arr2[1], arr1, sizeof arr1);

for (int i = 0; i < sizeof arr2 / sizeof *arr2; i++) {
for (int j = 0; j < sizeof arr2[i] / sizeof *arr2[i]; j++) {
printf("%d ", arr2[i][j]);
}
printf("\n");
}
return 0;
}

Copy two dimensional dynamic array(pointers) to a static array using memcpy

There are 2 problems with your memcpy:

1. src rows are not necessarily continuous

Consider:

#define x 2
float src_row_0[x] = { 0.0f, 0.1f };
float src_row_1[x] = { 1.0f, 1.1f };
float * src_rows[x] = { src_row_0, src_row_1 };
float ** src = src_rows;

There is no guarantee that rows are next to each other in memory. So you cannot copy bytes with single memcpy. Same applies even if you allocated srcrows with malloc.

You'll need to copy each row separately.

2. Size x is not enough to copy all bytes

memcpy copies number of bytes, not number of elements. Single float variable is usually more than 1 byte in size. Simply passing x to memcpy is not enough. You'll need to multiply each number of items by size of an single element.

Fixing the copy

Corrected copy would look something like this:

for(int i=0; i<x; ++i) {
memcpy(&dst[i], src[i], x * sizeof(float));
}

Example on Ideone.

How to memcpy a part of a two dimensional array in C?

That should work :

int i;
for(i = 0; i<10; i++)
{
memcpy(&a[i], &c[i], sizeof(c[0]));
}


Related Topics



Leave a reply



Submit