Allocating memory for elements inside a structure

N

Navaneeth

I have a struct which should hold a "pattern" and a "value". This
struct is allocated dynamically and I will have around 2500+ instances
of this in memory. Now I have two ways of defining this structure.

struct object {
char *pattern; /* I will have to malloc to allocate memory for
this */
char *value;
};

or

struct object {
char pattern[MAX_PATTERN_SIZE];
char value[MAX_VALUE_SIZE];
};

Now I am confused about allocating memory for the variables "pattern"
and "value". In my case, I will have a length of 10 for pattern and
100 for value.

So which approach do you prefer? If I declare them as arrays, where
will this get allocated? If it is on the stack, I am worried that it
may run out of space as I will have 2500+ items.

Any help?
 
S

Shao Miller

I have a struct which should hold a "pattern" and a "value". This
struct is allocated dynamically and I will have around 2500+ instances
of this in memory. Now I have two ways of defining this structure.

struct object {
     char *pattern;   /* I will have to malloc to allocate memory for
this */
     char *value;

};

or

struct object {
     char pattern[MAX_PATTERN_SIZE];
     char value[MAX_VALUE_SIZE];

};

Now I am confused about allocating memory for the variables "pattern"
and "value". In my case, I will have a length of 10 for pattern and
100 for value.
Will you _always_ have the same number of elements for 'pattern' and
'value'?

10 * 2500 + 100 * 2500 = 25000 + 250000 = 275000 = 268.5546875 KiB.
Not too bad.
So which approach do you prefer? If I declare them as arrays, where
will this get allocated? If it is on the stack, I am worried that it
may run out of space as I will have 2500+ items.

Any help?
Even if you use your latter form, you could 'malloc' space for all
2,500 of them, thus ensuring them on the heap. Hopefully you have
enough contiguous storage in the heap for that. Always check for
'malloc' returning 'NULL'! :)

struct object {
char pattern[MAX_PATTERN_SIZE];
char value[MAX_VALUE_SIZE];
};

int main(void) {
struct object (*all)[2500];
all = malloc(sizeof *all);
if (!all)
return 1;
(*all)[0].pattern[0] = 'c';
(*all)[0].pattern[1] = 'd';
(*all)[1].pattern[0] = 'e';
(*all)[1].pattern[1] = 'f';
/* Or */
all[0][2].pattern[0] = 'g';
all[0][2].pattern[1] = 'h';
/* ... */
return 0;
}
 
N

Nick Keighley

I have a struct which should hold a "pattern" and a "value". This
struct is allocated dynamically and I will have around 2500+ instances
of this in memory. Now I have two ways of defining this structure.

struct object {
     char *pattern;   /* I will have to malloc to allocate memory for
this */
     char *value;

};

or

struct object {
     char pattern[MAX_PATTERN_SIZE];
     char value[MAX_VALUE_SIZE];

};

Now I am confused about allocating memory for the variables "pattern"
and "value". In my case, I will have a length of 10 for pattern and
100 for value.

So which approach do you prefer? If I declare them as arrays, where
will this get allocated? If it is on the stack, I am worried that it
may run out of space as I will have 2500+ items.

Any help?

if it has file scope it won't be on the stack

/**** 1/
struct object {
char pattern[MAX_PATTERN_SIZE];
char value[MAX_VALUE_SIZE];
};

int main (void)
{
struct object object_table [2500]; /* will be on stack */
....
}
/****/


/**** 2/
struct object {
char pattern[MAX_PATTERN_SIZE];
char value[MAX_VALUE_SIZE];
};

struct object object_table [2500]; /*not on stack */

int main (void)
{ ....
}
/****/


/**** 3/
struct object {
char *pattern;
char *value;
};

int main (void)
{
struct object object_table [2500];


for (i = 0; i < 2500; i++)
{
/* allocate memory on heap */
if ((object_table.pattern = malloc (MAX_PATTERN_SIZE)) ==
NULL)
error ("memory error");
....
}

....
}
/****/

there are obviously more possibilities. If you aren't sure then try to
design your program so the memory alloaction strategy is hidden fromt
eh rest of the program. This makes it easy to change as you evolve
your ideas
 
B

Ben Bacarisse

