How to correctly initialise a struct in C - arrays

I'm working on a small project in C and I want to allocate structs in a function and add them to an array of structs.
For some reason, when I go to print the contents of the array of structs, I appear to start printing from unallocated memory.
A minimum working example is provided below:
#include <stdio.h>
#include <stdlib.h>
// This struct simply stores a list and its size
struct list {
int* values;
size_t size;
};
// This function initialises an array of lists
// of size len, which each list (i.e. list.values)
// having a size of list_len
// `lists` is an array of pointers to lists
void list_init(struct list** lists, size_t len, size_t list_len) {
for (size_t i = 0; i < len; i++) {
struct list list;
list.values = malloc(sizeof(int) * list_len);
list.size = list_len;
lists[i] = &list;
}
}
void main() {
int len = 3;
struct list* lists[len];
list_init(lists, len, 5);
// Print lists
for (size_t i = 0; i < len; i++) {
printf("list %zu: ", i);
printf("size: %zu\n", lists[i]->size);
for (size_t j = 0; j < 5; j++) { // Using 5 instead of lists[i]->size for obvious reasons
printf("%d ", lists[i]->values[j]);
}
printf("\n");
}
}
The output I would expect from this is:
list 0: size: 5
0 0 0 0 0
list 1: size: 5
0 0 0 0 0
list 2: size: 5
0 0 0 0 0
but instead I get:
list 0: size: 5
0 0 0 0 0
list 1: size: 140727488332736
0 0 0 0 0
list 2: size: 140727488332736
0 0 0 0 0
which is a pretty clear sign that I'm accessing memory that I'm not supposed to.
I noticed that rather than declaring the list with struct list list;, if I declare the list by allocating memory to a pointer with struct list* list = malloc(sizeof(struct list)); the program gives the expected output. Why is that? If I want to create the object, rather than a pointer, how can I do that properly.
P.S.: I am aware that I could just initialise list as a pointer. This question is mostly asking why can't I initialise it as an object

You save the references to the same local variable and it is an UB. Also malloced memory is lost. Your main is also wrong.
I would do it this way (calloc is used as in main you print not initialized allocated memory):
typedef struct list {
size_t size;
int values[];
}list;
list **list_init(list **array, size_t size, size_t list_len)
{
list **wrk;
if(!array) wrk = malloc(sizeof(*wrk) * size);
else wrk = array;
if(wrk)
for (size_t i = 0; i < size; i++) {
list *list = calloc(1, sizeof(*list) + list_len * sizeof(list -> values[0]));
/* check for allocation errors!!!! */
list -> size = list_len;
wrk[i] = list;
}
return wrk;
}
int main(void) {
size_t len = 3;
list **lists;
/* if you pass NULL it will create the list of lists itself */
lists = list_init(NULL, len, 5);
/* check for allocation errors!!!! */
// Print lists
for (size_t i = 0; i < len; i++) {
printf("list %zu: ", i);
printf("size: %zu\n", lists[i]->size);
for (size_t j = 0; j < 5; j++) { // Using 5 instead of lists[i]->size for obvious reasons
printf("%d ", lists[i]->values[j]);
}
printf("\n");
}
for (size_t i = 0; i < len; i++) free(lists[i]);
free(lists);
}
https://godbolt.org/z/9TPe1sM1a

