Home > front end >  Initialising a C vector pointer throws a bus error but initialising a vector doesn't
Initialising a C vector pointer throws a bus error but initialising a vector doesn't

Time:02-08

This C code:

#include <stdlib.h>

typedef struct
{
    int *a;
    int sz, cap;
} Vector;

void vector_init(Vector *v)
{
    v->a = (int *)malloc(sizeof(int));
    v->sz = 0;
    v->cap = 1;
}

int main()
{
    Vector *v;
    vector_init(v);
}

throws a bus error. but replacing Vector *v with Vector v and vector_init(v) with vector_init(&v) solves it. why?

CodePudding user response:

When you write Vector *v, you're creating an uninitialized pointer to Vector. There's no object of type Vector that exists in this program. v is uninitialized, dereferencing it is undefined behaviour, and in practice will often lead to a crash because of an illegal memory access.

Defining a pointer does not automatically create memory for an object of the pointed-to type, you need to do that.

Doing Vector v with vector_init(&v) works because you're creating an actual object of type Vector, then passing in a pointer to that.

To have an object of type Vector *, you have to give it an object to point to. That could be by explicitly creating an object:

Vector v_storage;
Vector *v = &v_storage;
vector_init(v);

Though it'd be silly to do the above in actual code, you should just stick with the Vector v and vector_init(&v).

You could also create storage for a Vector using a dynamic memory allocation using malloc, if that was appropriate for what you want to write, and then clean it up with free later.

CodePudding user response:

Design your API to parallel malloc() and free()

The way to handle objects in C is to take a pointer on create and pass a pointer on destroy. That is, you should write functions that parallel malloc() and free() (or, in my personal preference, free-and-null):

Vector * Vector_create( size_t size );
Vector * Vector_destroy( Vector * v );

They are used thus:

Vector * v0   = Vector_create( 0 );    // create an empty vector
Vector * v100 = Vector_create( 100 );  // create a vector with 100 elements

 

v0 = Vector_destroy( v0 );  // free and null
Vector_destroy( v100 );     // zeroing your pointer is optional

Useful operations on your vector should likewise have proper signatures:

Vector * Vector_copy( Vector * v );  // deep copy a vector
int * Vector_at( Vector * v, size_t index );  // etc
void Vector_push_back( Vector * v, int x );  // may resize the vector!

 

Vector * v2 = Vector_copy( v );  // create new vector v2 == v
*Vector_at( v, 7 ) = 123456;     // assign value to v[7] = 123456
Vector_push_back( v, 42 );       // increase size of v and assign 42 to new element (possible capacity change)

FAM

Depending on the object, I sometimes like to do as 0___________ did in his answer (use a flexible array member), but this does complicate your calling code a little because it removes a level of indirection — you must now pass references to your object as argument to every function (for consistency, even for functions that don’t need it, because some do!):

Vector * v = Vector_create( 0 );
Vector_push_back( &v, 42 );  // because v may be reallocated!
printf( "%d\n", *Vector_at( &v, 0 ) );
free( v );  // this might still be worth hiding behind a Vector_free() function

CodePudding user response:

  1. I would use flexible array members instead of pointers.
  2. In your code you need to pass double pointer In this case it is not the best idea, it is much better to use function return value instead side effects
  3. You need to allocate the memory for the structure itself, bot only for the data.
  4. Use correct types for sizes and indexes.
  5. Check for allocation errors!!
  6. Do not cast the result of malloc
typedef struct
{
    size_t sz, cap;
    int a[];
} Vector;


Vector *vector_init(void)
{
    Vector *vi = malloc(sizeof(*vi)   sizeof(vi->a[0]));
    
    if(vi)
    {
        vi->sz = 0;
        vi->cap = 1;
    }
    return vi;
}

If you do not want to return the value

void vector_init(Vector **vi)
{
    *vi = malloc(sizeof(**vi)   sizeof((*vi)->a[0]));
    
    if(*vi)
    {
        (*vi)->sz = 0;
        (*vi)->cap = 1;
    }
}

and usage

int main()
{
    Vector *v;
    vector_init(&v);
}
  •  Tags:  
  • Related