creating function to add word into dictionary - c

I want to create function that adds words into dictionary
so far i made this
void addWord(char **dictionary,int *dictionarySize,int *wordsInDictionary,char *word){
if(dictionary == NULL)
{
*dictionary = (char *)malloc(sizeof(char*)*(*dictionarySize));
}
else
{
if(*wordsInDictionary==*dictionarySize)
{
*dictionary = (char *)realloc(dictionary,sizeof(char*)*(*dictionarySize)*2);
(*dictionarySize)*=2;
}
}
dictionary[*wordsInDictionary]=word;
(*wordsInDictionary)++;
}
in main() i have
int i;
int dictSize = 1;
int wordsInDict = 0;
char *word;
char *dictionary;
dictionary=NULL;
then i want to print all words in dictionary , but here i get warning that %s is expecting char* but it is int
printf("Stats: dictsize: %d, words in dict: %d\n", dictSize,wordsInDict);
for(i=0;i<wordsInDict;i++)
{
printf("%d. %s\n",i, dictionary[i]);
}
it also gives me errors when i try to add words
i use this call to add words
addWord(&dictionary,&dictSize,&wordsInDict,word);

In your addWord function, dictionary will never be NULL.
And that's only the start of your problems. Because you want dictionary to be an array of arrays, which mean you need to declare it as a pointer to a pointer (if you want it to be dynamic). However, you declare it as just a (single) pointer. It's in the main function (or where ever you declare it originally) that you need to declare it as a pointer to a pointer. And you need to initialize it, or it will have an indeterminate value and using it in any way other than initializing it will lead to undefined behavior.
That means your addWord function should take a pointer to a pointer to a pointer, i.e. one more level of indirection. And you need to use the dereference operator to get the original pointer to pointer.
So the addWord function should start like e.g.
void addWord(char ***dictionary, int *dictionarySize, int *wordsInDictionary,char *word){
if(*dictionary == NULL)
{
*dictionary = malloc(sizeof(char*) * (*dictionarySize));
}
...
}
Also note that I don't cast the return of malloc.
Also note that realloc can fail, and then will return NULL, so if you assign the return to the same pointer you reallocate you will loose the original pointer. Always use a temporary pointer for the return-value of realloc and only assign to the real pointer after checking that the reallocation succeeded.

I suggest that you put together the members of the dictionary in one as a structure, rather than having individually.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct dictionary {
char **words;//array of char *
int size;
int numOfWords;
} Dictionary;
Dictionary *D_new(void){
Dictionary *dic = malloc(sizeof(*dic));
if(dic){
dic->size = 16;//initial size
dic->words = malloc(dic->size * sizeof(*dic->words));//check omitted
dic->numOfWords = 0;
}
return dic;
}
void D_drop(Dictionary *dic){
int i;
for(i=0;i<dic->numOfWords; ++i)
free(dic->words[i]);
free(dic->words);
free(dic);
}
void addWord(Dictionary *dic, const char *word){
if(dic == NULL){
return ;
}
if(dic->numOfWords == dic->size){
dic->words = realloc(dic->words, sizeof(*dic->words)*(dic->size*=2));//check omitted
}
dic->words[dic->numOfWords++]=strdup(word);//make copy
}
int main(void){
int i;
Dictionary *dictionary = D_new();
addWord(dictionary, "apple");
addWord(dictionary, "banana");
addWord(dictionary, "melon");
printf("Stats: dictsize: %d, words in dict: %d\n",
dictionary->size, dictionary->numOfWords);
for(i=0;i<dictionary->numOfWords;i++){
printf("%d. %s\n", i, dictionary->words[i]);
}
D_drop(dictionary);
return 0;
}

Related

How to properly replace day in a dynamically allocated C array

