2

At the beginning of a program I need to dynamically allocate memory for an unknown number of strings with unknown number of size to later manipulate with. To get the number of strings from a user I have:

int main(int argc, char *argv[]){

int number = atoi(argv[1]);

So far so good. "number" now holds holds the number that the user inputted on the command line for executing the code. Now here comes the part I don't quite understand. I now need to dynamically store the lengths of the strings as well as the contents of the strings. For example, I want the program to function like this:

Enter the length of string 1: 5
Please enter string 1: hello
Enter the length of string 2: ...

For this I recognize that I will have to create an array of strings. However, I can't quite understand the concept of pointers to pointers and what not. What I would like is perhaps a simplification of how this gets accomplished?

6
  • What do you exactly need in your program? Commented Apr 30, 2018 at 4:43
  • @AsthaSrivastava I would like these strings to later compare lengths, concatenate them, copy them, etc. but I'm familiar with those things. I just don't know how to dynamically allocate them if there are an unknown number with unknown lengths. Commented Apr 30, 2018 at 4:46
  • 1
    Enter the length of string 1: 5 then Please enter string 1: hello Be Careful, while you can store 5 characters 'h''e''l''l''o', in an array [5] char, it will NOT be a string of characters. (there is no nul-terminating character at the end -- which would require 6-characters of storage) Any attempt to use the array containing "hello" as a string will result in Undefined Behavior as the string-function madly searches all through your stack looking for a nul-character... Commented Apr 30, 2018 at 6:23
  • 1
    Also, don't use atoi(argv[1]) -- it provides Zero error checking capabilities (what if argv[1] = "duck"?. Instead use strtol which provides solid error handling capabilities. Commented Apr 30, 2018 at 6:25
  • the first thing the program should be doing is testing argc to assure that the user actually entered a parameter. If no parameter entered, the output a USAGE statement (fprintf( stderr, (USAGE: %s [numberOfStrings]\n", argv[0] );` then call exit( EXIT_FAILURE ) where exit() and EXIT_FAILURE are found in stdlib.h Commented Apr 30, 2018 at 20:25

2 Answers 2

8

You know from the start you will have number strings to store so you will need an array of size number to store a pointer to each string.

You can use malloc to dynamically allocate enough memory for number char pointers:

char** strings = malloc(number * sizeof(char*));

Now you can loop number times and allocate each string dynamically:

for (int i = 0; i < number; i++) {
   // Get length of string
   printf("Enter the length of string %d: ", i);
   int length = 0;
   scanf("%d", &length);

   // Clear stdin for next input
   int c = getchar(); while (c != '\n' && c != EOF) c = getchar();

   // Allocate "length" characters and read in string
   printf("Please enter string %d: ", i);
   strings[i] = malloc(length * sizeof(char));
   fgets(strings[i], length, stdin);
}
Sign up to request clarification or add additional context in comments.

9 Comments

I can follow most of this. Just to clarify: a char** would be a pointer to a pointer to char? And as for fgets I am unfamiliar with that function but I can research that. Thank you.
You are right about char**, it is a pointer to a character pointer. In this case, if char* is a string, char** is essentially a dynamically allocated array if strings.
And you can read about fgets here
Haha a pointer to a pointer bends my brain a little bit, but thank you for the answer and for where I can learn about new functions.
Unless you have redirected a file holding 10 numbers as input fseek(stdin,0,SEEK_END); will invoke Undefined Behavior as stdin is not seekable unless the input is provided by file-redirection. (e.g. a seekable file) To clear stdin to end of line, just use int c = getchar(); while (c != '\n' && c != EOF) c = getchar();
|
1

Since you want to save both the length and the string, I'll suggest that you put them together in a struct. Like

struct string
{
    int length;
    char* str;
};

Now you can dynamically create an array of this struct and dynamically assign memory for the individual strings.

Something like:

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

struct string
{
    int length;
    char* str;
};

int main(void) {
    int i;
    char tmp[128];
    int number = 3;
    struct string* strings = malloc(number * sizeof *strings);

    // read the input
    for (i=0; i<number; ++i)
    {
        printf("length?\n");
        if (fgets(tmp, sizeof tmp, stdin) == NULL)
        {
            printf("error 1");
            exit(1);
        }
        int length;
        if (sscanf(tmp, "%d", &length) != 1)
        {
            printf("error 2");
            exit(1);
        }
        strings[i].length = length;
        strings[i].str = calloc(length + 2, 1);
        printf("string?\n");
        if (fgets(strings[i].str, length + 2, stdin) == NULL)
        {
            printf("error 3");
            exit(1);
        }
        if (strings[i].str[length] != '\n')
        {
            printf("error 4");
            exit(1);
        }
        strings[i].str[length] = '\0';
    }

    // print the strings
    for (i=0; i<number; ++i)
    {
      printf("len=%d str=%s\n", strings[i].length, strings[i].str);
    }

    // Clean up, i.e. free the memory allocated
    for (i=0; i<number; ++i)
    {
        free(strings[i].str);
    }
    free(strings);

    return 0;
}

Note: You should also check that all malloc/calloc are succesful, i.e. doesn't return NULL. For clarity I skipped that.

1 Comment

By reading the exact number of characters exactly with fgets(strings[i].str, length + 1, stdin) fgets will use the +1 as storage for the nul-character and you will leave each '\n' (generated by the user pressing [Enter]) unread in stdin, this must be removed before your next call to fgets to avoid reading the unread '\n' as your next input.

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.