4

I tried to get the inputs(strings) from user and store them in an array.But after I ran this code, the program instantly crashed.

#include <stdio.h>
int main() {
    int i;
    char *word[3];
    for(i=0;i<3;i++)
    {
        printf(" Enter a word: ");
        scanf("%s", &word[i]);
    }
    printf("%s ", word[0]);
    return 0;
}
jtbandes
  • 115,675
  • 35
  • 233
  • 266
Top Serious
  • 149
  • 1
  • 1
  • 7
  • 5
    This basic task is covered in any basic C book or tutorial and also a search will bring up countless hits for articles that you can study. Please do basic research before posting a new question. Please look at what the compiler is telling you. It should have warnings. Read the warnings, try to understand what they are telling you and fix them so that at least your program compiles without any warnings. – kaylum Jan 07 '17 at 05:15
  • 4
    Based on the answers, I'm beginning to question the first part of that comment `:)` – David C. Rankin Jan 07 '17 at 06:06
  • I reopened this question, it is not *just* the matter of reading to unallocated string but how to read the lines into an array etc... – Antti Haapala -- Слава Україні Jan 27 '21 at 07:21

7 Answers7

10

In this line:

scanf("%s", &word[i]);

You need to make sure word[i] is pointing somewhere, and has enough space to occupy the string entered. Since word[i] is a char * pointer, you need to at some time allocate memory for this. Otherwise, it is just a dangling pointer not pointing anywhere.

If you want to stick with scanf(), then you can allocate some space beforehand with malloc.

malloc() allocates requested memory on the heap, then returns a void* pointer at the end.

You can apply malloc() in your code like this:

size_t malloc_size = 100;

for (i = 0; i < 3; i++) {
    word[i] = malloc(malloc_size * sizeof(char)); /* allocates 100 bytes */
    printf("Enter word: ");
    scanf("%99s", word[i]); /* Use %99s to avoid overflow */
                            /* No need to include & address, since word[i] is already a char* pointer */
} 

Note: Must check return value of malloc(), because it can return NULL when unsuccessful.

Additionally, whenever you allocate memory with the use of malloc(), you must use free to deallocate requested memory at the end:

free(word[i]);
word[i] = NULL; /* safe to make sure pointer is no longer pointing anywhere */

Another approach without scanf

A more proper way to read strings should be with fgets.

char *fgets(char *str, int n, FILE *stream) reads a line from an input stream, and copies the bytes over to char *str, which must be given a size of n bytes as a threshold of space it can occupy.

Things to note about fgets:

  • Appends \n character at the end of buffer. Can be removed easily.
  • On error, returns NULL. If no characters are read, still returns NULL at the end.
  • Buffer must be statically declared with a given size n.
  • Reads specified stream. Either from stdin or FILE *.

Here is an example of how it can be used to read a line of input from stdin:

char buffer[100]; /* statically declared buffer */

printf("Enter a string: ");
fgets(buffer, 100, stdin); /* read line of input into buffer. Needs error checking */

Example code with comments:

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

#define NUMSTR 3
#define BUFFSIZE 100

int main(void) {
    char *words[NUMSTR];
    char buffer[BUFFSIZE];
    size_t i, count = 0, slen; /* can replace size_t with int if you prefer */

    /* loops only for three input strings */
    for (i = 0; i < NUMSTR; i++) {

        /* read input of one string, with error checking */
        printf("Enter a word: ");
        if (fgets(buffer, BUFFSIZE, stdin) == NULL) {
            fprintf(stderr, "Error reading string into buffer.\n");
            exit(EXIT_FAILURE);
        }

        /* removing newline from buffer, along with checking for overflow from buffer */
        slen = strlen(buffer);
        if (slen > 0) {
            if (buffer[slen-1] == '\n') {
                buffer[slen-1] = '\0';
            } else {
                printf("Exceeded buffer length of %d.\n", BUFFSIZE);
                exit(EXIT_FAILURE);
            }
        } 

        /* checking if nothing was entered */
        if (!*buffer) {
            printf("No string entered.\n");
            exit(EXIT_FAILURE);
        }

        /* allocate space for `words[i]` and null terminator */
        words[count] = malloc(strlen(buffer)+1);

        /* checking return of malloc, very good to do this */
        if (!words[count]) {
            printf("Cannot allocate memory for string.\n");
            exit(EXIT_FAILURE);
        }

        /* if everything is fine, copy over into your array of pointers */
        strcpy(words[count], buffer);

        /* increment count, ready for next space in array */
        count++;
    }  

    /* reading input is finished, now time to print and free the strings */
    printf("\nYour strings:\n");
    for (i = 0; i < count; i++) {
        printf("words[%zu] = %s\n", i, words[i]);
        free(words[i]);
        words[i] = NULL;
    }

    return 0;
}

