0

I've been searching the net including stackoverflow for hours and didn't find any answer which suits my problem - maybe because it's not a real problem since the program works...but it shouldn't. Sounds strange? It is - at least to me.

It's part of a task for university. The task is to allocate memory for a char array, then print a string to the array using sprintf() and finally printing the array with printf(). For memory allocation malloc() is to be used (I know there a better ways, but we have to use exactly these functions).

That's what I've got:

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

int main()
{
    // Declare char array
    char *string;

    /* Allocate memory for string of certain length
    The 1 is only to show what's wrong. I'm aware of the actual size needed */
    if( (string = malloc( 1 * sizeof(char) ) ) == NULL )
    {
        perror("malloc failed to allocate n chars!");
        exit(1);
    }

    /* Print string to previously allocated memory. Now I would expect an error due to too few bytes allocated */
    sprintf(string, "Too many characters here...");

    // Print string to command line
    printf("%s\n", string);

    return 0;
}

So far it works: It compile without any notice using gcc -Wall -std=c99 as well on Ubuntu as on Mac OSX.

BUT

The problem is that it shouldn't. As you might have noticed I allocated to few bytes for the string I am writing to the array. Still it works, no matter how long the string is (tried up to 1000 chars) or how many bytes I allocate. Wouldn't care about it, if the university's automated testing unit wouldn't mark it as wrong. It says the program is not reading from the allocated array. That's why I assume, that sprintf puts the string anywhere but in the allocated array. But I can't explain how this could be possible.

I would be deeply grateful if you guys know what I'm doing wrong.

Thanks in advance!

------ UPDATE ------

As Mike pointed out I'm not using free(string) in this snippet (thanks the hint!). In the actual program I placed free(string) after the printf(). But when I try to print string after that statement again -> it's printed as if nothing happened! How is that possible?

MaxKay
  • 33
  • 4
  • So many dups of this brand of UB to count. – Martin James Oct 22 '15 at 18:58
  • 'I've been searching the net including stackoverflow for hours and didn't find any answer which suits my problem' really? – Martin James Oct 22 '15 at 18:59
  • @MartinJames: I think at least half of the C and C++ questions around here are simple UB stuff. – Ed S. Oct 22 '15 at 19:02
  • Possible duplicate of [C++ Accesses an Array out of bounds gives no error, why?](http://stackoverflow.com/questions/1239938/c-accesses-an-array-out-of-bounds-gives-no-error-why) – Martin James Oct 22 '15 at 19:03
  • http://stackoverflow.com/questions/15646973/how-dangerous-is-it-to-access-an-array-out-of-bounds – Martin James Oct 22 '15 at 19:04
  • http://stackoverflow.com/questions/9137157/no-out-of-bounds-error – Martin James Oct 22 '15 at 19:05
  • 1
    two words : Undefined Behavior :) – machine_1 Oct 22 '15 at 19:10
  • @Martin James Very valuable comment, thanks! Sure, the web is full of duplicates and yes, many of them are simply the result of laziness. But it's also full of duplicates of your contribution, which are the peak of stupidity. How can I complain about redundant content producing more redundant content with my post itself -.- In this case I simply had no more idea how to formulate my question - that's why I wrote a detailed, case-specific one – MaxKay Oct 22 '15 at 19:49
  • Concerning the update with another question: better to make that a new post and revert the edit. – chux - Reinstate Monica Oct 22 '15 at 20:10

4 Answers4

2

Overwriting the end of a malloc'd array is likely to mess things up, but exactly what gets messed up is a matter of chance. That it happens not to fail in a simple test is not surprising, especially since your program exited shortly after committing the miseed. The string that's written is, in itself, intact and valid -- it's only other things using that area of memory that may suffer. That doesn't mean it will work in a more complex circumstance.

Paul Kienitz
  • 878
  • 6
  • 25
2

The problem is with the assertion "The problem is that it shouldn't." or "expect an error".

/* Now I would expect an error due to too few bytes allocated */
sprintf(string, "Too many characters here...");

When code does something is should not, like writing beyond allocated memory space, C does not defined what should happen. Therefore it is Undefined Behavior (UB). To expect an error requires a defined behavior on C's part.

UB means anything may happen. The code is not required to check and complain that an attempt to access outside allocated memory occurred.

C provides you with lots of rope fro code to do all sorts of things quickly
- including enough rope for code to hang itself.


Given that sprintf() is prone to writing out of bounds, code could have used snprintf() and checked its results. snprintf() will not over-write the given size of the buffer.

char *string;
size_t size = 1;  // or whatever
string = malloc(size);
...
int n = snprintf(string, size, "Too many characters here...");
if (n < 0 || n >= size) return Error_code;
...
printf("%s\n", string);
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Thanks, that's indeed a good annotation. I've been approaching this with the wrong expectations. Sometimes I forget how much freedom C is giving us. – MaxKay Oct 22 '15 at 20:01
  • @MaxKay [Freedom walks with Responsibility](https://juangreatleap.files.wordpress.com/2012/02/spidey2.png) – chux - Reinstate Monica Oct 22 '15 at 20:06
  • Yep, absolutely right. But in this case I don't really know how to meet my responsibility :/ – MaxKay Oct 22 '15 at 20:10
  • @MaxKay Code could use `snprintf()` in this case to avoid over-run. Answer updated. – chux - Reinstate Monica Oct 22 '15 at 20:18
  • Yes, in real life I would do it that way. But in this case I have to use sprintf() as it is requested in the exercise. I'm now trying to figure out why the testing-system says I were not reading from the allocated memory, although - in the full program - I am calculating the needed bytes dynamically and allocating enough memory – MaxKay Oct 22 '15 at 20:22
  • @MaxKay Still code can use `snprintf()`, for now, and later shift to `sprintf()`. Mysterious problems often lurk in allocated/free'd memory. For development, use various checks and functions that help detect/prevent problems. – chux - Reinstate Monica Oct 22 '15 at 20:49
1

I just tested the code on the university's server again - the SAME code as before - and now it works. I have absolutely no idea why, certainly there was an error in the testing unit.

So there was no error in my code. But at least testing it with wrong parameters now taught me something important (what you guys pointed out): Undefined behavior can also be that everything appears to work fine; although it shouldn't.

So from this point of view you were right. This is very similar to the posted topics. It was me approaching the problem with wrong expectations.

Thank you!

MaxKay
  • 33
  • 4
0

The problem is that you're assuming that there should be something wrong and that the compiler should tell you that it's wrong.

The syntax is correct, but the semantics aren't - the compiler can only tell you so much. sprint() will print what you want it to, but what all it writes to in memory varies.

Consider using snprintf()

Lance Clark
  • 446
  • 7
  • 14