These statements within the function list_init
struct list list;
//...
lists[i] = &list;
does not make a sense because the local object list will not be alive after exiting the function. So you will have an array of invalid pointers of the type struct list *.
You need to allocate dynamically each object of the type struct list that will be pointed to by an element of the array.
The function can be declared and defined for example the following way
size_t list_init( struct list **lists, size_t len, size_t list_len )
{
size_t count = 0;
for ( size_t i = 0; i < len; i++ )
{
lists[i] = malloc( sizeof( struct list ) );
if ( lists[i] != NULL )
{
++count;
lists[i]->size = 0;
lists[i]->values = malloc( sizeof( int ) * list_len );
if ( lists[i]->values != NULL ) lists[i]->size = list_len;
}
}
return count;
}
Also as the function does not initialize the allocated array pointed to by the data member values then this loop in main
for (size_t j = 0; j < 5; j++) { // Using 5 instead of lists[i]->size for obvious reasons
printf("%d ", lists[i]->values[j]);
}
will invoke undefined behavior.
You could zero initialize the arras by using calloc instead of malloc. For example
lists[i]->values = calloc( list_len, sizeof( int ) );
And pay attention to that according to the C Standard the function main without parameters shall be declared like
int main( void )
Here is your updated program.
#include <stdio.h>
#include <stdlib.h>
// This struct simply stores a list and its size
struct list
{
int* values;
size_t size;
};
size_t list_init( struct list **lists, size_t len, size_t list_len )
{
size_t count = 0;
for ( size_t i = 0; i < len; i++ )
{
lists[i] = malloc( sizeof( struct list ) );
if ( lists[i] != NULL )
{
++count;
lists[i]->size = 0;
lists[i]->values = calloc( list_len, sizeof( int ));
if ( lists[i]->values != NULL ) lists[i]->size = list_len;
}
}
return count;
}
int main(void)
{
size_t len = 3;
struct list* lists[len];
list_init(lists, len, 5);
// Print lists
for ( size_t i = 0; i < len; i++ )
{
printf( "list %zu: ", i );
if ( lists[i] != NULL )
{
printf( "size: %zu\n", lists[i]->size );
for (size_t j = 0; j < lists[i]->size; j++)
{
printf( "%d ", lists[i]->values[j] );
}
printf("\n");
}
}
return 0;
}
The program output is
list 0: size: 5
0 0 0 0 0
list 1: size: 5
0 0 0 0 0
list 2: size: 5
0 0 0 0 0
Of course you need to add a code to your program that will free all the allocated memory in the function list_init.

Related

C: copying array in function by using pointers gave unusual result, why did it half work?

