0

I have a large dictionary of null terminated strings which will be declared in main as char dictionary[MAX_WORDS][MAX_WORD_LENGTH], where max_words can be 200000 and max word length can be 50. I want to malloc some space for it with another function like:

char ** AllocateMemory (char dictionary[MAX_WORDS][MAX_WORD_LENGTH])
{
    char **p;
    {
        p = (char **)malloc(sizeof(dictionary));
    }
    return p;
}

which will be called in main like

int main (void)
{
char dictionary[MAX_WORDS][MAX_WORD_LENGTH];

dictionary = AllocateMemory(dictionary);
}

Can I access this memory in the normal way? (like another function which loops through words in the dictionary like for (i = 0; dictionary[i][0]; i++), then the letters in each word). Also I may be missing something here but if I malloc space up for it, I have already created a large amount of space in main by having to declare the dictionary anyway? Please correct me I'm sure im just a bit confused here with malloc.

Finlandia_C
  • 385
  • 6
  • 19

3 Answers3

3

You have multiple problems. The first is that you in main you are declaring dictionary to be an array or arrays which means it's already allocated (the compiler allocates memory for the arrays for you), meaning your assignment is wrong.

Another problem is that the array is probably to large, since most compilers on most systems allocate local variables (including arrays) on the stack, and the stack space is limited. You array of arrays declaration would allocate 200000 * 50 bytes, which is almost 10 MB, far more than most default process stack sizes (on Windows the default stack size if only a single MB).

When you fix the above problems, and make dictionary a pointer to a pointer to char (what AllocateMemory returns) then you have a few other problems. The first is that AllocateMemory allocate the wrong size, and the other is that an array of arrays are not the same as a pointer to a pointer (see this old answer of mine for an explanation). Also, in C you should not cast the result of malloc, or any function returning void *.


A "correct" version of your program would look something like this:

char ** AllocateMemory (void)
{
    char **p = malloc(MAX_WORDS * sizeof(*p));
    for (size_t i = 0; i < MAX_WORDS; ++i)
        p[i] = malloc(MAX_WORD_LENGTH + 1);  // +1 for string terminator

    return p;
}

int main (void)
{
    char **dictionary = AllocateMemory();

    // Not you can use `dictionary` as an array of arrays of characters,
    // or an array of strings
}
Community
  • 1
  • 1
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Ahh I see. That makes more sense. So accessing the char dictionary** is the same as accessing dictionary[][]? Is it worth allocating space for something small like a single string? If so, I'd need another function to do that where the malloc line would be `char *p = malloc(MAX_WORDS * sizeof(p+1));`? and then in main `char *string = AllocateMemory();` – Finlandia_C Nov 17 '15 at 08:54
  • Why complicate the data structure? and now a bunch of pointers need to be passed to free() and the posted answer is missing the checking of the returned value from each call to malloc() to assure the operation was successful. – user3629249 Nov 17 '15 at 09:08
  • I gave this the best answer as It mostly cleanly explained what was wrong, and how to fix it. – Finlandia_C Nov 17 '15 at 09:12
  • 1
    @user3629249 That is not a "complicated" data structure, and it's very common when writing C code. And yes it doesn't do any error checking, because that would clutter up the code and make it harder to read. – Some programmer dude Nov 17 '15 at 09:19
  • @JoachimPileborg Do you mean one **should not** cast `void *` in C? The answer at the link you mentioned explicitly mentions that `"you don't cast", not "you don't need to cast"` i.e. the answer gives reasons for not casting which doesn't mean we shouldn't cast. Explicit casting of `void *` as made mandatory in c++, helps avoid errors due to implicit type conversion. Even for C compilers you may enable such a warning, e.g. `-Wc++-compat` in gcc. – user1969104 Nov 17 '15 at 09:22
  • @JoachimPileborg I expected this reply. I will include `` to fix this. In my opinion, it will be better to advise to get the compiler to give warnings for already researched issues, rather than advising not to typecast `void *`. For this case you will need compiler to warn on implicit conversion `-Wimplicit-function-declaration`, which is one of the more serious source of issues in code in general. – user1969104 Nov 17 '15 at 09:32
  • I agree that the posted answer is not a 'complicated' data structure, however it is many times more complicated than it needs to be. I have seen this same (parameters) question several times before on stackoverflow. That indicates the OP is a student in C language. Therefore, the answer should be as simple as possible while answering the question, without skipping good programming practice (like checking for errors). Sort of 'lead by example' so it is best to not skip error checking in the answer. – user3629249 Nov 17 '15 at 10:05
  • @user1969104, Down the road, when your working at a job, you will find that explicit type casting of a `void*` to some other type makes debug harder and makes it a real headache/hair puller when someone that is not familiar with your code has to 'maintain' that code. Best to get into good habits early in your career – user3629249 Nov 17 '15 at 10:10
