views:

851

answers:

14

Hi,

I want to dynamically allocate a C struct:

typedef struct {
    short *offset;
    char *values;
} swc;

Both 'offset' and 'values' are supposed to be arrays, but their size is unknown until runtime.

How can I dynamically allocate memory for my struct and the struct's arrays?

+1  A: 

Use malloc function or calloc to allocate memory dynamically . and search it on google to get examples.

The calloc function initializes allocated memory to zero.
Ashish
Nomemory , you see the question tag it has C++ also. you proves your name !!!!!
Ashish
Probably still worth mentioning new/delete are C++ constructs. Especially as structs aren't common in the C++ world.
Adam Luchjenbroers
yes i know it and i am removing new/delete part from the answer.
Ashish
+2  A: 

You will need a function to do this. Something like (my C/C++ is rusty)

swc* makeStruct(int offsetCount, int valuesCount) {
  swc *ans = new swc();
  ans->offset = new short[offsetCount];
  ans->values = new char[valuesCount];
  return ans;
}

myNewStruct = makeStruct(4, 20);

Syntax may be a bit off but that is generally what you are going to need. If you're using C++ then you probably want a class with a constructor taking the 2 args instead of the makeStruct but doing something very similar.

DaveC
You should also create a destroyStruct function as well. The symmetry will mean it is less likely for someone down the line to forget to delete all the data members.
doron
You're right. I'm spoilt by java GC now I'm afraid.
DaveC
If you are going to use C++ constructs then you should do it all the C++ way and make it constructor/destructor. This hybrid C/C++ is counter intuitive.
Martin York
@Dave: C++ has its own much finer grained version of GC. Its called smart pointers. Also spoilt is not the correct term, I would have said crippled :-) (Joke)
Martin York
@Martin: Yeah, I used smart pointers a while back. They almost made C++ into a reasonable language to use (although the codebase I was supporting mixed them with standard pointers - pure evil). Although from the level of question I doubt they are a good topic for here.
DaveC
+8  A: 
swc *a = (swc*)malloc(sizeof(swc));
a->offset = (short*)malloc(sizeof(short)*n);
a->values = (char*)malloc(sizeof(char)*n);

Where n = the number of items in each array and a is the address of the newly allocated data structure. Don't forget to free() offsets and values before free()'ing a.

spurserh
Casting the result of malloc isn't required in C, but is in C++. And sizeof(char) is always 1.
Joe Gauterin
@joe: sizeof(char) may always be one. But its still nice to have it in the code. It sorts of acts like documentation of what your intention are (and its not as if it costs anything at runtime!).
Martin York
@Joe Gauterin: No, but it's good practice to do so anyway. ;p
KingRadical
Also using sizeof(*a) and sizeof(a->values[0]) means you don't repeat the type in the code. Note sizeof is compile-time so it's safe.
Mike Weller
First, the casting of the return value of `malloc()` is a questionable practice, so stop acting like your preferred side of the debate is the "right" one. It's been debated many times, and these comments are no place to replicate the debate. If you need C++ compatibility, you don't have a choice, but if you don't, I recommend you don't do it, because if the type of `a->offset` changes to a `long` you'll have to change the cast of `malloc()` (and of any `realloc()`s you might also call) or face severe bugs, which leads to more maintenance than necessary. (con't in next comment)
Chris Lutz
Secondly, for much the same reason, you should use `sizeof(*a)` and `sizeof(*a->offset)` instead of `sizeof(swc)` and `sizeof(short)`. If the type of `a` changes (or, more likely, the type of `a->offset` changes from `short` to `long`), using the variable rather than the type will allow you to change only the line that declares the variable, rather than changing all the lines that `malloc()` and `realloc()` on it. If they all infer the correct type from the variable (or `struct` member), we have less headache if we have to change that type to another one. Everyone wins.
Chris Lutz
@Chris: I said 'Casting the result of malloc isn't required in C, but is in C++'. That isn't debatable; it's a fact, not an opinion. 'Required' isn't a prescription on what is 'right', only on what is neccessary.
Joe Gauterin
I said "If you need C++ compatibility, you don't have a choice." I know it's required in C++, but if we're using C (which the question is ambiguous about), we have a choice, and thus it becomes a debatable practice.
Chris Lutz
Much more important than the 'to cast or not to cast' issue is error checking the return values - especially the assignments using the result of the first malloc.
Jonathan Leffler
+3  A: 

You have to do it seperately. First allocate the struct, then the memory for the arrays.

In C:

swc *pSwc = malloc(sizeof(swc));
pSwc->offset = malloc(sizeof(short)*offsetArrayLength);
pSwc->values = malloc(valuesArrayLength);

In C++, you shouldn't be doing anything like that.

Joe Gauterin
A: 

You want to use malloc to allocate the memory, and probably also sizeof() to allocate the correct amount of space.

