Segmentation fault passing struct pointer to function - c

I'm pretty newb in C and in order to learn about structs I'm building a program which provides a limited set of functionality for a struct ll_string which basically is a linkedlist of strings.
The set of functions I'm trying to implement includes an insert_ll_string() function which should concanate a passed in struct ll_string element to the end of another struct ll_string element but fails to do so because the moment the function is called in my test cases, the program crashes with a sig fault. This is at the STILL WORKS and SIG FAULT comments of the test_insert() function.
This is its header file:
file: ll_string.h
struct ll_string {
char *string;
struct ll_string *next;
};
struct ll_string *create_ll_string(char *, struct ll_string *);
void insert_ll_string(struct ll_string *, struct ll_string *);
void remove_item_from_ll_string(struct ll_string *, struct ll_string *);
void free_ll_string(struct ll_string *);
void print_ll_string(struct ll_string *);
and this is it's corresponding .c file missing a few definitions for functions declared in ll_string.h, but I guess my problem probably only revolves around the functions create_ll_string() and insert_ll_string() anyways.
file: ll_string.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ll_string.h"
/* create_ll_string: allocates memory for a new struct ll_string and
* initializes it with given arguments returns a pointer to new struct */
struct ll_string *create_ll_string(char *string, struct ll_string *next) {
struct ll_string *new_ll_string;
if (!string) {
printf("string can\'t be NULL\n");
return NULL;
}
if (*string == '\0') {
printf("string needs to be at least 1 char long\n");
return NULL;
}
if (!(new_ll_string = (struct ll_string *) malloc(sizeof(struct ll_string)))) {
printf("couldn\'t allocate mem for new ll_string\n");
exit(EXIT_FAILURE);
}
new_ll_string->string = strdup(string);
new_ll_string->next = next;
return new_ll_string;
}
/* insert_ll_string: concanates item to the end of dest */
void insert_ll_string(struct ll_string *dest, struct ll_string *item) {
struct ll_string *cur;
if (!dest) {
printf("dest and item can\'t be NULL\n");
return;
}
if (!item) {
printf("item can\'t be NULL\n");
return;
}
cur = dest;
while (!cur->next) {
cur = cur->next;
}
cur->next = item;
return ;
}
/* remove_item_from_ll_string: removes item from list src */
void remove_item_from_ll_string(struct ll_string *src, struct ll_string *item) {
return ;
}
/* printf_ll_string: prints each string in ll_string */
void print_ll_string(struct ll_string *ll_string) {
if (!ll_string) {
printf("ll_string is NULL\n");
return ;
}
do {
printf("%s\n", ll_string->string);
} while (!(ll_string = ll_string->next));
}
/* free_ll_string: frees all memory pointed to by ll_string */
void free_ll_string(struct ll_string *ll_string) {
struct ll_string *next;
if (!ll_string) {
return ;
}
while ((next = ll_string->next)) {
free(ll_string->string);
free(ll_string);
ll_string = next;
}
}
and here are my tests. Everything works fine until insert_ll_struct() is evoked by the test_insert() function. (test_create() works as expected) Tests are done using the MinUnit framework.
file: tests_ll_string.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "minunit.h"
#include "ll_string.h"
#define MAX_ERROR_MSG_LENGTH 1000
int tests_run = 0;
static char *test_create(void) {
struct ll_string *test_ll;
struct ll_string *test_null_ll;
char *empty_string = strdup("");
char *null_string = NULL;
char *correct_string = strdup("this should work");
char *correct_string2 = strdup("this should also work");
char *error_msg;
if (!(error_msg = (char *) malloc(sizeof(char) * MAX_ERROR_MSG_LENGTH))) {
printf("couldn\'t allocate mem for error msg");
exit(EXIT_FAILURE);
}
// test_ll->string == correct_string
// test_ll->next == NULL
test_ll = create_ll_string(correct_string, NULL);
sprintf(error_msg, "error, test_ll->string != \"%s\" is %s", correct_string, test_ll->string);
mu_assert(
error_msg,
strcmp(test_ll->string, correct_string) == 0);
// test_ll->next->string == correct_string
// test_ll->string == correct_string2
test_ll = create_ll_string(correct_string2, test_ll);
sprintf(error_msg, "error, test_ll->string != \"%s\" is %s", correct_string2, test_ll->string);
mu_assert(
error_msg,
strcmp(test_ll->string, correct_string2) == 0);
sprintf(error_msg, "error, test_ll->next->string != \"%s\" is \"%s\"", correct_string, test_ll->next->string);
mu_assert(
error_msg,
strcmp(test_ll->next->string, correct_string) == 0);
test_null_ll = test_ll;
test_null_ll = create_ll_string(empty_string, test_ll);
// test_null_ll == NULL
mu_assert(
"error, test_null_ll != NULL",
test_null_ll == NULL);
test_null_ll = test_ll;
test_null_ll = create_ll_string(null_string, test_ll);
// test_null_ll == NULL
mu_assert(
"error, test_null_ll != NULL",
test_null_ll == NULL);
sprintf(error_msg, "error, test_ll->string != \"%s\" is \"%s\"", correct_string2, test_ll->string);
mu_assert(
error_msg,
strcmp(test_ll->string, correct_string2) == 0);
sprintf(error_msg, "error, test_ll->next->string != \"%s\" is \"%s\"", correct_string, test_ll->next->string);
mu_assert(
error_msg,
strcmp(test_ll->next->string, correct_string) == 0);
free_ll_string(test_ll);
free(correct_string);
free(correct_string2);
free(empty_string);
free(error_msg);
return 0;
}
static char *test_insert(void) {
struct ll_string *ll_test1;
struct ll_string *ll_test2;
struct ll_string *ll_test3;
char *test_string1 = strdup("test_string1");
char *test_string2 = strdup("test_string2");
char *test_string3 = strdup("test_string3");
char *error_msg;
if (!(error_msg = (char *) malloc(sizeof(char) * MAX_ERROR_MSG_LENGTH))) {
printf("couldn\'t allocate mem for error msg");
exit(EXIT_FAILURE);
}
ll_test1 = create_ll_string(test_string1, NULL);
ll_test2 = create_ll_string(test_string2, NULL);
ll_test3 = create_ll_string(test_string3, NULL);
// STILL WORKS
insert_ll_string(ll_test1, ll_test2); // SEG FAULT
insert_ll_string(ll_test1, ll_test3);
sprintf(error_msg, "error, ll_test1->string != \"%s\" is \"%s\"", test_string1, ll_test1->string);
mu_assert(
error_msg,
strcmp(ll_test1->string, test_string1) == 0);
sprintf(error_msg, "error, ll_test1->next->string != \"%s\" is \"%s\"", test_string2, ll_test1->next->string);
mu_assert(
error_msg,
strcmp(ll_test1->next->string, test_string2) == 0);
sprintf(error_msg, "error, ll_test1->next->next->string != \"%s\" is \"%s\"", test_string1, ll_test1->next->next->string);
mu_assert(
error_msg,
strcmp(ll_test1->next->next->string, test_string3) == 0);
free_ll_string(ll_test1);
free_ll_string(ll_test2);
free_ll_string(ll_test3);
free(test_string1);
free(test_string2);
free(test_string3);
return 0;
}
static char *all_tests(void) {
mu_run_test(test_create);
mu_run_test(test_insert);
return 0;
}
int main(int argc, char* argv[]) {
char *result = all_tests();
if (result != 0) {
printf("%s\n", result);
} else {
printf("ALL TESTS PASSED\n");
}
printf("Tests run: %d\n", tests_run);
return result != 0;
}
and this is the output of compilation and execution:
>> gcc -Wall -o test ll_string.c tests_ll_string.c
>> ./test
string needs to be at least 1 char long
string can't be NULL
[1] 6789 segmentation fault (core dumped) ./test
What's causing this Sigmentation fault? I'm not accessing any memory besides local variables in the section the program crashes. I'm not dereferencing the pointers I'm passing to insert_ll_struct() at least not immediately after the function has been evoked.
Thanks in advance for the help

