I want to create a function which can allocate a multidimensional array on the heap with only one call to malloc. (Pointer array) So a function call would look like this:
size_t dim[2] = {2, 4};
int **_2darray = alloc_array(sizeof(int), dim, 2);
// ^ should be the "same" as:
int __2darray[2][4];
What I have so far is the SIZE computation of the whole block needed to hold the array and the pointers:
void *alloc_array(size_t element_size, size_t dimensions[static 1], size_t ndims)
{
unsigned char *DATA = NULL;
size_t SIZE = 0;
size_t multiplicators[ndims];
// Calculate for each dimension the multiplier
// SIZE 3d array: (N1 * sizeof(T **) + (N1 * N2 + sizeof(T *) + (N1 * N2 * n3 + sizeof(T))
// ^- first mulitplier ^ second multiplier ^ third multiplier
for (size_t i = 0; i < ndims; ++i) {
multiplicators[i] = dimensions[i];
for (size_t j = 0; j < i; ++j) {
multiplicators[i] *= dimensions[j];
}
}
SIZE = 0;
for (size_t dimI = 0; dimI < ndims; ++dimI) {
size_t mulval = multiplicators[dimI];
// The elements are in the "last" dimension
if (dimI+1 == ndims) {
SIZE += element_size * mulval;
} else {
// All other elements are pointers to the specific element
SIZE += sizeof(void *) * mulval;
}
}
DATA = malloc(SIZE);
return DATA;
}
So by now the SIZE calculation works. But now I'm stuck with setting the pointers to the right element. I know it's easy with dealing with static dimensions but I want this to be done with dynamic dimensions.
#include <stdlib.h>
#include <stdio.h>
void fill_array_pointers (void** pointers, char* elements,
size_t element_size, size_t total_elements_size,
size_t dimensions[], size_t ndims)
{
if (ndims == 2)
{
size_t i;
for (i = 0; i < dimensions[0]; ++i)
{
pointers[i] = elements + i * element_size * dimensions[1];
}
}
else
{
size_t i;
size_t block_size = total_elements_size / dimensions[0];
for (i = 0; i < dimensions[0]; ++i)
{
pointers[i] = pointers + dimensions[0] + i * dimensions[1];
fill_array_pointers (pointers + dimensions[0]
+ i * dimensions[1],
elements + block_size * i,
element_size, block_size,
dimensions+1, ndims-1);
}
}
}
void* alloc_array (size_t element_size, size_t dimensions[],
size_t ndims)
{
size_t total_elements_size = element_size;
int i;
// total size of elements
for (i = 0; i < ndims; ++i)
total_elements_size *= dimensions[i];
// total size of pointers
size_t total_pointers_size = 0;
int mulval = 1;
for (i = 0; i < ndims-1; ++i)
{
total_pointers_size += dimensions[i] * sizeof(void*) * mulval;
mulval *= dimensions[i];
}
size_t total_size = total_pointers_size;
size_t oddball = total_pointers_size % element_size;
// really needs to be alignof but we don't have it
if (oddball) total_size += (element_size - oddball);
total_size += total_elements_size;
void* block = malloc (total_size);
void** pointers = block;
char* elements = (char*)block + total_size - total_elements_size;
fill_array_pointers (pointers, elements, element_size,
total_elements_size, dimensions, ndims);
return block;
}
Test drive:
int main ()
{
size_t dims[] = { 2, 3, 4 };
int*** arr = alloc_array(sizeof(int), dims, 3);
int i, j, k;
for (i = 0; i < dims[0]; ++i)
for (j = 0; j < dims[1]; ++j)
for (k = 0; k < dims[2]; ++k)
{
arr[i][j][k] = i*100+j*10+k;
}
for (i = 0; i < dims[0]*dims[1]*dims[2]; ++i)
{
printf ("%03d ", (&arr[0][0][0])[i]);
}
printf ("\n");
free (arr);
}
This will not work for multidimensional char arrays on systems where sizeof(char*) != sizeof(char**); such systems exist but are rare. Multidimensional char arrays are pointless anyway.
The test runs cleanly under valgrind.
This is more an intellectual exercise than anything else. If you need maximum performance, don't use arrays of pointers, use a flat array and ugly but efficient explicit index calculations. If you need clear and concise code, you are probably better off allocating each level separately.
Related
I created this function to allocate dynamically a 3D array.
int ***create_3D_Array(int nb_block, int nb_lin, int nb_col) {
int i, j;
int ***A = (int***)malloc(nb_block * sizeof(int**));
for (i = 0; i <nb_col; i++) {
A[i] = (int**)malloc(nb_col * sizeof(int*));
for (j = 0; j < nb_lin; j++) {
A[i][j] = (int*)malloc(nb_lin * sizeof(int));
}
}
return A;
}
I then used it here
int ***all_blocks = NULL;
all_blocks = create_3D_Array(54, 5, 5);
However, it is not working correctly because when I want to give a value to my 6th block all_blocks[5], the program stops working.
Is there any error in my function ?
The dimensions are incorrect in your allocation loops. The outer loop should run to nb_block, the second malloc should allocate nb_lin * sizeof(int*) and the third malloc should allocate nb_col * sizeof(int).
Here is a corrected version:
int ***create_3D_Array(int nb_block, int nb_lin, int nb_col) {
int i, j;
int ***A = (int***)malloc(nb_block * sizeof(int**));
for (i = 0; i < nb_block; i++) {
A[i] = (int**)malloc(nb_lin * sizeof(int*));
for (j = 0; j < nb_lin; j++) {
A[i][j] = (int*)malloc(nb_col * sizeof(int));
}
}
return A;
}
Note that it might be simpler to use a direct 3D array:
int (*all_blocks)[5][5] = malloc(54 * sizeof(*all_blocks));
Say I have some function that performs a matrix operation (like a transpose) on a float array:
void transpose(float *result, const float *input, int rows, int cols){
int i,j;
for(i = 0; i < rows; i++){
for(j = 0; j < cols; j++){
result[rows*j+i] = input[cols*i+j];
}
}
}
This function will work for any data type with size sizeof(float). Can this function be modified to work with arrays of arbitrary data type, or is it necessary to have separate functions for each data type of different size (e.g. transpose_8, transpose_32, etc.)?
From a comment by Eugene Sh., you can pass void *, the size of the data, and the size of the types you're passing so it works for all types.
You have to convert these to char * so you can use pointer arithmetic, though.
Here's how you can do that:
void transpose(void *result, const void *input, int size, int rows, int cols)
{
int i, j;
char *r = result;
const char *i = input;
for( i = 0; i < rows; i++ )
{
for( j = 0; j < cols; j++ )
{
memcpy(r + size * (rows * j + i), i + size * (cols * i + j), size);
}
}
}
Can this function be modified to work with arrays of arbitrary data type?
Yes, you can pass a generic void * pointer and the size of a single element as a parameter, which is exactly how qsort() handles any kind of data type (source).
Here's a working example:
void transpose(void *result, const void *input, size_t rows, size_t cols, size_t element_size) {
unsigned char *input_ptr = (unsigned char *)input;
unsigned char *result_ptr = (unsigned char *)result;
size_t i, j;
for(i = 0; i < rows; i++) {
for(j = 0; j < cols; j++) {
unsigned char *in = input_ptr + element_size * (cols * i + j);
unsigned char *res = result_ptr + element_size * (rows * j + i);
memcpy(res, in, element_size);
}
}
}
You could also do this in-place using the same swapping technique as qsort() does:
void transpose_inplace(void *input, size_t n, size_t element_size) {
unsigned char *input_ptr = (unsigned char *)input;
size_t i, j;
for(i = 0; i < n; i++) {
for(j = 0; j < i; j++) {
unsigned char *a = input_ptr + element_size * (n * i + j);
unsigned char *b = input_ptr + element_size * (n * j + i);
size_t size = element_size;
while (size--) {
unsigned char tmp = *a;
*a++ = *b;
*b++ = tmp;
}
}
}
}
I'm using n here since to transpose in-place you need a square matrix where rows = cols = n.
I've been implementing the mergesort algorithm in C, based on a dynamic array structure. I followed the pseudo-code step by step but I am not getting to the point. Here's how I have defined my structure and how I create and initialize it:
typedef struct dynarray {
void **memory;
size_t allocated;
size_t used;
int index;
} dynarray;
//creates a new, empty, dynarray
void create_dynarray(dynarray **array, size_t size) {
*array = calloc(size, sizeof **array);
(*array)->memory = NULL;
(*array)->allocated = 0;
(*array)->used = 0;
(*array)->index = -1;
}
And here is the implementation of the mergesort:
//function used to slice the dynarray in two subarrays and call merge function
void *dynarray_mergesort(dynarray *param) {
if (dynarray_length(param) > 1) {
size_t size = param->used / sizeof(void*);
size_t m = size / 2;
size_t n = size - size / 2;
struct dynarray *l;
create_dynarray(&l, m);
struct dynarray *r;
create_dynarray(&r, n);
for (size_t i = 0 ; i < m; i++) {
add_elem(l, param->memory[i]);
}
for (size_t j = m; j < size; j++) {
add_elem(r, param->memory[j]);
}
dynarray_mergesort(l);
dynarray_mergesort(r);
dynarray_merge(param, l, r, size);
}
return param;
}
//function used to mergesort the array
void *dynarray_merge(dynarray *param, dynarray *l, dynarray *r, int size) {
int i = 0,j = 0, k = 0;
while (i < size/2 && j < size - size / 2) {
if (l->memory[i] < r->memory[j]) {
param->memory[k] = l->memory[i];
i++;
k++;
} else {
param->memory[k] = r->memory[j];
j++;
k++;
}
}
while (i < size / 2) {
param->memory[k] = l->memory[i];
i++;
k++;
}
while (j < size - size / 2) {
param->memory[k] = r->memory[j];
j++;
k++;
}
return param;
}
When I call the function on an array such as [18, 14, 20, 16, 12] I get the same identical array. I have tried adding some printf()
in the mergesort function and I discovered that it seems to slice the array correctly. So the problem must be in the dynarray_merge() function. The way I am checking if an element in the first array is greater than an element of the other one seems right to me, so I am totally stuck.
I am posting a compilable example of my code, to show you better what I mean.
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
typedef struct dynarray {
void **memory;
size_t allocated;
size_t used;
int index;
} dynarray;
//creates a new, empty, dynarray
void create_dynarray(dynarray **array, size_t size) {
*array = calloc(size, sizeof **array);
(*array)->memory = NULL;
(*array)->allocated = 0;
(*array)->used = 0;
(*array)->index = -1;
}
//adds a new element at the bottom of dynarray
void add_elem(dynarray *array, void *data) {
size_t toallocate;
size_t size = sizeof(void *);
if ((array->allocated - array->used) < size) { // if M - N ...
toallocate = array->allocated == 0 ? size : (array->allocated * 2);
array->memory = realloc(array->memory, toallocate);
array->allocated = toallocate;
}
array->memory[++array->index] = data;
array->used = array->used + size;
}
//get length of the dynarray
int dynarray_length(dynarray *array) {
return array->index + 1;
}
//retrieves an element in a specific position of the dynarray
void *get_i_elem(dynarray *array, int index) {
if (index < 0 || index > array->index)
return NULL;
return array->memory[index];
}
//function used to mergesort the array
void *dynarray_merge(dynarray *param, dynarray *l, dynarray *r, int size) {
int i = 0,j = 0, k = 0;
while (i < size/2 && j < size - size / 2) {
if (l->memory[i] < r->memory[j]) {
param->memory[k] = l->memory[i];
i++;
k++;
} else {
param->memory[k] = r->memory[j];
j++;
k++;
}
}
while (i < size / 2) {
param->memory[k] = l->memory[i];
i++;
k++;
}
while (j < size - size / 2) {
param->memory[k] = r->memory[j];
j++;
k++;
}
return param;
}
//function used to slice the dynarray in two subarrays and call merge function
void *dynarray_mergesort(dynarray *param) {
if (dynarray_length(param) > 1) {
size_t size = param->used / sizeof(void*);
size_t m = size / 2;
size_t n = size - size / 2;
struct dynarray *l;
create_dynarray(&l, m);
struct dynarray *r;
create_dynarray(&r, n);
for (size_t i = 0 ; i < m; i++) {
add_elem(l, param->memory[i]);
}
for (size_t j = m; j < size; j++) {
add_elem(r, param->memory[j]);
}
dynarray_mergesort(l);
dynarray_mergesort(r);
dynarray_merge(param, l, r, size);
}
return param;
}
//print arrays, useful to test
void print_array(dynarray *array) {
for (int i = 0; i < dynarray_length(array); i++) {
printf("%d\t", *(int *)get_i_elem(array, i));
//puts("");
}
}
int main() {
struct dynarray *a;
create_dynarray(&a, 5);
int arr[5] = { 18, 14, 20, 16, 12};
int *ap = malloc(sizeof(int));
int *bp = malloc(sizeof(int));
int *cp = malloc(sizeof(int));
int *dp = malloc(sizeof(int));
int *ep = malloc(sizeof(int));
*ap = arr[0];
*bp = arr[1];
*cp = arr[2];
*dp = arr[3];
*ep = arr[4];
add_elem(a, ap);
add_elem(a, bp);
add_elem(a, cp);
add_elem(a, dp);
add_elem(a, ep);
printf("\nbefore mergesort\n");
print_array(a);
puts("");
printf("\nafter mergesort\n");
dynarray_mergesort(a);
print_array(a);
}
The problem is how you compare the elements:
if (l->memory[i] < r->memory[j]) ...
Here, you are comparing pointers, not the values pointed to. You get these pointers from the malloc calls in main, which gave you ascending addresses.
Your implenentation of the dynamic array uses void * as type for the elements, so that it can be used for elements of any type. Your merge sort doesn't know which type is used and therefore can't know how to compare.
You can provide a callback function to the sorting function like qsort does:
typedef int VoidPointerCmp(const void *a, const void *b);
Then pass this function to your two merge sort functions:
void *dynarray_mergesort(dynarray *param, , VoidPointerCmp *cmp) ...
and compare your items like so:
if (cmp(l->memory[i], r->memory[j]) < 0) ...
A suitable comparison function for integers could look like this:
int int_cmp(const void *pa, const void *pb)
{
const int *a = pa;
const int *b = pb;
if (*a < *b) return -1;
if (*a > *b) return 1;
return 0;
}
In main, call:
dynarray_mergesort(a, int_cmp);
Some notes:
You allocate memory for each of the entries. This is not necessary. You can make the void pointers point at the elements of the existing arrays:
for (int i = 0; i < 5; i++) add_elem(a, &arr[i]);
Sorting will not affect the original array arr.
You don't free the allocated memory or destroy your dynamic arrays. You should probably write a destructor function for the dynamic arrays and clean up the temporary arrays l and r after using them.
I think that you don't use the used field correctly. You probably don't need it: It is enough to have the allocated size and current length of the array. (You can replace the index with the length.)
I am having trouble with assigning a return value of a function in heap part of the program. When I tried it in main, it gives an error "Segmentation fault". I believe it is because of the size of my array, which is the return value that I mentioned earlier because when I make my max_size smaller, the code works correctly (I think up to 45000). When I call the function in main, it uses the memory of stack, which is much smaller than memory of heap. Therefore I tried to call the function in heap and make the assignment in there but the compiler gave an error
deneme.c:6:15: error: initializer element is not constant
int *primes = listPrimes(1000000, &size);
After that I did some research and found out that stack is 8 MB memory, which is around 8000000 bytes. Then I estimated my array size as using the prime number theorem (up to 1000000, there are approximately 200000 primes) and sizeof(int) = 4 bit value so it gives 100000 bytes, which is much less than 8 MB. Therefore I have two questions in mind:
1. Why the compiler gives segmentation fault error although my array size is not too large?
2. How can I make the assigment in heap instead of main in order to avoid this problem?
Here is my code:
#include "mathlib.h"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
int *listPrimes(int max_size, int *size) {
*size = 1;
int *result = malloc(*size * sizeof(int));
int i;
int index = 1;
// Finding the list of primes using a sieve algorithm:
int *nums = malloc(max_size*sizeof(int));
for (i = 0; i < max_size; i++) {
nums[i] = i;
}
result[0] = 2;
int j = 2;
while (j < max_size) {
int k = j;
while (j*k <= max_size) {
nums[j*k] = 0;
k++;
}
if (j == 2) {
j++;
*size = *size + 1;
result = realloc(result, *size * sizeof(int));
result[index++] = nums[j];
}
else {
j += 2;
if (nums[j] != 0) {
*size = *size + 1;
result = realloc(result, *size * sizeof(int));
result[index++] = nums[j];
}
}
}
return result;
}
and main function:
#include <stdio.h>
#include <stdlib.h>
#include "mathlib.h"
int size = 0;
int *primes = listPrimes(1000000, &size);
int main() {
printf("size = %d\n", size);
for (int i = 0; i < size; i++) {
printf("%d th prime is %d\n", i+1, primes[i]);
}
free(primes);
return 0;
}
Use unsigned int for j, k and max_size in listPrimes and it works properly . Below is the tested code:
// #include "mathlib.h"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
int size = 0;
int *
listPrimes (unsigned int max_size, int *size)
{
*size = 1;
int *result = malloc (*size * sizeof (int));
int i;
int index = 1;
// Finding the list of primes using a sieve algorithm:
int *nums = malloc (max_size * sizeof (int));
for (i = 0; i < max_size; i++)
{
nums[i] = i;
}
result[0] = 2;
unsigned int j = 2;
while (j < max_size)
{
unsigned int k = j;
while (j * k <max_size)
{
nums[j * k] = 0;
k++;
}
if (j == 2)
{
j++;
*size = *size + 1;
result = realloc (result, *size * sizeof (int));
result[index++] = nums[j];
}
else
{
j += 2;
if (nums[j] != 0)
{
*size = *size + 1;
result = realloc (result, *size * sizeof (int));
result[index++] = nums[j];
}
}
}
free(nums);
return result;
}
int
main ()
{
int *primes = listPrimes (1000000, &size);
printf ("size = %d\n", size);
for (int i = 0; i < size; i++)
{
printf ("%d th prime is %d\n", i + 1, primes[i]);
}
free (primes);
return 0;
}
nums is allocated to have max_size elements, so the index of its last element is max-size-1.
This loop:
while (j*k <= max_size) {
nums[j*k] = 0;
k++;
}
may access an element with index j*k that equals max_size, thus writing beyond the end of the array. The loop should be limited to j*k < max_size.
Regarding your second question, the size of the result array is determined while finding the primes and is not readily calculable in advance, so it cannot easily be allocated prior to calling listPrimes. It could be done by evaluating the prime-counting function, but that is likely more than you want to do for this project.
I'm using an example from https://phoxis.org/2012/07/12/get-sorted-index-orderting-of-an-array/ where he returns the sort indices from a sort of an array, i.e.
3,4,2,6,8 returns 4,3,1,0,2 (+1 for each index in R). This is the equivalent of R's order function
I've translated his/her code to work as a function returning an array of sorted indices. The code gives the correct answer.
keeping track of the original indices of an array after sorting in C has a similar response, but as #BLUEPIXY warns, his solution doesn't work in all circumstances. I need something that will work in all circumstances, including ties.
however, the original author uses a global pointer, which causes a memory leak, and free() doesn't fix it. which I don't know how to do this without the global pointer.
How can I fix this memory leak, or at least return sorted indices in C that will always work?
#include <stdio.h>
#include <stdlib.h>
/* holds the address of the array of which the sorted index
* order needs to be found
*/
int * base_arr = NULL;
/* Note how the compare function compares the values of the
* array to be sorted. The passed value to this function
* by `qsort' are actually the `idx' array elements.
*/
static int compar_increase (const void * a, const void * b) {
int aa = *((int * ) a), bb = *((int *) b);
if (base_arr[aa] < base_arr[bb]) {
return 1;
} else if (base_arr[aa] == base_arr[bb]) {
return 0;
} else {
// if (base_arr[aa] > base_arr[bb])
return -1;
}
}
int * order_int (const int * ARRAY, const size_t SIZE) {
int * idx = malloc(SIZE * sizeof(int));
base_arr = malloc(sizeof(int) * SIZE);
for (size_t i = 0; i < SIZE; i++) {
base_arr[i] = ARRAY[i];
idx[i] = i;
}
qsort(idx, SIZE, sizeof(int), compar_increase);
free(base_arr); base_arr = NULL;
return idx;
}
int main () {
const int a[] = {3,4,2,6,8};
int * b = malloc(sizeof(int) * sizeof(a) / sizeof (*a));
b = order_int(a, sizeof(a) / sizeof(*a));
for (size_t i = 0; i < sizeof(a)/sizeof(*a); i++) {
printf("b[%lu] = %d\n", i, b[i]+1);
}
free(b); b = NULL;
return 0;
}
A straightforward approach without using a global variable can look the following way
#include <stdio.h>
#include <stdlib.h>
int cmp_ptr(const void *a, const void *b)
{
const int **left = (const int **)a;
const int **right = (const int **)b;
return (**left < **right) - (**right < **left);
}
size_t * order_int(const int *a, size_t n)
{
const int **pointers = malloc(n * sizeof(const int *));
for (size_t i = 0; i < n; i++) pointers[i] = a + i;
qsort(pointers, n, sizeof(const int *), cmp_ptr);
size_t *indices = malloc(n * sizeof(size_t));
for (size_t i = 0; i < n; i++) indices[i] = pointers[i] - a;
free(pointers);
return indices;
}
int main( void )
{
const int a[] = { 3,4,2,6,8 };
const size_t N = sizeof(a) / sizeof(*a);
size_t *indices = order_int(a, N);
for (size_t i = 0; i < N; i++) printf("%d ", a[indices[i]]);
putchar('\n');
free(indices);
return 0;
}
The program output is
8 6 4 3 2
As for the memory leak then it is due to overwriting the value of the pointer to redundantly allocated memory.
int * b = malloc(sizeof(int) * sizeof(a) / sizeof (*a));
b = order_int(a, sizeof(a) / sizeof(*a));
The memory allocation does not make sense.
The problem I see is that within main function - you are allocating pointer b some memory -
int * b = malloc(sizeof(int) * sizeof(a) / sizeof (*a));
The next line calls order_int(...) that returns a pointer to already allocated memory -
b = order_int(a, sizeof(a) / sizeof(*a));
Looking at the order_int function -
int * order_int (const int * ARRAY, const size_t SIZE) {
int * idx = malloc(SIZE * sizeof(int));
base_arr = malloc(sizeof(int) * SIZE);
for (size_t i = 0; i < SIZE; i++) {
base_arr[i] = ARRAY[i];
idx[i] = i;
}
qsort(idx, SIZE, sizeof(int), compar_increase);
free(base_arr); base_arr = NULL;
return idx;
}
.. you see that idx has been already been allocated the correct memory.
I would suggest removing the malloc from b - see below.
int * b = NULL;