Example input:

Enter a word: Hello
Enter a word: World
Enter a word: Woohoo

Output:

Your strings:
words[0] = Hello
words[1] = World
words[2] = Woohoo
RoadRunner
  • 25,803
  • 6
  • 42
  • 75
3

There seems to be a bit of confusion in this area. Your primary problem is you are attempting to write each word to the address of each of pointers you declare with char *word[3];. (not to mention you have no storage allocated at the location pointed to by each pointer -- but you never get there as you attempt to write to the address of each pointer with &word[i] rather than to the pointer itself)

While you can use scanf you will quickly run into one of the many pitfalls with taking user input with scanf that plague all new C programmers (e.g. failing to handle the '\n' left in the input buffer, failing to handle whitespace in strings, failing to limit the number of characters read/written, failing to validate the read or handle EOF, etc...)

A better approach is to simply use fgets and then trim the '\n' that fgets read and includes in the buffer to which it stores the string. A simple example would be:

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

#define NWDS 3    /* declare a constant for the maximum number of words */

int main (void) {

    int i, n = 0;
    char word[NWDS][50] = { "" };       /* provide storage or allocate */

    for (i = 0; i < NWDS; i++) {        /* for a max of NWDS */
        printf ("Enter word : ");       /* prompt */
        if (!fgets (word[i], sizeof word[i], stdin))  /* read/validate */
            break;                      /* protect against EOF */
        size_t len = strlen (word[i]);  /* get length */
        if (word[i][len-1] == '\n')     /* check for trailing '\n' */
            word[i][--len] = 0;         /* overwrite with nulbyte  */
    }
    n = i;                              /* store number of words read */
    putchar ('\n');                     /* make it pretty */

    for (i = 0; i < n; i++)             /* output each word read */
        printf (" word[%d] : %s\n", i, word[i]);

#if (defined _WIN32 || defined _WIN64)
    getchar();  /* keep terminal open until keypress if on windows */
#endif

    return 0;
}

Go ahead and cancel input at any time by generating an EOF during input (ctrl + d on Linux or ctrl + z on windoze), you are covered.

Example Use/Output

$ ./bin/wordsread
Enter word : first word
Enter word : next word
Enter word : last word

 word[0] : first word
 word[1] : next word
 word[2] : last word

Looks things over, consider the other answers, and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Why `#if (defined _WIN32 || defined _WIN64)`? Terminal may close in linux after program execution too, right? That is, when running the program via GUI or using `terminal -e "./bin/wordsread"`. – Spikatrix Jan 07 '17 at 07:29
  • Yes, point well made. Generally though, it is only the windows terminal that provides the most problem. Those on Linux, are generally more familiar with (and unless using an IDE, are probably compiling from) the terminal. But for completeness, you can removed the `#if` and `#endif` preprocessor conditionals and have the code wait for a keypress before exiting. (or use the `__linux__` test macro to explicitly test for the OS) – David C. Rankin Jan 07 '17 at 07:58
0
char *word[3]; // <-- this is an array of 3 dangling pointers, of type char*
// they still point nowhere, we later need to set them to some allocated location.
 ...
 for(i=0;i<3;i++) {
     word[i] = malloc(some_max_size * sizeof(char)); // <-- allocate space for your word
     printf(" Enter a word: ");
     scanf("%s", word[i]); // <-- not &word[i]; word[i] is already a char* pointer
 }
A.S.H
  • 29,101
  • 5
  • 23
  • 50
0

You are declaring word as array of pointer (char *word[3];). You have to allocate memory to store data. Allocate memory with malloc or similar functions before assigning values.

Nithin P
  • 513
  • 3
  • 16
0

Yes the code crashes because declaring an array of character pointers is not enough, you need to set the pointers to point to memory where the strings can be stored.

E.g.

const int maxLen = 32;
char* word[3] = {NULL,NULL,NULL};

word[i] = malloc(maxLen);

then read the string from keyboard, to ensure that the string is not too long use fgets and maxLen:

printf("Enter a word:");
fgets(word[i],maxLen,stdin);
AndersK
  • 35,813
  • 6
  • 60
  • 86
0
#include <stdio.h>
int main(){
int n;
int i=0;
scanf("%d",&n);
char arr[n];
while(n>i){
    scanf("%s",&arr[i]);
    i+=1;
    }
while(n-i<n){
printf(" %c ",arr[n-i]);
i-=1;
    }
}
-2

The code char *word[3] made a 3-element array of pointers!

See, you have basically created a character array of pointers, so you cannot put a "string" into each one of them, because the type of a pointer variable is long hexadecimal.

Satyam Raj
  • 61
  • 1
  • 11