1

About C structs and pointers...

Yesterday I wrote sort of the following code (try to memorize parts of it out of my memory):

    typedef struct {
    unsigned short int iFrames;
    unsigned short int* iTime; // array with elements [0..x] holding the timing for each frame
    } Tile;

    Tile* loadTile(char* sFile)
    {
    // expecting to declare enough space for one complete Tile structure, of which the base memory address is stored in the tmpResult pointer
    Tile* tmpResult = malloc(sizeof(Tile));

    // do things that set values to the Tile entity
    // ...

    // return the pointer for further use
    return tmpResult;
    }

    void main()
    {
    // define a tile pointer and set its value to the returned pointer (this should also be allowed in one row)
// Expected to receive the VALUE of the pointer - i.e. the base memory address at where malloc made space available
    Tile* tmpTile;
    tmpTile = loadTile("tile1.dat");

    // get/set elements of the tile
    // ...

    // free the tile
    free(tmpTile);
    }

What I see: I cán use the malloced Tile structure inside the function, but once I try to access it in Main, I get an error from Visual Studio about the heap (which tells me that something is freed after the call is returned).

If I change it so that I malloc space in Main, and pass the pointer to this space to the loadTile function as an argument (so that the function does no longer return anything) then it does work but I am confident that I should also be able do let the loadTile function malloc the space and return a pointer to that space right?!

Thanks!!

2
  • 10
    void main ... RAAAAARRRRGGGGGHHHHH Commented Mar 16, 2011 at 14:54
  • please show us the part of the code with the bug in Commented Mar 16, 2011 at 14:56

7 Answers 7

4

There's nothing wrong with what you're trying to do, or at least not from the code here. However, I'm concerned about this line:

unsigned short int* iTime; // array with elements [0..x] holding the timing for each frame

That isn't true unless you're also mallocing iTime somewhere:

Tile* tmpResult = malloc(sizeof(Tile));
tmpResult->iTime = malloc(sizeof(short) * n);

You will need to free it when you clean up:

free(tmpTile->iTime);
free(tmpTile);
Sign up to request clarification or add additional context in comments.

1 Comment

... and put the free logic in a separate function, free_tile, so you don't tie tile-handling client code to the internals of the Tile structure. +1.
2

You are probably writing over memory you don't own. I guess that in this section:

// do things that set values to the Tile entity

you're doing this:

tmpResult->iFrames = n;

for (i = 0 ; i < n ; ++n)
{
  tmpResult->iTime [i] = <some value>;
}

which is wrong, you need to allocate separate memory for the array:

tmpResult->iTime = malloc (sizeof (short int) * n);

before writing to it. This make freeing the object more complex:

free (tile->iTime);
free (tile);

Alternatively, do this:

typedef struct {
  unsigned short int iFrames;
  unsigned short int iTime [1]; // array with elements [0..x] holding the timing for each frame
} Tile;

and malloc like this:

 tile = malloc (sizeof (Tile) + sizeof (short int) * (n - 1)); // -1 since Tile already has one int defined.

and the for loop remains the same:

for (i = 0 ; i < n ; ++n)
{
  tmpResult->iTime [i] = <some value>;
}

but freeing the tile is then just:

free (tile);

as you've only allocated one chunk of memory, not two. This works because C (and C++) does not do range checking on arrays.

1 Comment

I think you mean tmpResult->iTime = malloc (sizeof (short) * n). Cool hack though! It's worth pointing out that this only works if the dynamically-sized array is the last item in the struct.
1

You code, with as little changes as I could live with, works for me:

#include <stdio.h>
#include <stdlib.h>

typedef struct {
  unsigned short int iFrames;
  unsigned short int* iTime;
} Tile;

Tile *loadTile(char* sFile) {
  Tile *tmpResult = malloc(sizeof *tmpResult);
  if (!tmpResult) return NULL;

  /* do things that set values to the Tile entity */
  /* note that iTime is uninitialized */
  tmpResult->iFrames = 42;

  (void)sFile; /* used parameter */
  return tmpResult;
}

int main(void) {
  Tile* tmpTile;
  tmpTile = loadTile("tile1.dat");
  if (!tmpTile) return 1;

  printf("value: %d\n", tmpTile->iFrames);

  free(tmpTile);
  return 0;
}

Comments

0

The code you showed looks OK, the error must be in the elided code.

Comments

0

Whatever problem you are having, it is not in the code shown in this question. Make sure you are not clobbering the pointer before returning it.

Comments

0

This should work fine... could just be a warning from VisualStudio that you are freeing a pointer in a different function than it was malloced in.

Comments

0

Technically, your code will work on a C compiler. However, allocating dynamically inside functions and returning pointers to the allocated data is an excellent way of creating memory leaks - therefore it is very bad programming practice. A better way is to allocate the memory in the caller (main in this case). The code unit allocating the memory should be the same one that frees it.

Btw if this is a Windows program, main() must be declared to return int, or the code will not compile on a C compiler.

4 Comments

Returning malloc'd memory is a very common practice that greatly reduces coupling compared to what you suggest. Defining foo *make_foo() and free_foo is the idiomatic approach to foo construction/initialization and destruction in C.
@larsmans I think we are saying the same thing here. To reduce coupling, you should do roughly like in these two examples: stackoverflow.com/questions/5309245/c-char-question/…
It seemed to me as if you recommended Tile *p = malloc(sizeof(Tile)); init_tile(p); in main.
@larsmans There is nothing wrong with that syntax either, depending on the function. If the function is "something that does things with a data buffer", then the function doing the task shouldn't concern itself with malloc(). For example, the whole of Windows is built in that way: pass a pointer and a size of a buffer to a function. Now, of course if the data is of a nature that main() needn't concern itself with it, it should be kept privately encapsulated in the module allocating the data.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.