Something like:
structVariable = (*swc) malloc(sizeof(swc));

Should do the trick.

Adam Luchjenbroers
A: 

I have tried both

int lengthOfArray = 5;
swc *swcptr = (swc *) malloc(sizeof(swc)*lengthOfArray);

and

void swc_init(swc s, unsigned int l) {
    s.offset = (short *)malloc(l*sizeof(short));
    s.values = (char *)malloc(l*sizeof(char));
}

but I get segfaults anyway.

pf
You have to pass `s` by reference to the `swc_init` function. You are initializing a local copy of the struct that ceases to exist as soon as the function returns.
Roberto Bonvallet
@Roberto: That assumes C++. This is NOT C++ code this is all C. In which case swc should be passed as a pointer to swc_init().
Martin York
+2  A: 
swc* a = malloc(sizeof(*a));
a->offset = calloc(n, sizeof(*(a->offset)));
a->values = calloc(n, sizeof(*(a->values)));

You should not cast void* in c... in c++ you must!

warp
+7  A: 

In C:

swc *s = malloc(sizeof *s); // assuming you're creating a single instance of swc
if (s)
{
  s->offset = malloc(sizeof *(s->offset) * number_of_offset_elements);
  s->values = malloc(sizeof *(s->values) * number_of_value_elements);
}

In C++:

try
{
  swc *s = new swc;
  s->offset = new short[number_of_offset_elements];
  s->values = new char[number_of_value_elements];
}
catch(...)
{
   ...
}

Note that in C++, you might be better off using vectors as opposed to dynamically allocated buffers:

struct swc 
{
  std::vector<short> offset;
  std::vector<char> values;
};

swc *a = new swc;

Question: is values supposed to be an array of individual characters or an array of strings? That would change things a bit.

EDIT

The more I think about it, the less satisfied I am with the C++ answer; the right way to do this sort of thing in C++ (assuming you need dynamically allocated buffers as opposed to vectors, which you probably don't) is to perform the memory allocation for offset and values as part of a constructor within the struct type, and have a destructor deallocate those elements when the struct instance is destroyed (either by a delete or by going out of scope).

struct swc
{
  swc(size_t numOffset = SOME_DEFAULT_VALUE, 
      size_t numValues = SOME_OTHER_DEFAULT_VALUE)
  {
    m_offset = new short[numOffset];
    m_values = new char[numValues];
  }

  ~swc()
  {
    delete[] m_offset;
    delete[] m_values;
  }

  short *m_offset;
  char  *m_values;
};

void foo(void)
{
  swc *a = new swc(10,20); // m_offset and m_values allocated as 
                           // part of the constructor
  swc b;                   // uses default sizes for m_offset and m_values
  ...
  a->m_offset[0] = 1;
  a->m_values[0] = 'a';
  b.m_offset[0] = 2;
  b.m_values[0] = 'b';
  ...
  delete a; // handles freeing m_offset and m_values
            // b's members are deallocated when it goes out of scope
}
John Bode
+1. This is the only response that answers his question about unknown runtime values.
Luca Matteis
I wish I could +3 for 1) differentiating between C and C++, 2) checking the return value of `malloc()`, and 3) using `sizeof var` instead of `sizeof(type)`. Though instead of a `std::vector<char>` I might just go ahead and use a `std::string` myself.
Chris Lutz
If you are going to use C++ constructs like constructors and destructors please do so correctly. The above definition is dangerious. You __MUST__ add copy constructor and assignment operator or find another way to manage your array of offset and values. In addition if you are going to showcase exceptions then you should at least show how to tidy up the memory that was allocated (as it is not trivial with two RAW pointers)
Martin York
+3  A: 

One thing to add to the many correct answers here: you can malloc an over-sized structure to accommodate a variable sized array in the last member.

struct foo {
   short* offset;
   char values[0]
};

and later

struct *foo foo1 = malloc(sizeof(struct foo)+30); // takes advantage of sizeof(char)==1

to get room for 30 objects in the values array. You would still need to do

foo1->offsets = malloc(30*sizeof(short));

if you want them to use the same size arrays.

I generally wouldn't actually do this (maintenance nightmare if the structure ever needs to expand), but it is a tool in the kit.

[code here in c. You'll need to cast the malloc's (or better use new and RAII idioms) in c++]

dmckee
Yes! You admitted it: That's going to create a maintenance nightmare!
Eduardo León
I have, none-the-less, seen it done.
dmckee
A: 

In addition to the above, I would like to add freeing up the allocated memory as below.,

    typedef struct { 
    short *offset; 
    char *values; 
} swc;

swc* createStructure(int Count1, int Count2) { 
  swc *s1 = new swc(); 
  s1->offset = new short[Count1]; 
  s1->values = new char[Count2]; 
  return s1; 
} 