I am working on a basic framework to dynamically allocate array with the C language. I have created a function to create an array of strings titled init_string_vector. Data can be appended to the array with the append_string_vector function and data can be de-allocated from the heap with the free_string_array function. I am currently working on a function titled replace_string_vector_index that allows a user to pass an array index to the function as well as a pointer to the string array. If the array is typed as a STRING array and the index is not out of bounds, the function should replace the existing data with the string that a user passes to the function.
The replace_string_vector_index function appears to work properly and does replace the string at the index with the other string the user passed to the function. However, the free_string_array function no longer works once I have used to replace_string_vector_index function to act on the array. This makes me think that the process within the function is causing an issue, but I cannot see how. An example is shown below. When the free_string_array function fails, I get the following error, free(): invalid pointer.
vector.h
#ifndef ARRAY_H
#define ARRAY_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
typedef enum
{
FLOAT,
DOUBLE,
CHAR,
INT,
STRING
} dat_type;
// --------------------------------------------------------------------------------
typedef struct
{
char **array;
size_t len;
int elem;
dat_type dat;
} StringVector;
// --------------------------------------------------------------------------------
int string_vector_mem_alloc(StringVector *array, size_t num_indices);
// --------------------------------------------------------------------------------
StringVector init_string_vector();
// --------------------------------------------------------------------------------
int append_string_vector(StringVector *s, char *value);
// --------------------------------------------------------------------------------
void free_string_array(StringVector *array);
// --------------------------------------------------------------------------------
int replace_string_vector_index(StringVector *array, int index, char string[]);
// --------------------------------------------------------------------------------
vector.c
#include "vector.h"
int string_vector_mem_alloc(StringVector *array, size_t num_indices) {
// Determine the total memory allocation and assign to pointer
void *pointer;
pointer = malloc(num_indices * array->elem);
// If memory is full fail gracefully
if (pointer == NULL) {
printf("Unable to allocate memory, exiting.\n");
free(pointer);
return 0;
}
// Allocate resources and instantiate Array
else {
array->array = pointer;
array->len = 0;
return 1;
}
}
// --------------------------------------------------------------------------------
StringVector init_string_vector() {
StringVector array;
array.dat = STRING;
array.elem = sizeof(char *);
string_vector_mem_alloc(&array, array.elem);
return array;
}
// --------------------------------------------------------------------------------
int append_string_vector(StringVector *array, char *value) {
value = strdup(value);
if (!value) {
return -1;
}
array->len++;
char **resized = realloc(array->array, sizeof(char *)*array->len + 1);
if (!resized) {
free(value);
return -1;
}
resized[array->len-1] = value;
array->array = resized;
return 0;
}
// --------------------------------------------------------------------------------
void free_string_array(StringVector *array) {
if (array != NULL) {
for (int i = 0; i < array->len; i++) {
free(array->array[i]);
}
}
free(array->array);
// Reset all variables in the struct
array->array = NULL;
array->len = 0;
array->elem = 0;
}
// --------------------------------------------------------------------------------
int replace_string_vector_index(StringVector *array, int index, char string[]) {
if (array->dat != STRING) {
printf("Array data type must be a STRING");
return 0;
}
if (index > array->len) {
printf("Index is greater than array length");
return 0;
}
* (char **) ((char *) array->array + index * array->elem) = string;
return 1;
}
// --------------------------------------------------------------------------------
main.c
#include <stdio.h>
#include "vector.h"
int main(int argc, const char * argv[]) {
StringVector arr_test = init_string_vector();
char one[] = "Hello";
char two[] = "World";
char three[] = "Hello";
char four[] = "Goodbye";
append_string_vector(&arr_test, one);
append_string_vector(&arr_test, two);
append_string_vector(&arr_test, three);
append_string_vector(&arr_test, four);
// I can free the array at this point
free_string_array(&arr_test)
StringVector arr_test = init_string_vector();
append_string_vector(&arr_test, one);
append_string_vector(&arr_test, two);
append_string_vector(&arr_test, three);
append_string_vector(&arr_test, four);
replace_string_vector_index(&arr_test, 1, one);
// - Once I envoke replace_string_vector_index, free_string_array
// no longer works, and I get an invalid pointer error.
free_string_array(&arr_test);
}
If I understand the requirements for your replace_string_vector_index function, you should first free the memory of array->array[index], then assign the result of strdup(string) to that element.
No casting needed, no complex pointer arithmetic. Just simply:
free(array->array[index]);
array->array[index] = strdup(string);
What happens now (I think) is that you make array->array[index] point to the array that contains the string (i.e. you forget the strdup step). An array that wasn't allocated by malloc, and which can't be passed to free.
Since you will pass it to free as part of free_string_array you will have undefined behavior.

Realloaction of memory in C - Arrays

