0

I'm trying to make a simple program that keeps reading strings from the standard input using scanf and putting them into an array of strings (Right now I'm just testing it using 3 words, hence only 3 print statements at the end). I'm able to keep reading until there are no more strings, however I've encountered a bug where after the looping is done, all the strings in the array are the last string read in. I've tried putting a print statement within the loop to debug, and it is reading in the correct string. However when the looping is finished, all the strings in the array are the last string read in. Can anyone point out where I'm going wrong here? Thanks.

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



int main(void) {
    int c = 0;
    char** w_arr = malloc(3*sizeof(char*));
    char* w = malloc(10*sizeof(char));

    while (scanf("%s", w) == 1) {
        w_arr[c] = w;
        //printf("%s", w_arr[c]); debug print statement
        c++;
    }

    printf("%s, %s, %s\n", w_arr[0], w_arr[1], w_arr[2]);
    return 0;
}
3
  • You need to malloc a new w buffer for every scanf (or some other way of getting a new buffer such as w_arr[c]=strdup(w)). As it is, all your array entries point to the same buffer whose contents are being overwritten for every scanf. Commented Mar 18, 2016 at 3:26
  • Ah. Sorry I'm new to c. So basically all my entries in the array are not in fact pointing to a string, but to a pointer. So when the looping is done, they all point to the same pointer, which is the address of the last string? Commented Mar 18, 2016 at 3:33
  • @PCR It's not really "the address of a string" - it's the address of some space in memory. Then you read the first string into that space, and make w_arr[0] point to that space. Then you read the second string into that space, and make w_arr[1] point to that space. Then you read the third string into that space, and make w_arr[2] point to that space. The end result is that w_arr[0], w_arr[1] and w_arr[2] all point to that space, and that space contains the third string (because you overwrote the first two). Commented Mar 18, 2016 at 4:03

5 Answers 5

2

You are setting every element of w_arr to the single buffer w that was only allocated for once. You can see this by printing out the contents of w_arr by adding this line after your loop:

printf("Addresses of strings: %p, %p, %p\n", w_arr[0], w_arr[1], w_arr[2]);

You should see output similar to the following:

[hwibell@localhost tmp]$ ./strings
123
456
789
Addresses of strings: 0x1b0b030, 0x1b0b030, 0x1b0b030 <-- Same address!
Contents of strings: 789, 789, 789

As you can see, each element of w_arr points to the same address in memory. To fix this, you will need to allocate w each time and then assign the new character array to w_arr.

#define NUM_OF_STRINGS 3
#define MAX_CHARS      10

int main(void) {
    char** w_arr = malloc(NUM_OF_STRINGS * sizeof(char*));

    for (int i = 0; i < NUM_OF_STRINGS; ++i) {
        w_arr[i] = malloc(MAX_CHARS * sizeof(char)); // allocate a new buffer for each element in w_arr
        scanf("%s", w_arr[i]);
    }

    printf("%s, %s, %s\n", w_arr[0], w_arr[1], w_arr[2]);
    return 0;
}
Sign up to request clarification or add additional context in comments.

Comments

1

You are reusing w for each w_arr element (i.e. they all point to the same place). You need to allocate one for each string

Change:

w_arr[c] = w;

To:

w_arr[c] = strdup(w);

2 Comments

1) strdup() is not standard C. 2) What's happen if OP is on Linux?
@Michi strdup is POSIX 1003.1 compliant. POSIX [from IEEE] and ISO have run parallel since the 1980's. ISO lost control of the "libc spec" some 20 years ago. Most libc's adhere to POSIX not ISO. As for linux, strdup works fine
1

The statement

    w_arr[c] = w;

makes sure that all the elements of w_arr point to the same pointer, w. The data being held at w after the end of the while loop is that last input that was read. Hence, you see the same output from all the elements of w_arr.

I can think of couple of ways to solve this problem.

  1. Use strdup when assigning to w_arr[c]

    w_arr[c] = strdup(w);
    

    If strdup is not available on your platform, it is easy to implement one.

    char* strdup(char const* in)
    {
        char* ret = malloc(strlen(in)+1);
        strcpy(ret, in);
        return ret;
    }
    
  2. Allocate memory for w in the while loop for second, third, etc. inputs.

    Instead of

    char* w = malloc(10*sizeof(char));
    while (scanf("%s", w) == 1) {
       w_arr[c] = w;
       //printf("%s", w_arr[c]); debug print statement
       c++;
    }
    

    use

    char* w = malloc(10*sizeof(char));
    while (scanf("%s", w) == 1) {
       w_arr[c] = w;
       //printf("%s", w_arr[c]); debug print statement
       c++;
       w = malloc(10*sizeof(char));
    }
    

Make sure to add calls to deallocate mmory before the end of the function.

    free(w);
    for (int i = 0; i < 3; ++i )
       free(w_arr[i]);

1 Comment

"Use strdup when assigning to w_arr[c]" If OP is on Linux strdup() won't help.
0

You'd better use strdup and include string.h.

w_arr[c] = w;

To:

w_arr[c] = strdup(w);

And strcpy is not safe you'd better use strncpy instead;

1 Comment

strdup() is not standard C. The OP didn't mentioned if he's on windows. If he's on Linux this Answer is not helping he's problem.
0
 w_arr[c] = w;

As w_arr[c] points to w whose content at end of loop is the last read string ,so you get the last string printed .

You allocate memory to each pointer in w_arr -

 char** w_arr = malloc(3*sizeof(char*));      //allocated memory for 3 char *

 while (scanf("%s", w) == 1 && c<3) {
      w_arr[c]=malloc(10*sizeof(**w_arr));    // allocate memory to each char *
      strcpy(w_arr,w);                        //copy string
    //printf("%s", w_arr[c]); debug print statement
      c++;
 }

and then use strcpy after reading -

Note - Change your scanf to while (scanf("%9s", w) == 1) { so as to avoid getting more than required characters in w .

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.