So I don't want to make a function for every data type, I would like to be able to just use pointers to fix the problem. Tried the idea with a function for copying an array. I didn't get the result i expected or wanted, even if it had just gone entirely wrong with the new_arr not changing at all i would be ok this isn't it. but it gave me hope.
void *copy_array(const void *arr[], const void *new_arr[], size_t arr_len, size_t type_size) {
for (int i = 0; i < arr_len ; ++i) {
*(new_arr + (i * type_size)) = *(arr + (i * type_size));
}
}
void print_int_array(int * array, int length) {
for (int i = 0; i < length; ++i) {
printf("\nelement %d = %d", i, array[i]);
}
}
int main() {
int arr[ARRAY_LENGTH] = {12, 3,4};
int new_arr[ARRAY_LENGTH] = {0, 0, 0};
print_int_array(arr, ARRAY_LENGTH);
print_int_array(new_arr, ARRAY_LENGTH);
copy_array(&arr, &new_arr, ARRAY_LENGTH, sizeof(new_arr[0]));
print_int_array(arr, ARRAY_LENGTH);
print_int_array(new_arr, ARRAY_LENGTH);
return 0;
}
Console returns this for some reason, it gets the 12 and the 3 by why not the 4?
element 0 = 12
element 1 = 3
element 2 = 4
element 0 = 0
element 1 = 0
element 2 = 0
element 0 = 12
element 1 = 3
element 2 = 4
element 0 = 12
element 1 = 3
element 2 = 0
You declared a function
void *copy_array(const void *arr[], const void *new_arr[], size_t arr_len, size_t type_size) {
where the both first parameters have the qualifier const. So the function declaration is incorrect.
Instead it would be better to declare the first parameter of the function print_int_array with the qualifier const and the second parameter as having the type size_t
void print_int_array( const int * array, size_t length) {
On the other hand, the expressions
*(new_arr + (i * type_size))
and
*(arr + (i * type_size))
have pointer types. Their sizes can be greater than the size of an object of the type int. For example sizeof( void * ) can be equal to 8 while sizeof( int ) can be equal to 4.
Thus the function can invoke undefined behavior.
Apart from that the function has a non-void return type but returns nothing.
Also there is no great sense to call the function passing pointers to arrays like
copy_array(&arr, &new_arr, ARRAY_LENGTH, sizeof(new_arr[0]));
where each expression has the type int( * )[3].
The function can be defined the following way as shown in the demonstration program below.
#include <stdio.h>
#include <string.h>
void copy_array( void *new_arr, const void *arr, size_t arr_len, size_t type_size )
{
for (size_t i = 0; i < arr_len; i++)
{
memcpy( new_arr, arr, type_size );
new_arr = ( char * )new_arr + type_size;
arr = ( const char * )arr + type_size;
}
}
int main( void )
{
enum { ARRAY_LENGTH = 3 };
int arr[ARRAY_LENGTH] = { 12, 3,4 };
int new_arr[ARRAY_LENGTH] = { 0, 0, 0 };
printf( "arr : " );
for (size_t i = 0; i < ARRAY_LENGTH; i++)
{
printf( "%d ", arr[i] );
}
putchar( '\n' );
printf( "new_arr: " );
for (size_t i = 0; i < ARRAY_LENGTH; i++)
{
printf( "%d ", new_arr[i] );
}
putchar( '\n' );
putchar( '\n' );
copy_array( new_arr, arr, ARRAY_LENGTH, sizeof( int ) );
printf( "arr : " );
for (size_t i = 0; i < ARRAY_LENGTH; i++)
{
printf( "%d ", arr[i] );
}
putchar( '\n' );
printf( "new_arr: " );
for (size_t i = 0; i < ARRAY_LENGTH; i++)
{
printf( "%d ", new_arr[i] );
}
putchar( '\n' );
}
The program output is
arr : 12 3 4
new_arr: 0 0 0
arr : 12 3 4
new_arr: 12 3 4
So this is what i wanted to do which is basicly memcpy. The arrays get converted to char arrays which is only 1 byte, so every byte gets copied from the size of the original array.
void copy_array(void *arr, void *new_arr, size_t arr_size) {
char *char_new_arr = (char *) new_arr;
char *char_arr = (char *) arr;
for (int i = 0; i < arr_size; ++i) {
char_new_arr[i] = char_arr[i];
}
}

How to get struct field having pointer to the struct?

I have two structs:
struct first
{
int* array;
}
struct second
{
struct first* firsts;
} *SECOND;
Let's say somewhere I can get the address of one of struct first from SECOND->firsts by its index.
How could I get array of this struct? I've tried
SECOND->firsts[index]->array
but there is expression must have pointer-to-struct-or-union type error.
No malloc checks and no free - just to show the idea
struct first {
int* array; };
struct second {
struct first* firsts; } *SECOND;
#define NUMFIRSTS 50
#define ARRSIZE 50
int main() {
SECOND = malloc(sizeof(*SECOND));
SECOND -> firsts = malloc(NUMFIRSTS * sizeof(*SECOND -> firsts));
for(int f = 0; f < NUMFIRSTS; f++)
{
SECOND -> firsts[f].array = malloc(ARRSIZE * sizeof(*SECOND -> firsts[f].array));
}
//access
SECOND -> firsts[5].array[10] = 23;
}
Here you are.
#include <stdio.h>
#include <stdlib.h>
struct first
{
int* array;
};
struct second
{
struct first* firsts;
} *SECOND;
int main(void)
{
size_t n = 10;
struct first f = { NULL };
SECOND = malloc( sizeof( *SECOND ) );
SECOND->firsts = &f;
SECOND->firsts->array = malloc( n * sizeof( int ) );
for ( size_t i = 0; i < n; i++ )
{
SECOND->firsts->array[i] = ( int )i;
}
for ( size_t i = 0; i < n; i++ )
{
printf( "%d ", SECOND->firsts->array[i] );
}
putchar( '\n' );
for ( size_t i = 0; i < n; i++ )
{
printf( "%d ", f.array[i] );
}
putchar( '\n' );
// ...
// free all allocated memory
return 0;
}
the program output is
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
Pay attention to that as the pointer SECOND contains pointer to struct first f that in turn contains pointer to array then the both objects points to the same memory pointed to by the pointer array.
The array index operator [] has an implicit pointer dereference. So this expression:
SECOND->firsts[index]
Has type struct first, not struct first *, which means you can't use the -> operator on it. You need to instead use the member selector operator .:
SECOND->firsts[index].array

free(): invalid next size (fast) for resizable vector

I'm trying to implement a resizable vector in C without using the realloc and calloc functions. However, when I try to push a lot of values to the back of the array, I get a free(): invalid next size error. How can I remedy this?
I tried freeing the array right before I re-malloc it( the commented out block) but that causes a seg fault rather than the current error.
typedef struct {
size_t size;
size_t maxsize;
int* array;
}
vector_int_t;
// Push a new value into the vector_int_t, allocate just enough memory if
// the internal array is full.
void vector_int_push_back_v1( vector_int_t* this, int value )
{
if( this->size == this->maxsize ) {
int* temp = malloc( 4 * (sizeof( this->maxsize)+1) );
size_t j = 0;
for( size_t i = 0; i < this->maxsize; i++ ) {
temp[j] = this->array[i];
j++;
}
temp[this->size] = value;
/*if( this->size == 0 ) {
this->array = malloc( 4 * (sizeof(this->maxsize)+2));
this->size++;
this->maxsize++;
size_t z = 0;
for( size_t y = 0; y < this->maxsize; y++ ) {
this->array[z] = temp[y];
z++;
}
}
else {
free( this->array );*/
this->array = malloc( 4 * (sizeof(this->maxsize)+2) );
this->size++;
this->maxsize++;
size_t h = 0;
for( size_t k = 0; k < this->maxsize; k++ ) {
this->array[h] = temp[k];
h++;
}
free( temp );
}
else {
this->size++;
this->maxsize++;
this->array[this->size - 1] = value;
}
}
This error typically happens when you write out of bounds of the allocated memory.
A very likely culprit is the use of sizeof in e.g. malloc( 4 * (sizeof( this->maxsize)+1) ).
If you want to allocate this->maxsize + 1 elements, you should use just that not sizeof.
But you should use sizeof to get the size of each element (e.g. sizeof *temp).
So the statement
int* temp = malloc( 4 * (sizeof( this->maxsize)+1) );
should really look like
int* temp = malloc( sizeof *temp * (this->maxsize+1) );

reallocation of 2d arrays in c

I want to reallocate a 2d array, so that the arrays in the second array become bigger, so the things I want to store are bigger than the arrays I want to store them in and I want to make the arrays bigger. The problem is that I do not really know how to do this. I got it to compile without errors, but in Valgrind I saw a lot of memory errors, so I do something wrong. I saw a previous question about this here but I do not really understand it, so any help and explanation on how to do this would be greatly appreciated.
I have this so far.
int **create2darray(int a, int b) {
int i;
int **array;
array = malloc(a * sizeof(int *));
assert(array != NULL);
for (i = 0; i < a; i++) {
array[i] = calloc(b, sizeof(int));
assert(array[i] != NULL);
}
return array;
}
int **reallocArray(int **array, int size, int i) {
int i;
int **safe_array;
safe_array = realloc(*array ,2 * size);
assert(safe_array != NULL);
array = safe_array;
return array;
}
void free2DArray(int **array, int m) {
int i;
for (i = 0; i < m; i++) {
free(array[i]);
}
}
int main(int argv, char *argc[]) {
int i;
int size;
int **testArray = create2darray(1, 10);
size = 10;
for(i = 0; i < size; i++) {
testArray[0][i] = 2;
}
testArray[0] = reallocArray(testArray, size, 0);
size = 2 * size;
for(i = 9; i < size; i++) {
testArray[0][i] = 3;
}
for(i = 0; i < size; i++) {
printf("%d", testArray[0][i]);
free2DArray(testArray, size);
}
return 0;
}
You need a function reallocArray which realaoctes the outer array and all the inner arrays too.
Adapt youre code like this:
#include <malloc.h>
int **reallocArray( int **array, int oldSizeA, int newSizeA, int newSizeB )
{
// realloc the array of pointers ( allocates new memory if array == NULL )
int **safe_array = realloc( array, newSizeA * sizeof( int* ) );
assert(safe_array != NULL);
if ( safe_array == NULL )
return array;
array = safe_array;
// realloc the inner arrays of int ( allocates new memory if i >= oldSizeA )
for ( int i = 0; i < newSizeA; i ++ )
{
int *temp = NULL; // allocate new memory if i >= oldSizeA
if ( i < oldSizeA )
temp = array[i]; // reallocate array[i] if i < oldSizeA
temp = realloc( temp, newSizeB * sizeof( int ) );
assert( temp != NULL );
if ( temp == NULL )
return array;
array[i] = temp;
}
return array;
}
Use function reallocArray in your function create2darray to create your array. If the input paramter of ralloc is NULL, then new dynamic memory is allocated.
int **create2darray( int sizeA, int sizeB )
{
return reallocArray( NULL, 0, sizeA, sizeB );
}
First you have to free the inner arrays of int in a loop, then you have to free the array of pointers:
void free2DArray( int **array, int sizeA )
{
for (int i = 0; i < sizeA; i ++)
free( array[i] );
free( array );
}
int main( int argv, char *argc[] ){
int sizeA = 1;
int sizeB = 10;
int **testArray = create2darray( sizeA, sizeB );
for ( int i = 0; i < sizeB; i++ ) {
testArray[0][i] = 2;
}
int oldSizeA = sizeA;
int oldSizeB = sizeB;
sizeB = 2*sizeB;
testArray = reallocArray( testArray, oldSizeA, sizeA, sizeB );
for( int i = oldSizeB; i < sizeB; i++ ) {
testArray[0][i] = 3;
}
for( int i = 0; i < sizeB; i++ ) {
printf("%d", testArray[0][i]);
}
free2DArray(testArray, sizeA );
return 0;
}
In Free2DArray(), you free() the individual arrays of integers, but not the "outer" dimension of the array which holds the integer pointers.
You could add another call to free() after the loop to take care of that.
In main():
for(i = 0; i <size; i++) {
printf("%d", testArray[0][i]);
free2DArray(testArray, size);
}
you will end up free()-ing the (inner) integer arrays multiple times.
The call to free2DArray() should be outside the loop.

Move all values inside vectors in C

i'm writing a code for a project, in which i need to move values inside a vector! For example, i have a completely "null" vector:
[0][0][0][0][0]
Then i will add a value to it's first position: [10][0][0][0][0]!
Ok, now i need to add again a value to the first position, but i cant lose the value already there, i want to move it to the right, like:
[0][10][0][0][0]
To put the new one there: [5][10][0][0][0]
And then do this again, move the 5 and the 10 to the right again, to add a new one in the first, and so on!
I hope i was clear enough!
#EDIT :
Its done guys! Thanks to everyone that tried to help!
The problem was solved using #AkashPradhan function:
void changeposition(int* vetor, unsigned int size, int valor)
{
unsigned int i = size - 1;
for(; i > 0; --i)
vetor[i] = vetor[i-1] ;
vetor[0] = valor ;
}
Thanks #AkashPradhan! The function worked perfectly with my code!
You can use a Linked List and add to the front node or you could use an array and shift the elements by one position for each insertion.
In this case, I would recommend using a Linked List as the insertion routine will not involving moving every element. However if frequent random access to elements in required, use the array approach.
void insert_front(int* array, unsigned int size, int value)
{
unsigned int i = size - 1;
for(; i > 0; --i)
array[i] = array[i-1] ;
array[0] = value ;
}
Note that the routine does not check if you are inserting more elements that the size of the array.
You could implement this as a ring buffer, in which case you don't need to copy the elements all the time. An example of it would be:
typedef struct
{
size_t bufferSize;
size_t currentPos;
int *data;
} ring_buffer;
void init_buffer(ring_buffer *buffer, size_t N)
{
buffer->bufferSize = N;
buffer->currentPos = 0;
buffer->data = malloc(N*sizeof(int));
}
void free_buffer(ring_buffer *buffer)
{
free(buffer->data);
}
void push_buffer(ring_buffer *buffer, int value)
{
++buffer->currentPos;
if (buffer->currentPos >= buffer->bufferSize)
buffer->currentPos = 0;
buffer->data[buffer->currentPos] = value;
}
int get_buffer(ring_buffer *buffer, size_t pos)
{
size_t dataPos = buffer->currentPos + pos;
if (dataPos >= buffer->bufferSize)
dataPos -= buffer->bufferSize;
return buffer->data[dataPos];
}
or you could use a linked list as suggested by #AkashPradhan.
Define your own array, implement push operation, shift right data when push a new element. This implementation would delete the stale element.
typedef struct Array {
int *data;
size_t len; // current size of the array
size_t cap; // capacity of the array
}Array;
Array * new_array (size_t cap_){
Array *arr = (Array *)malloc(sizeof(Array));
arr->cap = cap_;
arr->len = 0;
arr->data = (int*)malloc(sizeof(int) * cap);
return arr;
}
void free_array(Array * arr){
if (arr->data != NULL)
free(arr->data);
free(arr);
}
void push(int x, Array *arr) {
int ptr = std::min(len, cap) - 2;
for(int i = ptr;i >= 0;i --) {
arr->data[ptr + 1] = arr->data[ptr];
}
arr->data[0] = x;
len = std::max(cap, len + 1);
}
Update : You can also use Tamás Szabó 's answer to avoid data movement.
If you need to write the corresponding function yourself then it can look the following way
size_t move( int a[], size_t n )
{
if ( n++ != 0 )
{
for ( size_t i = n; --i != 0; ) a[i] = a[i-1];
}
return n;
}
And you could use it the following way
size_t n = 0;
n = move( a, n );
a[0] = 10;
n = move( a, n );
a[0] = 5;
for ( size_t i = 0; i < n; i++ ) printf( "%d ", a[i] );
printf( "\n" );
Otherwise you have to use standard C function memmove the following way
memmove( a + 1, a, n * sizeof( int ) );
++n;
where n is the current number of actual elements in the array.

Resources