I think the answer is staring us in the face. In insert_ll_string() :
while (!cur->next) {
should be
while (cur->next) {

I'd look at the logic in free_ll_string(). Are you sure you aren't freeing memory twice? In the code it looks like if frees up all strings in the chain. Therefore I think you will free test_ll multiple times in test_create. See if you still get the error when disabling test_create, and if not then you issue I think is probably resulting from undefined behaviour because you are free-ing things more than once...
It is good practice to set any freed pointer to NULL after freeing the memory that it is pointing to, then you will avoid this problem.
/* free_ll_string: frees all memory pointed to by ll_string */
void free_ll_string(struct ll_string *ll_string) {
struct ll_string *next;
if (!ll_string) {
return ;
}
while ((next = ll_string->next)) {
free(ll_string->string);
free(ll_string);
ll_string = next;
}
}

while (!cur->next) { cur = cur->next; }
This code will cause crash suppose cur->next return null than you are trying to acess null next that is invalid you should put null check for cur in while loop like thiswhile (null!=cur&& !cur->next)

Related

custom malloc, segmentation fault

I'm doing a custom malloc. I did a very simple one but now I'm trying to merge and split blocks in order to improve the efficiency of calls to sbrk(). when I try to execute a custom program with not many mallocs it works perfectly. But as soon as I try more mallocs or for example the command ls after some successful allocations, it ends giving a weird segmentation fault (core dumped) when calling the split function.
Any help or hint would be greatly appreciated.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include "struct.h"
static p_meta_data first_element = NULL;
static p_meta_data last_element = NULL;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#define ALIGN8(x) (((((x)-1)>>3)<<3)+8)
#define MAGIC 0x87654321
void *malloc(size_t size_bytes);
p_meta_data search_available_space(size_t size_bytes);
p_meta_data request_space(size_t size_bytes);
p_meta_data merge(p_meta_data meta_data1, p_meta_data meta_data2);
void split(p_meta_data meta_data, size_t size_bytes);
void free(void *ptr);
void *calloc(size_t num_bytes, size_t num_blocs);
void *realloc(void *ptr, size_t size_bytes);
p_meta_data search_available_space(size_t size_bytes) {
p_meta_data current = first_element;
while (current && !(current->available && current->size_bytes >= size_bytes)){
fprintf(stderr, " %zu libre %d\n", current->size_bytes, current->available);
current = current->next;
}
if (current == NULL) {
fprintf(stderr, "null\n" );
} else {
fprintf(stderr, "%zu libre %d\n", current->size_bytes, current->available);
}
return current;
}
p_meta_data request_space(size_t size_bytes) {
if (size_bytes < 122880) {
size_bytes = 122880;
fprintf(stderr, "request %zu\n", size_bytes);
}
p_meta_data meta_data;
meta_data = (void *)sbrk(0);
if (sbrk(SIZE_META_DATA + size_bytes) == (void *)-1)
return (NULL);
meta_data->size_bytes = size_bytes;
meta_data->available = 0;
meta_data->magic = MAGIC;
meta_data->next = NULL;
meta_data->previous = NULL;
return meta_data;
}
p_meta_data merge(p_meta_data meta_data1, p_meta_data meta_data2) {
if (!meta_data1 || !meta_data2) {
return NULL;
}
meta_data1->size_bytes = meta_data1->size_bytes + SIZE_META_DATA + meta_data2->size_bytes;
meta_data1->next = meta_data2->next;
if (last_element == meta_data2) {
fprintf(stderr, "gleich\n");
last_element = meta_data1;
}
meta_data2 = NULL;
return meta_data1;
}
void free(void *ptr) {
p_meta_data meta_data;
if (!ptr)
return;
pthread_mutex_lock(&mutex);
meta_data = (p_meta_data)(ptr - SIZE_META_DATA);
if (meta_data->magic != MAGIC) {
fprintf(stderr, "ERROR free: value of magic not valid\n");
exit(1);
}
meta_data->available = 1;
fprintf(stderr, "Free at %x: %zu bytes\n", meta_data, meta_data->size_bytes);
p_meta_data meta_data_prev, meta_data_next;
meta_data_prev = meta_data->previous;
meta_data_next = meta_data->next;
if (meta_data_prev && meta_data_prev->available) {
meta_data = merge(meta_data_prev, meta_data);
}
if (meta_data_next && meta_data_next->available) {
meta_data = merge(meta_data, meta_data_next);
}
pthread_mutex_unlock(&mutex);
}
void split(p_meta_data meta_data, size_t size_bytes) {
if (!meta_data) {
fprintf(stderr, "no deberia entrar\n");
return;
}
p_meta_data meta_data2;
size_t offset = SIZE_META_DATA + size_bytes;
meta_data2 = (p_meta_data)(meta_data + offset);
fprintf(stderr, "size of metadata %d", meta_data->size_bytes - size_bytes - SIZE_META_DATA);
meta_data2->size_bytes = meta_data->size_bytes - size_bytes - SIZE_META_DATA;
meta_data2->available = 1;
meta_data2->magic = MAGIC;
meta_data2->previous = meta_data;
meta_data2->next = meta_data->next;
if (meta_data == last_element) {
last_element = meta_data2;
}
meta_data->size_bytes = size_bytes;
meta_data->next = meta_data2;
return;
}
void *malloc(size_t size_bytes) {
void *p, *ptr;
p_meta_data meta_data;
if (size_bytes <= 0) {
return NULL;
}
size_bytes = ALIGN8(size_bytes);
fprintf(stderr, "Malloc %zu bytes\n", size_bytes);
// Bloquegem perque nomes hi pugui entrar un fil
pthread_mutex_lock(&mutex);
meta_data = search_available_space(size_bytes);
if (meta_data) { // free block found
fprintf(stderr, "FREE BLOCK FOUND---------------------------------------------------\n");
meta_data->available = 0; //reservamos el bloque
} else { // no free block found
meta_data = request_space(size_bytes); //pedimos más espacio del sistema
if (!meta_data) //si meta_data es NULL (es decir, sbrk ha fallado)
return (NULL);
if (last_element) // we add the new block after the last element of the list
last_element->next = meta_data;
meta_data->previous = last_element;
last_element = meta_data;
if (first_element == NULL) // Is this the first element ?
first_element = meta_data;
}
fprintf(stderr, "die differenz %zu\n", meta_data->size_bytes - size_bytes);
if ((meta_data->size_bytes - size_bytes) > 12288) {
split(meta_data, size_bytes);
fprintf(stderr,"call split\n");
}
p = (void *)meta_data;
// Desbloquegem aqui perque altres fils puguin entrar
// a la funcio
pthread_mutex_unlock(&mutex);
// Retornem a l'usuari l'espai que podra fer servir.
ptr = p + SIZE_META_DATA; //p es puntero al inicio de meta_data, y ptr es el puntero al inicio del bloque de datos en sí (justo después de los metadatos)
return ptr;
}
void *calloc(size_t num_bytes, size_t num_blocs) {
size_t mem_to_get = num_bytes * num_blocs;
void *ptr = malloc(mem_to_get);
if (ptr == NULL) {
return ptr;
} else {
memset(ptr, 0, mem_to_get);
return ptr;
}
}
void *realloc(void *ptr, size_t size_bytes) {
fprintf(stderr, "realloc\n");
if (ptr == NULL) {
return malloc(size_bytes);
} else {
p_meta_data inic_bloc = (p_meta_data )(ptr - SIZE_META_DATA);
if (inic_bloc->size_bytes >= size_bytes) {
return ptr;
} else {
void *new_p = malloc(size_bytes);
memcpy(new_p, ptr, inic_bloc->size_bytes);
inic_bloc->available = 1;
return new_p;
}
}
}
where struct.h is:
#include <stddef.h>
#include <unistd.h>
#define SIZE_META_DATA sizeof(struct m_meta_data)
typedef struct m_meta_data *p_meta_data;
/* This structure has a size multiple of 8 */
struct m_meta_data {
size_t size_bytes;
int available;
int magic;
p_meta_data next;
p_meta_data previous;
};
Here are some remarks about your code:
it is confusing for the reader to hide pointers behind typedefs. Why not define m_meta_data as a typedef for struct m_meta_data and use m_meta_data * everywhere?
are you sure sbrk() is properly defined? The cast (void *)sbrk(0) seems to indicate otherwise. sbrk() is declared in <unistd.h> on POSIX systems.
BUG in split(), the computation meta_data2 = (p_meta_data)(meta_data + offset); is incorrect. It should be:
meta_data2 = (p_meta_data)((unsigned char *)meta_data + offset);
you should define strdup() and strndup() as the definitions from the C library might not call your redefined malloc():
char *strdup(const char *s) {
size_t len = strlen(s);
char *p = malloc(len + 1);
if (p) {
memcpy(p, s, len + 1);
}
return p;
}
char *strndup(const char *s, size_t n) {
size_t len;
char *p;
for (len = 0; len < n && s[n]; len++)
continue;
if ((p = malloc(len + 1)) != NULL) {
memcpy(p, s, len);
p[len] = '\0';
}
return p;
}
blocks allocated with malloc() should be aligned on 16-byte boundaries on 64-bit intel systems. As a matter of fact, the m_meta_data structure has a size of 32 bytes on 64-bit systems, but 20 bytes on 32-bit systems. You should adjust your m_meta_data structure for 32-bit systems.
you should check for overflow in size_t mem_to_get = num_bytes * num_blocs;
you should not rely on void * arithmetics, it is a gcc extension. Write this instead:
p_meta_data inic_bloc = (p_meta_data)ptr - 1;
in realloc(), when extending the size of the block, you just make the original block available but you do not coalesce it with adjacent blocks as you do in free(). You might just call free(ptr), especially since modifying inic_bloc->available = 1; without getting the lock seems risky.
you should check meta_data->available in free() and realloc() to detect invalid calls and prevent arena corruption.
in malloc(), you forget to release the lock in case of allocation failure.
calling fprintf while the lock is set is risky: if fprintf calls malloc, you would get a deadlock. You might assume that printing to stderr does not call malloc() because stderr is unbuffered, but you are taking chances.
when allocating a new block with sbrk(), you should use sbrk(0) after the allocation to determine the actual size made available as it may have been rounded up to a multiple of PAGE_SIZE.
you should split blocks if (meta_data->size_bytes - size_bytes) > SIZE_META_DATA. The current test is far too loose.

Deallocated memory functions C

I have problems with memory deallocation in C. Without the division of functions everything is OK, but unfortunately it does not work on the same functions. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct {
char *name;
enum {
summer,
winter
} index;
} student;
bool init(student *s) {
printf("The next stage in the allocation of memory\n");
s = (student*)malloc(sizeof(*s));
if (&s == NULL) {
printf("Allocation Failed\n");
return 0;
} else {
printf("Allocation completed successfully\n");
}
}
void delete(student *s) {
if (&s != NULL) {
printf("begin removal\n");
free(s);
printf("Released memory");
}
}
int main() {
student *s;
init(s);
delete(s);
return 0;
}
I do not know what I'm doing wrong. Please help.
First of all the function init has undefined bbehaviour because it returns nothing in the case of successful memory allocation.
You can check whether the memory was allocated or not by returning pointer to the allocated memory or NULL.
Also this statement
if(&s==NULL){
is wrong. The condition will always yield false because the address of the local variable s is not equal to NULL.
So the function can be rewritten either the following way
student * init()
{
printf("The next stage in the allocation of memory\n");
student *s = ( student* )malloc( sizeof( *s ) );
if ( s == NULL )
{
printf("Allocation Failed\n");
}
else
{
printf("Allocation completed successfully\n");
}
return s;
}
And called like
int main( void )
^^^^^
{
student *s = init();
//...
Or it can be defined the following way
int init( student **s )
{
printf("The next stage in the allocation of memory\n");
*s = ( student* )malloc( sizeof( **s ) );
int success = *s != NULL;
if ( !success )
{
printf("Allocation Failed\n");
}
else
{
printf("Allocation completed successfully\n");
}
return success;
}
and called like
int main( void )
^^^^^
{
student *s;
init( &s );
//...
The function delete should be defined at least like
void delete(student *s) {
if (s != NULL) {
^^^
printf("begin removal\n");
free(s);
printf("Released memory");
}
}
Firstly, free is NULL safe. If variable is NULL already, basically nothing happens. You do not have to check if it is NULL. (you can check page 313 ISO-IEC 9899 )
Also, when you initialize student->name and allocate, there will be memory leak. You have to free that too.
So, your delete function could be like this ;
void delete(student *s) {
printf("begin removal\n");
free(s->name);
free(s);
printf("Released memory");
}
And if (&s == NULL) is wrong. They must be changed with if (s == NULL).
Your allocation may cause really big troubles in the big codes. If you allocate s = (student*)malloc(sizeof(*s)); it means that "allocate s with size of *s". But pointer size is fixed memory block (mostly 8 bytes). It means that you blocks certain size of memory. If you have bigger struct than that, this kind of allocation will corrupt the memory and your executable will be killed by the OS(you can try add some more variables to your struct and initialize them). In small structs and very short runtimes, mostly this allocation works too. But i guarantee that this is not safe for run-time. And it will not give any warnings or errors in compile-time. True way is s = malloc(sizeof(student)). With this way, you exactly allocates all the memory blocks. And your memory stay safe in run-time.
Lastly, your init function should return the initialized variable. And your init function could be like this ;
#define NAME_LENGHT 128
...
student * init(student *s) {
printf("The next stage in the allocation of memory\n");
s = malloc(sizeof(student));
if (s == NULL) {
printf("Allocation Failed\n");
return NULL;
}
s->name = malloc(NAME_LENGHT);
if (s->name == NULL) {
printf("Allocation Failed\n");
return NULL;
} else {
printf("Allocation completed successfully\n");
}
//alternatively you can strdup directly without any allocation
// s->name = strdup("some name");
return s;
}
You never change the s in main.
Solution 1: Pass a pointer to the variable in main for init to populate.
void init_student(student** s_ptr) {
*s_ptr = (student*)malloc(sizeof(student));
if (*s_ptr == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
(*s_ptr)->name = malloc(MAX_NAME_SIZE + 1);
if ((*s_ptr)->name == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
(*s_ptr)->name[0] = 0;
(*s_ptr)->gpa = 0;
}
void delete_student(student* s) {
free(s->name);
free(s);
}
int main() {
student* s;
init_student(&s);
delete_student(s);
return 0;
}
Solution 1b: Same, but a cleaner implementation.
void init_student(student** s_ptr) {
student* s = (student*)malloc(sizeof(student));
if (*s_ptr == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name = malloc(MAX_NAME_SIZE + 1);
if (s->name == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name[0] = 0;
s->gpa = 0;
*s_ptr = s;
}
void delete_student(student* s) {
free(s->name);
free(s);
}
int main() {
student* s;
init_student(&s);
delete_student(s);
return 0;
}
Solution 2: Return the allocated point to main.
student* init_student() {
student* s = (student*)malloc(sizeof(student));
if (s == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name = malloc(MAX_NAME_SIZE + 1);
if (s->name == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name[0] = 0;
s->gpa = 0;
return s;
}
void delete_student(student* s) {
free(s->name);
free(s);
}
int main() {
student* s = init_student();
delete_student(s);
return 0;
}
Note that &s == NULL will never be true because &s is the address of the variable itself. You want just s == NULL to check the value of the pointer in s.

Trying to convert reverse polish notation to infix using an stack of strings, running into a small error and I don't know what's causing it

I've got a string stack implemented and I'm trying to convert rpn to infix using it. This is how the stack should look as the infix function works, for example if I entered 2 3 + 5 - 8 * :
2 //push 2
2,3 //push 3
(2+3) //reach operator, pop 2 and 3, format it, put result back on the stack
(2+3),5 //push 5
((2+3)-5) //reach operator, pop (2+3) and 5, format it, put result back
((2+3)-5),8 //push 8
(((2+3)-5)*8) //reach operator, pop ((2+3)-5) and 8, format, put result back
Sadly, this is not how the program has been working for me, and I think its something technical, not my algorithm.
When entering "2 5 A", it works, giving a result of "(2+5)". Trying "40 50 A", I get "(40+50)". However, when trying "50 100 A" I get "(+)". Longer expressions like "1 2 A 3 S" also gives me just "(-)". I just can't seem to figure out what's causing this. Please feel free to give a jab at it. Here is my code:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
/*options to later implement (-e for evaluate, -c for infix conversion, -g for mock assembly */
#define OP_EVAL "-e"
#define OP_INFX "-c"
#define OP_GENI "-g"
/* begin stack of strings implementation */
struct stack_entry {
char *data;
struct stack_entry *next;
};
struct stack_t
{
struct stack_entry *head;
size_t stackSize;
};
struct stack_t *newStack(void)
{
struct stack_t *stack = malloc(sizeof *stack);
if (stack)
{
stack->head = NULL;
stack->stackSize = 0;
}
return stack;
}
char *copyString(char *str)
{
char *tmp = malloc(strlen(str) + 1);
if (tmp)
strcpy(tmp, str);
return tmp;
}
void push(struct stack_t *theStack, char *value)
{
struct stack_entry *entry = malloc(sizeof *entry);
if (entry)
{
entry->data = copyString(value);
entry->next = theStack->head;
theStack->head = entry;
theStack->stackSize++;
}
else
{
printf("FAILURE\n");
}
}
char *top(struct stack_t *theStack)
{
if (theStack && theStack->head)
return theStack->head->data;
else
return NULL;
}
void pop(struct stack_t *theStack)
{
if (theStack->head != NULL)
{
struct stack_entry *tmp = theStack->head;
theStack->head = theStack->head->next;
free(tmp->data);
free(tmp);
theStack->stackSize--;
}
}
/*end stack of strings implementation */
/*begin argument handling for -c, -e and -g */
enum method
{
EVAL,
INFX,
GENI
};
int
is_option(const char *str)
{
return (strcmp(str, OP_EVAL) == 0) ||
(strcmp(str, OP_INFX) == 0) ||
(strcmp(str, OP_GENI) == 0);
}
enum method
manip_method(const char *arg)
{
if (strcmp(arg, OP_EVAL) == 0)
return EVAL;
else if (strcmp(arg, OP_INFX) == 0)
return INFX;
else if (strcmp(arg, OP_GENI) == 0)
return GENI;
else
return 0;
}
/* end of argument handling code (again, not even being used yet) */
/* gets operator, converts it, if not A, S, X, D, or M returns NULL */
char*
get_oper(char *op){
if(strcmp(op, "A") == 0)
return "+";
if(strcmp(op, "S") == 0)
return "-";
if(strcmp(op, "X") == 0)
return "*";
if(strcmp(op, "D") == 0)
return "\\";
if(strcmp(op, "M") == 0)
return "%";
else
return NULL;
}
/* formats an infix expression */
char*
formats(char *s1, char *s2, char* op){
int length = strlen(s1) + strlen(s2) + strlen(op) + 3;
char *buf = malloc(length);
strcpy(buf, "(");
strcat(buf, s2);
strcat(buf, op);
strcat(buf, s1);
strcat(buf, ")");
return buf;
}
/* 1) reads tokens and puts them on stack
2) when operator is reached, pops two elements off
the stack and formats it, then pushes the result onto the stack
3) after the loop ends, there should only be one element left on the
stack: the final infix expression */
char*
infix(int argc, char *argv[], int x){
int i;
char *format, *result, *s1, *s2, *op;
struct stack_t *stack = newStack();
for(i = x; i < argc; i++){
if(!get_oper(argv[i])){
push(stack, argv[i]);
} else {
s1 = top(stack);
pop(stack);
s2 = top(stack);
pop(stack);
op = get_oper(argv[i]);
format = formats(s1, s2, op);
push(stack, format);
}
}
result = top(stack);
pop(stack);
return result;
}
int
main(int argc, char *argv[])
{
char *result;
enum method method;
int x;
if (argc >= 4){
if (is_option(argv[1])){
if (argc < 5){
printf("not a valid calculatable expression, exiting now...\n");
exit (EXIT_FAILURE);
}
x = 2;
method = manip_method(argv[1]);
}
else {
x = 1;
method = EVAL;
}
} else {
printf("need option and or a valid reverse polish notation expression, exiting now...\n");
exit (EXIT_FAILURE);
}
result = infix(argc, argv, x);
printf("result: %s\n", result);
exit (EXIT_SUCCESS);
}
Edit: Here are some more examples of the programs behavior
mesquite$ rpcalc 1 5 A
result: (1+5)
mesquite$ rpcalc 1 100 S
result: (1-100)
mesquite$ rpcalc 1 2 A 3 D
result: (\)
mesquite$ rpcalc 10 100 M
result: (%)
Bluepixy has it: You free the data associated with the stack when you pop off elements, so this:
s1 = top(stack); // return handle to topmost item
pop(stack); // invalidate that handle
is a recipe for disaster, especially since you malloc in formats, which is likely to give you a handle to recetly freed memory. Your string concatenation might have the same source and destination buffers.
So remove the deallocation of the data, free(tmp->data), from pop and delegate it to the caller:
s1 = top(stack);
pop(stack);
if (s1 == NULL) exit(1); //Stack underflow
s2 = top(stack);
pop(stack);
if (s2 == NULL) exit(1); //Stack underflow
op = get_oper(argv[i]);
format = formats(s1, s2, op);
free(s1);
free(s2);
push(stack, format);
Of course, you must also free the final result:
result = infix(argc, argv, x);
printf("result: %s\n", result);
free(result);
Your current implementation doesn't guard against malformed expressions (e.g. "A 2 3", "1 2 A 3", "12 A 44"). These occur with stack underflow (which I've tried to repeir very crudely above) or when there is more than one item on the stack after procesing the RN expression.
You have mentioned a BST in a comment, although I think you mean an AST. It is probably a good idea to create a syntax tree from the RPN expression first. With that representation it is easy to generate infix notation, evaluate the expression and generate assembly.
That shouldn't be too difficult, because your formats function already creates a tree-like structure - left node, operator, right node - where the child nodes or strings max have further subexpressions.

pointer problems when trying to build a directory tree in memory

Problem 1: what's the best data structure to save the directory structure?
Problem 2: I have tried to use a general tree to solve it, but there are a lot of problems:
The number of files under a directory is not certain. So the number of child nodes under a tree node is also not certain. and I try to add a keyword nchild to each node, showing nchild child nodes. so there are nchild pointers (saved with **child) to the child nodes. And once that, **child and *child should be dynamically allocated space with no certain child nodes. So you know, this is really difficult to release these spaces(and the program below is not called free()). Is there a better way to solve it?
And sometimes the program below would get the garbage characters when I output the directory tree, which make me really confused. while debugging it, found that is the function ent=readdir(pDir); has read garbage characters. But when I write another simple program to read the same directory, that goes well. I think the problem is the recursive function, but I didn't get any idea. I will be appreciated if some one can give me a idea. Thanks!
```
#include <dirent.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
typedef struct tree_file_s
{
char path[512];
time_t date;
char type;
long size;
int nchild;
struct tree_file_s **child;
} tree_file_t;
int dir_child_len(const char *dir)
{
int nchild = 0;
DIR *pDir;
struct dirent *ent;
pDir = opendir(dir);
while((ent=readdir(pDir)) != NULL)
{
if (strcmp(ent->d_name, ".")==0 || strcmp(ent->d_name, "..")==0)
{
continue;
}
nchild++;
}
return nchild;
}
void tree_create(tree_file_t *tft, const char *dir)
{
int nchild; // the tft has n child
DIR *pDir;
struct dirent *ent; // the directory dir dirent info
struct stat file_stat; // the new file's stat info
stat(dir, &file_stat);
nchild = dir_child_len(dir);
pDir = opendir(dir);
// Initialize the parent
//tft->path = calloc(1, strlen(dir)+1);
strcpy(tft->path, dir);
tft->date = file_stat.st_mtime;
tft->type = 'D';
tft->size = file_stat.st_size;
tft->nchild = nchild;
tft->child = calloc(1, nchild);
nchild = 0;
while ((ent=readdir(pDir)) != NULL)
{
if (ent->d_type & DT_DIR)
{
if (strcmp(ent->d_name, ".")==0 || strcmp(ent->d_name, "..")==0)
{
continue;
}
tree_file_t *new_dir = calloc(1, sizeof(tree_file_t));
tft->child[nchild] = new_dir;
char *new_path = calloc(1, strlen(dir)+strlen(ent->d_name)+1);
sprintf(new_path, "%s/%s", dir, ent->d_name);
tree_create(new_dir, new_path);
free(new_path);
} else {
tree_file_t *new_file = calloc(1, sizeof(tree_file_t));
char *new_path = calloc(1, strlen(dir)+strlen(ent->d_name)+1);
// new_file->path = calloc(1, strlen(dir)+strlen(ent->d_name)+1);
sprintf(new_path, "%s/%s", dir, ent->d_name);
stat(new_path, &file_stat);
strcpy(new_file->path, new_path);
free(new_path);
new_file->date = file_stat.st_mtime;
new_file->type = 'F';
new_file->size = file_stat.st_size;
new_file->nchild = 0;
new_file->child = 0;
tft->child[nchild] = new_file;
}
//free(new_path);
//new_path = 0;
nchild++;
}
}
void display_tree(tree_file_t *tft)
{
int nchild, i;
nchild = tft->nchild;
printf("%c: %s\n", tft->type, tft->path);
for(i = 0; i < nchild; i++)
{
if(tft->child[i]->type == 'F')
{
printf("%c: %s\n", tft->child[i]->type, tft->child[i]->path);
} else {
display_tree(tft->child[i]);
}
}
}
int main(int argc, const char *argv[])
{
if(argc != 2)
{
printf("Usage: a.out dir\n");
exit(0);
}
char dir[512];
strcpy(dir, argv[1]);
tree_file_t *tft = calloc(1, sizeof(tree_file_t));
tree_create(tft, dir);
display_tree(tft);
return 0;
}
```
When you allocate space for new_path you need to add 2 (one for the slash, one for the null terminator). And you never close the directories you open (use closedir()).
An even more serious error is this line:
tft->child = calloc(1, nchild);
which only allocates nchild bytes, not enough to hold nchild pointers! Try:
tft->child = calloc(nchild, sizeof(*tft->child));

C array of pointers, casting and / or memory

I don't understand why these pointer values seem to be correct, but the values I am trying to get out of them are not. (I studied C a long time ago, and I am recently trying to get back into it for fun).
Here is a working example of the problem I am facing, but I am not sure what I am doing wrong - am I casting the pointers incorrectly, not correctly managing memory, or something else.
#include <unistd.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <stdlib.h>
#define Q_STACK_MAX 100
typedef struct
{
void* value[Q_STACK_MAX];
int top;
} Stack;
void push(Stack* S, void* val)
{
S->value[S->top] = val;
(S->top)++;
}
void* pop(Stack* S)
{
(S->top)--;
return (S->value[S->top]);
}
void init(Stack* S)
{
S->top = 0;
}
int full(Stack* S)
{
return (S->top >= Q_STACK_MAX);
}
void recursive_dir(char* dir, char* search, Stack* S)
{
DIR* dp;
struct dirent* entry;
struct stat statbuf;
if((dp = opendir(dir)) == NULL) {
fprintf(stderr, "error: %s\n", dir);
return;
}
chdir(dir);
while((entry = readdir(dp)) != NULL) {
lstat(entry->d_name, &statbuf);
if(S_ISDIR(statbuf.st_mode)) {
if(strcmp(".", entry->d_name) == 0 ||
strcmp("..", entry->d_name) == 0)
continue;
recursive_dir(entry->d_name, search, S);
} else {
if( strstr(entry->d_name, search) != NULL ) {
if(!full(S)) push(S, (void *)entry);
printf("%p\n", entry);
printf("%s [%x]\n", entry->d_name, entry->d_ino);
}
}
}
chdir("..");
closedir(dp);
}
int main() {
Stack S;
init(&S);
int i;
printf("\n");
recursive_dir("/etc/", "conf", &S);
printf("\n------------------------------\n");
int top = S.top;
struct dirent* entry;
for (i=0; i<top; i++)
{
//struct dirent* entry = (struct dirent*)pop(&S);
entry = (struct dirent*)S.value[i];
printf("%p\n", entry);
printf("%s [%x]\n", entry->d_name, entry->d_ino);
}
printf("\n");
return 0;
}
What this outputs are the pointer values to the dirent structures, the file name that is within that structure, and the file serial number. It then tries to cast the pointer to that direct to a (void*) and store it in a makeshift stack.
Later, I try to iterate over the values in the stack (well the array), cast it back to a dirent and then display the same information.
This works sometimes and doesn't work other times. A trimmed output example:
...
0x7ff831806360
httpd-multilang-errordoc.conf [2cb6e07]
0x7ff831806398
httpd-ssl.conf [2cb6e08]
0x7ff8318063c0
httpd-userdir.conf [2cb6e09]
0x7ff8318063ec
httpd-vhosts.conf [2cb6e0a]
0x7ff831805250
httpd.conf [2cb6e0b]
0x7ff831805274
httpd.conf~previous [187a3]
0x7ff831807230
httpd-autoindex.conf [2cb6e10]
0x7ff831807260
httpd-dav.conf [2cb6e11]
0x7ff831807288
httpd-default.conf [2cb6e12]
0x7ff8318072b4
httpd-info.conf [2cb6e13]
...
--------------------------
...
0x7ff831807360
httpd-multilang-errordoc.conf [2cb6e17]
0x7ff831807398
httpd-ssl.conf [2cb6e18]
0x7ff8318073c0
httpd-userdir.conf [2cb6e19]
0x7ff8318073ec
httpd-vhosts.conf [2cb6e1a]
0x7ff831806250
[0]
0x7ff831806230
320.whatis [2cb6ff6]
0x7ff8318042bc
asl.conf [2cb7d07]
0x7ff83180437c
autofs.conf [2cb6f5f]
0x7ff831805250
[0]
0x7ff831805274
??$ [61666564]
0x7ff8318052e0
_response [6e7261]
...
Sometimes everything seems correct, and other times the file handle and / or the name are completely wacked out.
Generally when I see something like that it is because I am not doing something with memory correctly - having lived in the garbage collection world for so long I wouldn't be surprised that was it, but I am not sure why or how.
Any help would be appreciated.
(I've been coding for quite a number of year, I've just been out of the C arena for quite a while. Please go easy on me.)
The dirent structure returned by readdir() can reuse memory on repeated calls. If you want to save them, you should either copy them into dynamic objects, or allocate the objects first and call readdir_r().

Resources