4

I wrote this code, but inserts garbage in the start of string:

void append(char *s, char c) {
    int len = strlen(s);
    s[len] = c;
    s[len + 1] = '\0';
}

int main(void) {
    char c, *s;
    int i = 0;
    s = malloc(sizeof(char));
    while ((c = getchar()) != '\n') {
        i++;
        s = realloc(s, i * sizeof(char));
        append(s, c);
    }   
    printf("\n%s",s);   
}

How can I do it?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Glicério
  • 75
  • 1
  • 8
  • Simply change `s = malloc(sizeof(char))` on `s = calloc(1, sizeof(char))` or add a new line after malloc `*s = '\0';` – Victor Gubin Oct 01 '18 at 18:36
  • 5
    Don't attempt to store 2 chars in a buffer of length 1. – melpomene Oct 01 '18 at 18:40
  • 1
    `s[len+1] = '\0';` is out of range of the allocated memory. And in the first call, there isn't a valid string to apply `strlen` to. – Weather Vane Oct 01 '18 at 18:40
  • @Weather Vane, yes, but he is clever guy; he uses reallock() before append() – purec Oct 01 '18 at 18:43
  • 1
    @purec at this time the allocated memory size is `1` and may not contain a terminating `0` required for a string. It needs to be size of `2` and be initialized. – Weather Vane Oct 01 '18 at 18:44

3 Answers3

3

There are multiple problems in your code:

  • you iterate until you read a newline ('\n') from the standard input stream. This will cause an endless loop if the end of file occurs before you read a newline, which would happen if you redirect standard input from an empty file.
  • c should be defined as int so you can test for EOF properly.
  • s should be null terminated at all times, you must set the first byte to '\0' after malloc() as this function does not initialize the memory it allocates.
  • i should be initialized to 1 so the first realloc() extends the array by 1 etc. As coded, your array is one byte too short to accommodate the extra character.
  • you should check for memory allocation failure.
  • for good style, you should free the allocated memory before exiting the program
  • main() should return an int, preferably 0 for success.

Here is a corrected version:

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

/* append a character to a string, assuming s points to an array with enough space */
void append(char *s, char c) {
    size_t len = strlen(s);
    s[len] = c;
    s[len + 1] = '\0';
}

int main(void) {
    int c;
    char *s;
    size_t i = 1;
    s = malloc(i * sizeof(char));
    if (s == NULL) {
        printf("memory allocation failure\n");
        return 1;
    }
    *s = '\0';
    while ((c = getchar()) != EOF && c != '\n') {
        i++;
        s = realloc(s, i * sizeof(char));
        if (s == NULL) {
            printf("memory allocation failure\n");
            return 1;
        }
        append(s, c);
    }
    printf("%s\n", s);
    free(s);
    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
0

when you call strlen it searches for a '\0' char to end the string. You don't have this char inside your string to the behavior of strlen is unpredictable. Your append function is acually good. Also, a minor thing, you need to add return 0; to your main function. And i should start from 1 instead if 0. Here is how it should look:

int main(void){
   char *s;
   size_t i = 1;
   s = malloc (i * sizeof(char));//Just for fun. The i is not needed.
   if(s == NULL) {
   fprintf(stderr, "Coul'd not allocate enough memory");
   return 1;
   }
   s[0] = '\0';
   for(char c = getchar(); c != '\n' && c != EOF; c = getchar()) {//it is not needed in this case to store the result as an int.
      i++;
      s = realloc (s,i * sizeof(char) );
      if(s == NULL) {
             fprintf(stderr, "Coul'd not allocate enough memory");
             return 1;
      }
      append (s,c);
    }   
printf("%s\n",s);   
return 0;
}

Thanks for the comments that helped me improve the code (and for my english). I am not perfect :)

Roy Avidan
  • 769
  • 8
  • 25
  • 3
    `sizeof (char)` is 1 by definition. Multiplying by 1 is pointless. – melpomene Oct 01 '18 at 18:44
  • 1
    Remaining bugs: Storing the result of `getchar()` in a `char` (it returns `int` for a reason); possible overflow in `i`; missing error check on `realloc` (it can return `NULL`). – melpomene Oct 01 '18 at 18:45
  • 1
    @WeatherVane `size_t i = 1;` would be better. But that is a minor point. – chux - Reinstate Monica Oct 01 '18 at 18:47
  • 1
    OK, so you fixed none of the issues but added a redundant cast. – melpomene Oct 01 '18 at 18:48
  • @melpomene he did, the allocated memory is now enough. – Weather Vane Oct 01 '18 at 18:49
  • @WeatherVane Oh. By "none of the issues" I mean the "remaining bugs" mentioned in my comment. OP's main problem (buffer too short / uninitialized) is fixed, yes. – melpomene Oct 01 '18 at 18:51
  • 1
    Although a side issue, `char c; while ((c = (char)getchar()) != '\n') {` should be `int c; while ((c = getchar()) != '\n' && c != EOF) {`. This well handles all the possible return values from `getchar()` and avoids the infinite loop on end-of-file. – chux - Reinstate Monica Oct 01 '18 at 18:51
  • "it is not needed in this case to store the result as an int." So what do you do if `stdin` reaches `EOF`? – Swordfish Oct 01 '18 at 18:52
  • @רועיאבידן That's just wrong. `getchar()` is equivalent to `getc(stdin)`. – melpomene Oct 01 '18 at 18:53
  • @רועיאבידן, Disagree about "getchar never return EOF.". C spec has "The getchar function returns the next character from the input stream pointed to by stdin. If the stream is at end-of-file, the end-of-file indicator for the stream is set and **getchar returns EOF**. If a read error occurs, the error indicator for the stream is set and getchar returns EOF." – chux - Reinstate Monica Oct 01 '18 at 18:54
  • but reading a char from `stdin` wait for input in case there is no more chars. Unlike files, which in thier case it will return EOF. But just-in-case I will add it. Normally I would add it but it is hard to program on my phone. – Roy Avidan Oct 01 '18 at 18:54
  • 1
    @רועיאבידן Who says `stdin` isn't a file? Also, you can type Ctrl-D (on unix) or Ctrl-Z (on Windows) to signal EOF interactively. – melpomene Oct 01 '18 at 18:55
  • You're comparing a `char` with `EOF`. That can't work because `EOF` is not a character. – melpomene Oct 01 '18 at 18:59
  • @chux true but it was not my `int` so your comment would be better directed to OP to use `size_t`. – Weather Vane Oct 01 '18 at 19:17
  • "it is not needed in this case to store the result as an int." Hmmm, posted code , when `char` is unsigned, is an infinite loop on end-of-file or the rare input error. – chux - Reinstate Monica Oct 01 '18 at 19:24
0

The inner realloc needs to allocate one element more (for the trailing \0) and you have to initialize s[0] = '\0' before starting the loop.

Btw, you can replace your append by strcat() or write it like

size_t i = 0;
s = malloc(1);
/* TODO: check for s != NULL */
while ((c = getchar()) != '\n') {
        s[i] = c;
        i++;
        s = realloc(s, i + 1);
        /* TODO: check for s != NULL */
}
s[i] = '\0';
ensc
  • 6,704
  • 14
  • 22