The way you have it written now , used to be called the "struct hack", until C99 blessed it as a "flexible array member". The reason you're getting an error (probably anyway) is that it needs to be followed by a semicolon:
#include <stdlib.h>
struct my_struct {
int n;
char s[];
};
When you allocate space for this, you want to allocate the size of the struct plus the amount of space you want for the array:
struct my_struct *s = malloc(sizeof(struct my_struct) + 50);
In this case, the flexible array member is an array of char, and sizeof(char)==1, so you don't need to multiply by its size, but just like any other malloc you'd need to if it was an array of some other type:
struct dyn_array {
int size;
int data[];
};
struct dyn_array* my_array = malloc(sizeof(struct dyn_array) + 100 * sizeof(int));
Edit: This gives a different result from changing the member to a pointer. In that case, you (normally) need two separate allocations, one for the struct itself, and one for the "extra" data to be pointed to by the pointer. Using a flexible array member you can allocate all the data in a single block.
Answer from Jerry Coffin on Stack OverflowThe way you have it written now , used to be called the "struct hack", until C99 blessed it as a "flexible array member". The reason you're getting an error (probably anyway) is that it needs to be followed by a semicolon:
#include <stdlib.h>
struct my_struct {
int n;
char s[];
};
When you allocate space for this, you want to allocate the size of the struct plus the amount of space you want for the array:
struct my_struct *s = malloc(sizeof(struct my_struct) + 50);
In this case, the flexible array member is an array of char, and sizeof(char)==1, so you don't need to multiply by its size, but just like any other malloc you'd need to if it was an array of some other type:
struct dyn_array {
int size;
int data[];
};
struct dyn_array* my_array = malloc(sizeof(struct dyn_array) + 100 * sizeof(int));
Edit: This gives a different result from changing the member to a pointer. In that case, you (normally) need two separate allocations, one for the struct itself, and one for the "extra" data to be pointed to by the pointer. Using a flexible array member you can allocate all the data in a single block.
You need to decide what it is you are trying to do first.
If you want to have a struct with a pointer to an [independent] array inside, you have to declare it as
struct my_struct {
int n;
char *s;
};
In this case you can create the actual struct object in any way you please (like an automatic variable, for example)
struct my_struct ms;
and then allocate the memory for the array independently
ms.s = malloc(50 * sizeof *ms.s);
In fact, there's no general need to allocate the array memory dynamically
struct my_struct ms;
char s[50];
ms.s = s;
It all depends on what kind of lifetime you need from these objects. If your struct is automatic, then in most cases the array would also be automatic. If the struct object owns the array memory, there's simply no point in doing otherwise. If the struct itself is dynamic, then the array should also normally be dynamic.
Note that in this case you have two independent memory blocks: the struct and the array.
A completely different approach would be to use the "struct hack" idiom. In this case the array becomes an integral part of the struct. Both reside in a single block of memory. In C99 the struct would be declared as
struct my_struct {
int n;
char s[];
};
and to create an object you'd have to allocate the whole thing dynamically
struct my_struct *ms = malloc(sizeof *ms + 50 * sizeof *ms->s);
The size of memory block in this case is calculated to accommodate the struct members and the trailing array of run-time size.
Note that in this case you have no option to create such struct objects as static or automatic objects. Structs with flexible array members at the end can only be allocated dynamically in C.
Your assumption about pointer aritmetics being faster then arrays is absolutely incorrect. Arrays work through pointer arithmetics by definition, so they are basically the same. Moreover, a genuine array (not decayed to a pointer) is generally a bit faster than a pointer object. Pointer value has to be read from memory, while the array's location in memory is "known" (or "calculated") from the array object itself.
You've tagged this as C++ as well as C.
If you're using C++ things are a lot easier. The standard template library has a template called vector which allows you to dynamically build up a list of objects.
#include <stdio.h>
#include <vector>
typedef std::vector<char*> words;
int main(int argc, char** argv) {
words myWords;
myWords.push_back("Hello");
myWords.push_back("World");
words::iterator iter;
for (iter = myWords.begin(); iter != myWords.end(); ++iter) {
printf("%s ", *iter);
}
return 0;
}
If you're using C things are a lot harder, yes malloc, realloc and free are the tools to help you. You might want to consider using a linked list data structure instead. These are generally easier to grow but don't facilitate random access as easily.
#include <stdio.h>
#include <stdlib.h>
typedef struct s_words {
char* str;
struct s_words* next;
} words;
words* create_words(char* word) {
words* newWords = malloc(sizeof(words));
if (NULL != newWords){
newWords->str = word;
newWords->next = NULL;
}
return newWords;
}
void delete_words(words* oldWords) {
if (NULL != oldWords->next) {
delete_words(oldWords->next);
}
free(oldWords);
}
words* add_word(words* wordList, char* word) {
words* newWords = create_words(word);
if (NULL != newWords) {
newWords->next = wordList;
}
return newWords;
}
int main(int argc, char** argv) {
words* myWords = create_words("Hello");
myWords = add_word(myWords, "World");
words* iter;
for (iter = myWords; NULL != iter; iter = iter->next) {
printf("%s ", iter->str);
}
delete_words(myWords);
return 0;
}
Yikes, sorry for the worlds longest answer. So WRT to the "don't want to use a linked list comment":
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char** words;
size_t nWords;
size_t size;
size_t block_size;
} word_list;
word_list* create_word_list(size_t block_size) {
word_list* pWordList = malloc(sizeof(word_list));
if (NULL != pWordList) {
pWordList->nWords = 0;
pWordList->size = block_size;
pWordList->block_size = block_size;
pWordList->words = malloc(sizeof(char*)*block_size);
if (NULL == pWordList->words) {
free(pWordList);
return NULL;
}
}
return pWordList;
}
void delete_word_list(word_list* pWordList) {
free(pWordList->words);
free(pWordList);
}
int add_word_to_word_list(word_list* pWordList, char* word) {
size_t nWords = pWordList->nWords;
if (nWords >= pWordList->size) {
size_t newSize = pWordList->size + pWordList->block_size;
void* newWords = realloc(pWordList->words, sizeof(char*)*newSize);
if (NULL == newWords) {
return 0;
} else {
pWordList->size = newSize;
pWordList->words = (char**)newWords;
}
}
pWordList->words[nWords] = word;
++pWordList->nWords;
return 1;
}
char** word_list_start(word_list* pWordList) {
return pWordList->words;
}
char** word_list_end(word_list* pWordList) {
return &pWordList->words[pWordList->nWords];
}
int main(int argc, char** argv) {
word_list* myWords = create_word_list(2);
add_word_to_word_list(myWords, "Hello");
add_word_to_word_list(myWords, "World");
add_word_to_word_list(myWords, "Goodbye");
char** iter;
for (iter = word_list_start(myWords); iter != word_list_end(myWords); ++iter) {
printf("%s ", *iter);
}
delete_word_list(myWords);
return 0;
}
If you want to dynamically allocate arrays, you can use malloc from stdlib.h.
If you want to allocate an array of 100 elements using your words struct, try the following:
words* array = (words*)malloc(sizeof(words) * 100);
The size of the memory that you want to allocate is passed into malloc and then it will return a pointer of type void (void*). In most cases you'll probably want to cast it to the pointer type you desire, which in this case is words*.
The sizeof keyword is used here to find out the size of the words struct, then that size is multiplied by the number of elements you want to allocate.
Once you are done, be sure to use free() to free up the heap memory you used in order to prevent memory leaks:
free(array);
If you want to change the size of the allocated array, you can try to use realloc as others have mentioned, but keep in mind that if you do many reallocs you may end up fragmenting the memory. If you want to dynamically resize the array in order to keep a low memory footprint for your program, it may be better to not do too many reallocs.
Best practice on allocating memory for a struct that has dynamic array members
Dynamic array size for structs?
Create dynamic array in struct
dynamic array of structs in C - Stack Overflow
Videos
Hi all! I am very new to C but am familiar with Python & Go quite a bit. Memory allocation is a new concept to me, but I want to start with best practices.
Lets say I want to create a struct that has some members, where one of them happens to be a pointer which I will size dynamically as an array.
Here is what I am doing in my init_my_struct(int n) function. I want to understand if I am doing something that is bad practice here
#include <stdlib.h>
#include <stdio.h>
struct my_struct {
int n;
int *my_arr; // this will be dynamically grown based on user arg
};
struct my_struct * init_my_struct(int n)
{
// allocate memory for one struct
struct my_struct *ms = malloc(sizeof(struct my_struct));
// use -> syntax since ms is a pointer
ms->n = n;
ms->my_arr = malloc(sizeof(int)*n);
return ms;
}
int main(int argc, char *argv[])
{
if (argc < 2) return 1;
int n = atoi(argv[1]);
// initialize the struct members
struct my_struct *ms = init_my_struct(n);
// check out contents of my struct
printf("my struct's n: %d\n", ms->n);
printf("my struct's my_arr: \n");
for (int i = 0; i < n; i++) {
printf("%d\n", ms->my_arr[i]);
}
free(ms); // clean up the memory
return 0;
}Any pointers or tips would be great! Thank you!
I have a function that returns a struct.
Let's call that struct type "StructX"
In that struct there is an array, call it "array[]"
Let's say the function is "2d_array_create(int w, int h)".
I want that function to create a StructX type variable with its "array" being the length of w*h.
How would I write code in StructX so that array[]'s length is determined when an instance of it is created?
("array" would be a 1d array, but that's just for sake of an example)
I have just recently started learning c++ and wad doing random exercises. In this problem, I want to dynamically create an array and assign it values using typedef and struct. Can someone guide me why am I getting garbage value on my object in init function? This might be a newbie question but I would really appreciate your guidance
#include <iostream>
typedef struct Set
{
int j;
int i;
int* arr;
} Set;
Set* init(int member);
void insert(Set* set, int member);
void print_set(Set* set);
int main()
{
Set* setA = init(4);
insert(setA, 5);
insert(setA, 6);
insert(setA, 7);
insert(setA, 7);
print_set(setA);
delete[] setA->arr;
}
Set* init(int member)
{
Set* newSet = new Set;
newSet->j = member;
newSet->arr = new int(member);
return newSet;
}
void insert(Set* set, int member)
{
set->arr[set->i] = member;
set->i++;
}
void print_set(Set* set)
{
for (int j = 0; j < set->j; j++)
{
std::cout << set->arr[j] << endl;
}
}You have several errors in your source code:
struct *struct_array;(l. 5)
What does it mean? Did you want to writestruct data *struct_array?printf("%s ", struct_array[i].inputA);(l.32 & l. 33)
The argumentstruct_arraymasks the global declaration, and it is not an array. Why did you add this argument?struct_array = (int *)realloc(n * sizeof(int));(l. 39)
You have forgotten an argument. Did you want to usemallocinstead? Besides, the cast is not necessary (and incorrect!).Unless you are using an hosted environnment and C99/C11, you should return a value from
main.Your variable
indexis not used. Why did you declare it?for(i = n; i < n; i++)(l. 53) You won't have any iteration here...
The following code works as expected.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* TODO: Avoid global variables. */
struct data *struct_array;
struct data {
char inputA[20];
char inputB[20];
};
/*
* TODO: Try to avoid passing your structure (40 bytes + padding)
* without pointer.
*/
struct data get_data(void)
{
struct data thisdata;
printf("Please enter input A\n");
/* TODO: Avoid using `scanf` for human inputs. */
scanf("%s", thisdata.inputA);
printf("Please enter input B\n");
scanf("%s", thisdata.inputB);
return thisdata;
}
void Output(size_t n)
{
size_t i;
for (i = 0; i < n; i++) {
printf("%s ", struct_array[i].inputA);
printf("%s ", struct_array[i].inputB);
}
}
void resizeArray(size_t n)
{
/* TODO: Handle reallocations errors. */
struct_array = realloc(struct_array, n * sizeof *struct_array);
}
void mainMenu(void)
{
size_t i, n;
int p;
/* TODO: Use a loop ? */
printf("Please select from the following options:\n");
printf("1: Add new students to database\n");
printf("2: Display current student database contents\n");
printf("3: exit the program\n");
scanf("%d", &p);
switch (p) {
case 1:
printf("Please enter the number of students to register:\n");
scanf("%u", &n);
resizeArray(n);
for (i = 0; i < n; i++)
struct_array[i] = get_data();
break;
case 2:
Output(n);
break;
}
}
int main(void)
{
struct_array = malloc(2 * sizeof(int));
mainMenu();
free(struct_array);
return 0;
}
Your definition
struct *struct_array;
is erroneous. You must use the name of your type, the data.
struct data *struct_array;
This way you can allocate the array
struct_array = malloc(MaxNumElements * sizeof(struct data));
and later you should free the memory
free(struct_array);
EDIT: Type definition must occur before the var declaration.
struct data ....
struct data* your_variable;
P.S. If you do not want to type struct keyword each time you use the data type, use the typedef:
typedef struct data_s
{
char inputA[20];
char inputB[20];
} data;
I am also concerned if there can be situation that I free a pointer twice in my case
... and ...
Yes just I am more interested in cases when/if there can be for example freeing unallocated memory or freeing already freed memory ...
After a quick inspection it doesn't appear that you free memory more than once:
freestatements are only in thefreeArrayandmainmethods- Each
free(a->array[0].name);is different because each name is allocated using its own malloc free(a->array)is only called oncefreeArrayis only called oncefree(x.name);doesn't free the same memory asfree(a->array[0].name);becauseinsertArrayallocates new memory for each name
and how to avoid that
Something which can help (though not guarantee) is to assign NULL to the pointer after you pass it to free.
- It can help, because calling free on a previously-nulled pointer will harmlessly do nothing
- It's not a guarantee, because you might have more than one pointer pointing to the same memory
dmcr_code's comment below points out a bug. You wrote,
for(int i=0; i<a->used; i++)
{
free(a->array[0].name);
a->array[0].name=NULL;
}
This should be,
for(int i=0; i<a->used; i++)
{
free(a->array[i].name);
a->array[i].name=NULL;
}
Because you set a->array[0].name=NULL; after freeing it, you don't free it twice.
But, you did fail to free the memory associated with a->array[i].name for values of i larger than 0.
But then how do I protect against that - when array[i].name can contain random value and I try to free it?
To protect yourself:
- Either, don't let it contain a random value (e.g. ensure that it's either a valid pointer, or zero)
- Or, don't use it (e.g. ensure that your
a->usedlogic is correct so that you don't touch elements which you haven't used/initialized).
is memset in the initArray method fine for that?
memset is good:
- You could use calloc instead of malloc to avoid having to use memset as well
- You could use memset on the whole array at once instead of using memset on each element of the array
memset in initArray isn't enough. It's enough to begin with, but there's a realloc in insertArray. So to be good enough, you'd also need to use memset after realloc (to memset the as-yet-unused end of the newly-reallocated array; without using memset on the beginning of the reallocated array, which already contains valid/initialized/used elements).
the only unclear part that remains from your response is how to memset realloced array
Your current code in initArray says,
// Initialize all values of the array to 0
for(unsigned int i = 0; i<initialSize; i++)
{
memset(&a->array[i],0,sizeof(Student));
}
Another way to do that would be:
// Initialize all elements of the array at once: they are contiguous
memset(&a->array[0], 0, sizeof(Student) * initialSize);
The memset statement to add to insertArray would be:
if (a->used == a->size)
{
a->size *= 2;
a->array = (Student *)realloc(a->array, a->size * sizeof(Student));
// Initialize the last/new elements of the reallocated array
for(unsigned int i = a->used; i<a->size; i++)
{
memset(&a->array[i],0,sizeof(Student));
}
}
Or:
if (a->used == a->size)
{
a->size *= 2;
a->array = (Student *)realloc(a->array, a->size * sizeof(Student));
// Initialize the last/new elements of the reallocated array
memset(&a->array[a->used],0,sizeof(Student) * (a->size - a->used));
}
and this comment: "It's not a guarantee, because you might have more than one pointer pointing to the same memory " would be nice if you can address that too
This is safe:
void* foo = malloc(10);
free(foo);
// protect against freeing twice
foo = NULL;
// this is useless and strange, but harmless
free(foo);
This is not safe:
void* foo = malloc(10);
void* bar = foo;
free(foo);
// protect against freeing twice
foo = NULL;
// this is useless and strange, but harmless
free(foo);
// but this is dangerous, illegal, undefined, etc.
// because bar is now pointing to memory that has already been freed
free(bar);
I have 3 suggestions.
If you need to allocate memory and initialize it to zero use
calloc.
Usingcallocis better than usingmalloc + memsetSo change your
initArrayfunction like:void initArray(Array *a, size_t initialSize) { // Allocate initial space a->array = (Student *)calloc(initialSize , sizeof(Student)); a->used = 0; // no elements used a->size = initialSize; // available nr of elements }Single character variable names are very bad. Use proper names for variables and follow naming conventions.
In your code you are only creating and adding 3 objects. But you are trying to print the details of 4th object. (Array index is starting from zero, so index 3 means 4th object)
printf("%d\n", a.array[3].ID); printf("%s\n", a.array[3].name);