1

I am working on a vocabulary training program where you can add, practice, view and change your vocabulary. For each word in english (max 50 characters), I want to have a swedish translation/definition (max 999 characters) and a few example sentences (max 10, each containing max 999 characters). For this I used a structure named Word, and since I want more than one word I made an array of type Word with a size of 100 (max 100 words). This worked fine until I created a function in which I want to declare a new temporary array of type Word (because I will randomize the order and I do not want the first array to be affected). I set the size to MAXSIZE (100) but then entire program terminated. After some experimenting I found that the program works when I set the size of the temporary array to 92, but does not work if I set it to 93. Why is this? Does it have to do with my array taking up too much memory? I am quite new to c programming and hence a simple explanation would be great. Code is below: Thank you.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAXWORDS 92
#define MAXEWORD 50
#define MAXSENTENCE 999
#define MAXNR 10


typedef struct {
    char eWord[MAXEWORD];
    char sWord[MAXSENTENCE];
    char sentence[MAXNR][MAXSENTENCE];
} Word;

void printEnglishWords(Word theWords[], int thePos) {
    for(int i=0; i<thePos; i++) {
        printf("%s, ", theWords[i].eWord);
    }
}

void shuffleWords(Word theWords[], int thePos) {
    Word wordTemplate[MAXWORDS];
    Word hold;

    for(int i=0; i<thePos; i++) {
        wordTemplate[i]=theWords[i];
    }


    for(int i=0; i<thePos; i++) {
        int index=rand()%thePos;
        hold=wordTemplate[i];
        wordTemplate[i]=wordTemplate[index];
        wordTemplate[index]=hold;
    }

    printf("\n");
    printEnglishWords(wordTemplate, thePos);  

}

int main(void) {

    srand((unsigned) time(NULL));
    Word myWords[MAXWORDS];
    int pos;
    
    myWords[0]=(Word){"Dog", "Hund", {"I own a dog"}};
    myWords[1]=(Word){"Cat", "Katt", {"I do not own a cat"}};
    myWords[2]=(Word){"Food", "Mat", {"I am hungry and I need some food"}};
    pos=3;

    printEnglishWords(myWords, pos);    //output is Dog, Cat, Food, 

    shuffleWords(myWords, pos);         //output is Food, Dog, Cat, 


   return 0;

}

When MAXWORDS is 92 the output is written above. However, when MAXWORDS is 93 only "Dog, Cat, Food, " is written, and the shuffleWords function does not work.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • What is the need to declare the local array Word wordTemplate[88];? Provide a minimal complete program that demonstrates the problem. – Vlad from Moscow Jun 10 '23 at 13:06
  • Now I have added some lines that shuffle the words without affecting the original array (theWords) – Alexander Jonsson Jun 10 '23 at 13:17
  • Can you turn the uncompileable not-exactly-pseudo-code into a [mre] please? But as a quick shot into the dark, if shrinking an array seems to fix something then my bet is on "better hidden undefined behaviour". Please try to provide proof that you never use any index beyond any array and other widely spread favorite mistakes. – Yunnosch Jun 10 '23 at 13:29
  • 2
    Stack has limited size. Declaring a huge automatic variable caused Stack Overflow, so I guess you came to the right site. – stark Jun 10 '23 at 13:36
  • Now i created a minimal example of my issue! Hope this makes it clearer! – Alexander Jonsson Jun 10 '23 at 14:27

2 Answers2

3

The size of the structure Word is at least

MAXEWORD + MAXSENTENCE + MAXNR * MAXSENTENCE

which is 11039 bytes. myWords[MAXWORDS] is then 920129 bytes, and instantiated as a local variable, which in a normal C execution environment will be instantiated on the stack. The stack size will depend on the execution environment, operating system or linker options provided at build time, but it is finite and in your case probably too small. This would be a "stack overflow" after which this site is named.

In this case you might simply declare it static:

static Word myWords[MAXWORDS];

In terms of lifetime and visibility, there is little difference between a static and a non-static when it is declared in main(), but in other cases it may not be an applicable solution because if the semantics of static.

shuffleWords() adds a further 1026627 bytes, I suspect that is when the stack overflows. That too could be static in this case.

You need to understand that there are general three kinds of memory allocation in C:

  • Stack - Temporary usage for local variables, function parameters and return values. Released automatically in function return
  • Static - allocated by the linker at build time and created and initialised when your program starts, and released when it terminated.
  • Dynamic - created and released "on demand" from a system heap by your code.

In both cases however static allocation while simple is wasteful. In this case you allocated 92 elements, but used only three. That is where dynamic allocation may be useful, to allocate only what you need, when that requirement is not known until runtime.

Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
Clifford
  • 88,407
  • 13
  • 85
  • 165
  • If I would have used two separate arrays, each with a size of 47, would my problem still occur? – Alexander Jonsson Jun 10 '23 at 15:24
  • Yes, of course. The stack usage is (at least) the sum of all local variables, including all local variables of a calling function, right up the entire call stack. For each function call, the return address will also normally be on the stack. You get one system stack, not one per function, or per variable. In multi-threaded applications, you would have on stack per thread however. Further a debugger may add padding or guard values to the stack - especially in debug builds. – Clifford Jun 10 '23 at 18:12
1

It is difficult to say without more Info, but if i count correctly, your array is approaching 1MB here. That is a lot of memory to allocate at once. This "might" be the problem. But stuff like this is very architecture and system dependent.

C misses a lot of the ease-of-use functionalities that for example Java provides. C will not allocate memory on the heap if you do not tell it to do so.

Try to place the array on the heap. Import stdlib.h and allocate dedicated memory for your array.

Instead of:

Word myWords[MAXWORDS];

It would look something like:

Word *myWords;

myWords = malloc(MAXWORDS * sizeof(Word));
 

You also can change the shuffleWords() function to:

void shuffleWords(Word *theWords, int thePos) {
    ...

}

Now wordtemplate is a pointer to your heap-memory. wordtemplate[0] would get you the first element and wordtemplate[0].eWord[0] your array inside the struct. You could also use a pointer-array and loop to allocate memory for every struct separately.

There is a lot of good sources out there that show you how to malloc if you google for it. Or right here on stackoverflow. It is good practice to also free memory you allocated once you do not need it anymore. I do not think it is necessary in your case. But i highly recommend that you make yourself familiar with all this, if you want to program more stuff in C.


Edit: To split up your allocated memory you can try something like the following.

Word *myWords[MAXWORDS];
for(i = 0; i < MAXWORDS; i++)
  myWords[i] = malloc(sizeof(Word));

This will change how you access the array though. You can even place the pointer array in the heap. But I guess you probably want to keep it simple.

led
  • 56
  • 4
  • It worked but now I experimented some more and now it will not work if MAXWORDS is larger than 184? – Alexander Jonsson Jun 10 '23 at 15:49
  • About your changed comment: This is a little hard to answer in short to be honest. Allocating too much memory at once will always fail sooner or later. Check the last section of my answer. You can allocate large junks of memory by creating a pointer-array. See here: https://stackoverflow.com/questions/455960/dynamic-allocating-array-of-arrays-in-c – led Jun 10 '23 at 16:04