Duplicate nodes when removing from kdtree - c

I am writing an algorithm that requires me to search nearest neighbors of points. I found the kdtree library from this post (Using Google's C KD Tree Library) but it does not have a function to delete individual nodes from the tree. So I started to implement my own using
www (dot) geeksforgeeks.org/k-dimensional-tree-set-3-delete/
as a template. It all runs through but unfortunately sometimes nodes get duplicated.
My test case is the following:
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <math.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include "kdtree.h"
/* (hopefully) platform independent directory creation */
#if defined(_WIN32) || defined(WIN32) /* this should be defined under windows, regardless of 64 or 32 bit*/
#include <direct.h>
#include <sys/stat.h>
#define GetWorkingDir _getcwd
#define MakeDir(str) _mkdir(str)
#else /* unix based system */
#include <unistd.h>
#include <sys/stat.h>
#define GetWorkingDir getcwd
#define MakeDir(str) mkdir(str, 0777)
#endif
#ifndef MAX_PATH
#define MAX_PATH 260
#endif
void GetLogDir(char* strPath, int nBufSize)
{
if(GetWorkingDir(strPath, nBufSize))
{
strncat(strPath, "/log/", 5);
MakeDir(strPath);
}
else
{
fprintf(stderr, "Could not get working directory");
exit(ENOENT);
}
}
FILE* GetOpenFileHandle(const char* strFilenamePlusPath, const char* strOpenMode)
{
if(strOpenMode == NULL) // too bad we dont have default arguments in C :(
{
strOpenMode = "a+";
}
return(fopen(strFilenamePlusPath, strOpenMode));
}
int CloseFile(FILE* pFile)
{
if(pFile != NULL)
{
fprintf(pFile, "\r\n"); // append a new line before closing!
return(fclose(pFile));
}
fprintf(stderr, "Invalid file handle");
exit(EFAULT);
}
void NodeLabelToFile(FILE* pFile, kdnode* node, const char* strName)
{
fprintf(pFile, "%s [label=\"(%.3f, %.3f)\"] \n", strName, node->pos[0], node->pos[1]);
}
char* NodeToString(kdnode* node, int* num)
{
char* strName = (char*) malloc(MAX_PATH);
if(*num == 0)
{
sprintf(strName, "%s","root");
}
else
{
sprintf(strName, "node%d", *num);
}
return strName;
}
void NodesToFile(FILE* pFile, kdnode* node, const char* strParentname, int* num)
{
if(node && pFile)
{
char* strLeft = NULL;
char* strRight = NULL;
if(node->left)
{
(*num)++;
strLeft = NodeToString(node->left, num);
NodeLabelToFile(pFile, node->left, strLeft);
fprintf(pFile, "%s -> %s \n", strParentname, strLeft);
}
if(node->right)
{
(*num)++;
strRight = NodeToString(node->right, num); // name of the current node
NodeLabelToFile(pFile, node->right, strRight);
fprintf(pFile, "%s -> %s \n", strParentname, strRight);
}
if(strLeft)
{
NodesToFile(pFile, node->left, strLeft, num);
free(strLeft);
}
if(strRight)
{
// (*num)++;
NodesToFile(pFile, node->right, strRight, num);
free(strRight);
}
}
}
FILE* MakeOpenLogFile(const char* strFilename, const char* strOpenMode)
{
if(strOpenMode == NULL)
{
strOpenMode = "a+";
}
char* strFilenamePlusPath = (char*) malloc(MAX_PATH);
GetLogDir(strFilenamePlusPath, MAX_PATH);
strncat(strFilenamePlusPath, strFilename, strlen(strFilename));
FILE* pFile = GetOpenFileHandle(strFilenamePlusPath, strOpenMode);
free(strFilenamePlusPath);
return(pFile);
}
void KDTreeToDotFile(kdtree* Tree, const char* strFilename)
{
if(Tree)
{
FILE* pFile = MakeOpenLogFile(strFilename, "w");
fprintf(pFile, "%s", "digraph d { \n"); // print opening statement for the graph in dot language
// traverse the tree and print the nodes
int* num = (int*) malloc(sizeof(int)); // make this a unique location to make sure numbers can't occur twice
*num = 0;
char* strRoot = NodeToString(Tree->root, num);
NodeLabelToFile(pFile, Tree->root, strRoot);
NodesToFile(pFile, Tree->root, "root", num);
if(strRoot)
{
free(strRoot);
}
free(num);
fprintf(pFile,"%s", "}"); // close the digraph environment
CloseFile(pFile);
}
}
int main(int argc, const char * argv[])
{
int numel = 20;
int toRemove = 19;
double dMax = 3000;
int nNumDim = 2;
printf("init rng");
srand(1234); // seed the rng // srand((unsigned) time(&t));
printf("creating kdtree");
kdtree* TreeRoot = kd_create(nNumDim); // construct the kd tree for the nearest neighbor search
kd_data_destructor(TreeRoot, free); // set free as data destructor
double* pos = (double*) malloc(nNumDim * numel * sizeof(double));
int retval;
for (int ii = 0; ii < numel; ii++)
{
pos[nNumDim * ii] = floor((double)rand()/(double)(RAND_MAX/dMax));
pos[nNumDim * ii + 1] = floor((double)rand()/(double)(RAND_MAX/dMax));
int* randint = (int*) malloc(sizeof(int));
*randint = rand();
retval = kd_insert2(TreeRoot,
pos[nNumDim * ii],
pos[nNumDim * ii + 1],
randint, sizeof(int));
assert(retval == 0);
}
KDTreeToDotFile(TreeRoot, "original.dot");
double* dRemovePos = (double*) malloc(sizeof(double)*nNumDim);
for (int ii = 0; ii < toRemove; ii++)
{
dRemovePos[0] = pos[2*ii];
dRemovePos[1] = pos[2*ii + 1];
kd_remove(TreeRoot, dRemovePos);
}
KDTreeToDotFile(TreeRoot, "removed.dot");
kd_free(TreeRoot); // free kdtree
return 0;
}
and the functions to remove the nodes are implemented like this:
(I don't think if it is too much code, so I only will post my changes to the kd library. If I should add the rest of the code, which is more than 1000 lines unfortunately, just tell me in the comments.)
int kd_remove(kdtree* tree, const double* pos)
{
printf("removing node %.3f, %.3f \n", pos[0], pos[1]);
if(tree->root != NULL)
{
assert(tree->dim != 0); // prevent division by 0 (error code 136)
assert(pos != NULL); // make sure a valid position is passed
tree->root = remove_rec(tree->root, pos, tree->dim, tree->destr, 0);
}
return(0);
}
kdnode* remove_rec(kdnode* node, const double* pos, int dim, void (*destr)(void*), int depth)
{
if(node == NULL)
{
return(NULL);
}
int curdim = depth % dim;
if(same_pos(node->pos, pos, dim))
{
// we found the droid we're looking for
if(node->right)
{
// find the minimum in the right subtree
kdnode* node_min = find_min(node->right, curdim, dim);
if(node_min)
{
copy_node_data(node_min, node, dim);
node->right = remove_rec(node->right, node_min->pos, dim, destr, depth + 1);
}
}
else if(node->left)
{
// find the minimum in the left subtree
kdnode* node_min = find_min(node->left, curdim, dim);
if(node_min)
{
copy_node_data(node_min, node, dim);
node->left = remove_rec(node->left, node_min->pos, dim, destr, depth + 1);
}
}
else
{
// no subtrees -> delete the found node
clear_rec(node, destr);
return(NULL);
}
return node; // return the newly filled node to the recursion step one "above"
}
else
{
// points are not the same, look further
if(pos[curdim] < node->pos[curdim])
{
// position we're looking for is smaller -> go left
node->left = remove_rec(node->left, pos, dim, destr, depth + 1);
}
else
{
// go right, position we're looking for is greater
node->right = remove_rec(node->right, pos, dim, destr, depth + 1);
}
return node;
}
}
void copy_node_data(const kdnode* src, kdnode* dst, int dim)
{
if(src && dst)
{
int nNumBytes = dim * sizeof(double);
memcpy(dst->pos, src->pos, nNumBytes);
if(dst->data != NULL)
{
free(dst->data);
dst->data = malloc(src->databytes);
}
memcpy(dst->data, src->data, src->databytes);
dst->databytes = src->databytes;
}
}
int same_pos(const double* pos1, const double* pos2, int dim)
{
for (int i = 0; i < dim; ++i)
{
if(pos1[i] != pos2[i])
{
return 0; // false
}
}
return 1; // true
}
kdnode* find_min(kdnode* node, int dir, int numdim)
{
return find_min_rec(node, dir, 0, numdim);
}
kdnode* find_min_rec(kdnode* node, int dir, int depth, int numdim)
{
if(!node)
{
return NULL;
}
if(node->left == NULL && node->right == NULL)
{
return node; // is leaf node
}
int curdim = depth % numdim;
if(curdim == numdim)
{
if(node->left == NULL)
{
// no smaller node in tree
return node;
}
else
{
// left subtree is populated -> we need to go deeper
return find_min_rec(node->left, node->dir, depth + 1, numdim);;
}
}
// we have to search both subtrees and find the smallest value compared to the current node
return min_node(node, find_min_rec(node->left, node->dir, depth + 1, numdim),
find_min_rec(node->right, node->dir, depth + 1, numdim), node->dir);
}
kdnode* min_node(kdnode* a, kdnode* left, kdnode* right, int dir)
{
if(a == NULL)
{
// node a is the only one that can't be NULL!
fprintf(stderr, "Error: invalid node passed! \n");
exit(EFAULT);
}
kdnode* result = a;
if(left != NULL)
{
if(left->pos[dir] < result->pos[dir])
{
result = left;
}
}
if(right != NULL)
{
if(right->pos[dir] < result->pos[dir])
{
result = right;
}
}
return result;
}
original.dot looks like this and removed.dot like that.
I've been debugging this since yesterday and I have the feeling it is something really obvious that I am missing here...
Thanks in advance to anyone willing to help :)

You are creating 40 elements
int numel = 20;
int nNumDim = 2;
double* pos = (double*) malloc(nNumDim * numel * sizeof(double)); // Don't cast
but removing only 38
int toRemove = 19;
for (int ii = 0; ii < toRemove; ii++)
{
dRemovePos[0] = pos[nNumDim * ii];
dRemovePos[1] = pos[nNumDim * ii + 1];
kd_remove(TreeRoot, dRemovePos);
}
In the last iteration:
pos[nNumDim * ii]; = pos[2 * 18]; = pos[36];
pos[nNumDim * ii + 1]; = pos[2 * 18 + 1]; = pos[37];
pos[38] and pos[39] are still there.
Change to int toRemove = 20;.
Your code is obfuscated due to the flat array, why don't you declare some type like
struct data {
double el1;
double el2;
};
or
typedef double data[2];
and then
data *value = malloc(numel * sizeof(*value));

So, I know this probably won't be read by anyone but I found the bug after not touching the code for a while and for completeness here is how:
In the find_min() function I start the recursion with depth = 0.
This can cause the split dimension to get messed up and therefore not access all the nodes.
I modified the function to take depth as an argument and pass the recursion depth of remove_rec() like this:
kdnode* node_min = find_min(node->right, curdim, dim, depth + 1);
and
kdnode* node_min = find_min(node->left, curdim, dim, depth + 1);
respectively.

Related

How to Combine 2 Struct arrays in C

iv tried a lot of solutions to try to get this working (i.e using memcpy etc) I cant seem to find the issue, depending on what I try I either end up with gibberish or SEGV
iv spent a lot of time already googling and trying different ways, i still cant figure out why the arrays won't combine successfully
#include <stdio.h>
#include <stdint.h>
#include <dirent.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/syscall.h>
#include <unistd.h>
#define log_info printf
typedef struct
{
char* name;
//size_t size;
} entry_t;
/* qsort struct comparison function (C-string field) */
static int struct_cmp_by_name(const void* a, const void* b)
{
entry_t* ia = (entry_t*)a;
entry_t* ib = (entry_t*)b;
return strcmp(ia->name, ib->name);
/* strcmp functions works exactly as expected from comparison function */
}
entry_t* get_item_entries(const char* dirpath, int* count)
{
struct dirent* dent;
char buffer[512]; // fixed buffer
int dfd = 0,
n, r = 1; // item counter, rounds to loop
entry_t* p = NULL; // we fill this struct with items
loop:
n = 0;
printf("loop: %d, count:%d\n", r, *count);
// try to open dir
dfd = open(dirpath, O_RDONLY, 0);
if (dfd < 0)
{
printf("Invalid directory. (%s)\n", dirpath);
*count = -1;
return NULL;
}
else
{
printf("open(%s)\n", dirpath);
}
memset(buffer, 0, sizeof(buffer));
while (syscall(SYS_getdents, dfd, buffer, sizeof(buffer)) != 0)
{
dent = (struct dirent*)buffer;
while (dent->d_fileno)
{ // skip `.` and `..`
if (!strncmp(dent->d_name, "..", 2)
|| !strncmp(dent->d_name, ".", 1)) goto skip_dent;
// deal with filtering outside of this function, we just skip .., .
switch (r)
{ // first round: just count items
case 1:
{
// skip special cases
if (dent->d_fileno == 0) goto skip_dent;
break;
}
// second round: store filenames
case 0: p[n].name = strdup(dent->d_name); break;
}
n++;
skip_dent:
dent = (struct dirent*)((void*)dent + dent->d_reclen);
if (dent == (void*)&buffer[512]) break; // refill buffer
}
memset(buffer, 0, sizeof(buffer));
}
close(dfd);
// on first round, calloc for our list
if (!p)
{ // now n holds total item count, note it
p = calloc(n, sizeof(entry_t));
*count = n;
}
// first round passed, loop
r--; if (!r) goto loop;
// report count
printf("%d items at %p, from 1-%d\n", *count, (void*)p, *count);
/* resort using custom comparision function */
qsort(p, *count, sizeof(entry_t), struct_cmp_by_name);
// report items
//for (int i = 0; i < num; ++i) log_error( "%s", p[i].name);
return p;
}
int main(int argc, char* argv[])
{
int HDD_count = -1;
uint32_t total = -1;
int ext_count = -1;
entry_t* e = NULL;
entry_t *HDD = get_item_entries("/mnt/f/n", &HDD_count);
entry_t* ext = get_item_entries("/mnt/f/dls", &ext_count);
total = ext_count + HDD_count;
e = (entry_t*)malloc(sizeof *e * total);
if (e != NULL)
{
for (int i = 1; i < HDD_count; i++)
{
log_info("HDD[%i].name %s\n", i, HDD[i].name);
e[i].name = strdup(HDD[i].name);
}
for (int i = 1; i < ext_count; i++)
{
log_info("ext[%i].name %s\n", i, ext[i].name);
e[i + HDD_count].name = strdup(ext[i].name);
}
}
else
printf("Failed to Allocate the Array");
char tmp[256];
int i = 1, j;
for(j = 1; j <= total; j++)
{
snprintf(&tmp[0], 255, "%s", e[ j].name);
log_info("%i:%s\n", j , tmp);
}
return 0;
}
Here is a rewrite of a snippet of main() that I mentioned in my comment above:
#define CHECK(p, msg) if(!(p)) { printf("%s:%d: %s", __FILE__, __LINE__, msg); return 1;}
...
entry_t *HDD = get_item_entries("/mnt/f/n", &HDD_count);
CHECK(HDD, "HDD failed to get entries");
entry_t *ext = get_item_entries("/mnt/f/dls", &ext_count);
CHECK(ext, "ext failed to get entries");
uint32_t total = HDD_count + ext_count;
entry_t *e = malloc(total * sizeof(*e));
CHECK(e, "malloc failed");
for(int i = 0; i < HDD_count; i++) {
log_info("HDD[%i].name %s\n", i, HDD[i].name);
e[i].name = strdup(HDD[i].name);
}
// write a function instead of duplicating code?
for (int i = 0; i < ext_count; i++) {
log_info("ext[%i].name %s\n", i, ext[i].name);
e[HDD_count + i].name = strdup(ext[i].name);
}
It looks like a short lived program, but I would still free the values from strdup() and e itself.

Segmentation fault while comparing elements in a dynamically allocated array

This program tries to simulate FIFO and LRU page replacement. I am trying to implement a simple queue using a dynamically allocated array for the FIFO queue. I want the "page" to be stored in the array.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
/*
* Some compile-time constants.
*/
#define REPLACE_NONE 0
#define REPLACE_FIFO 1
#define REPLACE_LRU 2
#define REPLACE_SECONDCHANCE 3
#define REPLACE_OPTIMAL 4
#define TRUE 1
#define FALSE 0
#define PROGRESS_BAR_WIDTH 60
#define MAX_LINE_LEN 100
/*
* Some function prototypes to keep the compiler happy.
*/
int setup(void);
int teardown(void);
int output_report(void);
long resolve_address(long, int);
void error_resolve_address(long, int);
/*
* Variables used to keep track of the number of memory-system events
* that are simulated.
*/
int page_faults = 0;
int mem_refs = 0;
int swap_outs = 0;
int swap_ins = 0;
/*
* Page-table information. You may want to modify this in order to
* implement schemes such as SECONDCHANCE. However, you are not required
* to do so.
*/
struct page_table_entry *page_table = NULL;
struct page_table_entry {
long page_num;
int dirty;
int free;
};
/*
* These global variables will be set in the main() function. The default
* values here are non-sensical, but it is safer to zero out a variable
* rather than trust to random data that might be stored in it -- this
* helps with debugging (i.e., eliminates a possible source of randomness
* in misbehaving programs).
*/
int size_of_frame = 0; /* power of 2 */
int size_of_memory = 0; /* number of frames */
int page_replacement_scheme = REPLACE_NONE;
long *queue;
void add_end(long page_num){
for(int i=0; i<size_of_memory; i++){
if(queue[i] == NULL){
queue[i] = page_num;
break;
}
}
}
long peek_front(){
return queue[0];
}
void remove_front(){
for(int i=0; i<size_of_memory; i++){
queue[i] = queue[i+1];
}
}
// typedef struct page_in_queue page_in_queue;
// struct page_in_queue {
// long page_num;
// page_in_queue *next;
// };
// page_in_queue *new_page(){
// page_in_queue *new_page;
// new_page = (page_in_queue *) malloc(sizeof(page_in_queue));
// new_page->next = NULL;
// return new_page;
// }
// page_in_queue *add_end(page_in_queue *queue, page_in_queue *page){
// page_in_queue *curr;
// if (queue == NULL) {
// page->next = NULL;
// return page;
// }
// for (curr = queue; curr->next != NULL; curr = curr->next);
// curr->next = page;
// page->next = NULL;
// return queue;
// }
// page_in_queue *peek_front(page_in_queue *queue) {
// return queue;
// }
// page_in_queue *remove_front(page_in_queue *queue){
// if (queue == NULL) {
// return NULL;
// }
// page_in_queue *new_front_page = queue->next;
// free(queue);
// return new_front_page;
// }
long *list;
void add(long page_num){
int i;
for (i=0; i<size_of_memory; i++){
list[i] = list[i+1];
}
list[i] = page_num;
}
long peek_least_used(){
return list[0];
}
/*
* Function to convert a logical address into its corresponding
* physical address. The value returned by this function is the
* physical address (or -1 if no physical address can exist for
* the logical address given the current page-allocation state.
*/
long resolve_address(long logical, int memwrite)
{
int i;
long page, frame;
long offset;
long mask = 0;
long effective;
/* Get the page and offset */
page = (logical >> size_of_frame);
for (i=0; i<size_of_frame; i++) {
mask = mask << 1;
mask |= 1;
}
offset = logical & mask;
if (page_replacement_scheme == 2){
add(page);
}
/* Find page in the inverted page table. */
frame = -1;
for ( i = 0; i < size_of_memory; i++ ) {
if (!page_table[i].free && page_table[i].page_num == page) {
frame = i;
break;
}
}
/* If frame is not -1, then we can successfully resolve the
* address and return the result. */
if (frame != -1) {
effective = (frame << size_of_frame) | offset;
return effective;
}
/* If we reach this point, there was a page fault. Find
* a free frame. */
page_faults++;
for ( i = 0; i < size_of_memory; i++) {
if (page_table[i].free) {
frame = i;
break;
}
}
// page_in_queue *temp_page;
// page_in_queue *queue;
long rem_page;
/* If we found a free frame, then patch up the
* page table entry and compute the effective
* address. Otherwise return -1.
*/
if (frame != -1) {
page_table[frame].page_num = page;
page_table[i].free = FALSE;
swap_ins++;
if (page_replacement_scheme == 1){
// temp_page = new_page();
// temp_page->page_num = page;
add_end(page);
}
effective = (frame << size_of_frame) | offset;
return effective;
}
else {
if (page_replacement_scheme == 1){
rem_page = peek_front();
for ( i = 0; i < size_of_memory; i++){
if(page_table[i].page_num == rem_page){
page_table[i].page_num = page;
page_table[i].free = FALSE;
page_table[i].dirty = memwrite;
swap_ins++;
if(page_table[i].dirty == 1){
swap_outs++;
}
frame = i;
break;
}
}
remove_front();
effective = (frame << size_of_frame) | offset;
return effective;
}
if (page_replacement_scheme == 2){
long temp = peek_least_used();
for ( i = 0; i < size_of_memory; i++){
if(page_table[i].page_num == temp){
page_table[i].page_num = page;
page_table[i].free = FALSE;
page_table[i].dirty = memwrite;
swap_ins++;
if(page_table[i].dirty == 1){
swap_outs++;
}
frame = i;
break;
}
}
effective = (frame << size_of_frame) | offset;
return effective;
}
if (page_replacement_scheme == 3){
}
}
}
/*
* Super-simple progress bar.
*/
void display_progress(int percent)
{
int to_date = PROGRESS_BAR_WIDTH * percent / 100;
static int last_to_date = 0;
int i;
if (last_to_date < to_date) {
last_to_date = to_date;
} else {
return;
}
printf("Progress [");
for (i=0; i<to_date; i++) {
printf(".");
}
for (; i<PROGRESS_BAR_WIDTH; i++) {
printf(" ");
}
printf("] %3d%%", percent);
printf("\r");
fflush(stdout);
}
int setup()
{
int i;
page_table = (struct page_table_entry *)malloc(
sizeof(struct page_table_entry) * size_of_memory
);
if (page_table == NULL) {
fprintf(stderr,
"Simulator error: cannot allocate memory for page table.\n");
exit(1);
}
for (i=0; i<size_of_memory; i++) {
page_table[i].free = TRUE;
}
return -1;
}
int teardown()
{
return -1;
}
void error_resolve_address(long a, int l)
{
fprintf(stderr, "\n");
fprintf(stderr,
"Simulator error: cannot resolve address 0x%lx at line %d\n",
a, l
);
exit(1);
}
int output_report()
{
printf("\n");
printf("Memory references: %d\n", mem_refs);
printf("Page faults: %d\n", page_faults);
printf("Swap ins: %d\n", swap_ins);
printf("Swap outs: %d\n", swap_outs);
return -1;
}
int main(int argc, char **argv)
{
/* For working with command-line arguments. */
int i;
char *s;
/* For working with input file. */
FILE *infile = NULL;
char *infile_name = NULL;
struct stat infile_stat;
int line_num = 0;
int infile_size = 0;
/* For processing each individual line in the input file. */
char buffer[MAX_LINE_LEN];
long addr;
char addr_type;
int is_write;
/* For making visible the work being done by the simulator. */
int show_progress = FALSE;
/* Process the command-line parameters. Note that the
* REPLACE_OPTIMAL scheme is not required for A#3.
*/
for (i=1; i < argc; i++) {
if (strncmp(argv[i], "--replace=", 9) == 0) {
s = strstr(argv[i], "=") + 1;
if (strcmp(s, "fifo") == 0) {
page_replacement_scheme = REPLACE_FIFO;
} else if (strcmp(s, "lru") == 0) {
page_replacement_scheme = REPLACE_LRU;
} else if (strcmp(s, "secondchance") == 0) {
page_replacement_scheme = REPLACE_SECONDCHANCE;
} else if (strcmp(s, "optimal") == 0) {
page_replacement_scheme = REPLACE_OPTIMAL;
} else {
page_replacement_scheme = REPLACE_NONE;
}
} else if (strncmp(argv[i], "--file=", 7) == 0) {
infile_name = strstr(argv[i], "=") + 1;
} else if (strncmp(argv[i], "--framesize=", 12) == 0) {
s = strstr(argv[i], "=") + 1;
size_of_frame = atoi(s);
} else if (strncmp(argv[i], "--numframes=", 12) == 0) {
s = strstr(argv[i], "=") + 1;
size_of_memory = atoi(s);
if (page_replacement_scheme == 1){
queue = (long *)malloc(sizeof(long)*size_of_memory);
}
if (page_replacement_scheme == 2){
list = (long *)malloc(sizeof(long)*size_of_memory);
}
} else if (strcmp(argv[i], "--progress") == 0) {
show_progress = TRUE;
}
}
if (infile_name == NULL) {
infile = stdin;
} else if (stat(infile_name, &infile_stat) == 0) {
infile_size = (int)(infile_stat.st_size);
/* If this fails, infile will be null */
infile = fopen(infile_name, "r");
}
if (page_replacement_scheme == REPLACE_NONE ||
size_of_frame <= 0 ||
size_of_memory <= 0 ||
infile == NULL)
{
fprintf(stderr,
"usage: %s --framesize=<m> --numframes=<n>", argv[0]);
fprintf(stderr,
" --replace={fifo|lru|optimal} [--file=<filename>]\n");
exit(1);
}
setup();
while (fgets(buffer, MAX_LINE_LEN-1, infile)) {
line_num++;
if (strstr(buffer, ":")) {
sscanf(buffer, "%c: %lx", &addr_type, &addr);
if (addr_type == 'W') {
is_write = TRUE;
} else {
is_write = FALSE;
}
if (resolve_address(addr, is_write) == -1) {
error_resolve_address(addr, line_num);
}
mem_refs++;
}
if (show_progress) {
display_progress(ftell(infile) * 100 / infile_size);
}
}
teardown();
output_report();
fclose(infile);
exit(0);
}
The file is saved as virtmem.c. This is the makefile:
#
# "makefile" for the virtual-memory simulation.
#
CC=gcc
CFLAGS=-c -Wall -g
all: virtmem
virtmem.o: virtmem.c
$(CC) $(CFLAGS) virtmem.c
virtmem: virtmem.o
$(CC) virtmem.o -o virtmem
clean:
rm -rf *.o virtmem
After running the "make" command, I run the executable with these inputs
./virtmem --framesize=12 --numframes=100 --replace=fifo --file=traces/ls_out.txt --progress
But it is giving a segmentation fault at the conditional "if(queue[i] == NULL)", saying the memory location cannot be accessed. The gdb output is as follows:
Program received signal SIGSEGV, Segmentation fault.
0x0000555555554bea in add_end (page_num=34158723704) at virtmem.c:80
80 if(queue[i] == (long)0){
(gdb) print queue[i]
Cannot access memory at address 0x0
(gdb)

Solving Maze Using BFS in C - Segmentation fault

I am attempting to solve a maze using BFS, but I am getting a segmentation fault. Can anyone help me figure out why this is happening? Also if you see anything else wrong with my code? my if statements are to check if the point exists in the maze and that there is a white space at the point. I also am solving the maze using DFS, but the seg fault happens during solve_bfs. Also, everything except solve.c was given to my professor, so the only thing I am suppose to edit is solve.c
Solve.c
#include <stdio.h>
#include <stdlib.h>
#include "maze.h"
#include "stack.h"
#include "queue.h"
int solve_bfs(maze * the_maze){
Queue Q= initQueue();
enqueue(Q, the_maze->entry);
while(!emptyQueue(Q)){
coord to_explore= dequeue(Q);
if(to_explore.row == the_maze->exit.row && to_explore.col == the_maze->exit.col){
print_maze(the_maze);
free(Q);
return 1;
}
else{
the_maze->data[to_explore.row][to_explore.col]= 'o';
if(to_explore.row-1 >= 0){
if(the_maze->data[to_explore.row-1][to_explore.col] == ' '){
coord new;
new.row= to_explore.row-1;
new.col= to_explore.col;
enqueue(Q, new);
}
}
if(to_explore.col+1 < the_maze->width){
if(the_maze->data[to_explore.row][to_explore.col+1]== ' '){
coord new;
new.row= to_explore.row;
new.col= to_explore.col+1;
enqueue(Q, new);
}
}
if(to_explore.row+1 < the_maze->height){
if(the_maze->data[to_explore.row+1][to_explore.col]== ' '){
coord new;
new.row= to_explore.row+1;
new.col= to_explore.col;
enqueue(Q, new);
}
}
if(to_explore.col-1 >= 0) {
if(the_maze->data[to_explore.row][to_explore.col-1]== ' '){
coord new;
new.row= to_explore.row;
new.col= to_explore.col-1;
enqueue(Q, new);
}
}
}
if(emptyQueue(Q)){
print_maze(the_maze);
free(Q);
return 0;
}
}
}
int solve_dfs(maze * the_maze){
Stack s= initStack();
push(s, the_maze->entry);
while(!emptyStack(s)){
coord to_explore= pop(s);
if(to_explore.row == the_maze->exit.row && to_explore.col == the_maze->exit.col){
print_maze(the_maze);
free(s);
return 1;
}
else{
if(to_explore.row-1 >= 0){
if(the_maze->data[to_explore.row-1][to_explore.col]== ' '){
coord new;
new.row= to_explore.row-1;
new.col= to_explore.col;
push(s, new);
}
}
if(to_explore.col+1 < the_maze->width){
if(the_maze->data[to_explore.row][to_explore.col+1]== ' '){
coord new;
new.row= to_explore.row;
new.col= to_explore.col+1;
push(s, new);
}
}
if(to_explore.row+1 < the_maze->height){
if(the_maze->data[to_explore.row+1][to_explore.col]== ' '){
coord new;
new.row= to_explore.row+1;
new.col= to_explore.col;
push(s, new);
}
}
if(to_explore.col-1 >= 0){
if(the_maze->data[to_explore.row][to_explore.col-1]== ' '){
coord new;
new.row= to_explore.row;
new.col= to_explore.col-1;
push(s, new);
}
}
}
if(emptyStack(s)){
print_maze(the_maze);
free(s);
return 0;
}
}
}
void print_maze(maze * the_maze){
the_maze->data[the_maze->entry.row][the_maze->entry.col]='S';
the_maze->data[the_maze->exit.row][the_maze->exit.col]='F';
for(int i=0; i<the_maze->width; i++){
for(int j=0; j<the_maze->height; j++){
printf("%s",the_maze->data[i][j]);
}
}
}
coord * make_coord(int r, int c){
coord * coord= malloc(sizeof(coord));
coord->row = r;
coord->col = c;
return coord;
}
void print_coord(coord c){
printf("(%d,%d)",c.row, c.col);
}
Maze.c
#include <stdio.h>
#include <stdlib.h>
#include "maze.h"
/**********************************************************
create_maze
creates a new maze from the input file characters
*********************************************************/
maze * create_maze(FILE *in) {
// create maze
maze * new_maze = (maze *) malloc(sizeof(maze));
// read first line of in
int start_row;
int start_col;
int end_row;
int end_col;
int num_rows;
int num_cols;
// start line
char * line = malloc(sizeof(char)*100); // start buffer out at 100
size_t num_read;
getline(&line, &num_read, in);
if(sscanf(line, "%d %d %d %d %d %d", &num_rows, &num_cols, &start_row, &start_col, &end_row, &end_col) != 6) {
fprintf(stderr, "Maze file format invalid. Top line must include 6 numbers.\n");
return NULL;
}
// if any are negative values, return NULL
if(start_row < 0 || start_col < 0 || end_row < 0 || end_col < 0 || num_rows < 0 || num_cols < 0) {
fprintf(stderr, "Maze file format invalid. Maze file numbers in first row must be non-negative.\n");
return NULL;
}
// make sure start_row is in bounds
if(start_row >= num_rows) {
fprintf(stderr, "Maze file format invalid. Start row must be < num rows in maze.\n");
return NULL;
}
// make sure end_row is in bounds
if(end_row >= num_rows) {
fprintf(stderr, "Maze file format invalid. End row must be < num rows in maze.\n");
return NULL;
}
// make sure start_col is in bounds
if(start_col >= num_cols) {
fprintf(stderr, "Maze file format invalid. Start col must be < num cols in maze.\n");
return NULL;
}
// make sure end_col is in bounds
if(end_col >= num_cols) {
fprintf(stderr, "Maze file format invalid. Start col must be < num cols in maze.\n");
return NULL;
}
// assign maze members
new_maze->entry.row = start_row;
new_maze->entry.col = start_col;
new_maze->exit.row = end_row;
new_maze->exit.col = end_col;
new_maze->width = num_cols;
new_maze->height = num_rows;
// allocate memory for maze data
new_maze->data = (char **) malloc(sizeof(char *)*num_rows);
int i;
for(i=0; i<num_rows; i++) {
new_maze->data[i] = (char *) malloc(sizeof(char)*num_cols);
}
// get characters from file, one line at a time
size_t num_vals_read = 0;
for(i=0; i<num_rows; i++) {
num_vals_read = getline(&line, &num_read, in);
if(num_vals_read != num_cols + 1) { //account for newline character
fprintf(stderr, "Maze file format invalid. Found %d chars on line %d and the width is %d.\n",
(num_vals_read-1), i, new_maze->width);
return NULL;
}
// parse out line
int j;
for(j=0; j<num_cols; j++) {
if(line[j] != ' ' && line[j] != '*') {
fprintf(stderr, "Maze file format invalid. Maze file data must contain spaces and stars. Read %c.\n", line[j]);
return NULL;
}
new_maze->data[i][j] = line[j];
}
}
// try to read more data
char c;
if((c = fgetc(in)) != EOF) {
fprintf(stderr, "Maze file format invalid. Too many characters past %d rows.\n", num_rows);
return NULL;
}
free(line);
return new_maze;
}
/******************************************************
free_maze
frees memory used by the_maze
******************************************************/
void free_maze(maze * the_maze) {
// first free the data
// need to free rows of data, then data
int i;
for(i = 0; i < the_maze->height; i++) {
free(the_maze->data[i]);
}
free(the_maze->data);
free(the_maze);
return;
}
Queue.c
#include <stdio.h>
#include <stdlib.h>
#include "queue.h"
/* initializes empty queue */
Queue initQueue() {
Queue q = malloc(sizeof(QueueType));
q->head = 0;
q->tail = 0;
return q;
}
/* returns 1 if queue is empty and 0 otherwise */
int emptyQueue(Queue Q) {
return (Q->head == Q->tail);
}
/* puts data item d into queue */
void enqueue(Queue Q, QueueData d) {
if(full(Q)) {
printf("Queue is full. Did not add item.\n");
return;
}
Q->tail++;
Q->tail = Q->tail % MAX_Q; // in case it goes off array
Q->data[Q->tail] = d;
}
/* removes data item from queue */
QueueData dequeue(Queue Q) {
if(emptyQueue(Q)) {
printf("Attempting to remove from empty queue\n");
exit(1);
}
Q->head++;
Q->head = Q->head % MAX_Q; // in case it goes off array
return Q->data[Q->head];
}
/* checks if queue is full */
int full(Queue Q) {
return (Q->tail + 1) % MAX_Q == Q->head;
}
/* freeQueue */
void freeQueue(Queue Q) {
free(Q);
}
Main.c
#include <stdio.h>
#include <stdlib.h>
#include "maze.h"
#define NUM_PARAMS 2
/* prototypes */
void usage(char * executable);
/***********************************************************************
* main
executable_name input_filename.txt
opens input_filename.txt for reading
creates maze object
runs the maze solver
frees maze
*********************************************************************/
// function completed for the CS 305 students: DO NOT MODIFY (Unless you find a bug)
int main(int argc, char * argv[]) {
if(argc != NUM_PARAMS) {
usage(argv[0]);
return EXIT_FAILURE;
}
// open file for reading
FILE *fp = NULL;
fp = fopen(argv[1], "r");
if(fp == NULL) {
fprintf(stderr, "Error opening input file %s. Exiting.\n", argv[1]);
return EXIT_FAILURE;
}
// create maze objects
// need 2 since we are running BFS on one (which modifies the
// maze with the path marker character)
// need clean copy of maze for DFS
maze * the_maze = create_maze(fp);
rewind(fp); // resets file pointer to beginning of file
maze * the_maze2 = create_maze(fp);
// done with file at this point
fclose(fp);
fp = NULL;
// check maze to see if it was created successfully
if(the_maze == NULL || the_maze2 == NULL) {
fprintf(stderr, "Error creating maze data structure\n");
return EXIT_FAILURE;
}
// run breadth-first-search on maze
printf("\nSolving using breadth-first search.\n");
int a = solve_bfs(the_maze);
// run depth-first-search on maze
printf("\nSolving using depth-first search:\n");
int b = solve_dfs(the_maze2);
printf("\ncan solve BFS: %d, can solve DFS: %d\n\n", a, b);
// free memory and exit
free_maze(the_maze);
free_maze(the_maze2);
return EXIT_SUCCESS;
}
/*********************************************************
usage
prints error message to user
**********************************************************/
void usage(char * executable) {
printf("Usage: \n%s maze_file.txt\n", executable);
}
stack.c
#include <stdio.h>
#include <stdlib.h>
#include "stack.h"
/* initializes a new stack */
Stack initStack() {
Stack s = (Stack) malloc(sizeof(StackType));
s->top = NULL;
return s;
}
/* empty returns 0 if S is empty and non-zero if S is not empty */
int emptyStack(Stack S) {
return (S->top == NULL);
}
/* pushes d to S */
void push(Stack S, StackData d) {
Node * n = (Node *)malloc(sizeof(Node));
n->data = d;
n->next = S->top;
S->top = n;
}
/* pops top item from S */
StackData pop(Stack S) {
if(emptyStack(S)) {
printf("Stack is empty. Attempting to pop an empty stack. Exiting program.\n");
exit(1); // exiting program
}
// there is data to pop
StackData toReturn = S->top->data;
Node * tmp = S->top; // in order to free this later
S->top = S->top->next; // move pointer to next item in stack
free(tmp);
return toReturn;
}
/* frees stack memory */
void freeStack(Stack S) {
while(!emptyStack(S)) {
pop(S);
}
free(S);
}
queue.h
#ifndef QUEUE_H
#define QUEUE_H
#include "maze.h"
#define MAX_Q 5000 // 1 more than what can be stored in the queue
// in this application, the mazes are on the small
// side
/* data to store into queue */
typedef coord QueueData; // putting coordinates into queue
/* queue data structure */
typedef struct QueueTag QueueType;
typedef struct QueueTag* Queue; // pointer to queue struct
// so when it is passed, the values
// can be updated in functions
struct QueueTag {
int head;
int tail;
QueueData data[MAX_Q]; // space for items in queue
};
/* function prototypes on queues */
Queue initQueue();
int emptyQueue(Queue Q);
void enqueue(Queue Q, QueueData d);
QueueData dequeue(Queue Q);
int full(Queue Q);
void freeQueue();
#endif
maze.h
#ifndef MAZE_H
#define MAZE_H
/* struct definition for coord */
typedef struct coord {
int row;
int col;
} coord;
/* struct definition for maze */
typedef struct maze {
coord entry;
coord exit;
int width;
int height;
char ** data;
} maze;
/* prototypes */
/* in maze.c */
maze * create_maze(FILE *in);
void free_maze(maze * the_maze);
/* in solve.c */
int solve_bfs(maze * the_maze);
int solve_dfs(maze * the_maze);
void print_maze(maze * the_maze);
coord * make_coord(int r, int c);
void print_coord(coord c);
#endif
stack.h
#ifndef STACK_H
#define STACK_H
#include "maze.h"
#define BAD {-1, -1} // coordinate off maze
/* data to store into stack */
typedef coord StackData;
/* stack data structures */
typedef struct NodeTag Node;
typedef struct StackTag StackType;
typedef struct StackTag* Stack;
/* linked list implementation of stacks */
struct NodeTag {
StackData data;
Node *next;
};
struct StackTag {
Node * top;
};
/* function prototypes on stacks */
Stack initStack();
int emptyStack(Stack S);
void push(Stack S, StackData d);
StackData pop(Stack S);
void freeStack(Stack S);
#endif
you need to fix your bound checking, assuming zero based indexing
# incorrect
to_explore.col+1 =< the_maze->width
# correct
to_explore.col+1 < the_maze->width
similar is the case for checking of to_explore.rowemphasized text
The error is in print_maze function.
void print_maze(maze * the_maze){
the_maze->data[the_maze->entry.row][the_maze->entry.col]='S';
the_maze->data[the_maze->exit.row][the_maze->exit.col]='F';
for(int i=0; i<the_maze->width; i++){
for(int j=0; j<the_maze->height; j++){
printf("%s",the_maze->data[i][j]);
}
}
data[i] represents ith row, so i should iterate over height.
data[i][j] represents jth element in ith row, so j should iterate over width. You need to swap height and width in the for loop.
void print_maze(maze * the_maze){
the_maze->data[the_maze->entry.row][the_maze->entry.col]='S';
the_maze->data[the_maze->exit.row][the_maze->exit.col]='F';
for(int i=0; i<the_maze->height; i++){
for(int j=0; j<the_maze->width; j++){
printf("%s",the_maze->data[i][j]);
}
}
}

Manipulating structs with a void function in C

so I've been set a task of creating a faux string struct and implementing all the usual string functions on my faux string struct. I'm stuck on the tests of my strcat implementation called append, with the first test failing (segfault) being the 5th line. My function for creating new structs should be OK because it passed all the tests, but I've included it just incase.
I've already been able to successfully implement length, get, set and copy functions for my faux string structs.
The struct:
struct text {
int capacity;
char *content;
};
typedef struct text text;
My function for creating new structs:
text *newText(char *s) {
printf("new Text from %s\n", s);
int sizeNeeded = (strlen(s)+1);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
text *out = malloc(sizeGot);
char *c = malloc(sizeGot);
strcpy(c, s);
out->content = c;
out->capacity = (sizeGot);
printf("the capacity is %d\n", sizeGot);
return out;
free(c);
}
My append function:
void append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %d\n", t1->content, *t2->content);
int sizeNeeded = (t1->capacity + t2->capacity);
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot = sizeGot * 2;
}
char *stringy = calloc(sizeGot, 32);
stringy = strcat(t1->content, t2->content);
free(t1);
t1 = newText(stringy);
}
and finally the tests:
void testAppend() {
text *t = newText("car");
text *t2 = newText("pet");
append(t, t2);
assert(like(t, "carpet"));
assert(t->capacity == 24);
text *t3 = newText("789012345678901234");
append(t, t3);
assert(like(t, "carpet789012345678901234"));
assert(t->capacity == 48);
freeText(t);
freeText(t2);
freeText(t3);
}
You are allocating memory in the wrong way. You could fix this by using a flexible array member like this:
typedef struct {
int capacity;
char content[];
} text;
text *out = malloc(sizeof(text) + sizeof(something));
strcpy(out->content, str);
...
And obviously code such as this is nonsense:
return out;
free(c);
}
Enable compiler warnings and listen to them.
Och, some errors you have:
Inside text_new you allocate memory for text *out using text *out = malloc(sizeGot); when sizeGot = 24 is a constant value. You should allocate sizeof(*out) or sizeof(text) bytes of memory for it.
I don't know what for int sizeGot = 24; while (sizeNeeded > sizeGot) the loop inside text_new and append is for. I guess the intention is to do allocations in power of 24. Also it mostly looks like the same code is in both functions, it does look like code duplication, which is a bad thing.
Inside append You pass a pointer to t1, not a double pointer, so if you modify the t1 pointer itself the modification will not be visible outside of function scope. t1 = newText(stringy); is just pointless and leaks memory. You could void append(text **t1, text *t2) and then *t1 = newText(stringy). But you can use a way better approach using realloc - I would expect append to "append" the string, not to create a new object. So first resize the buffer using realloc then strcat(&t1->content[oldcapacity - 1], string_to_copy_into_t1).
int sizeNeeded = (t1->capacity + t2->capacity); is off. You allocate capacity in power of 24, which does not really interact with string length. You need to have strlen(t1->content) + strlen(t2->content) + 1 bytes for both strings and the null terminator.
Try this:
size_t text_newsize(size_t sizeNeeded)
{
// I think this is just `return 24 << (sizeNeeded / 24);`, but not sure
int sizeGot = 24;
while (sizeNeeded > sizeGot) {
sizeGot *= 2;
}
return sizeGot;
}
text *newText(char *s) {
printf("new Text from %s\n", s);
if (s == NULL) return NULL;
int sizeNeeded = strlen(s) + 1;
int sizeGot = text_newsize(sizeNeeded);
text *out = malloc(sizeof(*out));
if (out == NULL) {
return NULL;
}
out->content = malloc(sizeGot);
if (out->content == NULL) {
free(out);
return NULL;
}
strcpy(out->content, s);
out->capacity = sizeGot;
printf("the capacity is %d\n", sizeGot);
return out;
}
and this:
int append(text *t1, text *t2) {
printf("t1 content is %s, t2 content is %s\n", t1->content, t2->content);
int sizeNeeded = strlen(t1->content) + strlen(t2->content) + 1;
if (t1->capacity < sizeNeeded) {
// this could a text_resize(text*, size_t) function
int sizeGot = text_newsize(sizeNeeded);
void *tmp = realloc(t1->content, sizeGot);
if (tmp == NULL) return -ENOMEM;
t1->content = tmp;
t1->capacity = sizeGot;
}
strcat(t1->content, t2->content);
return 0;
}
Some remarks:
Try to handle errors in your library. If you have a function like void append(text *t1, text *t2) let it be int append(text *t1, text *t2) and return 0 on success and negative number on *alloc errors.
Store the size of everything using size_t type. It's defined in stddef.h and should be used to represent a size of an object. strlen returns size_t and sizeof also returns size_t.
I like to put everything inside a single "namespace", I do that by prepending the functions with a string like text_.
I got some free time and decided to implement your library. Below is the code with a simple text object storing strings, I use 24 magic number as allocation chunk size.
// text.h file
#ifndef TEXT_H_
#define TEXT_H_
#include <stddef.h>
#include <stdbool.h>
struct text;
typedef struct text text;
text *text_new(const char content[]);
void text_free(text *t);
int text_resize(text *t, size_t newsize);
int text_append(text *to, const text *from);
int text_append_mem(text *to, const void *from, size_t from_len);
const char *text_get(const text *t);
int text_append_str(text *to, const char *from);
char *text_get_nonconst(text *t);
size_t text_getCapacity(const text *t);
bool text_equal(const text *t1, const text *t2);
#endif // TEXT_H_
// text.c file
//#include "text.h"
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <assert.h>
struct text {
size_t capacity;
char *content;
};
text *text_new(const char content[])
{
text * const t = malloc(sizeof(*t));
if (t == NULL) goto MALLOC_ERR;
const struct text zero = {
.capacity = 0,
.content = NULL,
};
*t = zero;
if (content != NULL) {
const int ret = text_append_str(t, content);
if (ret) {
goto TEXT_APPEND_ERR;
}
}
return t;
TEXT_APPEND_ERR:
free(t);
MALLOC_ERR:
return NULL;
}
void text_free(text *t)
{
assert(t != NULL);
free(t->content);
free(t);
}
int text_resize(text *t, size_t newcapacity)
{
// printf("%s %d -> %d\n", __func__, t->capacity, newcapacity);
// we resize in chunks
const size_t chunksize = 24;
// clap the capacity into multiple of 24
newcapacity = (newcapacity + chunksize - 1) / chunksize * chunksize;
void * const tmp = realloc(t->content, newcapacity);
if (tmp == NULL) return -ENOMEM;
t->content = tmp;
t->capacity = newcapacity;
return 0;
}
int text_append_mem(text *to, const void *from, size_t from_len)
{
if (to == NULL || from == NULL) return -EINVAL;
if (from_len == 0) return 0;
const size_t oldcapacity = to->capacity == 0 ? 0 : strlen(to->content);
const size_t newcapacity = oldcapacity + from_len + 1;
int ret = text_resize(to, newcapacity);
if (ret) return ret;
memcpy(&to->content[newcapacity - from_len - 1], from, from_len);
to->content[newcapacity - 1] = '\0';
return 0;
}
int text_append_str(text *to, const char *from)
{
if (to == NULL || from == NULL) return -EINVAL;
return text_append_mem(to, from, strlen(from));
}
int text_append(text *to, const text *from)
{
if (to == NULL || from == NULL) return -EINVAL;
if (text_getCapacity(from) == 0) return 0;
return text_append_str(to, text_get(from));
}
const char *text_get(const text *t)
{
return t->content;
}
const size_t text_strlen(const text *t)
{
return t->capacity == 0 ? 0 : strlen(t->content);
}
size_t text_getCapacity(const text *t)
{
return t->capacity;
}
bool text_equal_str(const text *t, const char *str)
{
assert(t != NULL);
if (str == NULL && t->capacity == 0) return true;
const size_t strlength = strlen(str);
const size_t t_strlen = text_strlen(t);
if (t_strlen != strlength) return false;
if (memcmp(text_get(t), str, strlength) != 0) return false;
return true;
}
// main.c file
#include <stdio.h>
int text_testAppend(void) {
text *t = text_new("car");
if (t == NULL) return -1;
text *t2 = text_new("pet");
if (t2 == NULL) return -1;
if (text_append(t, t2)) return -1;
assert(text_equal_str(t, "carpet"));
assert(text_getCapacity(t) == 24);
text *t3 = text_new("789012345678901234");
if (t3 == NULL) return -1;
if (text_append(t, t3)) return -1;
assert(text_equal_str(t, "carpet789012345678901234"));
assert(text_getCapacity(t) == 48);
text_free(t);
text_free(t2);
text_free(t3);
return 0;
}
int main()
{
text *t1 = text_new("abc");
text_append_str(t1, "def");
printf("%s\n", text_get(t1));
text_free(t1);
printf("text_testAppend = %d\n", text_testAppend());
return 0;
}

