2

I want to write a program which asks user some input values and gives results according to the input values. The following code works but when I enter "John" only 4 spaces are used in the 100 memory. Other 96 are wasted. Also, there is no guarantee that input will be smaller than 100 characters (very unlikely but possible). Is there a way to optimize this?

P.S: When I researched about it, some sites say I should use sscanf() or fgets() but I didn't understand how to use them in my code.

P.P.S : I am very new to C so please show me the simplest way.

#include <stdio.h>
int main()
{
     char name[100];
     printf("Your name: \n");
     scanf("%100s", name);
     printf("Name: %s  \n", name);

    return 0;
}
harold_finch
  • 80
  • 3
  • 11
  • 1
    Take a look: [How can I read an input string of unknown length?](https://stackoverflow.com/questions/16870485/how-can-i-read-an-input-string-of-unknown-length) – woz Jun 29 '17 at 16:43
  • I saw that discussion but I couldn't understand what the first answer said. It looked a little complicated. – harold_finch Jun 29 '17 at 16:46
  • In a nutshell, the answer linked above reads the user input *one character at a time* and uses dynamic memory allocation for the input buffer, allocating 16 bytes more at a time as needed as the number of characters input increases. If you take the time to study that answer line by line and look up the documentation and use of the functions involved, you'll learn a bit about C programming. – lurker Jun 29 '17 at 16:57
  • `scanf("%100s", name);`-- note that this should be `scanf("%99s", name);` to avoid buffer overflow. The `\0` character is automatically written by `scanf()` when using `%s`; the maximum width specified is the maximum length of an input item, not including the null-terminator. – ad absurdum Jun 29 '17 at 18:43

3 Answers3

3

You need to realloc memory for this operation( the memory will be changed when the program is running )

char *name = 0;
char *tmp = 0;
int inputAsingleChar,i=0,j=4;
printf("Your name: \n");
while((inputAsingleChar = getchar()) != '\n' && inputAsingleChar != EOF)
{
    if(i==j){
        j+=4;
        tmp = realloc(name, j);
        if(tmp== NULL){printf("Couldn't realloc but the 'name' array is still valid");}
        else{name = tmp;}
    }
    name[i++] = inputAsingleChar ;
}
name[i] = '\0';
printf("Name: %s  \n", name);
free(name);
free(tmp);

Version 2 (Final Version) with the help of the generous David Bowling

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

int main(void)
{
    int inputAsingleChar,i=0,j=4;
    char *name = malloc(j);
    if (name == NULL){
        printf("No memory");
        exit(1);
    }
    printf("Your name: \n");
    while((inputAsingleChar = getchar()) != '\n' && inputAsingleChar != EOF)
    {
        if(i==j){
            j+=4;
            char * tmp = realloc(name, j);
            if(tmp== NULL){printf("Couldn't realloc but the 'name' array is still valid");}
            else{name = tmp;}
        }
        name[i++] = inputAsingleChar ;
    }
    name[i] = '\0';
    printf("Name: %s  \n", name);
    free(name);
    return 0;
}

Also is live here http://ideone.com/cDxkDV

O.Rares
  • 1,031
  • 1
  • 16
  • 19
  • 2
    It is very ineffcitient but yeah, it works. Btw, what is `c` here? – unalignedmemoryaccess Jun 29 '17 at 16:57
  • @tilz0R is more efficient than wasting 95 bytes. I edited is inputAsingleChar – O.Rares Jun 29 '17 at 16:58
  • and if I write 92 bytes? – unalignedmemoryaccess Jun 29 '17 at 16:59
  • what about writting 1 million bytes ? – O.Rares Jun 29 '17 at 17:00
  • It gives errors. Did you import something from c library? – harold_finch Jun 29 '17 at 17:00
  • @O.Rares at 1 million bytes you failed twice. Your execution time will take 2 days comparing to single predefined buffer. ;) – unalignedmemoryaccess Jun 29 '17 at 17:01
  • @harold_finch those headers stdio ,stdlib, string – O.Rares Jun 29 '17 at 17:01
  • 1
    @tilz0R At least it didn't wastes resources in future . If Harold wants to make that input an username he can simply use an array with 16 bytes.But he wants to no waste bytes – O.Rares Jun 29 '17 at 17:04
  • @harold_finch Read the comment above,if you want to make it an input for username use 16 bytes,is enough – O.Rares Jun 29 '17 at 17:05
  • 1
    Not much sense in worrying about 95 bytes for one name. This might matter when storing many names; then it is better to use a generous buffer and copy to custom-allocated memory, or to trim allocations to size with realloc(). IAC, this answer is very inefficient with so many unnecessary allocations. Why `malloc` 4 characters at the start, when you just `realloc()` to 2 in the first loop iteration? Also, there is a potential memory leak with: `name = realloc(name, i+1);`. There is no reason for this bad practice to be used in an accepted answer. – ad absurdum Jun 29 '17 at 19:02
  • @DavidBowling You're right I changed the initialization, I wanted to make an if to check if input has at least 4 chars and if doesn't to realloc memory but someone in the questions comments posted a link to something similar but more complicated and Harold asked for something more easy to understand.I agree that can be improved – O.Rares Jun 29 '17 at 19:41
  • 1
    Hmmm. Now there is UB since `name[i++] = inputAsingleChar;` occurs before the call to `realloc()`. Efficiency aside, (best when growing memory to grow from reasonable size to needed size by periodic doubling), my main complaint is with assigning the result of `realloc()` directly to the pointer to the object of reallocation. `realloc()` may return a null pointer, then losing access to the previous allocation, causing a memory leak and lost data. Simple to illustrate good practice for learners by first storing in and then checking a temporary variable. – ad absurdum Jun 29 '17 at 19:48
  • @DavidBowling Thanks ,can you give it a last view ? – O.Rares Jun 29 '17 at 20:03
  • 1
    There are some issues. 1) `tmp` is not declared. 2)`tmp` _should not_ be `realloc`ed twice, but reassigned, and _not_ `free`d, as this deallocates the recent reallocation. 3) need to do some allocation _before_ assigning `name[i++] = ...`. 4) `sizeof(name)` gives the size of a pointer, not an array. There are some typos too; suggest compiling with `#include`s and `main()` to test before posting. [Here is an Ideone link](http://ideone.com/khKncC) to an improved version of your code. I have used doubling here, but you could just as well grow with `j += 4;`. – ad absurdum Jun 29 '17 at 20:36
  • @DavidBowling Big thanks ,I updated the post ,I like how you declared tmp in the loop so it will erase at the end of the execution.You should post the last version and it should be taken as the final answer – O.Rares Jun 29 '17 at 20:57
  • 1
    Always trying to code just a little better.... Note that `tmp` is merely a pointer to the allocation; the allocation must still be `free`d, but making it local to the block hides it from the rest of the code. The reassignment of `name` to `tmp` ensures that the allocation can still be `free`d later. Also note that your initial answer worked fine, with some efficiency issues and a _potential_ memory leak. UV for successful efforts to improve code. A final note: when code handles `malloc()` or `realloc()` failure, exit is probably simplest, since further writing to `name` will lead to UB. – ad absurdum Jun 29 '17 at 21:14
  • 1
    @DavidBowling I added that now . Thanks for your time,who said you can't learn from your own answer ? ahaha .Good luck! – O.Rares Jun 29 '17 at 21:23
1

No, there is no way. You have to make sure you have enough memory to save data from user at any time. Maybe at some point user will try to write 100 characters. In this case, you need 101 bytes available in memory for trailling zero.

If you use sscanf or gets you still need memory to save your string and in both cases you don't know how many bytes you can expect from input.

The following code works but when I enter "John" only 4 spaces are used in the 100 memory. Other 96 are wasted.

No, other 95 are wasted, you are missing trailling zero.


If you want to do inefficient code, then you can realloc memory for each received character but then you have to read char by char from input using getchar function.


Further reading

How can I read an input string of unknown length?

unalignedmemoryaccess
  • 7,246
  • 2
  • 25
  • 40
1

If you are concerned about memory usage, use dynamic allocation of memory i.e using malloc() and realloc(), you can get certain behavior, like this.

int main() {

 char a;
int i=0;
char *str=0;
while((a=getchar())!='\n')
{
    str=realloc(str,i+2);   //1 for character to store, + 1 for '\0' terminating character
    str[i++]=a;      
}
 str[i]='\0';
printf("%s\n",str);
return 0;

} As the user enters the character, this code generates the memory and stores the results immediately. As user press Enter, the input reading process terminates.

This method is quite memory efficient, but takes more time to execute. So, always we need to manage the trade-off between performance vs memory. Hope this helps.

anil
  • 158
  • 1
  • 12