1

I'm trying to make a simple program where you put some text in it and it write back what you just wrote.

For example if I write "Hello World", the program should write me back "Hello World"

How I think it should work is like that :

loop to check if the current character is '\0'

if not print the current character and reallocate 1 more byte of memory

else stop the loop

So it's looks like an easy thing to do but my attempt is not working correctly, for example if you put only a few characters it is going to write you back with no problem but with longer string.. it is not working at all.

I know it is possible using fgets(), but I would like to understand why my version with scanf() isn't working.

(my code)

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

int main(void){
    int mem = 2;
    char * str = malloc(mem);

    scanf("%s", str);

    while (*str != '\0') {
        printf("%c", *str);
        realloc(str, mem++);
        str++;
    }

    free(str);

    return 0;
} 

edit : I was thinking that I only did a small mistake but, after reading the comments it looks like there is a lot of things that I did wrong in this tiny program. I'm going make sure that I better understand how C work and retry to do this program later. Thanks for the help!

nrbt
  • 35
  • 4
  • 1
    Is your explanation of how it is supposed to work supposed to be explaining how you read in the text or how you print it? Or are you trying to do both at the same time? Because your `scanf` format string is `"%s"` which reads a string, not a character. Why would you need to `realloc` after you read the entire string? – David Schwartz Dec 21 '20 at 18:40
  • Sorry, I should have given an example of what I wanted the program to do. So for example if I put "Hello World", the program should write me back "Hello World". – nrbt Dec 21 '20 at 18:43
  • Well your code doesn't come anywhere close to reflecting your description of how it would work. You only read in one place, and you reallocate after that. You're re-allocating as you're printing, which makes no sense. You need to allocate the space before you read into it, not after. – David Schwartz Dec 21 '20 at 18:44
  • The `scanf` call can easily go out of bounds of your allocated memory before you even begin. Not to mention that you need to get the pointer that `realloc` *returns*. – Some programmer dude Dec 21 '20 at 18:45
  • Your program works ok if you take out the `realloc` line. - would make it an answer, but it is such a short fix...- you should also take out the line with `free` - and why not just print back using `printf("%s",str)`?? – tom Dec 21 '20 at 18:46
  • Please read the manual page for `scanf` - Will help you understand a bit more – Ed Heal Dec 21 '20 at 18:47
  • Code ignores the return value of `realloc()` rendering the re-allocation worthless. – chux - Reinstate Monica Dec 21 '20 at 18:54
  • You must never use `"%s"` with scanf. If the input stream contains more non-whitespace characters than the size of the buffer to which it is writing, it will overflow. – William Pursell Dec 21 '20 at 18:55
  • If the input stream is `Hello World`, there is no `'\0'` in the input. – William Pursell Dec 21 '20 at 18:56

3 Answers3

2

Your program could be much more simple

#include <stdio.h>

int main() {

    char c;
    
    while( scanf("%c", &c) == 1 ) {
        
        printf("%c", c);
    }
    
    return 0;
}
anotherOne
  • 1,513
  • 11
  • 20
0

Reading functions' manuals and searching for similar questions is a great method to get things clearer, and undestand the problem you are facing better :)

Try looking at this question: Reading input of unknown length, and particularly at this answer that uses scanf: scanf answer (I did not verify if that works, but it teaches you another method you can use).

Ofer Arial
  • 1,129
  • 1
  • 10
  • 25
0

If you're going to use scanf for this, you shouldn't use "%s". (You should never use "%s" without a field width, since this will potentially overflow a buffer. Using "%s" is no better that gets().) If you are going to use a variant of "%s", you need to understand that it will ignore whitespace. The following does almost what you want, except for the whitespace issue. If you want to handle whitespace with precision, you cannot use "%s".

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

void * xrealloc(void *buf, size_t num, size_t siz, void *endvp);

int
main(void)
{
    size_t cap = 2;
    char *end = NULL;
    char *str = xrealloc(NULL, cap, sizeof *str, &end);

    /*
     * Read 1 char at a time, discarding whitespace.  This should
     * be done with getchar().  If using scanf, a single char
     * should be read with "%c".  We use "%1s" here only
     * because the question is specifically about `%s`.
     * Note that %1s will allow scanf to write up to 2 characters into
     * *end, so we need to ensure there is space for the terminator.
     */
    while( 1 == scanf("%1s", end++) ){
        while( end > str + cap - 2 ){
            str = xrealloc(str, cap *= 2, sizeof *str, &end);
        }
    }
    fputs(str, stdout);
    free(str);

    return 0;
}

void *
xrealloc(void *buf, size_t num, size_t siz, void *endvp)
{
    char **endp = endvp;
    char *b = buf;
    ptrdiff_t offset = b && endp && *endp ? *endp - b : 0;
    b = realloc(b, num * siz);
    if( b == NULL ){
        perror("realloc");
        exit(EXIT_FAILURE);
    }
    if( endp != NULL ){
        *endp = b + offset;
    }
    return b;
}
William Pursell
  • 204,365
  • 48
  • 270
  • 300
  • 1
    But if you discard white spaces how can you print `Hello world`? Just use `%c` instead of `%1s` – anotherOne Dec 21 '20 at 19:21
  • @Davide With `%s`, you cannot. The OP was wondering what is wrong with his code that uses `%s` with scanf, and this is certainly one (big) issue. `%c` is definitely a better approach. But using `scanf` instead of `getcchar` is .... silly. – William Pursell Dec 21 '20 at 19:46
  • Yes it is. But apart from typing more, there's no drawback on using `%c` rather than `getchar` – anotherOne Dec 21 '20 at 21:01
  • @Davide, I completely agree. I only use `%s` because the question was specifically about that. I suppose a `%[` conversion specifier would be a reasonable thing to demonstrate as well. – William Pursell Dec 21 '20 at 21:09