Navaneeth said:
I have a struct which should hold a "pattern" and a "value". This
struct is allocated dynamically and I will have around 2500+ instances
of this in memory. Now I have two ways of defining this structure.

struct object {
char *pattern; /* I will have to malloc to allocate memory for
this */
char *value;
};

or

struct object {
char pattern[MAX_PATTERN_SIZE];
char value[MAX_VALUE_SIZE];
};

Something that I don't think has yet been mentioned is that the pointer
approach allows for sharing either all or part of a pattern or value.
This can be a big win in some low-memory situations but, of course, it
could be entirely irrelevant to you.

<snip>
 
M

Michael Angelo Ravera

I have a struct which should hold a "pattern" and a "value". This
struct is allocated dynamically and I will have around 2500+ instances
of this in memory. Now I have two ways of defining this structure.

struct object {
     char *pattern;   /* I will have to malloc to allocate memory for
this */
     char *value;

};

or

struct object {
     char pattern[MAX_PATTERN_SIZE];
     char value[MAX_VALUE_SIZE];

};

Now I am confused about allocating memory for the variables "pattern"
and "value". In my case, I will have a length of 10 for pattern and
100 for value.

So which approach do you prefer? If I declare them as arrays, where
will this get allocated? If it is on the stack, I am worried that it
may run out of space as I will have 2500+ items.

Any help?

You haven't said whether this is part of a library where you are
solving some larger problem or whether the data is volatile or static
or whether the program will run, do its business, and then terminate
or, in the alternative, provide lookup facilities or something like
that.

I say, if you are going to run, do your business, and then terminate,
that you should allocated it all on the stack, if it fits.

If you are going to work as part of a library, null terminated
pointers in the struct probably make sense. It's pretty common
practice

If you are going to load up and then hang around providing facilities,
you do best by allocating one or two large chunks (assuming that you
can determine how big these might be) and set pointers into the chunks.
 
N

Navaneeth

You haven't said whether this is part of a library where you are
solving some larger problem or whether the data is volatile or static
or whether the program will run, do its business, and then terminate
or, in the alternative, provide lookup facilities or something like
that.

This is part of a console application that does it's job and
terminates.

So if I write,

struct object {
char pattern[MAX_PATTERN_SIZE];
char value[MAX_VALUE_SIZE];
};

struct object *o = malloc(sizeof(struct object));

where will the pattern and value gets allocated? On the heap right?
 
S

Shao Miller

So if I write,

struct object {
   char pattern[MAX_PATTERN_SIZE];
   char value[MAX_VALUE_SIZE];
};

struct object *o = malloc(sizeof(struct object));

where will the pattern and value gets allocated? On the heap right?
Right, assuming 'malloc' does not return a null pointer.
 
M

Morris Keesan

So if I write,

struct object {
char pattern[MAX_PATTERN_SIZE];
char value[MAX_VALUE_SIZE];
};

struct object *o = malloc(sizeof(struct object));

where will the pattern and value gets allocated? On the heap right?

In typical C implementations, yes. For your purposes, almost certainly.

But to satisfy the pedantic among us, you should be aware that the
abstract machine defined by the C standard has no concept of a "heap" or
a "stack", and nothing requires malloc() to allocate memory in either
of those hypothetical locations. The library on my Deathstart 9000
allocates memory inside the attached microwave oven.
 
M

Morris Keesan

I have a struct which should hold a "pattern" and a "value". This
struct is allocated dynamically and I will have around 2500+ instances
of this in memory. Now I have two ways of defining this structure.

struct object {
     char *pattern;   /* I will have to malloc to allocate memory for
this */
     char *value;

};

or

struct object {
     char pattern[MAX_PATTERN_SIZE];
     char value[MAX_VALUE_SIZE];

};

Now I am confused about allocating memory for the variables "pattern"
and "value". In my case, I will have a length of 10 for pattern and
100 for value.
Will you _always_ have the same number of elements for 'pattern' and
'value'?

10 * 2500 + 100 * 2500 = 25000 + 250000 = 275000 = 268.5546875 KiB.
Not too bad.
So which approach do you prefer? If I declare them as arrays, where
will this get allocated? If it is on the stack, I am worried that it
may run out of space as I will have 2500+ items.

