Ren
Ren

Reputation: 4680

Dynamically allocated C string stores more than it is specified to be able to hold

I have the following code. It accepts any value for card from 1 to 52.

char* formatCard( int card )
{
    char suit[4][4] = {"♠", "♥", "♣", "♦"};
    char number[13][6] = {"2", "3", "4", "5", "6", "7",
        "8", "9", "10", "Jack", "Queen", "King", "Ace"};

    char* s = malloc(1);
    sprintf(s, "%s of %s", number[card%13], suit[card/13]);
    return s;
}

The code works fine and all, but when I dynamically allocate the c-string char* s = malloc(1); it can take any amount of memory, no matter how small it is and despite the fact that the c-string can reach to have a maximum size of 10 (12 if you consider the suit symbols to be of size 3... which now that I think about it, it is another thing that I don't understand and puzzles me).

Could someone explain this thing that seems weird to me? Is it perhaps something that only happens on my computer/OS? I am currently using Ubuntu.

Upvotes: 0

Views: 65

Answers (3)

Deduplicator
Deduplicator

Reputation: 45674

You are working under a grave mis-apprehension:

The language does not guarantee your program will crash if you do something forbidden.
Instead:

  1. It seems to work as intended.
  2. It corrupts internal state to better surprise you later.
  3. It silently destroys your data.
  4. It crashes loudly.

Pick any number of the above points at random, that could be what happens.
Or maybe I just wasn't imaginative enough yet, there are untold further possibilities of equal validity.

Undefined, unspecified and implementation-defined behavior

Regarding the fruit-symbol being a multi-byte-character: You are probably using UTF-8 (As you are on Linux, that's just about guaranteed), any character may be composed of many codepoints, which each use up to 4 bytes.

Upvotes: 1

Haris
Haris

Reputation: 12272

when you allocate memory using malloc(1) call, 1 byte of memory gets allocated to s. But that wont stop the pointer from going astrayand reading/writing from further memory location. this results in undefined behaviour and should be taken care of. whatever memory is allocated to the pointer, that much amount should only be accessed.

Upvotes: 0

Dietrich Epp
Dietrich Epp

Reputation: 213538

Your program is wrong, and you know this. But the compiler doesn't know it, and so the program still runs. What gives?

char* s = malloc(1);
sprintf(s, "%s of %s", number[card%13], suit[card/13]);

This invokes something which is called "undefined behavior". This means that your program is wrong, and it will do something unexpected. Maybe it will crash, maybe it will not crash, maybe it will write the wrong answer, maybe it will format your hard drive.

In this case, sprintf() is probably writing past the end of the buffer you allocated, writing into other parts of memory. When this happens, sometimes the memory will not be mapped, causing a segmentation fault (crash). Sometimes the memory will be used for something else, and some other part of the program will misbehave. Maybe the program will crash later, when the corrupted memory is used by some other part of the program.

The fix

You want to use snprintf() instead. The snprintf() function takes a second argument, which is the size of the buffer. The snprintf() function is safer because it will stop writing data once it gets to the end of the buffer.

size_t sz = 1;
char *s = malloc(sz);
snprintf(s, sz, "%s of %s", number[card % 13], suit[card / 13]);

You can also try using the address sanitizer, if you are using recent Clang or GCC.

Upvotes: 1

Related Questions