1

Any hints on how I would dynamically allocate myArray so I can enter any amount of strings and it would store correctly.

int main()
{

char myArray[1][1]; //how to dynamically allocate the memory?
counter = 0;
char *readLine;
char *word;
char *rest;

printf("\n enter: ");
ssize_t buffSize = 0;
getline(&readLine, &buffSize, stdin);//get user input 

//tokenize the strings
while(word = strtok_r(readLine, " \n", &rest )) {

            strcpy(myArray[counter], word);
            counter++;
            readLine= rest;
}

//print the elements user has entered 
int i =0;
for(i = 0;i<counter;i++){
    printf("%s ",myArray[i]);
}
printf("\n");

}
Dancrumb
  • 26,597
  • 10
  • 74
  • 130
cmptUser
  • 51
  • 1
  • 10
  • `getline()` is not a Standard C function, though I believe it is Posix. Better to use `fgets()` for portability. – ad absurdum Feb 03 '17 at 17:27
  • ill keep that in mind thanks – cmptUser Feb 03 '17 at 17:29
  • If portability to non-POSIX machines is important, use [`fgets()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fgets.html) but POSIX does define [`getline()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html) and it is very useful as long as you remember to release the memory it allocates. – Jonathan Leffler Feb 04 '17 at 00:35

3 Answers3

2

Use realloc like this:

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

int main(void){
    char **myArray = NULL;
    char *readLine = NULL;
    size_t buffSize = 0;
    size_t counter = 0;
    char *word, *rest, *p;

    printf("\n enter: ");
    getline(&readLine, &buffSize, stdin);
    p = readLine;
    while(word = strtok_r(p, " \n", &rest )) {
        myArray = realloc(myArray, (counter + 1) * sizeof(*myArray));//check omitted
        myArray[counter++] = strdup(word);
        p = NULL;
    }
    free(readLine);
    for(int i = 0; i < counter; i++){
        printf("<%s> ", myArray[i]);
        free(myArray[i]);
    }
    printf("\n");
    free(myArray);
}
BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
1

Here is one way you might approach this problem. If you are going to dynamically allocate storage for an unknown number of words of unknown length, you can start with a buffSize that seems reasonable, allocate that much space for the readLine buffer, and grow this memory as needed. Similarly, you can choose a reasonable size for the number of words expected, and grow word storage as needed.

In the program below, myArray is a pointer to pointer to char. arrSize is initialized so that pointers to 100 words may be stored in myArray. First, readLine is filled with an input line. If more space than provided by the initial allocation is required, the memory is realloced to be twice as large. After reading in the line, the memory is again realloced to trim it to the size of the line (including space for the '\0').

strtok_r() breaks the line into tokens. The pointer store is used to hold the address of the memory allocated to hold the word, and then word is copied into this memory using strcpy(). If more space is needed to store words, the memory pointed to by myArray is realloced and doubled in size. After all words have been stored, myArray is realloced a final time to trim it to its minimum size.

When doing this much allocation, it is nice to write functions which allocate memory and check for errors, so that you don't have to do this manually every allocation. xmalloc() takes a size_t argument and an error message string. If an allocation error occurs, the message is printed to stderr and the program exits. Otherwise, a pointer to the allocated memory is returned. Similarly, xrealloc() takes a pointer to the memory to be reallocated, a size_t argument, and an error message string. Note here that realloc() can return a NULL pointer if there is an allocation error, so you need to assign the return value to a temporary pointer to avoid a memory leak. Moving realloc() into a separate function helps protect you from this issue. If you assigned the return value of realloc() directly to readLine, for example, and if there were an allocation error, readLine would no longer point to the previously allocated memory, which would be lost. This function prints the error message and exits if there is an error.

Also, you need to free all of these memory allocations, so this is done before the program exits.

This method is more efficient than reallocing memory for every added character in the line, and for every added pointer to a word in myArray. With generous starting values for buffSize and arrSize, you may only need the initial allocations, which are then trimmed to final size. Of course, there are still the individual allocations for each of the individual words. You could also use strdup() for this part, but you would still need to remember to free those allocations as well.Still, not nearly as many allocations will be needed as when readLine and myArray are grown one char or one pointer at a time.

#define _POSIX_C_SOURCE 1

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

void * xmalloc(size_t size, char *msg);
void * xrealloc(void *ptr, size_t size, char *msg);

