Allocate data through a function (ANSI C) - c

I d love to know how I can allocate data through a function, and after the function is returned the data is still allocated. This is both for basic types (int, char**) and user defined types. Below are two snipsets of code. Both have the allocation within the function though after the return the allocation goes.
int* nCheck = NULL;
int nCount = 4;
CallIntAllocation(nCheck, nCount);
nCheck[1] = 3; // Not allocated!
...
CallIntAllocation(int* nCheck, int nCount)
{
nCheck = (int*)malloc(nCount* sizeof(int));
for (int j = 0; j < nCount; j++)
nCheck[j] = 0;
}
The same behaviour for as before though for user defined type:
typedef struct criteriatype
{
char szCriterio[256];
char szCriterioSpecific[256];
} _CriteriaType;
typedef struct criteria
{
int nCount;
char szType[128];
_CriteriaType* CriteriaType;
} _Criteria;
...
_Criteria* Criteria;
AllocateCriteria(nTypes, nCriteria, Criteria);
...
void AllocateCriteria(int nTypes, int nCriteria[], _Criteria* Criteria)
{
int i = 0;
int j = 0;
Criteria = (_Criteria*)malloc(nTypes * sizeof(_Criteria));
for (i = 0; i < nTypes; i ++)
{
// initalise FIRST the whole structure
// OTHERWISE the allocation is gone
memset(&Criteria[i],'\0',sizeof(_Criteria));
// allocate CriteriaType
Criteria[i].CriteriaType = (_CriteriaType*)malloc(nCriteria[i] * sizeof(_CriteriaType));
// initalise them
for (j = 0; j < nCriteria[i]; j ++)
memset(&Criteria[i].CriteriaType[j],'\0',sizeof(_CriteriaType));
}
}
Any ideas? I think I need to pass the pointers as a reference, though how can i do so?
Thanks in advance,
Sunscreen

using return?
Criteria *
newCriteria() {
Criteria *criteria = malloc(..);
...
return criteria;
}
/* the caller */
Criteria *c1 = newCriteria();
Criteria *c2 = newCriteria();
EDIT
the caller is responsible for calling free()

You have 2 possible solutions:
int *CallIntAllocation(int nCount)
{
int *nCheck = (int*)malloc(nCount* sizeof(int));
for (int j = 0; j < nCount; j++)
nCheck[j] = 0;
return nCheck;
}
int* nCheck = NULL;
int nCount = 4;
nCheck = CallIntAllocation(nCount);
or you should pass a pointer to int* if you want to alloc array:
void CallIntAllocation(int **nCheck, int nCount)
{
*nCheck = (int*)malloc(nCount* sizeof(int));
for (int j = 0; j < nCount; j++)
*nCheck[j] = 0;
}
int* nCheck = NULL;
int nCount = 4;
CallIntAllocation(&nCheck, nCount);

To answer your question directly:
int* nCheck = NULL;
int nCount = 4;
CallIntAllocation(&nCheck, nCount);
nCheck[1] = 3; // allocated!
...
void CallIntAllocation(int** pnCheck, int nCount)
{
int* nCheck = NULL;
nCheck = (int*) malloc(nCount * sizeof(*nCheck));
for (int j = 0; j < nCount; j++)
nCheck[j] = 0;
*pnCheck = nCheck;
}
but I would suggest this in stead:
nCheck = CallIntAllocation(nCount);
nCheck[1] = 3; // allocated!
...
int *CallIntAllocation(int nCount)
{
int * nCheck
nCheck = (int*) malloc(nCount * sizeof(*nCheck));
for (int j = 0; j < nCount; j++)
nCheck[j] = 0;
return nCheck;
}

