1

I'm writing how to dynamically allocate memory for n strings with each string be however long you want. So I know to use double pointers, but can't understand how to allocate each string with the right amount of space. My code:

     int i,n;
     char **str;

    printf("Enter n (strings of chracters): ");
    scanf("%d",&n);

    str = (char**)malloc(n*sizeof(char*));

    printf("Enter strings one by one:\n");
    for(i=0; i<n; i++) {
        str[i] = (char*)malloc(//SOMETHING//*sizeof(char));
        gets(str[i]);
    }

I tried to be cheeky and put gets() first like this

for(i=0; i<n; i++) {
        gets(str[i]);
        str[i] = (char*)malloc(strlen(str[i])*sizeof(char));

but that obviously is wrong, I can't read string then allocate it.
So is there anyway to replace //SOMETHING// with the length of each string? Thanks for reading my amateur post.

Nam Hoang
  • 39
  • 5
  • 1
    The loop condition `i <= n` will lead to you using `n` as an index, which will be out of bounds. For an array of `n` elements, what is the highest valid index? – Some programmer dude Jun 04 '20 at 09:14
  • 1
    OOPS my bad then, it's supposed to be ``i – Nam Hoang Jun 04 '20 at 09:17
  • 1
    On another note: ***Never ever*** use `gets`. It's an old and [dangerous](https://stackoverflow.com/questions/1694036/why-is-the-gets-function-so-dangerous-that-it-should-not-be-used) function that have even been removed from the C standard. Use e.g. [`fgets`](https://en.cppreference.com/w/c/io/fgets) instead. – Some programmer dude Jun 04 '20 at 09:23
  • A string cannot be really "as long we want". You need to choose: either there is a maximum size harcoded in your program OR you ask the user what is the maximum size for each string. – pifor Jun 04 '20 at 09:28

2 Answers2

1

If you don't want to waste space and only allocate enough memory to fit the string, then you need to read character by character until you read the newline, reallocating (using realloc) as needed.

Perhaps something like this:

for (i = 0; i < n; ++i)
{
    size_t current_size = 1;  // Start with space for one character, the null-terminator
    str[i] = malloc(current_size);
    str[i][0] = '\0';  // String null-terminator

    int ch;  // Must be an int for the EOF check to work

    // Read characters one by one, until eof (or error) or newline
    while ((ch = getchar()) != EOF && ch != '\n')
    {
        char *temp_str = realloc(str[i], current_size + 1);  // Increase memory by one character
        if (temp_str == NULL)
        {
            // Error allocating memory, do something useful here
            break;
        }

        str[i] = temp_str;
        str[i][current_size - 1] = ch;  // Add the newly read character
        str[i][current_size] = '\0';  // Add the new null-terminator
        ++current_size;  // Increase the size
    }
}
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
1

You can't magically 'predict' how long a string will be before a user enters it! However, you can assign a 'working buffer' of a given maximum length, read into that, then assign the required memory and copy that string.

You could use a combination of malloc, strlen and strcpy to do this, but the strdup function will do it all for you in one fell swoop:

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

#define MAXLENGTH 5000

int main()
{
    int i, n;
    char** str;
    char buffer[MAXLENGTH]; // Working buffer to read in strings

    printf("Enter n (strings of chracters): ");
    scanf("%d", &n);

    str = malloc(n * sizeof(char*));

    printf("Enter strings one by one:\n");
    for (i = 0; i < n; i++) {
        fgets(buffer, MAXLENGTH, stdin);
        // You probably want to remove the trailing newline from the string:
        if (buffer[strlen(buffer) - 1] == '\n') buffer[strlen(buffer) - 1] = '\0';
        str[i] = strdup(buffer); // Will only allocate enough memory - not 5000 each time.
    }

    // Show the results (to verify operation) ...
    for (i = 0; i < n; i++) printf("%s\n", str[i]);
    // And don't forget to free the allocated buffers:
    for (i = 0; i < n; i++) free(str[i]);
    free(str);

    return 0;
}

Also, as mentioned in the comments, the gets function has been removed from the C library: use fgets() instead (as shown). See: Why is the gets function so dangerous that it should not be used?.

Also, you don't need to cast the result of malloc: Do I cast the result of malloc?.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83