0

I'm trying to get a hold of dynamic memory allocation and I just want my program to get a string and the max number of characters that should be printed from the string from the user, then just output the string up to the number of characters I allocated with calloc. When I run the program, it completely disregards the limit I set for it using calloc() and just prints out the whole string.

I tried using malloc but had the same results. Also, I dereferenced text when I first tried printing out the inputted text but it caused the program to stop after you entered the string you wanted printed.

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

int main()
{
    int max;
    char *text = NULL;

    printf("\n\n");
    printf("Please enter the limit for the string as a positive integer: \n");
    scanf("%d", &max);

    text = (char *)calloc(max, sizeof(char));

    if (text != NULL)
    {
        printf("Please enter the string you want printed: \n");
        scanf(" "); //read in next character so it pauses
        gets(text);

        printf("Inputted text is : %s\n", text);
    }

    free(text);
    text = NULL;


    return 0;
}

Yes, I know, I get the warning that gets is unsafe but I was watching from a tutorial and the instructor's version built and ran fine. Even if I use scanf to read in a string into text, the result it the same.


Revised code using fgets():

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

int main()
{
    int max;
    char *text = NULL;

    printf("\n\n");
    printf("Please enter the limit for the string as a positive integer: \n");
    scanf("%d", &max);

    text = (char *)calloc(max, sizeof(char));

    if (fgets(text, max, stdin))
    {
        printf("Please enter the string you want printed: \n");
        fgets(text, max, stdin);
        text[strcspn(text, "\n")] = '\0';

        printf("Inputted text is : %s\n", text);
    }

    free(text);
    text = NULL;


    return 0;
}

I changed my code to use fgets instead and made some corrections. It returns 1 less character than the "max" the user inputs. Also, does using fgets mean I don't need to bother with calloc?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Y. J.
  • 45
  • 6
  • 2
    Because it doesn't know how big that limit is. [Don't use `gets()` — ever!](https://stackoverflow.com/questions/1694036/why-is-the-gets-function-dangerous-why-should-it-not-be-used) — that's a Q&A which explains why not and what to do about it (the key advice being 'never use `gets()`). Note that `gets()` is no longer a part of Standard C — it was removed from the C11 standard. See [How to prevent `scanf()` from causing a buffer overflow in C?](https://stackoverflow.com/questions/1621394/how-to-prevent-scanf-causing-a-buffer-overflow-in-c) for information on how to use `scanf()` safely. – Jonathan Leffler Jan 13 '19 at 20:53
  • " just want my program to get a string " --> Detail, `fgets()` does **not** read a _string_. It attempts to read a _line_ (all the characters typed up to and including the `'\n'` and then _converts_ that into a _string_ by appedning a _null character_. To read 3 characters and the (4 char) takes a 5 char to save as a string. – chux - Reinstate Monica Jan 13 '19 at 21:22
  • Y. J., Be specific. Post exactly what you typed. – chux - Reinstate Monica Jan 13 '19 at 21:24
  • Using `fgets()` still means you need to allocate the correct amount of memory (you could use `malloc()` instead of `calloc()` if you prefer (and I would use `malloc()`) — and you need to allow for both the newline and the null terminator. If you don't allow for the newline, it will be left in the input stream ready to confuse the next I/O operation. If you want the I/O code to do the memory allocation, look at POSIX [`getline()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html). – Jonathan Leffler Jan 13 '19 at 21:31
  • Also, note that your edits invalidate the answers — which is not kosher. After you've got answers, you need to modify the question more carefully. I've reinstated the original question text (changing a reference to "the warning that `fgets` is unsafe" to "the warning that `gets` is unsafe", which is a more plausible warning), and then adding your revised code and commentary separately. This leaves the original answers still meaningful, whereas eliminating `gets()` did not. – Jonathan Leffler Jan 13 '19 at 21:42
  • How many characters did you tell `gets` to read in? – user253751 Jan 13 '19 at 23:36

2 Answers2

2

When you allocate memory and assign it to a pointer, there is no way to deduce the size of the memory from the pointer in hand. So gets has no chance (and will therefore not check) if it will exceed the amount of memory you reserved. BTW: gets is not part of C standard any more (since C11). Use fgets instead and pass your max as argument:

if (fgets(text, max, stdin)) {
   // something successfully read in
   text[strcspn(text, "\n")] = '\0';
}

Note that fgets, in contrast to gets, preserves any entered new line and keeps it at the end of text. To get rid of this, you can use text[strcspn(text, "\n")] = '\0', which will let the string end at the new line character (if any).

Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
0

I think the exact reason your code is disregarding the max variable is that the gets() function is writing over all the null bytes in your text character array when the string provided on standard input is longer than max. This is one of the many reasons why we always say “never use gets()”!

More specifically, gets() will continue to write into your array from stdin until it reaches a newline or EOF character, with no regard to the bound of it. The fact that you’re seeing the entire string printed if just undefined behavior.

max1000001
  • 314
  • 1
  • 13
  • I find the first sentence rather confusing — or misguided. It is `gets()` that has no knowledge of `max`. While it is possible that overflowing the allocated memory would overwrite `max`, it is actually fairly unlikely since `max` is probably stored on the stack and the string is probably in heap memory. It would be more of a problem if `text` were a local array — `char text[max];` for example, assuming VLA (variable length array) support is available. – Jonathan Leffler Jan 13 '19 at 21:46
  • I don’t think my answer has anything to do with overwriting the max variable itself. I’m just describing what happens, as I said, “when the string provided on standard input is longer than max,” which is simply a buffer overflow. – max1000001 Jan 13 '19 at 22:03
  • OK; I still find the first sentence a bit confusing, even if what I thought it was saying isn't what it was trying to say. – Jonathan Leffler Jan 13 '19 at 22:08