Any help?
Even if you use your latter form, you could 'malloc' space for all
2,500 of them, thus ensuring them on the heap. Hopefully you have
enough contiguous storage in the heap for that. Always check for
'malloc' returning 'NULL'! :)

struct object {
char pattern[MAX_PATTERN_SIZE];
char value[MAX_VALUE_SIZE];
};

int main(void) {
struct object (*all)[2500];
all = malloc(sizeof *all);

This is a very strange construct. I've never yet encountered code where
declaring something as a "pointer to array" is actually a good
idea. In general, all this does is add an extra level of indirection
to everything, without adding any value.

Much clearer is
#define N_OBJECTS 2500
struct object *all = malloc(N_OBJECTS * sizeof *all);

if (!all)
return 1;
return EXIT_FAILURE; /* 1 is not portable */
(*all)[0].pattern[0] = 'c';
(*all)[0].pattern[1] = 'd';
(*all)[1].pattern[0] = 'e';
(*all)[1].pattern[1] = 'f';
/* Or */
all[0][2].pattern[0] = 'g';
all[0][2].pattern[1] = 'h';

Or having declared 'all' to be a pointer to struct, instead of
a pointer to array of structs, we can simply write the much cleaner

all[0].pattern[0] = 'c';
all[0].pattern[1] = 'd';
all[1].pattern[0] = 'e';
all[1].pattern[1] = 'f';
 
B

Ben Bacarisse

Morris Keesan said:
struct object {
char pattern[MAX_PATTERN_SIZE];
char value[MAX_VALUE_SIZE];
};

int main(void) {
struct object (*all)[2500];
all = malloc(sizeof *all);

This is a very strange construct. I've never yet encountered code where
declaring something as a "pointer to array" is actually a good
idea.

It's certainly odd in this context, but it is surprisingly common in
some programming domains. For example, when a 2D array is passed to a
function, the receiving parameter will often be declared like this:

double determinant(double matrix[][N]);

Despite the deceptive [] syntax, 'matrix' is being declared to be a
pointer to an array (presumably the first of N similar arrays). The
other way to write this is, of course,

double determinant(double (*matrix)[N]);

<snip>
 
S

Shao Miller

Morris Keesan said:
@gmail.com> wrote:
struct object {
  char pattern[MAX_PATTERN_SIZE];
  char value[MAX_VALUE_SIZE];
};
int main(void) {
  struct object (*all)[2500];
  all = malloc(sizeof *all);
 This is a very strange construct.  I've never yet encountered code where
 declaring something as a "pointer to array" is actually a good
 idea.

It's certainly odd in this context, but it is surprisingly common in
some programming domains.
... ... ...
I agree to disagree, since "odd" is subjective.

If you have:
and you have N_OBJECTS throughout your code, you need to search-and-
replace if you ever decide to change the name of 'N_OBJECTS'. Not so
with 'sizeof'. If the value doesn't fit within a 'size_t', it's
trouble anyway because the C Standard Library memory management
functions take a 'size_t' argument.

The tradition of using preprocessor macros for constant expressions is
perhaps well-established and does serve to document a programmer's
choices, but if we see:

for (index = 0; index < N_OBJECTS; index++) {
all[index].pattern = 'c';
/* ... */

is it _immediately_ apparent that this loop will iterate through every
valid element that 'all' points to? Compare with 'all' as a pointer
to an array:

for (index = 0; index < (sizeof *all / sizeof **all); index++) {
(*all)[index].pattern = 'c';
/* ... */

Or use '/ sizeof (*all)[0]' if you like.

Now speaking of macros, perhaps you have some macros which expand to
common iteration logic that you enjoy:

#define FOR_EACH_ELEMENT(index_, total_) \
for ((index_) = 0; (index_) < (total_); (index_)++)

Compare to:

#define FOR_EACH_ELEMENT(index_, array_) \
for ((index_) = 0; (index_) < (sizeof (array_) / sizeof *(array_));
(index_)++)

Why should we need to repeatedly insist on the total number of
elements when that should be a property of an array itself?

FOR_EACH_ELEMENT(i, N_OBJECTS) {

or for a VLA:

FOR_EACH_ELEMENT(i, num_of_elements) {

Versus with arrays, it's the same in both cases:

FOR_EACH_ELEMENT(i, my_array) {

Or for an allocated array via pointer-to-array:

FOR_EACH_ELEMENT(i, *my_array) {

Now please consider:

typedef int ten_ints[10];

/* Called using 'fill(some_array, 5);' */
void fill(ten_ints my_array, int pattern) {
size_t i;
/* Uh oh, my_array is a pointer, not an array! */
for (i = 0; i < (sizeof my_array / sizeof my_array[0]); i++) {
my_array = pattern;
}
return;
}

Compare to:

/* Called using 'fill(&some_array, 5);' */
void fill(ten_ints *my_array, int pattern) {
size_t i;
for (i = 0; i < (sizeof *my_array / sizeof **my_array); i++) {
/* *my_array is an array */
(*my_array) = pattern;
}
return;
}

Please ponder:

#define NUM_OF_ELEMENTS(array_) \
(sizeof (array_) / sizeof *(array_))

Well that's kind of handy.

int foo[20];
int (*bar)[30];
/* ... */
.... NUM_OF_ELEMENTS(foo) ...
/* ... */
.... NUM_OF_ELEMENTS(*bar) ...

Anyway, it's most likely a matter of preference, but not really
odd. :) My suggestion simply keeps the number of elements tagging
along with the type. Because arrays "decay" to pointers outside of
'sizeof' and '&', we have to keep typing the number of elements. :)
My preference is to keep things in one place.

If you really want to use 'N_OBJECTS' to document a choice, it's still
possible to use it along with 'sizeof' logic and pointers-to-arrays:

int main(void) {
struct object (*all)[N_OBJECTS];
all = malloc(sizeof *all);
 
S

Shao Miller

int main(void) {
  struct object (*all)[2500];
  all = malloc(sizeof *all);

  This is a very strange construct.  I've never yet encountered code where
  declaring something as a "pointer to array" is actually a good
  idea.  In general, all this does is add an extra level of indirection
  to everything, without adding any value.
Explain, if you will, how you know that:

struct object *all = malloc(N_OBJECTS * sizeof *all);
/* ... Check for null pointer, etc. ... */
all[1].pattern[0] = 'c';

is well-defined. 'all[1]' is defined to be identical to pointer
arithmetic. Pointer arithmetic is defined if we know a number of
elements. How many elements are in the array object pointed to by
'all'? I'd guess you to answer "'N_OBJECTS' assuming 'malloc'
succeeds." But how might a bounds-checking implementation know that,
since the 'size_t' parameter to 'malloc' carries no information about
element type nor element size?

That would suggest that "pure" pointers have well-defined arithmetic
based on some esoteric knowledge that the programmer possesses, such
as "I'll never try to point past '(all + N_OBJECTS)', so I'm well-
defined without overflow." Encoding the bounds into the pointer type
seems a little more easily statically verifiable, in my opinion. :)
 
S

Shao Miller

is well-defined.  'all[1]' is defined to be identical to pointer
arithmetic.  Pointer arithmetic is defined if we know a number of
elements.
"The pedantic among us" should clarify that:

all[1]

is defined to be identical to:

(*((all) + (1)))
 
B

Ben Bacarisse

Shao Miller said:
Morris Keesan said:
@gmail.com> wrote:
struct object {
  char pattern[MAX_PATTERN_SIZE];
  char value[MAX_VALUE_SIZE];
};
int main(void) {
  struct object (*all)[2500];
  all = malloc(sizeof *all);
 This is a very strange construct.  I've never yet encountered code where
 declaring something as a "pointer to array" is actually a good
 idea.

It's certainly odd in this context, but it is surprisingly common in
some programming domains.
... ... ...
I agree to disagree, since "odd" is subjective.

Yes, it is subjective and I did not intend that. I really meant
uncommon or unusual (except in the situation I then outlined).
If you have:

Well, I don't have. Your reply would be better directed at the poster
whose opinion you are commenting on.

<snip>
 
S

Shao Miller

... ... ... Your reply would be better directed at the poster
whose opinion you are commenting on.
I'm happy to admit that it's just a light-hearted offering of ideas
for why the construct is valuable. :) I agree with your statement
regarding "surprisingly common." I do believe that its use in the
response to the original post is not odd. Sorry about that, Ben.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top