int _tmain(int argc, _TCHAR* argv[])
{
    swc *mystruct;
    mystruct = createStructure(11, 11);

    delete[] mystruct->offset;
    delete[] mystruct->values;
     delete mystruct;
    return 0;
}
Red
Don't mix and match C/C++ they are distinct languages.
Martin York
+3  A: 

In C:

typedef struct
{
    short *offset;
    char  *values;
} swc;

/// Pre-Condition:  None
/// Post-Condition: On failure will return NULL.
///                 On Success a valid pointer is returned where
///                 offset[0-n) and values[0-n) are legally de-refrancable.
///                 Ownership of this memory is returned to the caller who
///                 is responsible for destroying it via destroy_swc()
swc *create_swc(unsigned int size)
{
    swc *data    = (swc*)  malloc(sizeof(swc));
    if (data)
    {
        data->offset = (short*)malloc(sizeof(short)*n);
        data->values = (char*) malloc(sizeof(char) *n);
    }
    if ((data != NULL) && (size != 0) && ((data->offset == NULL) || (data->values == NULL)))
    {
        // Partially created object is dangerous and of no use.
        destroy_swc(data);
        data = NULL;
    }
    return data;
}
void destroy_swc(swc* data)
{
    free(data->offset);
    free(data->values);
    free(data);
}

In C++

struct swc
{
    std::vector<short>   offset;
    std::vector<char>    values;
    swc(unsigned int size)
        :offset(size)
        ,values(size)
    {}
};
Martin York
IMHO don't cast the return value of `malloc()` in C. If the type of `offset` changes from `short` to `int` (or `long` or `long long`) then casting creates one more line of code that needs to change. I'd rather do `data->offset = malloc(sizeof(*data->offset) * n)`, which will still work if the type of `offset` changes.
Chris Lutz
All good points :-)
Martin York
If `size` is zero, then `malloc` might validly return `NULL`, so you should either check that before deciding that you have a partially created object, or you should add "size is positive" as a precondition.
Rob Kennedy
@Rob: You are correct. I had assumed a valid pointer is returned, but on reading the documentation it is actually implementation defined (another reason to prefer C++ new).
Martin York
+1  A: 

Most of the answers are correct. I would like to add something that you haven't explicitly asked but might also be important.

C / C++ arrays don't store their own size in memory. Thus, unless you want offset and values to have compile-time defined values (and, in that case, it's better to use fixed-size arrays), you might want to store the sizes of both arrays in the struct.

typedef struct tagswc {
    short  *offset;
    char   *values;
    // EDIT: Changed int to size_t, thanks Chris Lutz!
    size_t offset_count;
    size_t values_count; // You don't need this one if values is a C string.
} swc;

DISCLAIMER: I might be wrong. For example, if all offsets of all swc instances have the same size, it would be better to store offset_count as a global member, not as a member of the struct. The same can be said about values and values_count. Also, if values is a C string, you don't need to store its size, but beware of Schlemiel the painter-like problems.

Eduardo León
Don't use `int` to store sizes. You don't want anything to have a size of -12, do you? Use the type `size_t` which is created to store sizes.
Chris Lutz
Yeah, you're right. My bad.
Eduardo León
+1  A: 

Since nobody has mentioned it yet, sometimes it is nice to grab this chunk of memory in one allocation so you only have to call free() on one thing:

swc* AllocSWC(int items)
{
    int size = sizeof(swc); // for the struct itself
    size += (items * sizeof(short)); // for the array of shorts
    size += (items * sizeof(char)); // for the array of chars
    swc* p = (swc*)malloc(size);
    memset(p, 0, size);
    p->offset = (short*)((char*)swc + sizeof(swc)); // array of shorts begins immediately after the struct
    p->values = (char*)((char*)swc + sizeof(swc) + items * sizeof(short)); // array of chars begins immediately after the array of shorts
    return p;
}

Of course this is a bit more difficult to read and maintain (especially if you dynamically resize the arrays after it is first allocated). Just an alternative method I've seen used in a number of places.

Luke
1) Use `size_t` instead of `int`. 2) The hack value of this is amazing (over 9000, perhaps). Don't use this unless you've found that allocation is a performance bottleneck in your application and you've found that this eases the bottleneck
Chris Lutz
That is definitely not a good idea. You are not considering memory alignment issues. If somebody decided for maintenance reasons to reorder the members in the structure or add another member this could lead to real problems down the road.
Martin York
A: 

**If** you will not be resizing the arrays, then you can get away with a single call to malloc().

swc *new_swc (int m, int n) {
    swc *p;
    p = malloc (sizeof (*p) + m * sizeof (p->offset[0]) + n * sizeof (p->values[0]);
    p->offset = (short *) &p[1];
    p->values = (char *) &p->offset[m];
    return p;
}

You can then free it with a single call to free().

(In general, there are alignment considerations to take into account, but for an array of shorts followed by an array of chars, you will be fine.)

Dave Hinton