Designing a generic hash

I am trying to implement a generic hash structure that can support any type of data and any hash function.
A wrote the code and try to run it, it dosn't work, it breaks. I try to debug it and there it works well. I don't know where the problem is?
Here is the code that I used for implementing the structure:
The "hash.h" file:
typedef struct tip_hash_nod
{
void *info;
struct tip_hash_nod *urm;
}NOD_LISTA_HASH;
typedef struct
{
NOD_LISTA_HASH *Table;
int size;
int sizeMemory;
int (*hash)(const void *obiect,const int m);
void (*distruge)(void *obiect);
}*HASH;
void initializare_hash(HASH *h,int size,int (*hash_dat)(const void *obiect,const int m),void (*distruge)(void *obiect));
int hash_insert(HASH *h,void *obiect,int sizeOfObiect);
int hash_search(HASH h,void *obiect,int (*compara)(const void *a,const void *b));
void hash_delete(HASH *h);
And the "hash.c" file:
void initializare_hash(HASH *h,int size,int (*hash_dat)(const void *obiect,const int m),void (*distruge)(void *obiect))
{
int i;
(*h) = (HASH)malloc(sizeof(HASH));
(*h)->sizeMemory = size;
if(size != 0)
{
(*h)->Table = (NOD_LISTA_HASH *)malloc((*h)->sizeMemory * sizeof(NOD_LISTA_HASH));
for(i=0;i<(*h)->sizeMemory;i++)
{
(*h)->Table[i].info = NULL;
(*h)->Table[0].urm = NULL;
}
}
else
{
(*h)->Table = (NOD_LISTA_HASH *)malloc(sizeof(NOD_LISTA_HASH));
(*h)->Table[0].info = NULL;
(*h)->Table[0].urm = NULL;
(*h)->sizeMemory = 1;
}
(*h)->size = 0;
(*h)->hash = hash_dat;
(*h)->distruge = distruge;
}
int hash_insert(HASH *h,void *obiect,int sizeOfObiect)
{
int i,poz;
NOD_LISTA_HASH *p;
if((*h)->size == (*h)->sizeMemory)
{
HASH h1;
initializare_hash(&h1,2*(*h)->sizeMemory,(*h)->hash,(*h)->distruge);
for(i=0;i<(*h)->sizeMemory;i++)
{
if((*h)->Table[i].info != NULL)
hash_insert(&h1,(*h)->Table[i].info,sizeOfObiect);
p=(*h)->Table[i].urm;
while(p!=NULL)
{
hash_insert(&h1,p->info,sizeOfObiect);
p = p->urm;
}
}
hash_delete(h);
*h=h1;
return hash_insert(h,obiect,sizeOfObiect);
}
else
{
poz = (*h)->hash(obiect,(*h)->sizeMemory);
if((*h)->Table[poz].info == NULL)
{
(*h)->Table[poz].info = malloc(sizeOfObiect);
memcpy((*h)->Table[poz].info,obiect,sizeOfObiect);
(*h)->Table[poz].urm = NULL;
(*h)->size++;
}
else
{
p = &((*h)->Table[poz]);
while(p->urm!=NULL)
p = p->urm;
p->urm = (NOD_LISTA_HASH *)malloc(sizeof(NOD_LISTA_HASH));
p = p->urm;
p->info = malloc(sizeOfObiect);
memcpy(p->info,obiect,sizeOfObiect);
p->urm = NULL;
}
return poz;
}
}
int hash_search(HASH h,void *obiect,int (*compara)(const void *a,const void *b))
{
int poz;
NOD_LISTA_HASH *p;
poz = h->hash(obiect,h->sizeMemory);
if(h->Table[poz].info == NULL)
return -1;
else
if(compara(h->Table[poz].info,obiect)==0)
return poz;
else
{
p=h->Table[poz].urm;
while(p != NULL)
{
if(compara(p->info,obiect)==0)
return poz;
p = p->urm;
}
return -1;
}
}
static void distruge_lista(NOD_LISTA_HASH *p,void (*distruge_obiect)(void *obiect))
{
if(p->urm != NULL)
distruge_lista(p->urm,distruge_obiect);
else
{
if(p->info != NULL)
distruge_obiect(p->info);
free(p);
}
}
void hash_delete(HASH *h)
{
int i;
for(i=0;i<(*h)->sizeMemory;i++)
{
if((*h)->Table[i].info != NULL && (*h)->Table[i].urm != NULL)
{
distruge_lista((*h)->Table[i].urm,(*h)->distruge);
}
}
free((*h)->Table);
*h = NULL;
}
And this is my "main.c" file:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include "hash.h"
int comparare(const void *a,const void *b)
{
return (*(int *)a - *(int *)b);
}
int hash(const void *obiect,int m)
{
return (*(int *)obiect) % m;
}
void distruge_obiect(void *obiect)
{
free((int *)obiect);
}
int main()
{
HASH h;
int val,error;
initializare_hash(&h,0,hash,distruge_obiect);
val = 20;
hash_insert(&h,&val,sizeof(int));
val = 800;
hash_insert(&h,&val,sizeof(int));
val = 2000;
hash_insert(&h,&val,sizeof(int));
val = 765;
hash_insert(&h,&val,sizeof(int));
val = 800;
error = hash_search(h,&val,comparare);
if(error == -1)
printf("Elementul %d nu se afla in hash.\n",val);
else
printf("Elementul %d se afla pe pozitia: %d.\n",val,error);
hash_delete(&h);
getch();
return 0;
}
How I already sad if I try to debug it works with no problem, but when I run it, it crashes. I can onely make an assumption that it can not dealocate the memory or something. My call stack loocks like this:
You've dropped a pretty big pile of code on us, without much to go on. I had a quick look anyway, and noticed this incorrect allocation:
(*h) = (HASH)malloc(sizeof(HASH));
HASH is a pointer type, so you are allocating only enough memory for one pointer. You want to allocate memory for the thing to which it points:
*h = malloc(sizeof(**h));
(The cast is not required in C, and some folks around here will be strident about not using one.)
That error would be entirely enough to cause all manner of bad behavior. In particular, the erroneous code might seem to work until you dynamically allocate more memory and write to that, so perhaps that explains why your tests crash on the second insertion.

Resources