I'm starting with an array array[0] let's say.
As I loop through a text file and find keywords I'd like to store those words in that array.
So the first run though I'd assing the first keyword very simply
array[0] = "Word"
However I'm not sure how to increment that array to 1, and 2, etc.
I've read a few posts on memory allocaiton but that seemed to be specific to strings; perhaps I'm misunderstanding the concept.
I'd like to preserve the current array's contents, and increment it.
I've rigged it by setting my array[10], but I'd prefer to learn the correct way to do this.
I've included the code below so far (without any memory allocation)
#include <stdio.h>
#include <memory.h>
#include "tables.h"
int main() {
insertVarbleTble("Name","CSTRING",1,0,"");
return 0;
}
int insertVarbleTble(char *ident, char *type, int local, int constVar, char *constVal){
int successful;
int sizeArry;
sizeArry = sizeof(varible)/ sizeof(varible[0]);
if(sizeArry <= 0){
varible[sizeArry]== ident;
}else{
successful = (searchVarbleTble(ident,sizeArry)==1;
}
if(successful ==0){
varible[sizeArry+1]==ident;
}else{
printf("Already exists");
}
}
void realocMem(int size){
varible[size];
}
int searchVarbleTble(char * ident, int arrySize){
int i;
int results = 0;
for(i=0;i<arrySize;i++){
if(!strcmp(varible[i],ident)){
results= 1;
}
}
return results;
}
Header file contains the array I'm using they are:
char varible[0];
int insertVarbleTble(char *, char *, int, int , char *);
int searchVarbleTble(char *, int);
Would a potential solution be to first count the number of keywords that exist, and then dimension the array?
Okay generally what you want to do is not possible with arrays. Your code is really hard to understand but I will try to give you an example on how it is done.
This is not directly what you want, but should help you find your own solution. If you want to work with strings it gets a bit more complicated because strings are arrays in itself so you have to make sure that you always have enough memory for your current string available.
#include <stdio.h>
#include <stdlib.h>
typedef struct data_container_{
int *mem;
int size;
} data_container;
void add_to_memory(data_container *data, int pos, int value)
{
if (pos+1 > data->size) //check if memory for this position is allocated
{
int *dummy = realloc(data->mem, pos+1); // call realloc to get more memory
if(dummy == NULL) //check if reallocation was succesful
{
puts("Memory reallocation failed");
exit(1); //terminate program
}
else
{
data->size = pos+1; //set size to the newly allocated size
data->mem = dummy; //if succesful point to the new memory location
}
}
data->mem[pos] = value;
}
int main(void)
{
data_container data; // create stuct
data.size = 2;
data.mem = malloc(sizeof (int) * data.size); //allocate memory, similar to an array but dynamic
if(data.mem == NULL) //check if allocation was succesful
{
puts("Memory allocation failed");
exit(1); //terminate program
}
add_to_memory(&data, 0, 3); //pass the address of the struct
add_to_memory(&data, 1, 6);
add_to_memory(&data, 2, 8); //now it uses realloc, pos would be out of the allocated range
for(int i =0; i<data.size; i++)
{
printf("%d\n",data.mem[i]); //a pointer can be accessed similar to an array
}
free(data.mem); //free the allocated memory
}
as Pablo said you should read about malloc and realloc especially you should keep in mind that the memory allocated is not initialized. calloc initializes with 0.
And always remember to free allocated space when it is not used anymore.

Dynamic memory allocation of a structure

I need to write a program in which is structure with two fields: integer and string. Next I need to write a function which dynamically allocates this structure and takes int and string as parameters to pass them down to allocated structure. This function will also return pointer to newly made structure. Second element of this program should be function which takes struct pointer as parameter, then prints all of the fileds on screen and then free memory of struct. This is the best I could come up with.
#include <stdio.h>
#include <stdlib.h>
struct str{
int num;
char text[20];
};
struct str* return_address(int *num, char *text){
struct str* new_struct=malloc(sizeof(struct str));
new_struct->num=num;
new_struct->text[20]=text;
return new_struct;
};
void release(struct str* s_pointer){
printf("%d %s", s_pointer->num, s_pointer->text);
free(s_pointer);
};
int main()
{
struct str* variable=return_address(1234, "sample text");
release(variable);
return 0;
}
Your array is very small, also it's not dynamic at all. If you are allocating using malloc() anyway, why not allocate everything dynamically?
You cannot assign to an array.
The num member, which I suppose is meant to store the length of the "string", is being assigned a pointer, which is not what you apparently want. And also, the behavior is only defined in very special circumstances when you assign a pointer to an integer, the compiler should be warning you unless you turned off warnings.
Perhaps you want this,
struct string {
char *data;
int length;
};
struct string *
allocate_string(int length, const char *const source)
{
struct string *string;
string = malloc(sizeof *string);
if (string == NULL)
return NULL;
string->length = strlen(source);
// Make an internal copy of the original
// input string
string->data = malloc(string->length + 1);
if (string->data == NULL) {
free(string);
return NULL;
}
// Finally copy the data
memcpy(string->data, source, string->length + 1);
return string;
}
void
free_string(struct string *string)
{
if (string == NULL)
return;
free(string->data);
free(string);
}

C - Printing a dynamic array

I am creating a program that modifies a dynamic array. It must initialize the array and be able to insert into it. I have been unable print the array after in order to test it, how would I go about this?
Piece of relevant code:
typedef struct {
char first;
char second;
} name;
typedef struct {
int number;
name name;
} data;
/*points to array, number allocated, number used*/
typedef struct {
data *info;
size_t numof;
size_t numused;
} list;
void init(list *l) {
l->data = malloc(sizeof(l) * l->numof);
l->numused = 0;
l->numof = 2;
}
int insert(list *l, const data *dat) {
if (l->numused == l->numof) {
l->numof *= 2;
l->data = (int *)realloc(l->data, l->numof * sizeof(int));
}
l->data[l->numused++] = *dat;
return 0;
}
int main(void) {
int i;
list l;
data list1;
/*example info for testing*/
list.number = 1234;
strcpy(list1.name.first, "abc");
strcpy(list1.name.second, "xyz");
init(&l);
insert(&l, list1);
/*runs through array elements to print*/
for (i=0; i < ((int)sizeof(&l)) /(int)sizeof(&l); i++) {
printf("%s\n", list1);
}
return 0;
}
Edit: I just need to know how to print the array to see if I'm doing it correctly, the code above will have errors as I had been messing around trying to figure it out.
strcpy(list1.name.first, "abc");
strcpy(list1.name.second, "xyz");
These both will invoke undefined behaviour as first and second are declared as char variables , and you copy string literals to them .
You need to declare both of them as character arrays .
And this -
for (i=0; i < ((int)sizeof(&l)) /(int)sizeof(&l); i++) {
printf("%s\n", list1);
}
You try to print struct variable list1 with %s specifier, maybe you tend to print the strings that you wanted to copy. So directly print list1.name.first and list1.name.second in printf with %s specifier.
And the condition -
i < ((int)sizeof(&l)) /(int)sizeof(&l)
The cast is not necessary , and it will yield 1 so, loop will run for 1 time . Change the condition .
In your code, the member of structure name is defined as char. But you are trying to copy a string into it. May be this was a typo. If not you should define them as character array or character pointer. Also in your print statement you are trying to print structure data as string. It should be like -
printf("%s %s\n", list1.name.first, list1.name.second);
Also you assigned value 1234 to list.number. You may have meant list1.number. The parameters in function call of insert is wrong as well. And lastly, you have put l->data in functions init and insert which should be l->info.

Using malloc on variables created from typedef in a function

I want to create a new intarr_t with initial size len, but I've never handled this type of problem with a typedef'ed variable.
My problem is that intarr_create() should allocate the array space and then return a pointer to it if malloc was successful or a pointer to NULL if I failed. How can I fix this?
Also, why there is a * symbol in the function?
Here's my code:
#include <stdio.h>
typedef struct {
int* data;
unsigned int len;
} intarr_t;
intarr_t* intarr_create(unsigned int len) {
//intarr_t with initial size len
intarr_t = (int *) malloc(len); // not working here, can someone explain why?
if(intarr_t != NULL) {
return intarr_t;
} else {
return NULL;
}
}
int main() {
int len = 15;
int h = intarr_create(len);
printf("%d\n", h);
return 0;
}
It's not working because you did not give your variable a name. Also, int* and intarr_t are not the same type, so you will get a type mismatch unless you change the cast.
Rewrite your function into this:
intarr_t* intarr_create(unsigned int len)
{
intarr_t *result;
result = (intarr_t *)malloc(sizeof(intarr_t)); // allocate memory for struct
if(result != NULL)
{
result->data = (int *)malloc(len * sizeof(int)); // allocate memory for data
result->len = len;
if (result->data == NULL)
{
/* handle error */
}
}
else
{
/* handle error */
}
return (result);
}
You have to do a "double" malloc to get it right. First you have to allocate the memory for the intarr_t and if that was successful you have to allocate the memory for the data array.
Additionally malloc returns a void * which must be cast to the correct pointer type (should be a warning or maybe even an error with some compilers).
You have a few problems with your intarr_create function. First of all, you need to name your intarr_t variable. Now you have the slightly trickier problem of allocating memory for the actual array of integers in addition to your intarr structure. Remember, that you will have to call delete twice to destroy this object. Once on the data, and once on the actual structure itself.
intarr_t* intarr_create(unsigned int len)
{
intarr_t* array = (intarr_t*)malloc(sizeof(intarr_t));
array->data = (int*)malloc(len * sizeof(int));
return array;
}

Resources