0

I prefer to create a Dictionary object and add 3 words to it. My program has no compilation error but gets a run time error in the second for loop, is the problem in addNewWord function? Do I need pass a pointer to the DictionaryWord object ?

Please help me.

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

typedef struct{
    char* name;
    char* mean;
} Words;

typedef struct{
    Words* word;
    int size;
} Dictionary;

Dictionary createNewDictionary();
Words createNewWord();
void addNewWord(Words newword, Dictionary dic);

Dictionary createNewDictionary(){
    Dictionary dic;
    dic.size = 0;
    dic.word = (Words*)malloc(dic.size*sizeof(Words));
    return dic;
}

Words createNewWord(){
    Words newword;
    newword.name = (char*)malloc(30*sizeof(char));
    newword.mean = (char*)malloc(30*sizeof(char));
    printf("============================\n");
    printf("Enter word: ");
    scanf("%[^\n]", newword.name);
    fflush(stdin);
    printf("\nEnter meaning: ");
    scanf("%[^\n]", newword.mean); 
    return newword;
}

void addNewWord(Words newword, Dictionary dic){
    dic.size++;
    dic.word = (Words*)realloc(dic.word,dic.size*sizeof(Words));
    strcpy(dic.word[dic.size-1].name, newword.name);
    strcpy(dic.word[dic.size-1].mean, newword.mean);
}

int main(){
    Dictionary d = createNewDictionary();
    for (int i=0;i<3;i++){
        addNewWord(createNewWord(), d);
    }
    return 0;
}
cdlane
  • 40,441
  • 5
  • 32
  • 81
Applepine
  • 33
  • 7
  • What's the run time error? Where is the "second" for loop? I can only see one. – dave Nov 28 '16 at 03:11
  • All function parameters are passed by value in C. That means you are passing a *copy* of `d` to `addNewWord`. Thus all the modifications to `dic` in that function do not affect the caller's original `d` variable. The result is that the same `d.word` is being freed multiple times (hence the error on the second loop). – kaylum Nov 28 '16 at 03:14
  • 1) Don't cast the result of `malloc` & friends or `void *` in general. 2) `fflush(stdin)` invokes _undefined behaviour_. 3) See [ask]. – too honest for this site Nov 28 '16 at 03:24
  • @kaylum: I used passing by pointer but it has the same problem... – Applepine Nov 28 '16 at 03:30
  • @Olaf: i was wondering if u explain more... – Applepine Nov 28 '16 at 03:31
  • @Applepine: I don't see how to be more clear. Remove the casts and about 2) serach for it! Your program can do anything, including nasal deamons appearing. – too honest for this site Nov 28 '16 at 03:32
  • @Applepine Well then you have something else wrong with your code. Lets fix one problem at a time. What you have shown is certainly wrong for the reason that I have mentioned. So there's no point continuing with it. If you have tried the pointer approach and still get a problem then please show that code - we can't identify what might be wrong with code we can't see. – kaylum Nov 28 '16 at 03:35
  • @kaylum : see the changes suggested by me, the code works perfectly then. – Jarvis Nov 28 '16 at 03:38
  • @Jarvis I doubt it works perfectly. At a minimum the first `malloc` needs to be a `realloc`. – kaylum Nov 28 '16 at 03:39
  • @kaylum : Yeah, thanks for pointing that out, the answer is edited. Check the code at `pastebin` to see for yourself. – Jarvis Nov 28 '16 at 03:41
  • @Olaf: ah sr, because im writing in cpp file so i casted in malloc. – Applepine Nov 28 '16 at 03:53
  • @kaylum: this is the code i fixed before post this ask. `void addNewWord(Words newword, Dictionary* dic){ dic->size++; dic->word = (Words*)realloc(dic->word,dic->size*sizeof(Words)); strcpy(dic->word[dic->size-1].name, newword.name); strcpy(dic->word[dic->size-1].mean, newword.mean); }` Via the answer of Jarvis, i forgot allocating for name and mean of dic – Applepine Nov 28 '16 at 03:56
  • C++ is not C! They are different languages. Use the correct tags! And use the C++ features; writing C-style in C++ is a bad idea. E.g. use `static_cast`, not C-style casts. – too honest for this site Nov 28 '16 at 04:09
  • @Olaf: I just have known it since some mins before, because i am using dev c to compile my program. After this post i will change all my code to C, thanks a lot for your advices – Applepine Nov 28 '16 at 04:16
  • I'm not playing tag wars but since the code presented is pure **C**, the accepted answer is pure **C** and OP states "i will change all my code to C", I'm rolling back the [C++] tag back to [C] and dropping the [dictionary] tag as this post is about *Webster* not "efficient retrieval" as the data is not hashed but stored sequentially. – cdlane Nov 29 '16 at 18:10

2 Answers2

1

There are lots of problem with your code:

Given the longest word in English is around 30 characters, this size allocation is realistic for the word, but not for the defintion:

newword.name = (char*)malloc(30*sizeof(char));
newword.mean = (char*)malloc(30*sizeof(char));

This makes little obvious sense:

dic.size = 0;
dic.word = (Words*)malloc(dic.size*sizeof(Words));

you called malloc() on zero! You're only spared by your later realloc(). Even if intentional, it really deserves a comment.

This doesn't really work as fflush() is for output streams:

fflush(stdin);

see: How to clear input buffer in C? And whatever fix you use has to apply to both scanf() calls, not just one!

