I have been cutting my teeth for the past 48 hours or so trying to implement this hash table function in C. My code is rather long (I realize it is not the most efficient, some of it is more me playing around with C to get a feel for how it works etc).
The problem I am having is with the last line of my main program at the bottom (printing MyEntry->Name). I am receiving a bus error and am unsure why. I do not believe I am supposed to allocate memory in the main driver for this pointer but I could be wrong.
Sorry about the length of this code. BTW SymEntry is 'struct SymEntry{char *Name, void *Attributes, struct SymEntry *Next}
#include <strings.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdbool.h>
#include "SymTab.h"
struct SymTab * CreateSymTab(int Size)
{
struct SymTab *symtable;
if(!(symtable=malloc(sizeof(struct SymTab)))) return NULL;
if(!(symtable->Contents=calloc(Size, sizeof(struct SymEntry*)))) {
free(symtable);
return NULL;
}
symtable->Size=Size;
return symtable;
}
/* hash form hash value for string s, taken from 'The C Programming Language'*/
unsigned hash(struct SymTab *ATable, const char *s)
{
unsigned hashval, size;
size = ATable->Size;;
for (hashval = 0; *s != '\0'; s++)
hashval = *s + 31 * hashval;
return hashval % size;
}
bool EnterName(struct SymTab *ATable,
const char *Name,
struct SymEntry **AnEntry)
{
struct SymEntry *ptr;
unsigned hashvalue;
char *string;
struct SymEntry *previous;
string = malloc(strlen(Name)+1);
AnEntry=(struct SymEntry**)malloc(sizeof(struct SymEntry*));
strcpy(string, Name);
printf("string is: is %s\n",string);
hashvalue = hash(ATable, string);
printf("hv is %d\n",hashvalue);
ptr = ATable->Contents[hashvalue];
previous = NULL;
while(ptr)
{
printf("WHILE LOOP\n");
if(!(strcmp(ptr->Name,string)))
{
printf("if(!strcmp(ptr->Name,string))\n");
*AnEntry = ptr;
return true;
}
previous = ptr;
ptr=ptr->Next;
}
if(previous)
{
printf("IF (PREVIOUS)\n");
if(!(ptr=malloc(sizeof(struct SymEntry)))) return false;
if(!(ptr->Name=string))
{
printf("if(!(ptr->Name=string))\n");
free(ptr);
return false;
}
ptr->Name = string;
previous->Next = ptr;
printf("Previous->Next: %s\n", previous->Next->Name);
*AnEntry = ptr;
return false;
}
else
{
printf("ELSE (PREVIOUS)\n");
if(!(ptr=malloc(sizeof(struct SymEntry)))) return false;
if(!(ptr->Name=string))
{
printf("if(!(ptr->Name=string))\n");
free(ptr);
return false;
}
ptr->Name = string;
ATable->Contents[hashvalue] = ptr;
printf("here\n");
*AnEntry = ptr;
printf("there\n");
return false;
}
}
struct SymEntry * FindName(struct SymTab *ATable, const char *Name)
{
struct SymEntry *Entry;
unsigned hashvalue;
hashvalue = hash(ATable, Name);
Entry = ATable->Contents[hashvalue];
while(Entry)
{
if(strcmp(Name,Entry->Name)==0)
{
return Entry;
}
}
return NULL;
}
main(int argc, char **argv)
{
struct SymTab *mysymtab;
struct SymEntry *myEntry;
mysymtab = CreateSymTab(1);
const char *string1 = "HELLO";
printf("%d\n",6);
EnterName(mysymtab, string1, &myEntry);
printf("first: %s\n", mysymtab->Contents[0]->Name);
EnterName(mysymtab, string1, NULL);
EnterName(mysymtab, "WORLD", NULL);
printf("second: %s\n", mysymtab->Contents[0]->Name);
printf("second->Next: %s\n", mysymtab->Contents[0]->Next->Name);
EnterName(mysymtab, "!##$%", &myEntry);
printf("third: %s\n", mysymtab->Contents[0]->Name);
printf("third->Next: %s\n", mysymtab->Contents[0]->Next->Name);
printf("third->Next->Next: %s\n", mysymtab->Contents[0]->Next->Next->Name);
printf("myEntry->Name: %s\n", myEntry->Name);
}
The problem is this line in EnterName:
AnEntry=(struct SymEntry**)malloc(sizeof(struct SymEntry*));
You need to remove that as you want AnEntry to point to the argument that the caller specified.
Because AnEntry may be NULL, you will also need to change every instance of:
*AnEntry = ptr;
to:
if (AnEntry)
*AnEntry = ptr;
What is happening is that when the function starts, AnEntry is pointing to the pointer the caller wants to change. When you change the value of AnEntry (i.e AnEntry = ...;), your code will not modify the pointer the caller want you to change but some internal pointer. Therefore, when EnterName returns, myEntry is still pointing to some random place in memory.
While you're at learning, there are some stylistic WTFs in your code. Take this part, for example.
if(!(ptr=malloc(sizeof(struct SymEntry)))) return false;
if(!(ptr->Name=string))
{
printf("if(!(ptr->Name=string))\n");
free(ptr);
return false;
}
ptr->Name = string;
It's inconsistent. You cast the return of malloc for AnEntry above, but not this malloc. Either do one or the other, but don't mix it. Better yet, write it in a way that doesn't "need" a cast at all.
You shouldn't assign values within if-statements. While it is still clear what you want to do in the malloc-case, the intention is obfuscated in the string assignment. Especially since it is superfluous. When the if evaluates to true, ptr is immediately freed. When it evaluates to false, the exact same assignment is done again. Additionally, in this case it prevents an obvious optimization.
Here is the same code rewritten:
if (string == NULL)
{
printf("string == NULL\n");
return false;
}
ptr = malloc(sizeof *ptr);
if (ptr == NULL)
{
return false;
}
ptr->Name = string;
Related
When I try to free my struct, the program crashes because of a segfault. Inspecting the program with valgrind I have found:
==9761== Invalid free() / delete / delete[] / realloc()
==9761== at 0x484827F: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9761== by 0x109242: destroyHashTable (hashtable.c:38)
==9761== by 0x10942E: main (hashtable_main.c:17)
==9761== Address 0x1ffefffa70 is on thread 1's stack
==9761== in frame #2, created by main (hashtable_main.c:7)
I cannot really say anything more useful than having no idea, how to solve it. The crash happens during the free(ht) in destroyHashTable(ht) in hashtable.c. What am I doing wrong?
Below the code hashTable_main.c:
#include <stdio.h>
#include <stdlib.h>
#include "hashtable.h"
int main() {
hashTable* ht = NULL;
initHashTable(&ht);
int totalColCount = 0;
totalColCount += addHashTableEntry(&ht, "PRPR2");
destroyHashTable(&ht);
return EXIT_SUCCESS;
}
hashtable.c:
#include <stdlib.h>
#include <stdio.h>
#include "hashtable.h"
/* private internal API */
int hash_funktion(char *string);
hashtableEntry* createTableEntry(char* newKey) ;
/* end of private internal API */
int hash_funktion(char *string) {
unsigned int hash_adresse;
unsigned char *pointer;
hash_adresse = 0;
pointer = (unsigned char *) string;
while(*pointer != '\0') {
hash_adresse = 19 * hash_adresse + *pointer;
pointer++;
}
return hash_adresse % MAX_HASH;
}
hashtableEntry* createTableEntry(char* newKey) {
hashtableEntry* e = (hashtableEntry*) malloc (sizeof(hashtableEntry));
e->hashKey = newKey;
return e;
}
void initHashTable(hashTable* ht) {
ht = (hashTable*) malloc (sizeof (struct hashTable));
ht->table = (hashtableEntry*) malloc (MAX_HASH * sizeof (hashtableEntry));
}
void destroyHashTable(hashTable* ht) {
if (ht) {
free(ht);
ht = NULL;
}
}
int addHashTableEntry(hashtableEntry* ht, char* keyValue) {
hashtableEntry *e = createTableEntry(keyValue);
int colCounter = 0;
int hashValue = hash_funktion(keyValue);
if (ht[hashValue].hashKey == NULL) {
ht[hashValue] = *e;
return 0;
} else {
int newVal = (hashValue + 1) % MAX_HASH;
colCounter++;
while (ht[newVal].hashKey != NULL && newVal != hashValue ) {
newVal = (newVal + 1) % MAX_HASH;
colCounter++;
}
if (newVal != hashValue) {
ht[newVal] = *e;
return colCounter;
} else {
return -1;
}
}
}
bool searchValue(hashtableEntry* ht, char* searchValue) {
for (int i = 0; i < MAX_HASH; i++)
{
if(ht[i].hashKey == searchValue) {
return true;
}
}
return false;
}
and hashtable.h:
#pragma once
#define MAX_HASH 20
#include <stdbool.h>
typedef struct hashtableEntry {
char* hashKey;
} hashtableEntry;
typedef struct hashTable {
hashtableEntry* table;
int elemCount;
} hashTable;
void initHashTable(hashTable* ht);
void destroyHashTable(hashTable* ht);
int addHashTableEntry(hashtableEntry* ht, char* keyValue);
bool searchValue(hashtableEntry* ht, char* searchValue);
There never was a hashtable to begin with. The issue lies in initHashTable. It should be accepting a double pointer since it is given a pointer to a pointer it should initialize. The reason it can segfault despite the check in destroyHashTable is that the pointer is left uninitialized and may be non-zero at the start of program execution.
void initHashTable(hashTable** ht) {
*ht = (hashTable*) malloc (sizeof (struct hashTable));
(*ht)->table = (hashtableEntry*) malloc (MAX_HASH * sizeof (hashtableEntry));
}
You may find it easier to instead return the newly created hash table. This better expresses that initHashTable is giving you a new hashTable * value.
hashTable *initHashTable() {
hashTable *ht = (hashTable *) malloc (sizeof (struct hashTable));
ht.table = (hashtableEntry *) malloc (MAX_HASH * sizeof (hashtableEntry));
return ht;
}
There are also a bunch of other places where pointers are not handled correctly.
void doThing(Foo *foo) {
// This changes foo, but not the data foo points to.
foo = something;
// This changes the data foo points to
*foo = someOtherThing;
}
void doStuff() {
Foo *foo;
// This is incorrect since it creates a double pointer. doThing would need to
// be defined as "void doThing(Foo **foo)" to be correct.
doThing(&foo);
// Instead we can just pass the existing pointer
doThing(foo);
// We only need to create a reference if the value does not start out as a pointer
Foo bar;
doThing(&bar);
}
I have a stack implementation that stores variable: char *items on the stack. But for some reason when I use stack->items[position], it treats it as a regular char (not a pointer) and I am unable to store the full char (it is a URL) on the stack.
I want to give the push function a char * (that is a URL) and I want to take that and put in on my stack, that is either:
p->items[p->pos] = item;
or
strcpy(p->items[p->pos], item);
Here is the part of the code that gives the error:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "shm_stack.h"
typedef struct int_stack{
int size; /* the max capacity of the stack */
int pos; /* position of last item pushed onto the stack */
char *items; /* stack of stored chars */
} ISTACK;
int is_full(ISTACK *p){
if ( p == NULL ) {
return 0;
}
return ( p->pos == (p->size -1) );
}
int sizeof_shm_stack(int size){
return (sizeof(ISTACK) + sizeof(char) * size);
}
int init_shm_stack(ISTACK *p, int stack_size){
if ( p == NULL || stack_size == 0 ) {
return 1;
}
p->size = stack_size;
p->pos = -1;
p->items = (char *) (p + sizeof(ISTACK));
return 0;
}
ISTACK *create_stack(int size){
int mem_size = 0;
ISTACK *pstack = NULL;
if ( size == 0 ) {
return NULL;
}
mem_size = sizeof_shm_stack(size);
pstack = malloc(mem_size);
if ( pstack == NULL ) {
perror("malloc");
} else {
char *p = (char *)pstack;
pstack->items = (char *) (p + sizeof(ISTACK));
pstack->size = size;
pstack->pos = -1;
}
return pstack;
}
void destroy_stack(ISTACK *p){
if ( p != NULL ) {
free(p);
}
}
int push(ISTACK *p, char *item){
if ( p == NULL ) {
return -1;
}
if ( !is_full(p) ) {
++(p->pos);
//p->items[p->pos] = item;
strcpy(p->items[p->pos], item);
//printf("push method: %d\n", p->items[p->pos]);
return 0;
} else {
return -1;
}
}
The issue is in my push method where I can neither use strcpy() or just assign the char to p->items[p-pos] without it saying something like "assigning char from incompatible type char *", but dereferencing "item" will only get me the first character, and I want the entire "string".
Why is this happening and how can I fix it?
p->items is a char*, so p->items[...] is a char. strcpy expects a char*, so there's a mismatch between what you provide and what's needed.
Not only that, it expects the pointer to point to the first of enough characters to contain the string being copied in. You did not even attempt to get the length of the string pointed by item, much less allocate enough memory for it.
I presume you want a stack of strings. If so, we need a array of pointers (char *items[]) or a pointer to a block of memory for pointers (char **items). The latter is simpler here. As such,
char *items;
should be
char **items;
It would be allocated using
malloc(sizeof(char*) * size)
There are two approaches to adding a string to the stack.
The stack could take ownership of the string provided.
p->items[p->pos] = item;
The stack could make a copy of the string provided.
p->items[p->pos] = strdup(item);
The difference is in who is responsible for freeing the string.
I tried to read on it from past questions and watch youtube videos but i don't get it.
I have a program with struct called info. I created a function that adds element of the structure and return a pointer to it.
then i want to use the element fields through the pointer.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
struct Info {
char* name;
char type;
char* path;
};
struct Info* AddInfo(char* input);
int main(void) {
char input[128];
fgets(input, sizeof(input), stdin);
struct info *Puser;
Puser=malloc(sizeof(AddInfo(input)));
&Puser=AddInfo(input);
//here is my problem.
return 0;
}
struct Info *AddInfo( char* input) {
struct Info user1;
struct info* Puser=0;
char *s;
//assign to name
for (int i = strlen(input) - 1; i >= 0; i--) {
if (input[i] == '/') {
s = malloc(sizeof(input));
strncpy(s, input + i + 1, i);
user1.name = malloc(sizeof(s));
if (user1.name == NULL) {
fprintf(stderr, "%s\n", "Error in malloc");
}
strcpy(user1.name, s);
user1.name[i] = '\0';
free(s);
break;
}
}
//assign to type
if ((user1.type = malloc(sizeof(user1.type)) == NULL)) {
fprintf(stderr, "%s\n", "Error in malloc");
}
if (input[strlen(input) - 1] == '/') {
user1.type = 'd';
} else {
user1.type = 'f';
}
//assign to path
user1.path = malloc(sizeof(input));
if (user1.path == NULL) {
fprintf(stderr, "%s\n", "Error in malloc");
}
strcpy(user1.path, input);
// printf("%s \n", user1.path);
// printf("%s\n", user1.name);
// free(user1.name);
Puser= &user1;
return Puser;
}
How should i do that correctly ? how can i take user1 and access it through pointer outside the function ?
Thanks in advance
Your function AddUser assigns all the data to a function-local variable then returns a pointer to that data, but as soon as the function returns that local is no longer valid, you need to allocate the new Info in AddUser rather than main and assign the data to that allocated instance and return that pointer.
typedef struct Info {
char * name;
...
} Info;
Info * AddUser(char const * name);
int main()
{
Info * pNewUser = AddUser("Bob");
...
free(pNewUser);
return 0;
}
Info AddUser(char const * name)
{
if(!name || !*name)
return NULL;
Info * pNew = malloc(sizeof(Info));
if(!pNew)
return NULL;
size_t len = strlen(name);
pNew->name = malloc(len+1);
if(!pNew->name)
{
free(pNew);
return NULL;
}
strcpy(pNew->name, name);
return pNew;
}
So I am new to C and I have a question that I am hoping someone can help me with. Suppose I have a string.
typedef struct String {
char *value;
int size;
} String;
And what I want to do is initialize this string with a function. My first question is which would be better
bool init_String(String **s, char *p) {
if (s == NULL || *s == NULL) {
return false;
}
(*s)->value = p;
(*s)->size = strlen(p);
return true;
}
In this version the function takes a pointer to a pointer and doesn't return the string. My other version is this one:
String *init_String(String **s, char *p) {
if (s == NULL) {
return NULL;
}
s->value = p;
s->size = strlen(p);
return s;
}
Which is better for the user? My second question is is it better to malloc according to the user or according to me. In other words should the user malloc a String and then pass it to the init function or should the init function work as alloc_init and do both the call to malloc and the string initialization?
Thanks
How about this?
typedef struct String {
char *value;
size_t size;
int ref; // if nonzero, do not free(value)
} String;
String refer_String(const char *p) {
String out = { p, strlen(p), 1 };
return out;
}
String copy_String(const char *p) {
String out = { strdup(p), strlen(p), 0 };
return out;
}
void free_String(const String *s) {
if (!s->ref) {
free(s->value);
}
}
This provides a way to refer to existing literal strings like your original code, but also a way to create new strings which may be modified.
I am a beginner in C. Below is my scenario - I have created a pointer variable in main function and it has been passed on to several functions(in this example 2 levels). And one of the functions frees it up. Now I need to have check in Main to see whether the pointer is freed or not, that means i need to setup the value of &str in main() to point to NULL. Not sure my approach is right here. Any help would be much appreciated
void func2(char *str)
{
free(str);
}
void func1(char *str)
{
func2(str);
}
int main()
{
char *str;
str=(char *) malloc(10);
func1(str);
if(str){ do something; } // if condition to check whether str is freed
}
#include <stdio.h>
#include <stdlib.h>
func2(char **str)
{
free(*str); //free
*str = NULL; //Set to NULL
}
func1(char **str) //func1 receives as **
{
func2(str); //Pass pointer to func2()
}
int main()
{
char *str = NULL;
str=(char *) malloc(10);
func1(&str); //Pass Address of pointer to func1()
if(str) //Check for NULL
{
printf("\n Not - Freed...\n");
}
else
{
printf("\n Freed...\n");
}
return 0;
}
In C all are pass by value. I suggest to study http://www.cs.fsu.edu/~myers/cgs4406/notes/pointers.html for understanding of this.
You could try something like this - first redefine malloc and free (track.h)
#ifndef track_h
#define track_h
extern void* trackmalloc(size_t size);
extern void trackfree(void* array);
extern void trackismalloc(void* array);
#define malloc trackmalloc
#define free trackfree
#endif
Then for every piece of code that uses malloc and free, replace #include with #include "track.h"
#include <stdlib.h>
#include <stdio.h>
#include "track.h" /* was <malloc.h> */
// A function which has a 20% chance of freeing the pointer
void twentypercent(char* array)
{
if (rand() < (RAND_MAX / 5))
free(array);
}
int main(int argc, char* argv[])
{
char* list = malloc(256);
int ii;
for (ii = 0; ii < 10; ++ii)
twentypercent(list);
if (trackismalloc(list)
printf("Not freed yet");
return 0;
}
Now define track.c. This will only free memory that has been allocated by by trackmalloc. If it was not allocated by trackmalloc, then it will report that the memory has already been freed.
#include <stdio.h>
#include <malloc.h>
#define TRACKER_MAX 2048
static void* tracker[TRACKER_MAX] = { 0 };
static int track_last = -1;
void* trackmalloc(size_t size)
{
// For simplicity, tracker will not be reused
tracker[++track_last] = malloc(size);
return tracker[track_last];
}
void trackfree(void* array)
{
// This will slow down as the list gets filled up.
// You will need a more efficient way of searching lists (possibly bsearch)
int tt;
for (tt = 0; tt < track_last; ++tt)
{
if (array == tracker[tt])
{
free(tracker[tt]);
tracker[tt] = 0;
break;
}
}
if (tt == track_last)
printf("%p already freed\n", array);
}
int trackismalloc(void* array)
{
// This will slow down as the list gets filled up.
// You will need a more efficient way of searching lists (possibly bsearch)
int tt, result = 0;
for (tt = 0; tt < track_last; ++tt)
{
if (array == tracker[tt])
{
result = 1;
break;
}
}
return result;
}
void func1(char** str) {
free(*str);
*str = NULL;
}
void func2(char** str) {
free(*str);
*str = NULL;
}
int main() {
char *str;
str = (char*) malloc(10);
func1(&str);
if (str) {
do something;
}
}
void func2(char **str)
{
free(*str);
*str = 0;
}
void func1(char **str)
{
func2(str);
}
int main()
{
char *str;
// I'd recommend using sizeof(type_you_want) * amount_of_elements instead of
// a constant number: -> malloc(sizeof(char) * 10);
str=(char *) malloc(10);
func1(&str); // You must pass the address of the pointer, because you want
// to change "WHAT IT POINTS TO", not "WHAT IS POINTED BY IT"
if(str){ do something; } // if condition to check whether str is freed
}
When you call a function in C, you pass a copy of those arguments, so you are passing a copy of that pointer (that copy still points to the same place, so you can change that place that it points to) but you want to change the pointer value, so you need to pass its address.
I have explained a little bit how pointers inside functions can be used in here
#include <stdio.h>
#include <stdlib.h>
void func2(char **str)
{
printf("%d %s\n",__LINE__,__func__);
free(*str);
*str = NULL;
}
void func1(char **str)
{
printf("%d %s\n",__LINE__,__func__);
func2(str);
}
char * allocaMem(char **ptr)
{
*ptr=(char *) malloc(sizeof(char)* 10);
if(!*ptr)
{
perror("");
}
else
{
return *ptr;
}
}
int main()
{
char *str = allocaMem(&str);
if (!str) {
printf("Error in malloc()\n");
return -1;
}
func1(&str);
if (str) {
printf("Memory Not freed\n");
} else {
printf("Memory freed\n");
}
}