The reason it is not working is that function arguments in C are copied. So, nCheck = NULL in the outside context, you pass it into the CallIntAllocation function and a copy is made. The CallIntAllocation defines it's local copy of nCheck to be the return of the malloc call. But the outer copy is not updated -- it still points at NULL.
The simplest solution is to return the new pointer value and assign it as already suggested by several people.
When you have functions that need to modify data structures, you need to pass around a pointer to them, rather than copies of them, so that the function can modify what the pointer points at. The same principle applies here, although the data structure you want to modify is itself a pointer.
So another solution would be for CallIntAllocation to take a pointer-to-a-pointer which would let you modify where the pointer points, and also dereference it:
CallIntAllocation(int** nCheck, int nCount)
{
*nCheck = (int*)malloc(nCount* sizeof(int));
for (int j = 0; j < nCount; j++)
(*nCheck)[j] = 0;
}
and invocation
CallIntAllocation(&nCheck, nCount);
Clearly in this situation returning a new pointer value is the sensible approach.
Final point: if you have it available, "memset" (C90 but not C89 afaik, part of the single unix specification however) can be used in place of your "for" loop
memset(ncheck, 0, nCount);
(that's for your version of the function, not the one that takes an int ** argument)

You can "return" the pointer to the allocated memory. If NULL is returned, that means the allocation was unsuccessful.

Related

C allocate 'array pointer' inside a function

Related to dynamic allocation inside a function, most questions & answers are based on double pointers.
But I was recommended to avoid using double pointer unless I have to, so I want to allocate a 'array pointer' (not 'array of pointer') and hide it inside a function.
int (*arr1d) = calloc(dim1, sizeof(*arr1d));
int (*arr2d)[dim2] = calloc(dim1, sizeof(*arr2d));
Since the above lines are the typical dynamic-allocation of pointer of array, I tried the following.
#include <stdio.h>
#include <stdlib.h>
int allocateArray1D(int n, int **arr) {
*arr = calloc(n, sizeof(*arr));
for (int i = 0; i < n; i++) {
(*arr)[i] = i;
}
return 0;
}
int allocateArray2D(int nx, int ny, int *(*arr)[ny]) {
*arr[ny] = calloc(nx, sizeof(*arr));
for (int i = 0; i < nx; i++) {
for (int j = 0; j < ny; j++) {
(*arr)[i][j] = 10 * i + j;
}
}
return 0;
}
int main() {
int nx = 3;
int ny = 2;
int *arr1d = NULL; // (1)
allocateArray1D(nx, &arr1d);
int(*arr2d)[ny] = NULL; // (2)
allocateArray2D(nx, ny, &arr2d);
for (int i = 0; i < nx; i++) {
printf("arr1d[%d] = %d \n", i, arr1d[i]);
}
printf("\n");
printf("arr2d \n");
for (int i = 0; i < nx; i++) {
for (int j = 0; j < ny; j++) {
printf(" %d ", arr2d[i][j]);
}
printf("\n");
}
return 0;
}
And the error message already comes during the compilation.
03.c(32): warning #167: argument of type "int (**)[ny]" is incompatible with parameter of type "int *(*)[*]"
allocateArray2D(nx, ny, &arr2d);
^
It is evident from the error message that it has been messed up with the argument types (that I wrote as int *(*arr)[ny]) but what should I have to put there? I tried some variants like int *((*arr)[ny]), but didn't work).
And if I remove the 2D parts, then the code well compiles, and run as expected. But I wonder if this is the right practice, at least for 1D case since there are many examples where the code behaves as expected, but in fact there were wrong or un-standard lines.
Also, the above code is not satisfactory in the first place. I want to even remove the lines in main() that I marked as (1) and (2).
So in the end I want a code something like this, but all with the 'array pointers'.
int **arr2d;
allocateArray2D(nx, ny, arr2d);
How could this be done?
You need to pass the array pointer by reference (not pass an array pointer to an array of int*):
int *(*arr)[ny] -> int (**arr)[ny]
The function becomes:
int allocateArray2D(int nx, int ny, int (**arr)[ny]) {
*arr = calloc(nx, sizeof(int[ny])); // or sizeof(**arr)
for (int i = 0; i < nx; i++) {
for (int j = 0; j < ny; j++) {
(*arr)[i][j] = 10 * i + j;
}
}
return 0;
}
For details, check out Correctly allocating multi-dimensional arrays
Best practices with malloc family is to always check if allocation succeeded and always free() at the end of the program.
As a micro-optimization, I'd rather recommend to use *arr = malloc( sizeof(int[nx][ny]) );, since calloc just creates pointless overhead bloat in the form of zero initialization. There's no use of it here since every item is assigned explicitly anyway.
Wrong parameter type
Strange allocation
Wrong size type
I would return the array as void * too (at least to check if allocation did not fail).
void *allocateArray2D(size_t nx, size_t ny, int (**arr)[ny]) {
//*arr = calloc(nx, sizeof(**arr)); calloc is not needed here as you assign values to the array
*arr = malloc(nx * sizeof(**arr));
for (size_t i = 0; i < nx; i++) {
for (size_t j = 0; j < ny; j++) {
(*arr)[i][j] = 10 * i + j;
}
}
return *arr;
}

Why this counting sort return input instead of sorted table?

I'm writing counting sort in C. N is the number of elements in table which is to be sorted, k is max value that any of this element can be. However, this code, leaves me with the same table as the input. What's wrong?
void countingSort(int *tab, int n, int k) {
int *counters = (int *)malloc(k * sizeof(int));
int *result = (int *)malloc(n * sizeof(int*));
for (int i = 0; i < k; i++) {
counters[i] = 0;
}
for (int i = 0; i < n; i++) {
counters[tab[i]]++;
}
int j = 0;
for (int i = 0; i < k; i++) {
int tmp = counters[i];
while (tmp--) {
result[j] = i;
j++;
}
}
tab = result;
}
There are some problems in your code:
int *result = (int *)malloc(n * sizeof(int*)); uses an incorrect size. The array element type is int, not int*. You should write:
int *result = (int *)malloc(n * sizeof(int));
or better:
int *result = (int *)malloc(n * sizeof(*result));
note also that the cast is useless in C, unlike C++ where it is mandatory:
int *result = malloc(n * sizeof(*result));
you could avoid the extra initializing loop by using calloc():
int *counters = calloc(n, sizeof(*counters));
a major problem: the result array is never returned to the caller: tab = result; just modifies the argument value, not the caller's variable. You should instead use the tab array to store the results directly.
you do not free the arrays, causing memory leaks.
you do not test for allocation success, causing undefined behavior if memory is not available. You should return an error status indicating this potential problem.
Here is a corrected version:
// assuming all entries in tab are > 0 and < k
int countingSort(int *tab, int n, int k) {
int *counters = calloc(k, sizeof(*counters));
if (counters == NULL)
return -1;
for (int i = 0; i < n; i++) {
counters[tab[i]]++;
}
int j = 0;
for (int i = 0; i < k; i++) {
int tmp = counters[i];
while (tmp--) {
tab[j++] = i;
}
}
free(counters);
return 0;
}
You pass tab to the function by pointer. However you need to change not the value, but address of the variable. So you should pass address of the pointer to countingSort.
void countingSort(int **tab, int n, int k)

Function to initialize two dimensional array

I have a c program in which I want to initialize a 2 dimensional array.
So I made this function :
void initLayer(int **layer, int *dimensions) {
printf("initLayer\n");
layer = malloc(sizeof(int*) * dimensions[0]);
for (int i = 0; i < dimensions[0]; i++) {
layer[i] = malloc(sizeof(int) * dimensions[1]);
}
}
When I use this function there is no problem, but when I try to read the 2D array later I always get a segmentation fault.
I think it may be because the initialization made in the function are not saved when its finished.
Do you know how I could correct my function ? Thank you in advance.
To passing pointer to function you need one more pointer.
int **matrix; is an array of arrays, so to fill it you need to pass it as a pointer, which is int ***layer. but it is weird.
also for changing data by pointer you need to add a star * before it. *layer = ...
#include <stdlib.h>
void initLayer(int ***layer, int *dimensions)
{
*layer = malloc(sizeof(int *) * dimensions[0]);
for (int i = 0; i < dimensions[0]; i++)
{
*(*layer + i) = malloc(sizeof(int) * dimensions[1]);
}
}
int main()
{
int **matrix;
int dimensions[2] = { 4, 6 };
initLayer(&matrix, dimensions);
// then do whatever you want
for (int i = 0; i < dimensions[0]; i++)
{
for (int j = 0; j < dimensions[1]; j++)
{
matrix[i][j] = i * j;
}
}
}
as for me, better to use typedef to make code more readable:
#include <stdlib.h>
typedef int * Array;
typedef int ** Matrix;
void initLayer(Matrix *layer, Array dimensions)
{
*layer = malloc(sizeof(Array) * dimensions[0]);
for (int i = 0; i < dimensions[0]; i++)
{
(*layer)[i] = malloc(sizeof(int) * dimensions[1]);
}
}
int main()
{
Matrix matrix;
int dimensions[2] = { 4, 6 };
initLayer(&matrix, dimensions);
// then do whatever you want
for (int i = 0; i < dimensions[0]; i++)
{
for (int j = 0; j < dimensions[1]; j++)
{
matrix[i][j] = i * j;
}
}
}
When you call the function, the int **layer pointer is copied. So, when you do layer = malloc(...) what actually happens is the function sets its local copy to the malloc result. What you want is to mutate the variable which you called the function with. You can do this by taking a int ***layer and passing in &layer when calling initLayer. Note that you must then use *layer instead of layer in your code.
You have two approaches here:
to pass a reference to the double pointer (***int in this case)
or to return the allocated pointer as the result of your function:
in the first case:
void initLayer(int ***layer, int *dimensions) {
printf("initLayer\n");
*layer = malloc(sizeof(int*) * dimensions[0]);
for (int i = 0; i < dimensions[0]; i++) {
layer[i] = malloc(sizeof(int) * dimensions[1]);
}
}
you pass a reference to a pointer, instead of passing the (uninitialized) pointer. Remember, in C, all parameters are passed by value. In this case, you can call your function as:
...
int**vector;
...
initLayer(&vector, dims); /* you pass the address of your double pointer */
In the second case:
int** initLayer(int *dimensions) {
printf("initLayer\n");
int **layer = malloc(sizeof(int*) * dimensions[0]);
for (int i = 0; i < dimensions[0]; i++) {
layer[i] = malloc(sizeof(int) * dimensions[1]);
}
return layer;
}
in this case, you call it as:
...
int**vector;
...
vector = initLayer(dims); /* you receive your double pointer as a return value */

Filling an array with random numbers

Currently I am trying to fill an array of size num with random values. To do this, I need to create two functions:
1: Write a function (*createdata) that allocates a dynamic array of num double values, initialising the values to 0.0.
2: Write a different function (gendata) that will populate an array of double values with random values generated using the rand() function.
Here is my attempt at writing how the functions operate (outside main() ):
double *createdata(int num)
{
int i = 0;
double *ptr;
ptr = (double *)malloc(sizeof(double)*num);
if(ptr != NULL)
{
for(i = 0; i < num; i++)
{
ptr[i] = 0.0;
}
}
}
double gendata(int num)
{
createdata(num);
int j = 0;
for(j = 0; j < num; j++)
{
ptr[j] = rand();
}
}
However, I know that there is something certainly wrong with the above.
What I would like is that once I have called both functions in main, I will have generated an array of size num that is filled with random numbers.
You have to pass the allocated pointer to the gendata function, because in it's current form, ptr is unknown. Does that even compile?
Example:
double *createdata(int num)
{
int i = 0;
double *ptr;
ptr = (double *)malloc(sizeof(double)*num);
if(ptr != NULL)
{
for(i = 0; i < num; i++)
{
ptr[i] = 0.0;
}
}
return ptr;
}
And:
double gendata(int num)
{
double *ptr = createdata(num);
int j = 0;
if(ptr != NULL)
{
for(j = 0; j < num; j++)
{
ptr[j] = rand();
}
}
}
Also note I'm checking for return NULL again from malloc.
Other hints others might say here:
Don't cast the return of malloc.
Don't forget to free the pointer after you use it entirely.
You're not returning anything in your function gendata, but you declared it as double. Use void instead if you're not going to return anything.
But you're probably gonna need to return the pointer from it anyways, so that you can use it later in other functions, such as main.
EDIT: So, this would be like this, as an example:
double *gendata(int num)
{
double *ptr = createdata(num);
int j = 0;
if(ptr != NULL)
{
for(j = 0; j < num; j++)
{
ptr[j] = rand();
}
}
return ptr;
}
And in your main:
int main(void)
{
double *data;
data = gendata(100);
printf("%g\n", data[5]);
// When you're done, call free
free(data);
return 0;
}
You need to return the array allocated in the createdata() function, otherwise you can't use it in gendata(), or anywhere else.
I'm not sure why gendata() is declared as returning a double, either.
In createdata(), you forgot to actually return the pointer to the newly allocated array at the end:
return ptr;
Also, your gendata() function needs to work on an already existing array, not generate its own. You should add it as an argument. Also, it doesn't need to return anything. So:
void gendata(double ptr[], int num)
{
int j = 0;
if (ptr != NULL) {
for(j = 0; j < num; j++) {
ptr[j] = rand();
}
}
}
However, you can simplify the above and reduce the nesting level:
void gendata(double ptr[], int num)
{
int j = 0;
if (ptr == NULL)
return;
for(j = 0; j < num; j++) {
ptr[j] = rand();
}
}
Note that double ptr[] actually means the same as double* ptr, but the [] syntax makes it more apparent that what the function wants is a pointer to an array of doubles rather than a pointer to a single double.
So now in order to create an array and then randomize it, you would do:
double* array = createdata(N); // Create an array of N doubles.
gendata(array, N); // Randomize it.
Well, I'm not quite familiar with how malloc works, although it looks like that will work fine, so I can't evaluate that, but the only issues I can see here are this:
You aren't returning ptr in your createdata function. So when you try to access ptr in your gendata function, there is no ptr array in scope. That will give you an error. Also, gendata function does not even return anything.
Look at this code, this should work:
double *createdata(int num)
{
int i = 0;
double *ptr;
ptr = (double *)malloc(sizeof(double)*num);
if(ptr != NULL)
{
for(i = 0; i < num; i++)
{
ptr[i] = 0.0;
}
}
return ptr; // return the pointer
}
double *gendata(int num) // you forgot the '*' here
{
double *ptr = createdata(num);
int j = 0;
for(j = 0; j < num; j++)
{
ptr[j] = rand();
}
return ptr; // i added this so you obviously return the pointer
}
I believe this will work, here's an example of using it:
int main()
{
int c;
double *data = gendata(8);
for(c = 0; c < 8; c++)
{
printf("%f\n", data[c]);
}
}

C memory leak despite free

In debugging my program with Valgrind, I have discovered a memory leak despite what I thought were effective calls to free. First, the code that is allocating the memory and storing it:
row = malloc(sizeof(Row));
row->columns = malloc(sizeof(char*) * headcnt);
row->numcol = 0;
...
row->numcol = colcnt;
rows = realloc(rows, (rowcnt+1) * sizeof(Row));
rows[rowcnt++] = *row;
The code responsible for attempting to free the memory:
void cleanUp(){
int i = 0;
int j = 0;
for (i = 0; i < rowcnt; i++){
for (j = 0; j < rows[i].numcols; j++){
free(rows[i].columns[j]);
}
free(&rows[i]);
}
free(rows);
exit(0);
}
The declaration of Row:
typedef struct {
char** columns;
unsigned short int numcol;
} Row;
Row* rows = NULL;
Worse still, this program sometimes causes a glibc error at free(&rows[i]) that complains of a double free. I am new to C, and would appreciate any pointers (ahem) someone might have.
Doing rows[rowcnt++] = *row; effectively makes a copy of the memory you allocated. Your array rows should be an array of pointers. Also like Oli Chalesworth pointed out, you free for columns should be a single free for all the columns.
rows = malloc(count * sizeof(Row*)); // This is probably done somewhere
row->columns = malloc(sizeof(char*) * headcnt);
row->numcol = 0;
...
row->numcol = colcnt;
rows = realloc(rows, (rowcnt+1) * sizeof(Row*));
rows[rowcnt++] = row;
Now if your cleanup
void cleanUp(){
int i = 0;
int j = 0;
for (i = 0; i < rowcnt; i++){
free(rows[i]->columns);
}
free(rows);
exit(0);
}
Every call to malloc (or realloc) must be matched with a corresponding call to free. If you dynamically allocate an array thus:
int *p = malloc(sizeof(int) * NUM);
You free it like this:
free(p);
Not like this:
for (int i = 0; i < NUM; i++)
{
free(p[i]);
}
It appears that you are doing this incorrectly. I suspect that your cleanup code ought to be:
void cleanUp(){
int i = 0;
int j = 0;
for (i = 0; i < rowcnt; i++){
for (j = 0; j < rows[i].numcols; j++){
free(rows[i].columns[j]); // Free whatever rows[i].columns[j] points to
}
free(rows[i].columns); // Matches row->columns = malloc(sizeof(char*) * headcnt);
}
free(rows); // Matches rows = realloc(rows, (rowcnt+1) * sizeof(Row));
exit(0);
}
Also, there is no way to match the row = malloc(sizeof(Row));. I suspect that your allocation code ought to be:
row->numcol = colcnt;
rows = realloc(rows, (rowcnt+1) * sizeof(Row));
rows[rowcnt].columns = malloc(sizeof(char*) * headcnt);
rows[rowcnt].numcol = 0;
rowcnt++;
Maybe I'm being dense, but isn't this totally unnecessary? All of your memory will be released as soon as the program exits, anyway.

Resources