Per @Jarvis, this doesn't work:

dic.word = (Words*)realloc(dic.word,dic.size*sizeof(Words));
strcpy(dic.word[dic.size-1].name, newword.name);
strcpy(dic.word[dic.size-1].mean, newword.mean);

as you didn't allocate any space for name and mean in dic so you're copying into random memory.

Per @Jarvis, doesn't work:

void addNewWord(Words newword, Dictionary dic){
    dic.size++;
    dic.word = (Words*)realloc(dic.word,dic.size*sizeof(Words));

You're passing dic by value so inside addnewWord() you've a copy of dic so the original dic's size will be the same as it was before the call!

Memory leak:

addNewWord(createNewWord(), d);

you dropped your handle onto what createNewWord() returned so you can never free the memory it malloc()'d

You malloc() memory but provide no means to eventually free it.

Passing and returning structs by value is a disaster in a situation like this, as the data keeps getting copied. At the least it's inefficient, at worst its buggy like the size issue above. Rather than risk it, pretend they can only be passed and returned by pointer and you'll be playing it safe and get a better result.

Below is a rework of your code (in C) with fixes, style tweaks and an attempt at a consistent terminology. It also provides some minimal test code and the ability to free your data:

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

#define MAX_WORD_LENGTH 30
#define MAX_DEFINITION_LENGTH 1024

typedef struct entry {
    char *word;
    char *definition;
} Entry;

typedef struct dictionary {
    Entry *entries;
    int num_entries, max_entries;
} Dictionary;

Dictionary *createNewDictionary() {
    Dictionary *dictionary = malloc(sizeof(*dictionary));

    dictionary->num_entries = 0;
    dictionary->max_entries = 1;
    dictionary->entries = calloc(dictionary->max_entries, sizeof(*dictionary->entries));

    return dictionary;
}

void freeEntry(Entry *entry) {
    free(entry->word);
    free(entry->definition);
    free(entry);
}

void freeDictionary(Dictionary *dictionary) {
    for (--dictionary->num_entries; dictionary->num_entries >= 0; --dictionary->num_entries) {
        // we can't call freeWord() here -- why.
        free(dictionary->entries[dictionary->num_entries].word);
        free(dictionary->entries[dictionary->num_entries].definition);
    }

    free(dictionary->entries);
    free(dictionary);
}

void purgeInput() {
    int c;

    while ((c = getchar()) != '\n' && c != EOF) { }
}

Entry *requestNewEntry() {
    Entry *entry = malloc(sizeof(*entry));
    entry->word = malloc(MAX_WORD_LENGTH);
    entry->definition = malloc(MAX_DEFINITION_LENGTH);

    printf("============================\n");
    printf("Enter word: ");
    scanf("%[^\n]", entry->word);

    purgeInput();

    printf("\nEnter definition: ");
    scanf("%[^\n]", entry->definition);

    purgeInput();

    return entry;
}

void addNewEntry(Entry *entry, Dictionary *dictionary) {
    if (dictionary->num_entries == dictionary->max_entries) {
        dictionary->max_entries *= 2;
        dictionary->entries = realloc(dictionary->entries, dictionary->max_entries * sizeof(*dictionary->entries));
        // check if realloc returns NULL and if so, handle the error.
    }

    dictionary->entries[dictionary->num_entries].word = strdup(entry->word);
    dictionary->entries[dictionary->num_entries].definition = strdup(entry->definition);

    dictionary->num_entries++;
}

int main() {
    Dictionary *d = createNewDictionary();

    for (int i = 0; i < 3; i++) {
        Entry *e = requestNewEntry();

        addNewEntry(e, d);

        freeEntry(e);
    }

    printf("\nRead: ");
    for (int i = 0; i < d->num_entries; i++) {
        printf("%s (%lu chars) ", d->entries[i].word, strlen(d->entries[i].definition));
    }
    printf("\n");

    freeDictionary(d);

    return 0;
}

CREATING A PUN DICTIONARY

> ./a.out
============================
Enter word: silkworm

Enter definition: Two silkworms had a race but ended up in a tie.
============================
Enter word: horse

Enter definition: A horse is a stable animal.
============================
Enter word: termite

Enter definition: A termite walks into a pub and asks, "Is the bar tender here?"

Read: silkworm (47 chars) horse (27 chars) termite (62 chars) 
>
Community
  • 1
  • 1
cdlane
  • 40,441
  • 5
  • 32
  • 81
0

I see what's wrong with your code. First of all, you need to pass your Dictionary object by pointer to the function, addNewWord, and in the function addNewWord, you again need to allocate memory to each of the char* fields, name and mean, of the dic object. Here is the corrected code :

void addNewWord(Words newword, Dictionary *dic){
    dic->size++;
    dic->word = (Words*)realloc(dic->word, dic->size*sizeof(Words));
    dic->word[dic->size-1].name = (char*)malloc(30*sizeof(char));  //added
    dic->word[dic->size-1].mean = (char*)malloc(30*sizeof(char));  //added
    strcpy(dic->word[dic->size-1].name, newword.name);
    strcpy(dic->word[dic->size-1].mean, newword.mean);
}

Pass the dictionary's address as :

addNewWord(createNewWord(), &d);

and change the definition as well as prototype of the function as well :

void addNewWord(Words newword, Dictionary *dic)

Find the complete code here : http://pastebin.com/ZN69hevj

Jarvis
  • 8,494
  • 3
  • 27
  • 58