I am solving binary tree paths leet code programming question 257. I am having issue for one of the larger input where my code is getting segmentation fault. I suspect that there is an problem with my realloc but I am not able to figure it out.
Below is my approach:
Initially I started by dynamically allocating 80 bytes of memory of type char (80/8 = 10 rows)and storing the returned address to char **res variable.
char ** res = (char **)malloc(sizeof(char *) * sum);
I am calling findpath function recursively to find all the binary tree paths. Whenever one path is found , I dynamic allocate 100 bytes for each row index.
res[resIdx] = (char *)malloc(sizeof(char) * 100);
I have one global variable resIdx which points to the current row index where I copy the found binary tree path and increment the global variable resIdx.
if the resIdx becomes greater then total number of rows which was previously allocated then I do realloc of the memory but it looks like realloc is getting failed.
if (resIdx >= sum)
{
sum = sum + 10;
res = (char **)realloc(res,sizeof(char *) * sum); //Any issue here?
}
Can anyone please help me to figure out what's wrong I am doing in my code. Below is my full code
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* struct TreeNode *left;
* struct TreeNode *right;
* };
*/
/**
* Note: The returned array must be malloced, assume caller calls free().
*/
int sum;
int resIdx;
void findpath (struct TreeNode* root, int *ls,int ls_idx,char **res);
char ** binaryTreePaths(struct TreeNode* root, int* returnSize){
if (root == NULL)
{
*returnSize = 0;
return NULL;
}
resIdx = 0;
sum = 10;
char ** res = (char **)malloc(sizeof(char *) * sum);
int ls[100];
findpath(root,&ls[0],0,res);
*returnSize = resIdx;
return &res[0];
}
void findpath (struct TreeNode* root, int *ls,int ls_idx,char **res)
{
char temp[100];
int l=0,i=0;
if (root->left == NULL && root->right == NULL)
{
ls[ls_idx] = root->val;
ls_idx+=1;
if (resIdx >= sum)
{
sum = sum + 10;
res = (char **)realloc(res,sizeof(char *) * sum);
}
res[resIdx] = (char *)malloc(sizeof(char) * 100);
while (i < ls_idx)
{
if (i==0)
{
l = l + sprintf(&temp[l], "%d", ls[i]);
}
else
{
l = l + sprintf(&temp[l], "->%d", ls[i]);
}
i++;
}
strcpy(res[resIdx],temp);
resIdx++;
return;
}
ls[ls_idx] = root->val;
if (root->left != NULL)
{
findpath(root->left,ls,ls_idx+1,res);
}
if (root->right != NULL)
{
findpath(root->right,ls,ls_idx+1,res);
}
return;
}
The last argument to your findPath function is declared as a char** type; thus, when you make the call findpath(root,&ls[0],0,res); in binaryTreePaths, where the res variable is a char** type, a copy of that pointer is passed to the findPath function (most likely, but not necessarily, by placing that copy on the stack).
Then, if reallocation is required, the res = (char **)realloc(res,sizeof(char *) * sum); line in that function overwrites the value in the passed copy and, at the same time (if the call is successful – vide infra), will (probably) invalidate (i.e. free) the memory referenced by the previous address in that res copy. Thus, when control returns to the calling binaryTreePaths function, its own version of res will not have been modified and will remain pointing to that (now invalid) memory.
So, in order for your findPath function to be able to modify the given res argument, that must be passed as a pointer – in this case, a pointer to a char**, which will be of type char***; then, when called, you will need to pass the address of the res variable in binaryTreePaths.
Note also that directly overwriting a pointer in a call to realloc, as you have done in the line of code quoted above is dangerous. This is because, should that call fail, then you have lost the original data pointer (it will have been overwritten with NULL) and error recovery will be very difficult. You should save the return value in a temporary variable and only replace your original if the call succeeds.
With the code you have provided, I cannot properly test for any other errors but, taking the points above in hand, the below is a possible fix. See also: Do I cast the result of malloc?
int sum;
int resIdx;
void findpath(struct TreeNode* root, int* ls, int ls_idx, char*** res); // Note last argument type!
char** binaryTreePaths(struct TreeNode* root, int* returnSize)
{
if (root == NULL) {
*returnSize = 0;
return NULL;
}
resIdx = 0;
sum = 10;
char** res = malloc(sizeof(char*) * sum);
int ls[100];
findpath(root, &ls[0], 0, &res); // Pass ADDRESS of res
*returnSize = resIdx;
return &res[0];
}
void findpath(struct TreeNode* root, int* ls, int ls_idx, char*** res)
{
char temp[100];
int l = 0, i = 0;
if (root->left == NULL && root->right == NULL) {
ls[ls_idx] = root->val;
ls_idx += 1;
if (resIdx >= sum) {
sum = sum + 10;
char** test = realloc(*res, sizeof(char*) * sum);
if (test == NULL) {
// Handle/signal error
return;
}
*res = test; // Only replace original if realloc succeeded!
}
(*res)[resIdx] = malloc(sizeof(char) * 100);
while (i < ls_idx) {
if (i == 0) {
l = l + sprintf(&temp[l], "%d", ls[i]);
}
else {
l = l + sprintf(&temp[l], "->%d", ls[i]);
}
i++;
}
strcpy((*res)[resIdx], temp);
resIdx++;
return;
}
ls[ls_idx] = root->val;
if (root->left != NULL) {
findpath(root->left, ls, ls_idx + 1, res);
}
if (root->right != NULL) {
findpath(root->right, ls, ls_idx + 1, res);
}
return;
}
Related
For some reason, when I realloc an array to append an item to the array, it only works twice before segfaulting. The segfault occurs when I try to print the strings inside of the array. I currently have an array which is NULL terminated.
void apparr(char** arr, char* line) {
int length = 0;
// find the length of the array
while(arr[length] != NULL) {
length++;
}
// realloc with 2 extra spaces (1 for line, 1 for NULL)
arr = realloc(arr, sizeof(char*) * (length+2));
// set last element (which was NULL) to line
arr[length] = line;
// set the NULL terminator
arr[length+1] = NULL;
}
I have no clue where I could be going wrong here, my only guess would be with how I am calling realloc. However, I would understand that not working for 1 resize, but I have no clue why this works for two resizes then segfaults when I am printing back the array.
How it could be used in main:
int main(int argc, char** argv){
char** hist = malloc(sizeof(char**));
char* linep1;
char* linep2;
char* linep3;
char* linep4;
linep1 = (char*)malloc(strlen("test")*sizeof(char));
linep2 = (char*)malloc(strlen("test2")*sizeof(char));
linep3 = (char*)malloc(strlen("test3")*sizeof(char));
linep4 = (char*)malloc(strlen("test4")*sizeof(char));
strcpy(linep1, "test");
strcpy(linep2, "test2");
strcpy(linep3, "test3");
strcpy(linep4, "test4");
apphist(hist, linep1);
apphist(hist, linep2);
//apphist(hist, linep3); //uncommenting this line causes nothing to be printed
//apphist(hist, linep4); //uncommenting this line causes only test4 to be printed
int x = 0;
while (hist[x] != NULL) {
printf("%s\n", hist[x]);
x++;
}
}
In the main function you need to set the first element of hist as NULL as you are checking it in the function apphist
char** hist = malloc(sizeof(char*));
*hist = NULL;
The function apphist only changes the value of arr locally. To reflect the changes in the main function, you need to pass a pointer to a arr i.e. a 3D pointer char ***arr
You should always check the result of realloc and perform actions on failure.
Code for the function is below.
void apparr(char*** arr2, char* line) {
int length = 0;
char **arr = *arr2;
while(arr[length] != NULL) {
length++;
}
arr = realloc(arr, sizeof(char*) * (length+2));
if (arr == NULL) {
exit(1); // handle error
}
*arr2 = arr;
arr[length] = line;
arr[length+1] = NULL;
}
Alternatively, you can return a pointer to a pointer to char and update the value in main.
char** apparr(char** arr, char* line) {
int length = 0;
char **temp;
while(arr[length] != NULL) {
length++;
}
temp = realloc(arr, sizeof(char*) * (length+2));
if (temp == NULL) {
exit(1); // handle error
}
arr = temp;
arr[length] = line;
arr[length+1] = NULL;
return (arr);
}
//in main
hist = apphist(hist, linep1);
hist = apphist(hist, linep2);
I think you should dereference the arr before you use in realloc. Another observation; the sizeof(char*) is usually 4 in 32 bit architecture and 8 in 64 bit instead of 1.
For the general case I think you only need to call realloc with length +1
arr = realloc(arr, sizeof(char*) * (length+1));
This is because you already had space for the NULL terminating pointer from the prior state. With the code you are proposing what's happening is something like this
// state prior to realloc
String String NULL
// apparr() call
String String NULL undefined undefined // realloc
String String String undefined undefined // arr[length] = line;
String String String NULL undefined // arr[length+1] = NULL;
The first time it will work (leaving a dangling allocated node at the end) but the second time it can crash in multiple ways due to the extra allocation.
All errors and may pitfalls have been mentioned by others already.
Below find a more general implementation of the append-element-to-array function:
#include <stdlib.h>
#include <errno.h> /* for EINVAL */
int apparr(char *** parr, char * line) {
size_t length = 0;
if (NULL == *parr) {
if (NULL != line) {
errno = EINVAL;
return -1;
}
} else {
// find the length of the array
while (NULL != (*parr)[length]) {
++length;
}
}
{
// realloc with 2 extra spaces (1 for line, 1 for NULL)
void * pv = realloc(*parr, (length+1) * sizeof **parr);
if (NULL == pv) {
return -1; /* By convention -1 indicates failure. */
}
*parr = pv;
}
(*parr)[length] = line;
if (0 < length) {
(*parr)[length + 1] = NULL;
}
return 0; /* By convention 0 indicates success. */
}
And use it like this:
#include <stdlib.h>
#include <stdio.h>
int apparr(char *** parr, char * line) {
int main(int argc, char** argv) {
char ** hist = NULL;
char * linep1;
char * linep2;
char * linep3;
char * linep4;
if (-1 == apparr(&hist, NULL)) {
perror("apphist() failed initially\n");
exit(EXIT_FAILURE);
}
linep1 = malloc(strlen("test") + 1);
linep2 = malloc(strlen("test2") + 1); /* +1 for the c-string's 0-termination; sizeof (char) is 1 by definition */
linep3 = malloc(strlen("test3") + 1);
linep4 = malloc(strlen("test4") + 1);
strcpy(linep1, "test");
strcpy(linep2, "test2");
strcpy(linep3, "test3");
strcpy(linep4, "test4");
if (-1 == apphist(&hist, linep1)) {
perror("apphist() failed for line 1\n");
exit(EXIT_FAILURE);
}
if (-1 == apphist(&hist, linep2) {
perror("apphist() failed for line 2\n");
exit(EXIT_FAILURE);
}
if (-1 == apphist(&hist, linep3) {
perror("apphist() failed for line 3\n");
exit(EXIT_FAILURE);
}
if (-1 == apphist(&hist, linep4) {
perror("apphist() failed for line 4\n");
exit(EXIT_FAILURE);
}
{
size_t x = 0;
while (hist[x] != NULL) {
printf("%s\n", hist[x]);
++x;
}
}
}
You have several errors in your code:
This is your main error. You don't replace the value in the given pointer. It's correct to use a pointer to the pointer, but you need to dereference it. For this you need to pass the pointer to hist and dereference it at the re-allocation:
*arr = realloc(*arr, sizeof(char*) * (length + 2));
The list of pointers is not initialized, after the first allocation you need to set the first pointer:
hist[0] = NULL;
The allocations for your test strings are 1 off:
linep1 = malloc((strlen("test") + 1) * sizeof(char));
linep2 = malloc((strlen("test2") + 1) * sizeof(char));
linep3 = malloc((strlen("test3") + 1) * sizeof(char));
linep4 = malloc((strlen("test4") + 1) * sizeof(char));
Additional notes:
The includes are missing for a complete minimal reproducable example.
The name apparr() is wrong, you call apphist() in main().
Check the return values of any allocation for NULL, meaning that the allocation failed.
You don't use argc and argv, so write int main(void)
The first allocation has the "wrong" type, but both are pointers, so it's the same size: char** hist = malloc(sizeof(char*));
There is no need to cast pointers returned by malloc() as it returns a pointer to void. Pointers to void and other pointers can be assigned back and forth without casts.
You can replace the malloc()/strcpy() pairs with strdup().
You can even call apphist() with the string as "immediate" value like this: apphist(hist, "test");
main() should return an int, EXIT_SUCCESS is the right value.
You can put some const at the parameters and declarations to make things safer. But think about what is constant.
In my below code I am trying to create a dynamically expandable array of memory.
#include <stdio.h>
#include <stdlib.h>
#define BLOCKSIZE 5
int hash_table_length = 0;
int *currentblock = NULL;
int size_left;
int *hash_table = NULL;
int *start = NULL;
int *create_hash_table() {
int *tmp;
if (currentblock == NULL || size_left == 0) {
if (currentblock == NULL) {
currentblock = (int *) malloc( BLOCKSIZE * sizeof(int));
start = currentblock;
size_left = BLOCKSIZE;
} else {
currentblock = (int *) malloc( BLOCKSIZE * sizeof(int));
size_left = BLOCKSIZE;
}
}
tmp = currentblock++;
size_left -= 1;
return tmp;
}
void build() {
int hash;
int i = 0;
for (i = 0; i < 20; i++) {
hash = i + 3;
if (hash_table_length == 0) {
hash_table = create_hash_table();
hash_table_length++;
} else {
hash_table = create_hash_table();
hash_table_length++;
}
hash_table = &hash;
printf("hash value is %d\n", *hash_table);
}
}
int main() {
build();
// How do I reach the start of the hash table again?
// the below start does not give me the first value
printf("Hash table first value is %d\n", *start);
return 0;
}
My problem here is I wish to traverse through the values stored in the hash_table. I am unable to reach to the first element/address of the hash_table. I wish to print out all the values stored in my hash table. How can this be done?
In your code the hash values never get stored inside the hash table(inside currentblock). Inside the create_hash_table() function you allocate memory for a new block but never store values inside this block. Thus if you try dereferencing any of these int* locations you might get a garbage value(which may be a 0).
This is what is precisely happening inside your main() function when you dereference the start pointer. It is infact pointing to the start of the hash table and as that location is uninitialized it gives an output of 0.
To actually store values inside the hash table change the following inside build():
hash_table = &hash;
to:
*hash_table = hash; // Store value of 'hash' inside the memory location pointed to by hash table(which happens to be 'current_block' inside build())
Now if you try running the code, it will output 3.
Coming to the second part of question as to how you'll traverse the entire hash table: It cannot be done using this code. This is because there is no linkage between your malloc'd blocks of integers. The malloc() call can assign any block of free memory from the heap. Thus in the current form you have disconnected blocks of locations which cannot be traversed.
Instead of malloc you can use realloc to increase the size of your current block. realloc allocates memory for the larger block and copies your previous data to this new block. This will essentially allow you to traverse the entire hash table using start.
Here is how you might do that:
#include <stdio.h>
#include <stdlib.h>
#define BLOCKSIZE 5
int hash_table_length = 0;
int *currentblock = NULL;
int size_left;
int *hash_table = NULL;
int *start = NULL;
int *create_hash_table() {
int *tmp;
if (currentblock == NULL || size_left == 0) {
if (currentblock == NULL) {
currentblock = (int *) malloc(BLOCKSIZE * sizeof(int));
start = currentblock;
size_left = BLOCKSIZE;
} else {
/* Call realloc() to allocate new memory block of size (hash_table_length+BLOCKSIZE) and copy previous data*/
currentblock = ((int *) realloc(start,(hash_table_length + BLOCKSIZE) * sizeof(int))) + hash_table_length;
size_left = BLOCKSIZE;
}
}
tmp = currentblock++;
size_left -= 1;
return tmp;
}
void build() {
int hash;
int i = 0;
for (i = 0; i < 20; i++) {
hash = i + 3;
if (hash_table_length == 0) {
hash_table = create_hash_table();
hash_table_length++;
} else {
hash_table = create_hash_table();
hash_table_length++;
}
/* Store value of hash inside the hash_table */
*hash_table = hash;
printf("hash value is %d\n", *hash_table);
}
}
int main() {
int i;
build();
printf("Hash table first value is %d\n", *start);
/* Traverse the hash table */
for(i = 0; i < hash_table_length; ++i)
printf("hash_table[%d] = %d\n",i,*start++);
return 0;
}
In my binary search tree I want to create a function that can get all words starting with a prefix and store all words in an array called results
this is my tree
struct BinarySearchTree_t
{
char *mot,*def;
struct BinarySearchTree_t *left;
struct BinarySearchTree_t *right;
};
typedef struct BinarySearchTree_t BinarySearchTree;
my function :
size_t findWordsByPrefix(BinarySearchTree* tree, char* prefix, char*** results)
{
BinarySearchTree *tmp;
tmp=tree;
static int size=0;
if (!tmp)
return 0;
else if (strncmp(tmp->mot,prefix,strlen(prefix))==0)
{
(*results)= realloc(*results,(1+size)*sizeof(*(*results)));
(*(*results+size))= malloc(strlen(tmp->mot)*sizeof(char));
strcpy((*results)[size],tmp->mot);
size++;
return (1 + findWordsByPrefix(tmp->left,prefix, &results) + findWordsByPrefix(tmp->right,prefix, &results));
}
else
return (strncmp(tmp->mot,prefix,strlen(prefix))<0)?findWordsByPrefix(tmp->right,prefix, &results):findWordsByPrefix(tmp->left,prefix, &results) ;
}
This function should return a number of words starting with the given prefix.
my problem is that the program crash when it is run , and I don't how to resize my array results
so every time I found a word I should increase the size of the results array .
and I would know how exacly manipulate the pointer of pointer of pointer given in arg of this function (char ***results) : what exactly means?
If I simply compile your code, I get severe compiler warnings including:
1>binarysearchtree.c(98) : warning C4047: 'function' : 'char ***' differs in levels of indirection from 'char ****'
1>binarysearchtree.c(98) : warning C4024: 'findWordsByPrefix' : different types for formal and actual parameter 3
This alone will cause a crash -- you are calling your own function recursively with the wrong arguments.
Next, I believe you need to allocate one more than the length of the string, to hold a copy of a string:
malloc((strlen(tmp->mot) + 1 )*sizeof(char))
Next, you're passing around an array of strings of variable size -- and storing the size in a static variable. It's impossible to know if this will work, so don't do it.
Instead, if you want to use a dynamic array of strings, I suggest extracting out a struct to hold them, like so:
struct ResultTable_t
{
int size;
char **results;
};
typedef struct ResultTable_t ResultTable;
void InitializeResults(ResultTable *p_table)
{
p_table->size = 0;
p_table->results = NULL;
}
void AddResult(ResultTable *p_table, char *result)
{
if (result == NULL)
return;
p_table->size++;
p_table->results = realloc(p_table->results, p_table->size * sizeof(*p_table->results));
p_table->results[p_table->size-1] = malloc((strlen(result) + 1) * sizeof(**p_table->results));
strcpy(p_table->results[p_table->size-1], result);
}
void FreeResults(ResultTable *p_table)
{
if (p_table->results != NULL)
{
int i;
for (i = 0; i < p_table->size; i++)
{
free(p_table->results[i]);
}
free(p_table->results);
}
p_table->size = 0;
p_table->results = NULL;
}
(As an improvement, you might consider using geometric growth instead of linear growth for your table of results.)
Then your function becomes:
size_t findWordsByPrefix(BinarySearchTree* tree, char* prefix, ResultTable *p_table)
{
if (!tree)
return 0;
else if (strncmp(tree->mot,prefix,strlen(prefix))==0)
{
AddResult(p_table, tree->mot);
return (1 + findWordsByPrefix(tree->left,prefix, p_table) + findWordsByPrefix(tree->right,prefix, p_table));
}
else if (strncmp(tree->mot,prefix,strlen(prefix))<0)
{
return findWordsByPrefix(tree->right,prefix, p_table);
}
else
{
return findWordsByPrefix(tree->left,prefix, p_table);
}
}
And you would use it like:
ResultTable results;
InitializeResults(&results);
// Get some prefix to search for.
char prefix = GetSomePrefix();
int size = findWordsByPrefix(tree, prefix, &results);
// Do something with the results
// Free all memory of the results
FreeResults(&results);
Update
If the ResultTable is distasteful for some reason, you can pass the dynamic array and array sizes in directly:
void AddResult(char ***p_results, int *p_size, char *word)
{
if (word == NULL)
return;
(*p_size)++;
(*p_results) = realloc(*p_results, ((*p_size)+1) * sizeof(**p_results));
(*p_results)[(*p_size)-1] = malloc((strlen(word) + 1) * sizeof(***p_results));
strcpy((*p_results)[(*p_size)-1], word);
}
void FreeResults(char ***p_results, int *p_size)
{
int i;
if (p_results == NULL || *p_results == NULL)
return;
for (i = 0; i < (*p_size); i++)
{
free ((*p_results)[i]);
}
free (*p_results);
*p_results = NULL;
*p_size = 0;
}
size_t findWordsByPrefix(BinarySearchTree* tree, char* prefix, char ***p_results, int *p_size)
{
if (!tree)
return 0;
else if (strncmp(tree->mot,prefix,strlen(prefix))==0)
{
AddResult(p_results, p_size, tree->mot);
return (1 + findWordsByPrefix(tree->left,prefix, p_results, p_size) + findWordsByPrefix(tree->right,prefix, p_results, p_size));
}
else if (strncmp(tree->mot,prefix,strlen(prefix))<0)
{
return findWordsByPrefix(tree->right,prefix, p_results, p_size);
}
else
{
return findWordsByPrefix(tree->left,prefix, p_results, p_size);
}
}
and use like:
char **results = NULL;
int tablesize = 0;
// Get some prefix to search for.
char prefix = GetSomePrefix();
int size = findWordsByPrefix(tree, prefix, &results, &tablesize);
// Do something with the results
// Free all memory of the results
FreeResults(&results, &tablesize);
I know this question might sound quite stupid, but I've tried my best and I can't seem to have solved issues with this code:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
int ind = 0;
char** yay = strsplit(code, ";");
char* a = *yay;
while (a != NULL) {
puts(a);
if (strncmp(a, "#", 1)) {
struct bacon_statement statm;
int valid = bacon_make_statement(a, statm);
if (valid != 0) { return valid; }
statms[sta] = statm;
}
sta++;
*(yay)++;
a = *yay;
}
puts("Running BACON INTERNAL MAKE");
int ret = bacon_internal_make(statms, internal);
printf("%d\n", ret);
return ret;
It segfaults when returning, since it the printf is executed alright., and another printf call /after/ the function is called (on int main()) doesn't print anything at all.
I'm sorry if this sounds too specific, but I don't know where else to get help.
Apparently you are corrupting the stack somehow.
your line does not initialize the pointer:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
but you write to it like this, which will have undefined behavior:
statms[sta] = statm;
Easiest way to fix is probably to restore the malloc (provided the size is correct)
struct bacon_statement* statms = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
Seems like a stack overrun, you keep assigning data to the buffer (statms) without checking for boundaries. Sooner or later, the return address (which is on the stack) will be overridden and when return is reached, the address to return to will be corrupted. and there is your segmentation fault.
Try doing something like:
int count = 0;
char *ptr = strchr(code, ';');
while (ptr) {
count++;
ptr = strchr(ptr + 1, ';');
}
to predict the number of statements, and then:
struct bacon_statement* statms = (struct bacon_statement*) malloc(
count * sizeof(struct bacon_statement));
to allocate enough bacon_statement slots. Alternatives are probably more difficult: a linked list or other mutable structure; or using realloc to increase the size of the array, keeping a note of how many sots you have left.
The mistake can still be in the other functions though!
The code does too much. Let me summarise:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
int ind = 0;
char** yay = strsplit(code, ";");
char* a;
while ((a = *yay)) {
puts(a);
if (strncmp(a, "#", 1)) {
struct bacon_statement statm;
int valid = bacon_make_statement(a, statm);
if (valid != 0) { return valid; }
statms[sta] = statm;
}
sta++;
*(yay)++;
}
puts("Running BACON INTERNAL MAKE");
int ret = bacon_internal_make(statms, internal);
printf("%d\n", ret);
return ret;
But we can get more compact than that:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
int ind = 0;
char** yay;;
char* a;
for (yay = strsplit(code, ";"); (a = *yay); *(yay)++ ) {
puts(a);
if (strncmp(a, "#", 1)) {
struct bacon_statement statm;
int valid = bacon_make_statement(a, statm);
if (valid != 0) { return valid; }
statms[sta] = statm;
}
sta++;
}
puts("Running BACON INTERNAL MAKE");
int ret = bacon_internal_make(statms, internal);
printf("%d\n", ret);
return ret;
Now let's remove the silly stringcompare:
struct bacon_statement* statms;// = (struct bacon_statement*)malloc(3000 * sizeof(struct bacon_statement));
int ind = 0;
char** yay;
char* a;
for (yay = strsplit(code, ";"); (a = *yay); *(yay)++ ) {
puts(a);
if (*a != '#') {
struct bacon_statement statm;
int valid = bacon_make_statement(a, statm);
if (valid != 0) { return valid; }
statms[sta] = statm;
}
sta++;
}
puts("Running BACON INTERNAL MAKE");
int ret = bacon_internal_make(statms, internal);
printf("%d\n", ret);
return ret;
Still does not make sense. IMO the OP wants to loop trough an array of string-pointers (yay) and process each string in it. Especially the *(yay)++ looks awkward.
Maybe with the '#' he wants to skip comments. I'd expect something like:
sta=0;
for (yay = strsplit(code, ";"); (a = *yay); yay++ ) {
int err;
if (*a == '#') continue;
/* make bacon from a */
err = bacon_make_statements(a, statms[sta] );
if (err) return err;
sta++; /* could overflow ... */
}
/* you need the number of assigned struct members ("sta") to this function */
return bacon_internal_make(statms,sta internal);
On second thought, my guess is that the strsplit() function return a pointer to an automatic ("stack") variable. Or the *yay variable is incremented beyond recognition. Or the statms[] array is indexed out of bounds.
Is it possible that you meant to use ind instead of sta as the array index variable and sta is uninitialized?
I am trying to write a Huffman encoding program to compress a text file. Upon completetion, the program will terminate at the return statement, or when I attempt to close a file I was reading from. I assume I have memory leaks, but I cannot find them. If you can spot them, let me know (and a method for fixing them would be appreciated!).
(note: small1.txt is any standard text file)
Here is the main program
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define ASCII 255
struct link {
int freq;
char ch[ASCII];
struct link* right;
struct link* left;
};
typedef struct link node;
typedef char * string;
FILE * ofp;
FILE * ifp;
int writebit(unsigned char);
void sort(node *[], int);
node* create(char[], int);
void sright(node *[], int);
void Assign_Code(node*, int[], int, string *);
void Delete_Tree(node *);
int main(int argc, char *argv[]) {
//Hard-coded variables
//Counters
int a, b, c = 0;
//Arrays
char *key = (char*) malloc(ASCII * sizeof(char*));
int *value = (int*) malloc(ASCII * sizeof(int*));
//File pointers
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "can't open %s\n", argv[1]);
return 0;
}
//Nodes
node* ptr;//, *head;
node* array[ASCII];
//
int u, carray[ASCII];
char str[ASCII];
//Variables
char car = 0;
int inList = 0;
int placeinList = -1;
int numofKeys;
if (argc < 2) {
printf("Usage: huff <.txt file> \n");
return 0;
}
for (a = 0; a < ASCII; a++) {
key[a] = -1;
value[a] = 0;
}
car = fgetc(fp);
while (!feof(fp)) {
for (a = 0; a < ASCII; a++) {
if (key[a] == car) {
inList = 1;
placeinList = a;
}
}
if (inList) {
//increment value array
value[placeinList]++;
inList = 0;
} else {
for (b = 0; b < ASCII; b++) {
if (key[b] == -1) {
key[b] = car;
break;
}
}
}
car = fgetc(fp);
}
fclose(fp);
c = 0;
for (a = 0; a < ASCII; a++) {
if (key[a] != -1) {
array[c] = create(&key[a], value[a]);
numofKeys = c;
c++;
}
}
string code_string[numofKeys];
while (numofKeys > 1) {
sort(array, numofKeys);
u = array[0]->freq + array[1]->freq;
strcpy(str, array[0]->ch);
strcat(str, array[1]->ch);
ptr = create(str, u);
ptr->right = array[1];
ptr->left = array[0];
array[0] = ptr;
sright(array, numofKeys);
numofKeys--;
}
Assign_Code(array[0], carray, 0, code_string);
ofp = fopen("small1.txt.huff", "w");
ifp = fopen("small1.txt", "r");
car = fgetc(ifp);
while (!feof(ifp)) {
for (a = 0; a < ASCII; a++) {
if (key[a] == car) {
for (b = 0; b < strlen(code_string[a]); b++) {
if (code_string[a][b] == 48) {
writebit(0);
} else if (code_string[a][b] == 49) {
writebit(1);
}
}
}
}
car = fgetc(ifp);
}
writebit(255);
fclose(ofp);
ifp = fopen("small1.txt", "r");
fclose(ifp);
free(key);
//free(value);
//free(code_string);
printf("here1\n");
return 0;
}
int writebit(unsigned char bitval) {
static unsigned char bitstogo = 8;
static unsigned char x = 0;
if ((bitval == 0) || (bitval == 1)) {
if (bitstogo == 0) {
fputc(x, ofp);
x = 0;
bitstogo = 8;
}
x = (x << 1) | bitval;
bitstogo--;
} else {
x = (x << bitstogo);
fputc(x, ofp);
}
return 0;
}
void Assign_Code(node* tree, int c[], int n, string * s) {
int i;
static int cnt = 0;
string buf = malloc(ASCII);
if ((tree->left == NULL) && (tree->right == NULL)) {
for (i = 0; i < n; i++) {
sprintf(buf, "%s%d", buf, c[i]);
}
s[cnt] = buf;
cnt++;
} else {
c[n] = 1;
n++;
Assign_Code(tree->left, c, n, s);
c[n - 1] = 0;
Assign_Code(tree->right, c, n, s);
}
}
node* create(char a[], int x) {
node* ptr;
ptr = (node *) malloc(sizeof(node));
ptr->freq = x;
strcpy(ptr->ch, a);
ptr->right = ptr->left = NULL;
return (ptr);
}
void sort(node* a[], int n) {
int i, j;
node* temp;
for (i = 0; i < n - 1; i++)
for (j = i; j < n; j++)
if (a[i]->freq > a[j]->freq) {
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
void sright(node* a[], int n) {
int i;
for (i = 1; i < n - 1; i++)
a[i] = a[i + 1];
}
If your program is crashing on what is otherwise a valid operation (like returning from a function or closing a file), I'll near-guarantee it's a buffer overflow problem rather than a memory leak.
Memory leaks just generally mean your mallocs will eventually fail, they do not mean that other operations will be affected. A buffer overflow of an item on the stack (for example) will most likely corrupt other items on the stack near it (such as a file handle variable or the return address from main).
Probably your best bet initially is to set up a conditional breakpoint on writes to the file handles. This should happen in the calls to fopen and nowhere else. If you detect a write after the fopen calls are finished, that will be where your problem occurred, so just examine the stack and the executing line to find out why.
Your first problem (this is not necessarily the only one) lies here:
c = 0;
for (a = 0; a < ASCII; a++) {
if (key[a] != -1) {
array[c] = create(&key[a], value[a]);
numofKeys = c; // DANGER,
c++; // WILL ROBINSON !!
}
}
string code_string[numofKeys];
You can see that you set the number of keys before you increment c. That means the number of keys is one less than you actually need so that, when you access the last element of code_string, you're actually accessing something else (which is unlikely to be a valid pointer).
Swap the numofKeys = c; and c++; around. When I do that, I at least get to the bit printing here1 and exit without a core dump. I can't vouch for the correctness of the rest of your code but this solves the segmentation violation so anything else should probably go in your next question (if need be).
I can see one problem:
strcpy(str, array[0]->ch);
strcat(str, array[1]->ch);
the ch field of struct link is a char array of size 255. It is not NUL terminated. So you cannot copy it using strcpy.
Also you have:
ofp = fopen("small1.txt.huff", "w");
ifp = fopen("small1.txt", "r");
If small1.txt.huff does not exist, it will be created. But if small1.txt it will not be created and fopen will return NULL, you must check the return value of fopen before you go and read from the file.
Just from counting, you have 4 separate malloc calls, but only one free call.
I would also be wary of your sprintf call, and how you are actually mallocing.
You do an sprintf(buf, "%s%d", buf, c[i]) but that can potentially be a buffer overflow if your final string is longer than ASCII bytes.
I advise you to step through with a debugger to see where it's throwing a segmentation fault, and then debug from there.
i compiled the program and ran it with it's source as that small1.txt file and got "can't open (null)" if the file doesn't exist or the file exist and you give it on the command like ./huf small1.txt the program crashes with:
Program terminated with signal 11, Segmentation fault.
#0 0x08048e47 in sort (a=0xbfd79688, n=68) at huf.c:195
195 if (a[i]->freq > a[j]->freq) {
(gdb) backtrace
#0 0x08048e47 in sort (a=0xbfd79688, n=68) at huf.c:195
#1 0x080489ba in main (argc=2, argv=0xbfd79b64) at huf.c:99
to get this from gdb you run
ulimit -c 100000000
./huf
gdb --core=./core ./huf
and type backtrace
You have various problems in your Code:
1.- mallocs (must be):
//Arrays
char *key = (char*) malloc(ASCII * sizeof(char));
int *value = (int*) malloc(ASCII * sizeof(int));
sizeof(char) == 1, sizeof(char *) == 4 or 8 (if 64 bits compiler is used).
2.- Buffer sizes 255 (ASCII) is too short to receive the contents of array[0]->ch + array[1]->ch + '\0'.
3.- Use strncpy instead of strcpy and strncat instead of strcat.
4.- key is an array of individuals chars or is a null terminated string ?, because you are using this variable in both ways in your code. In the characters counting loop you are using this variables as array of individuals chars, but in the creation of nodes you are passing the pointer of the array and copying as null terminated array.
5.- Finally always check your parameters before used it, you are checking if argc < 2 after trying to open argv[1].