Home > Software design >  How to create dynamically an array of strings
How to create dynamically an array of strings

Time:07-28

I need to create a dynamic array of strings of same length. User first enters the string length, and then inputs as many strings as it wants. This is what I've done so far:

printf("Insert string length parameter:\n");
if (scanf("%d", &k) == 0) {
    printf("Error in parameter read!");
    exit(EXIT_FAILURE);
}
char buffer[k 1];
char **dictionary = malloc(sizeof(char*)); //how to allocate enough pointers to str when n° of strs is unkn?

printf("Insert string sequence (allowed chars: a-Z, - e _):\n");
scanf("%s", buffer);
while(strcmp(buffer, " exit") != 0) {
    if ((dictionary[words] = malloc(k * sizeof(char)   1)) == NULL) {
        printf("Memory allocation error!");
        exit(EXIT_FAILURE);
    }
    strcpy(dictionary[words], buffer);
    words  ;
    scanf("%s", buffer);
}
for (i = 0; i < words; i  )
    printf("%s\n", dictionary[i]);

My idea is to create an array of pointers to each string. First user inputs the string, then I allocate memory for it and lastly I copy the string from the buffer to the allocated memory. The issue is that the memory behaves weirdly, I can for example set k to 3 and input a string 10 chars long and it would still work. However, if I enter some inputs of the correct length, the first ones get corrupted or something. For example, with k = 3, the series of inputs:

abc
def
ghi
jkl
mno
pqr
 exit

results in:

­¶F☼
☻
def
ghi
jkl
mno
pqr

Any suggestion on why this happens? Thanks a lot to whoever helps me, it's really appreciated!

CodePudding user response:

As you have correctly noted in your code There is no way to allocate enough space for the string pointers if number of strings is not previously known.

You kind of have 4 options in this scenario

  1. Guess a max number of strings your user could input

    Are there other constraints that limit how many strings your user can actually input? If so you can simply allocate the maximum amount of buffer required by doing something like

    int MAX_STR = 100; // Could also be a #define somewhere
    char **dictionary = (char**) malloc(MAX_STR*sizeof(char*));
    

    This is kind of overkill and uses more memory than it needs. But in most modern systems where memory is not a tight constraint, this should be completely fine. And may even by more performant than using realloc

  2. Make the user tell you beforehand

    This is kind of an obvious one. You add one more step to the UX and make the user tell you explicitly how much space you need. Then you can allocate accordingly

    int num_str;
    printf("Enter number of strings: ");
    scanf("%d", &num_str); // Do the due diligence of checking if it is number
    char **dictionary = (char**) malloc(num_str*sizeof(char*));
    

    Of course this solution may not always be viable. For example if the 'user' themselves don't know the number.

  3. Reallocate on a need basis

    As suggested in the comments, you can use realloc() to reallocate memory as you need

    It would go something like this:

    int num_str = 0;
    char **dictionary = NULL;   // Don't initialize yet.
    while(strcmp(buffer, " exit") != 0) {
        num_str  = 1;
        dictionary = (char**) realloc(dictionary, num_str*sizeof(char*));
        ...
    }
    

    You can find more information about the realloc() function here (as mentioned in the comments)

    Lucrative as this method may sound, this kind of memory reallocation can be quite resource intensive on a system as everything may have to be copied over to the new location etc.

  4. Use a different data structure

    You can use something like a linked list to store list of data whose size can grow or shrink on demand. See this for more info (or practically any other online guide)

CodePudding user response:

char **dictionary = malloc(sizeof(char*)); only allocates memory for 1 pointer. Code needs a new approach to form the array of strings.

@Parth K outlines some alternatives.

Another is recursion. Use the stack to store the strings until "exit" encountered. Then allocate the array of string pointers.

// Untested illustrative code.
char** get_strings(size_t length_max, size_t count) {
  char buf[length_max   3];  // plus 3 for \n \0 and extra detection
  printf("Insert string sequence (allowed chars: a-Z, - e _):\n");
  if (fgets(buf, sizeof buf, stdin)) {
    size_t len = strlen(buf);
    if (len > 0 && buf[len - 1] == '\n') {
      buf[--len] = '\0';
    }
    if (len > length_max) {
      fprintf(stderr, "Line <%s> too long\n", buf);
      exit(EXIT_FAILURE);
    }
    if (strcmp(buf, " exit")) {
      char **list = get_strings(length_max, count   1);
      if (list == NULL) {
        return NULL;
      }
      list[count] = strdup(buf);
      return list;
    }
  }
  char **list = malloc(sizeof *list * (count   1));
  if (list == NULL) {
    return NULL;
  }
  list[count] = NULL; // Mark last entry as NULL
  return list;
}

Usage

void foo() {
  char buf[40];
  printf("Insert string length parameter:\n");
  size_t k;
  if (fgets(buf, sizeof buf, stdin) && sscanf(buf, "%zu", &k) == 1) {
    char **list = get_strings(k, 0);
    if (list) {
      for (size_t i = 0; list[i]; i  ) {
        printf("<%s>\n", list[i]);
        free(list[i]);
      }
      free(list);
    }
  }
}
  • Tip do not use scanf() until you understand why it is bad.

GTG

  • Related