2

AllocateMemory can return a pointer of type char (*)[MAX_WORD_LENGTH] as shown below (typedef for simpler understanding)

#include <stdlib.h>

typedef char word[MAX_WORD_LENGTH];
word * AllocateMemory()
{
    word *p;
    {
        p = (word *)malloc(MAX_WORDS*sizeof(word));
    }
    return p;
}

int main (void)
{
    word * dictionary;
    dictionary = AllocateMemory();
    /* Access using dictionary[x][y] */
    return 0;
}
user1969104
  • 2,340
  • 14
  • 15
  • 1
    There is no need to cast `malloc` in C, [there are several reasons why](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) and there is no reason for me to argue with you about this, but I saw that you aren't agree with this and this makes me to ask you, to ask a question if there is something which makes you to `cast` `malloc` in `C` like it should. **Please stick to the C language**. Here is an Explenation of [what could happens if you do that](http://stackoverflow.com/questions/7545365/why-does-this-code-segfault-on-64-bit-architecture-but-work-fine-on-32-bit). – Michi Nov 17 '15 at 09:56
  • I am not talking with respect to a specific language and also not with respect to `malloc`, but with respect to generic coding practices. C++ has been made more type safe because of finding such issues in C which allow implicit conversion from `void *`, which is a type unsafe operation. Still we need `void *` in C because it is the only way to represent generic pointer. So instead of advising type unsafe operation, it is better to ask the compiler to warn about such a conversion and explicitly cast which we are sure is type-safe. – user1969104 Nov 17 '15 at 10:09
  • I wasn't talking about other `Languages`, please stick to the `C language`. If you ask your compiler about that, this means you are using a wrong compiler and Language. I repeat, please stick to the language which is `C` in the question. – Michi Nov 17 '15 at 10:12
  • We cannot have languages intermediate between C and C++. People either code in C or C++. It is not a good coding advice to generically say that "should not cast in C" always implying "pure C". – user1969104 Nov 17 '15 at 10:20
  • Please [ask a question](http://stackoverflow.com/questions/ask) if there are some things which you do not understand/accept. I give up. Have a nice Day. – Michi Nov 17 '15 at 10:23
  • @michi I will take it as my win in the argument as you give up :-). There is nothing to ask in this regard as you have already asked [Why does the book say I must cast malloc?](http://stackoverflow.com/questions/32652213/why-does-the-book-say-i-must-cast-malloc). I still repeat what I said "I cast malloc in **C** so that I can ask the compiler to emit warnings for any other type unsafe conversion from `void *` in other parts of code, allowing me to find common coding errors". Thanks. Have a nice day. – user1969104 Nov 17 '15 at 12:36
  • There was about what the book said and not about casting malloc, whatever you understand is your problem :) – Michi Nov 17 '15 at 12:40
2

there are several problems with the posted code.

Rather than trying to enumerate them all, I have provided a possible solution to your question.

struct dictionary
{
    char words[MAX_WORDS][ MAX_WORD_LENGTH];
};

struct dictionary *AllocateMemory ()
{
    char *p = NULL;

    if( NULL == (p = malloc( sizeof( struct dictionary ) ) ) )
    { // then malloc failed
        perror( "malloc for dictionary failed ");
        exit( EXIT_FAILURE );
    }

    // implied else, malloc successful

    return p;
}  // end function: AllocateMemory


which will be called in main like

int main (void)
{
    struct dictionary * pDictionary = AllocateMemory();

    free( pDictionary);
    return 0;
}  // end function: main
user3629249
  • 16,402
  • 1
  • 16
  • 17