开发者

Freeing memory, all?

开发者 https://www.devze.com 2023-04-06 03:52 出处:网络
Maybe a bad topic, but given the following code, do i need to free(player->name) too? #include <stdio.h>

Maybe a bad topic, but given the following code, do i need to free(player->name) too?

#include <stdio.h>

struct Player
{
       char *name;
       int level;       
};

int main()
{
    struct Player *player;
    player->name = malloc(sizeof(player->name)*256);
    player->name = "John";

    printf(player->na开发者_StackOverflow社区me);

    free(player);

    printf("\n\n\n");
    system("PAUSE");
    return 0;
}


Oh boy, where to start? You really need a good book. Sigh. Let's start at the top of main():


This

struct Player *player;

defines a pointer to a struct Player, but it doesn't initialize it. It has thus a more or less random value, pointing somewhere into memory. This

player->name = malloc(sizeof(player->name)*256);

now writes into parts of that random location the address of a piece of memory obtained by malloc(). Writing to memory through an uninitialized pointer invokes Undefined Behavior. After that, all bets are off. No need to look further down your program. You are unlucky that, by accident, you write to a piece of memory that is owned by your process, so it doesn't crash immediately, making you aware of the problem.

There's two ways for you to improve that. Either stick to the pointer and have it point to a piece of memory allocated for a Player object. You could obtain it by calling malloc(sizeof(Player).
Or just use a local, automatic (aka stack-based) object:

struct Player player;

The compiler will generate the code to allocate memory on the stack for it and will release it automatically. This is the easiest, and should certainly be your default.


However, your code has more problems than that.

This

player->name = malloc(sizeof(player->name)*256);

allocates consecutive memory on the heap to store 256 pointers to characters, and assigns the address of the first pointer (the address of a char*, thus a char**) to player->name (a char*). Frankly, I'm surprised that even compiles, but then I'm more used to C++' stricter type enforcement. Anyway, what you probably want instead instead is to allocate memory for 256 characters:

player->name = malloc(sizeof(char)*256);

(Since sizeof(char) is, by definition, 1, you will often see this as malloc(256).)
However, there more to this: Why 256? What if I pass a string 1000 chars long? No, simply allocating space for a longer string is not the way to deal with this, because I could pass it a string longer still. So either 1) fix the maximum string length (just declare Player::name to be a char array of that length, instead of a char*) and enforce this limit in your code, or 2) find out the length needed dynamically, at run-time, and allocate exactly the memory needed (string length plus 1, for the terminating '\0' char).

But it gets worse. This

player->name = "John";

then assigns the address of a string literal to player->name, overriding the address of the memory allocated by malloc() in the only variable you store it in, making you lose and leak the memory.
But strings are no first-class citizens in C, so you cannot assign them. They are arrays of characters, by convention terminated with a '\0'. To copy them, you have to copy them character by character, either in a loop or, preferably, by calling strcpy().

To add insult to injury, you later attempt to free the memory a string literal lives in

free(player);

thereby very likely seriously scrambling the heap manager's data structures. Again, you seem to be unlucky for that to not causing an immediate crash, but the code seemingly working as intended is one of the worst possibilities of Undefined Behavior to manifest itself. If it weren't for all bets being off before, they now thoroughly would be.


I'm sorry if this sounds condemning, it really wasn't meant that way, but you certainly and seriously fucked up this one. To wrap this up:

  1. You need a good C++ book. Right now. Here is a list of good books assembled by C programmers on Stack Overflow. (I'm a C++ programmer by heart, so I won't comment on their judgment, but K&R is certainly a good choice.)

  2. You should initialize all pointers immediately, either with the address of an existing valid object, or with the address of a piece of memory allocated to hold an object of the right type, or with NULL (which you can easily check for later). In particular, you must not attempt to read from or write to a piece of memory that has not been allocated (dynamically on the heap or automatically on the stack) to you.

  3. You need to free() all memory that was obtained by calling malloc() exactly once.

  4. You must not attempt to free() any other memory.


I'm sure there is more to that code, but I'll stop here. And did I mention you need a good C book? Because you do.


You have to free() everything that you malloc() and you must malloc() everything that is not allocated at compile time.

So:

You must malloc player and you must free player->name


Ok, so your variable player is a pointer, which you have not initialized, and therefore points to a random memory location.

You first need to allocate the memory for player the way you have done for player->name, and then alocate for player->name.

Any memory allocated with malloc() needs to be freed with free().

Take a look at this and this.


This is awful code. Why? Firstly you allocate memory for player->name. malloc returns pointer to allocated memory. In next step you lose this pointer value because reassign player->name to point to static "John" string. Maybe you want to use strdup or sprintf functions?

Also the big mistake is to use uninitialized pointer to player struct. Try to imagine that it can point to random memory location. So it is good idea allocate memory for your structure with help of malloc. Or don't use pointer to structure and use real structure variable.


player doesn't need to be freed because it was never malloc'd, it's simply a local stack variable. player->name does need to be freed since it was allocated dynamically.

int main()
{
    // Declares local variable which is a pointer to a Player struct
    // but doesn't actually point to a Player because it wasn't initialised
    struct Player *player;

    // Allocates space for name (in an odd way...)
    player->name = malloc(sizeof(player->name)*256);

    // At this point, player->name is a pointer to a dynamically allocated array of size 256*4

    // Replaces the name field of player with a string literal
    player->name = "John";


    // At this point, the pointer returned by malloc is essentially lost...
    printf(player->name);

    // ?!?!
    free(player);

    printf("\n\n\n");
    system("PAUSE");
    return 0;
}

I guess you wanted to do something like this:

int main() {
  struct Player player;
  player.name = malloc( 256 );
  // Populate the allocated memory somehow...

  printf("%s", player.name);
  free(player.name);
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号