I have a pointer to a 3-dimensional array, like this:
char ***_cube3d
And I am initialising it like this:
_cube3d = (char ***)malloc(size * (sizeof(char**)));
for (int i = 0; i< size; i++) {
_cube3d[i] = (char **)malloc(size * sizeof(char*));
for (int j = 0; j<size; j++) {
_cube3d[i][j] = (char *)malloc(size * sizeof(char));
}
}
Note that the array is of dynamic size, and can contain thousands of elements, so we cannot declare it as an array in advance.
Now, I want to copy all of its contents into another array, as efficiently as possible. I know the nested loop solution where we copy each element one by one, however, it seems extremely inefficient to me. Is there a way to speed this process up? C++ code is welcome, although I would prefer it in plain C, since I am planning to iterate this solution into Objective C, and I would like to avoid injecting C++ code into a clean Objective C project.
Can anyone point me in the right direction?
Using what you already have (but fixing the first malloc with sizeof(char***))
You could copy the array by running a bunch of for loops like this:
char new_cube[side][side][side];
for(unsigned int x = 0; x < side; x++)
for(unsigned int y = 0; y < side; y++)
for(unsigned int z = 0; z < side; z++)
new_cube[x][y][z] = old_cube[x][y][z];
OR:
char new_cube[side][side][side];
for(unsigned int x = 0; x < side; x++)
for(unsigned int y = 0; y < side; y++)
memcpy(new_cude[x][y], old_cube[x][y], sizeof(char)*side);
which might be a bit faster.
using this method you avoid using any c++(as you said you would like) and your code complexity is kept minimal.
If you are using C.99, you can use a variable length array (VLA) to dynamically allocate your 3-dimensional array. Once side is determined, you can declare your pointer to be:
char (*cube3d_)[side][side];
And then initialize it like this:
cube3d_ = malloc(side * sizeof(*cube3d_));
Note that in C, you are not required to cast the return value of malloc(), and doing so can actually lead to undefined behavior in the worst case. Since the "cube" has been allocated as a contiguous block, it can be copied with memcpy().
C++ does not have VLA. You can use a vector to get the C++ equivalent of your multi-dynamic allocation structure:
std::vector<std::vector<std::vector<char> > >
cube3d_(side, std::vector<std::vector<char> >(side, std::vector<char>(side)));
You can then copy it using a copy constructor or an assignment.
If cube3d_ is a member variable of an object/structure, so long as your object knows the value of side, you can still use a VLA pointer to access the memory. For example:
struct Obj {
size_t side_;
void *cube3d_;
};
//...
size_t side = 3;
//...
Obj o;
o.side_ = side;
char (*p)[o.side_][o.side_] = malloc(o.side_ * sizeof(*p));
o.cube3d_ = p;
//...
char (*q)[o.side_][o.side_] = o.cube3d_;
q[1][2][2] = 'a';
Here is an approach using C and structs to provide some degree of object oriented along with a set of helper functions.
The idea here was to use Kerrick's suggestion of a contiguous array.
I am not sure if I got the offset calculation correct and it has not been tested so it is worth what you are paying for it. However it may be helpful as a starting place.
The idea is to have a single contiguous area of memory to make memory management easier. And to use a function to access a particular element using a zero based offset in the x, y, and z directions. And since I was not sure as to the element size/type, I made that a variable as well.
#include <malloc.h>
typedef struct _Array3d {
int elSize; // size of each element of the array in bytes
int side; // length of each side of the 3d cube in elements
char * (*Access) (struct _Array3d *pObj, int x, int y, int z);
char buffer[1];
} Array3d;
static char * Array3d_Access (Array3d *pObj, int x, int y, int z)
{
char *pBuf = NULL;
if (pObj && x < pObj->side && y < pObj->side && z < pObj->side) {
pBuf = &(pObj->buffer[x * pObj->side * pObj->elSize * pObj->side * pObj->elSize + y * pObj->side * pObj->elSize + z * pObj->elSize]);
}
return pBuf;
}
// Create an Array3d cube by specifying the length of each side along with the size of each element.
Array3d *Array3d_Factory (int side, int elSize)
{
Array3d *pBuffer = malloc (sizeof(Array3d) + side * elSize * side * elSize * side * elSize);
if (pBuffer) {
pBuffer->elSize = elSize;
pBuffer->side = side;
pBuffer->Access = Array3d_Access;
}
return pBuffer;
}
// Create an Array3d cube that is the same size as an existing Array3d cube.
Array3d *Array3d_FactoryObj (Array3d *pObj)
{
Array3d *pBuffer = NULL;
if (pObj) {
int iBufferSize = pObj->side * pObj->elSize * pObj->side * pObj->elSize * pObj->side * pObj->elSize;
pBuffer = malloc (sizeof(Array3d) + iBufferSize);
if (pBuffer) {
pBuffer->elSize = pObj->elSize;
pBuffer->side = pObj->side;
pBuffer->Access = pObj->Access;
}
}
return pBuffer;
}
// Duplicate or clone an existing Array3d cube into new one.
// Returns NULL if cloning did not happen.
Array3d *Array3d_Dup (Array3d *pObjDest, Array3d *pObjSrc)
{
if (pObjSrc && pObjDest && pObjSrc->elSize == pObjDest->elSize && pObjSrc->side == pObjDest->side) {
int iBufferSize = pObjSrc->side * pObjSrc->elSize * pObjSrc->side * pObjSrc->elSize * pObjSrc->side * pObjSrc->elSize;
memcpy (pObjDest->buffer, pObjSrc->buffer, iBufferSize);
} else {
pObjDest = NULL;
}
return pObjDest;
}
int main(int argc, _TCHAR* argv[])
{
Array3d *pObj = Array3d_Factory (10, 20 * sizeof(char));
char *pChar = pObj->Access (pObj, 1, 2, 3);
return 0;
}
Related
I am learning C++ at the moment and currently I am experimenting with pointers and structures. In the following code, I am copying vector A into a buffer of size 100 bytes. Afterwards I copy vector B into the same buffer with an offset, so that the vectors are right next to each other in the buffer. Afterward, I want to find the vectors in the buffer again and calculate the dot product between the vectors.
#include <iostream>
const short SIZE = 5;
typedef struct vector {
float vals[SIZE];
} vector;
void vector_copy (vector* v, vector* target) {
for (int i=0; i<SIZE; i++) {
target->vals[i] = v->vals[i];
}
}
float buffered_vector_product (char buffer[]) {
float scalar_product = 0;
int offset = SIZE * 4;
for (int i=0; i<SIZE; i=i+4) {
scalar_product += buffer[i] * buffer[i+offset];
}
return scalar_product;
}
int main() {
char buffer[100] = {};
vector A = {{1, 1.5, 2, 2.5, 3}};
vector B = {{0.5, -1, 1.5, -2, 2.5}};
vector_copy(&A, (vector*) buffer);
vector_copy(&B, (vector*) (buffer + sizeof(vector)));
float prod = buffered_vector_product(buffer);
std::cout << prod <<std::endl;
return 0;
}
Unfortunately this doesn't work yet. The problem lies within the function buffered_vector_product. I am unable to get the float values back from the buffer. Each float value should need 4 bytes. I don't know, how to access these 4 bytes and convert them into a float value. Can anyone help me out? Thanks a lot!
In the function buffered_vector_product, change the lines
int offset = SIZE * 4;
for (int i=0; i<SIZE; i=i+4) {
scalar_product += buffer[i] * buffer[i+offset];
}
to
for ( int i=0; i<SIZE; i++ ) {
scalar_product += ((float*)buffer)[i] * ((float*)buffer)[i+SIZE];
}
If you want to calculate the offsets manually, you can instead replace it with the following:
size_t offset = SIZE * sizeof(float);
for ( int i=0; i<SIZE; i++ ) {
scalar_product += *(float*)(buffer+i*sizeof(float)) * *(float*)(buffer+i*sizeof(float)+offset);
}
However, with both solutions, you should beware of both the alignment restrictions and the strict aliasing rule.
The problem with the alignment restrictions can be solved by changing the line
char buffer[100] = {};
to the following:
alignas(float) char buffer[100] = {};
The strict aliasing rule is a much more complex issue, because the exact rule has changed significantly between different C++ standards and is (or at least was) different from the strict aliasing rule in the C language. See the link in the comments section for further information on this issue.
For two dimensional array allocation in C/C++ , the very common code is :
const int array_size = .. ;
array = (int**) malloc(array_size);
for (int c=0;c<array_size;c++)
array[c] = (int*) malloc(other_size);
But I think we should be writing this:
const int array_size = .. ;
array = (int*) malloc(array_size);
int c;
bool free_array = false;
for (c=0;c<array_size;c++) {
array[c] = (int*) malloc(other_size);
if(array[c] == NULL){
free_array = true;
break;
}
}
if(free_array) {
for (int c1=0;c1<c;c1++)
free(array[c1]);
}
to make sure that if one allocation failed we will free the previously allocated memory.
Am I correct?
Note : in C++ there is an alternative safe method with smart pointers and STL containers, but lets talk about raw pointers here or about C pointers.
Generally speaking, if you detect that malloc fails, the only thing you can really do is exit(). At that point, you can't safely do anything regarding memory allocation or deallocation.
The only exception is if you're in an embedded environment where exiting is not an option. In that case, you probably shouldn't be using malloc in the first place.
Firstly, your code is malformed
array = (int*)
array[c] = (int*)
this suggests you intended
array = (int**)
array[c] = (int*)
Next you claim this is "very common", when all it is is "very lazy".
A better solution is a single allocation.
#include <string.h>
void* alloc_2d_array(size_t xDim, size_t yDim, size_t elementSize)
{
size_t indexSize = sizeof(void*) * xDim;
size_t dataSize = elementSize * yDim * xDim;
size_t totalSize = indexSize + dataSize;
void* ptr = calloc(1, totalSize);
if (!ptr)
return ptr;
void** index = (void**)ptr;
void** endIndex = index + xDim;
char* data = (char*)ptr + indexSize;
do {
*index = *data;
data += elementSize;
} while (++index < endIndex);
return ptr;
}
int main()
{
int** ptr = (int**)alloc_2d_array(3, 7, sizeof(int));
for (size_t x = 0; x < 3; ++x) {
for (size_t y = 0; y < 7; ++y) {
ptr[x][y] = (10 * (x+1)) + (y + 1);
}
}
free(ptr);
return 0;
}
However, this assumes the language is C, in C++ the above code is pretty much total fail.
I'm trying to convert the a 1D array to a 2D array
If you want to allocate an actual 2D array, not an array of pointers, the syntax gets a little tricky:
int X = 16, Y = 8;
char (*pt)[X][Y] = malloc(X*Y);
Above, pt is a pointer to an X by Y array of chars. Because it's a pointer, accessing its elements requires an asterisk and parentheses, too:
for (int i = 0 ; i != X ; i++) {
for (int j = 0 ; j != Y ; j++) {
(*pt)[i][j] = (char)(i*X+j);
}
}
Of course you need to free the pointer once you are done using the array:
free(pt);
You need to allocate each row of the matrix separately:
unsigned char** index;
index = malloc(X * sizeof(unsigned char*)); // Allocate an array of pointers
for(int i = 0; i < X; i++)
index[i] = malloc(Y * sizeof(unsigned char)) // Allocate each row
Refer also to this answer on malloc pointer casting.
unsigned char **index;
index = (unsigned char**)malloc( X*Y );
is wrong. index is an unsigned char**. You need to malloc two times to get a 2D array. If you want to allocate X rows, each with Y columns, use
unsigned char **index;
index = (unsigned char**)malloc( X * sizeof(unsigned char*) ); //Allocating number of rows
for(int i=0 ; i<X ; i++ )
{
index[i] = (unsigned char*)malloc( Y * sizeof(unsigned char)); // Allocate each column
}
You should check if malloc does not fail. You should also free everything after its use to avoid a memory leak.
After your edit, it appears you are using C++.
In C++, you should prefer std::vector< std::vector< unsigned char > > for dynamic 2d array.
std::vector< std::vector< unsigned char > > index(X, std::vector<unsigned char>(Y));
Now you can use it as index[i][j] and it will be automatically cleaned, so need to free/delete explicitly.
Live demo here
Still if you want to allocate c-compliant arrays in conventional way, use
unsigned char** index;
try {
index = new int*[X];
for(int i = 0; i < X; ++i)
index[i] = new int[Y];
} catch(...) {...}
For delete also you need to delete each element individually.
Old answer
unsigned char **index;
index = malloc(X * sizeof *index);
if(!index) fail();
for(xi = 0; xi < X; ++xi) {
index[xi] = malloc(Y * sizeof **index);
if(!index[xi]) free_and_fail();
}
You first allocate space for X pointers and then in all X pointers you allocate one array.
While freeing the memory, you need to free each row individually:
if(index) for(xi = Y-1; xi >= 0; --xi) { /* xi is signed */
free(index[xi];
}
free(index);
index = NULL;
I am a beginning programmer writing a graphical game using SDL. The function that splits a tile-sheet into sections or "clips" and puts it into a array and the function that draws specific "clips" onto the screen are not working as intended.
void split_tilesheet(int width, int height, int space, Entity * ent){
std::cout << "Splitting Tileset...";
int t_width = (width / SPR_W);
int t_height = (height / SPR_H);
int numTiles = (t_width * t_height);
ent = new Entity [numTiles + 1];
if( ent == NULL){
err("!failed to alloc!");
}else{
std::cout << "allocated"<< std::endl;
}
int count = 0;
for(int i = 0; i < t_width; i++){
for(int j = 0; j < t_height; j++){
ent[count].bounds.x = i * SPR_W;
ent[count].bounds.y = j * SPR_H;
ent[count].bounds.w = SPR_W;
ent[count].bounds.h = SPR_H;
ent[count].id = ent[i].x + ( ent[i].y * t_width);
count++;
}
}
}
void draw_room(char tiledata[MAP_MAX_X][MAP_MAX_Y], Entity * ent){
SDL_Rect bounds;
for(int x = 0; x < MAP_MAX_X; x++){
for(int y = 0; y < MAP_MAX_Y; y++){
if(tiledata[x][y] == '0' || tiledata[x][y] == ' ' || tiledata[x][y] == '\n' ){
draw_img(x * SPR_W , y * SPR_H, tiles, bounds, ent[0].bounds);
}
if(tiledata[x][y] == '1'){
draw_img(x * SPR_W , y * SPR_H, tiles, bounds, ent[1].bounds);
}
}
}
}
class Entity
{
public:
SDL_Rect bounds;
SDL_Surface* sprite;
int id;
int x;
int y;
int w, h;
};
I was trying to use pointers to dynamically allocate the memory at runtime.
The program compiles, but segfaults. gdb says that the segfault is due to the draw_room() function, but I cannot figure out why. The pointer I was passing to the draw_room function was:
Entity * floor0_clips = NULL;
This didn't work either
Entity * floor0_clips;
Please help...
C++ uses pass-by-value (unless you specify pass-by-reference), which you didn't.
A variable in a function is a copy of the argument given. For example:
int func(int x)
{
x = 5;
}
int main()
{
int y = 6;
func(y);
// here, `y` is still `6`
}
Your case is fundamentally the same as this. You send floor0_clips to a function, the function updates a copy of it, leaving the original unchanged.
To use pass-by-reference instead, put the & symbol just before the variable name in the function's parameter list, i.e. in your case Entity * &ent . Do not change anything in the code which calls the function; it is the function's parameter list declaration that decides whether the value is passed by value or by reference.
NB. You appear to be allocating too many Entities anyway (why the + 1?).
How to make a 2d pointer like **check point a 2d array like
mycheck[][]?
How to convert a 1d like check[16], to 2d array like mycheck[4][4]?
My attempt
float (*mycheck)[4] = (float (*)[4]) check;
But if second time I want to use mycheck again for some other 1d array, how can I do? My attempt:
float (*mycheck)[4] = (float (*)[4]) other1darray;
this will definitely give a re-declaration error.
The answer to the first question is that you cannot do that. All you can do is allocate some memory and copy the data over.
The answer to the second question is very simple
mycheck = (float (*)[4]) other1darray;
You only have to declare variables once, after that just use the variable name.
Array a[] decays to a pointer to the first element when you drop the []. This does not happen recursively, in other words, it doesn't work for a[][].
Secondly, you can't assign arrays in C. You can ONLY initialize them. You will have to set each member yourself.
You can create a 2D array in C like this.
Use a typedef to make it easier.
typedef int **matrix;
matrix create2Darray(int row, int col)
{
int idx;
matrix m = malloc(row * sizeof(int*));
for (idx = 0; idx < row; ++idx)
{
m[idx] = malloc(col * sizeof(int));
}
return m;
}
And then call this in another function;
matrix check = create2Darray(2, 2);
To assign a 1D array to a 2D array you can assign the pointers to the right position in the array. An example below. It also show how to create a 2D array dynamically, but I commented it out, since it is not needed for the example.
#include <stdio.h>
#include <stdlib.h>
int main()
{
float **matrix;
float *array;
array = (float *) malloc(16 * sizeof(float));
for (size_t idx = 0; idx != 16; ++idx)
{
array[idx] = idx;
}
matrix = (float **) malloc(4 * sizeof(float *));
for (size_t idx = 0; idx != 4; ++idx)
{
// matrix[idx] = malloc(4 * sizeof(int));
matrix[idx] = &array[idx * 4];
}
for (size_t row = 0; row != 4; ++row)
{
for (size_t col = 0; col != 4; ++col)
{
printf("%.1f ", matrix[row][col]);
}
printf("\n");
}
}
Note: this makes the 1D array and 2D array point to the same memory. If you change something in the 1D it also changes in the 2D and vice-versa. If you don't want this, first copy the array.