int main(void)
{

    char **myArray;
    size_t buffSize = 1000;
    size_t arrSize = 100;
    size_t charIndex = 0;
    size_t wordIndex = 0;
    char *readLine;
    char *inLine;
    char *word;
    char *rest;
    char *store;

    /* Initial allocations */
    readLine = xmalloc(buffSize, "Allocation error: readLine");
    myArray = xmalloc(sizeof(*myArray) * arrSize,
                      "Allocation error: myArray\n");

    /* Get user input */    
    printf("\n enter a line of input:\n");

    int c;
    while ((c = getchar()) != '\n' && c != EOF) {
        if (charIndex + 1 >= buffSize) {      // keep room for '\0'
            buffSize *= 2;
            readLine = xrealloc(readLine, buffSize,
                                "Error in readLine realloc()\n");
        }
        readLine[charIndex++] = c;
    }
    readLine[charIndex] = '\0';             // add '\0' terminator

    /* If you must, trim the allocation now */
    readLine = xrealloc(readLine, strlen(readLine) + 1,
                       "Error in readLine trim\n");

    /* Tokenize readLine */
    inLine = readLine;
    while((word = strtok_r(inLine, " \n", &rest)) != NULL) {
        store = xmalloc(strlen(word) + 1, "Error in word allocation\n");
        strcpy(store, word);

        if (wordIndex >= arrSize) {
            arrSize *= 2;
            myArray = xrealloc(myArray, sizeof(*myArray) * arrSize,
                               "Error in myArray realloc()\n");
        }   
        myArray[wordIndex] = store;
        wordIndex++;
        inLine = NULL;
    }

    /* You can trim this allocation, too */
    myArray = xrealloc(myArray, sizeof(*myArray) * wordIndex,
                      "Error in myArray trim\n");

    /* Print words */
    for(size_t i = 0; i < wordIndex; i++){
        printf("%s ",myArray[i]);
    }
    printf("\n");

    /* Free allocated memory */
    for (size_t i = 0; i < wordIndex; i++) {
        free(myArray[i]);
    }
    free(myArray);
    free(readLine);

    return 0;
}

void * xmalloc(size_t size, char *msg)
{
    void *temp = malloc(size);
    if (temp == NULL) {
        fprintf(stderr, "%s\n", msg);
        exit(EXIT_FAILURE);
    }
    return temp;
}

void * xrealloc(void *ptr, size_t size, char *msg)
{
    void *temp = realloc(ptr, size);
    if (temp == NULL) {
        fprintf(stderr, "%s\n", msg);
        exit(EXIT_FAILURE);
    }
    return temp;
}
ad absurdum
  • 19,498
  • 5
  • 37
  • 60
  • very good and complete answer. I would like to add (for completeness) that it's recommended to cast the result to the variable type after doing memory allocation. So; I would add this. store = (char *) xmalloc(...) – J. Laderoute Feb 04 '17 at 02:30
  • 1
    @J.Laderoute-- In C++ you must cast, but it is not required in C. In fact many feel that it is best to avoid casting the result of `malloc()` and friends. [You can read more about this here](http://stackoverflow.com/questions/953112/should-i-explicitly-cast-mallocs-return-value). Some are vehement on this point. I personally prefer not to cast, but to each his own :) Note the use of identifers instead of explicit types, e.g., `sizeof(*myArray)` instead of `sizeof(char *)` with `sizeof` operator. I consider this more important, as it avoids errors and is more maintainable if types change. – ad absurdum Feb 04 '17 at 02:38
  • 1
    `inLine = rest;` : It depends on implementation. It should be `inLine = NULL;` – BLUEPIXY Feb 04 '17 at 02:56
  • thanks for taking the time to respond with such detail – cmptUser Feb 04 '17 at 02:56
  • @BLUEPIXY-- thanks for pointing this out; I am not used to the reentrant version and was wondering about this. Will fix.... – ad absurdum Feb 04 '17 at 02:59
  • @cmptUser-- sure. You might look at [BLUEPIXY's answer](http://stackoverflow.com/a/42033900/6879826) too. It is more succinct, and demonstrates the use of `strdup()` which would save you some potential headaches. Note that `getline()` (as I mentioned earlier), `strtok_r()`, and `strdup()` are all POSIX, but not Standard C. Still they are exdeedingly common and useful. To use `strtok_r()`, since you already had it, I added the `_POSIX_C_SOURCE` feature test macro to enable this. – ad absurdum Feb 04 '17 at 03:10
0

I suggest you first scan the data and then call malloc() with the appropriate size. Otherwise, you can use realloc() to reallocate memory as you go through the data.

Daniel
  • 144
  • 1
  • 9
  • how do i scan data? im new to c – cmptUser Feb 03 '17 at 17:28
  • Please refer to [this answer](http://stackoverflow.com/questions/2496668/how-to-read-the-standard-input-into-string-variable-until-eof-in-c) for a more detailed explanation. – Daniel Feb 03 '17 at 17:46