But, it seems that I am allocating the memory for y->x twice, one while allocating memory for y and the other while allocating memory for y->x, and it seems a waste of memory. It is very much appreciated if let me know what compiler really do and what would be the right way to initialize both y, and y->x.
43.7k 21 21 gold badges 143 143 silver badges 742 742 bronze badges asked Feb 8, 2013 at 8:08 1,949 3 3 gold badges 20 20 silver badges 26 26 bronze badgesAs pointed out eminently by paxdiablo, please don't cast the return value of malloc() in C. I'll never understand why everyone feels a need to do so. :(
Commented Feb 8, 2013 at 8:37 @unwind, maybe they're old C++ programmers upgrading to C :-) Commented Feb 8, 2013 at 8:55@unwind When using Nvidia's nvcc compiler on C code, if I don't cast the result of malloc, it throws an error.
Commented Jul 12, 2017 at 19:44@Nubcake According to this link that might be because nvcc runs the underlying compiler in C++ mode, due to their CUDA interface being C++. In C, you will not get errors for this. In C++ void * does not automatically convert to other pointers, and the cast is needed (or, just don't use malloc() in C++, of course).
Commented Jul 15, 2017 at 12:10@unwind Yep, I later found about this :) Just wanted to state a situation where if you didn't cast the result then it would thrown an error.
Commented Jul 15, 2017 at 14:36No, you're not allocating memory for y->x twice.
Instead, you're allocating memory for the structure (which includes a pointer) plus something for that pointer to point to.
Think of it this way:
1 2 +-----+ +------+ y------>| x------>| *x | | n | +------+ +-----+
You actually need the two allocations ( 1 and 2 ) to store everything you need.
Additionally, your type should be struct Vector *y since it's a pointer, and you should never cast the return value from malloc in C.
It can hide certain problems you don't want hidden, and C is perfectly capable of implicitly converting the void* return value to any other pointer.
And, of course, you probably want to encapsulate the creation of these vectors to make management of them easier, such as with having the following in a header file vector.h :
struct Vector < double *data; // Use readable names rather than x/n. size_t size; >; struct Vector *newVector(size_t sz); void delVector(struct Vector *vector); //void setVectorItem(struct Vector *vector, size_t idx, double val); //double getVectorItem(struct Vector *vector, size_t idx);
Then, in vector.c , you have the actual functions for managing the vectors:
#include "vector.h" // Atomically allocate a two-layer object. Either both layers // are allocated or neither is, simplifying memory checking. struct Vector *newVector(size_t sz) < // First, the vector layer. struct Vector *vector = malloc(sizeof (struct Vector)); if (vector == NULL) return NULL; // Then data layer, freeing vector layer if fail. vector->data = malloc(sz * sizeof (double)); if (vector->data == NULL) < free(vector); return NULL; >// Here, both layers worked. Set size and return. vector->size = sz; return vector; > void delVector(struct Vector *vector) < // Can safely assume vector is NULL or fully built. if (vector != NULL) < free(vector->data); free(vector); > >
By encapsulating the vector management like that, you ensure that vectors are either fully built or not built at all - there's no chance of them being half-built.
It also allows you to totally change the underlying data structures in future without affecting clients. For example:
You could also add more functionality such as safely setting or getting vector values (see commented code in the header), as the need arises.
For example, you could (as one option) silently ignore setting values outside the valid range and return zero if getting those values. Or you could raise an error of some description, or attempt to automatically expand the vector under the covers (1) .
In terms of using the vectors, a simple example is something like the following (very basic) main.c
#include "vector.h" #include int main(void)
(1) That potential for an expandable vector bears further explanation.
Many vector implementations separate capacity from size. The former is how many elements you can use before a re-allocation is needed, the latter is the actual vector size (always
When expanding, you want to generally expand in such a way that you're not doing it a lot, since it can be an expensive operation. For example, you could add 5% more than was strictly necessary so that, in a loop continuously adding one element, it doesn't have to re-allocate for every single item.