我意识到这个特殊的问题在这里被打死了,但我绝对不知道为什么这个错误仍在我身上发生。
请记住,这不是作业或其他任何内容,因此您在实施中可能会看到的逻辑缺陷/错误是由于缺乏编程经验;我没有,这是我第一次尝试实现典型的数据结构。我正在尝试自学编程,所以请理解我是否在任何地方都有任何明显的错误。
也就是说,这里是二元搜索树实现的源代码(我投入了其他一些垃圾内容;我试图像C ++的cin那样制作cin):
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <limits.h>
#include <stdint.h>
#define INIT_BUF_SIZE 1
#define INIT_STACK_SIZE 1
#define ERROR_MSG "The program could not allocate enough memory :[\n"
typedef enum {false, true} bool;
typedef struct Node {
int value;
int quantity;
struct Node *parent;
struct Node *left;
struct Node *right;
} Node;
typedef struct Node_Stack {
unsigned int size;
unsigned int max_size;
Node **s;
} Node_Stack;
typedef struct Count_Stack {
unsigned int size;
unsigned int max_size;
unsigned int *s;
} Count_Stack;
Node *root = NULL;
Node_Stack stack;
Count_Stack c_stack;
/********************* Stack Operations *********************/
bool init_c_stack (void) {
unsigned int *temp = calloc(INIT_STACK_SIZE, sizeof(int));
if (temp == NULL) {return false;}
c_stack.s = temp;
c_stack.size = 0;
c_stack.max_size = INIT_STACK_SIZE;
return true;
}
void delete_c_stack (void) {
c_stack.s = NULL;
c_stack.size = 0;
c_stack.max_size = 0;
free(c_stack.s);
}
bool init_stack (void) {
Node **temp = calloc(INIT_STACK_SIZE, sizeof(Node *));
if (temp == NULL) {return false;}
stack.s = temp;
stack.size = 0;
stack.max_size = INIT_STACK_SIZE;
return true;
}
void delete_stack (void) {
stack.s = NULL;
stack.size = 0;
stack.max_size = 0;
free(stack.s);
}
bool init_stacks (void) {return init_stack() && init_c_stack();}
void delete_stacks (void) {
delete_c_stack();
delete_stack();
}
bool update_c_stack (void) {
if (c_stack.s == NULL) {return false;}
unsigned int *t = realloc(c_stack.s,
sizeof(*c_stack.s)*(c_stack.max_size + 1));
if (t == NULL) {return false;}
c_stack.s = t;
c_stack.max_size++;
return true;
}
bool update_stack (void) {
if (stack.s == NULL) {return false;}
Node **t = realloc(stack.s, sizeof(*stack.s)*(stack.max_size + 1));
if (t == NULL) {return false;}
printf("stack.max_size before = %u\n", stack.max_size);
printf("reallocated to %d bytes\n",
sizeof(Node *)*(stack.max_size + 1));
stack.s = t;
stack.max_size++;
printf("stack.max_size after = %u\n", stack.max_size);
return true;
}
bool push_count (unsigned int i) {
if (c_stack.s == NULL) {return false;}
if (c_stack.size < c_stack.max_size || update_c_stack()) {
c_stack.s[c_stack.size++] = i;
return true;
}
return false;
}
bool push (Node *i) {
if (stack.s == NULL) {return false;}
if (stack.size < stack.max_size || update_stack()) {
stack.s[stack.size++] = i;
return true;
}
return false;
}
/******************* End of Stack Operations *******************/
bool add_node (int x) {
if (get_num_levels(root) == UINT_MAX) {
printf("Failed to add %d to tree\n", x);
return false;
}
Node *f = find(root, x), *t = (Node *)calloc(1, sizeof(Node)), *p;
if (root == NULL) {
t->value = x;
t->quantity = 1;
t->parent = NULL;
t->left = NULL;
t->right = NULL;
root = t;
}
else if (f == NULL) { // x doesn't exist in tree
f = root;
p = f->parent;
while (f != NULL) {
p = f;
if (x < f->value) {f = f->left;}
else if (x > f->value) {f = f->right;}
}
t->value = x;
t->quantity = 1;
t->parent = p;
t->left = NULL;
t->right = NULL;
if (x < p->value) {p->left = t;}
else if (x > p->value) {p->right = t;}
else {f->quantity++;} // x already exists in tree
}
printf("\n" TREE_SEPERATOR "\n");
printf("\nAdded node containing %d\n", x);
print_tree_formatted();
printf("\nNow %d nodes in the tree.\n", get_num_nodes(root));
printf("\nNow %d levels in the tree.\n", get_num_levels(root));
return true;
}
bool remove_node (int x) {
Node *f = find(root, x);
if (f != NULL) {
if (f->quantity > 1) {f->quantity--;}
else {replace_node(f); free(f);}
printf("\n" TREE_SEPERATOR "\n");
printf("\nRemoved node containing %d\n", x);
print_tree_formatted();
printf("\nNow %d nodes in the tree.\n", get_num_nodes(root));
printf("\nNow %d levels in the tree.\n", get_num_levels(root));
return true;
}
return false;
}
/*************************************************************************
* Function Name: *
* str_split *
* Purpose: *
* The function below takes a single string and breaks it up into a *
* set of constituent sub-strings, based on the delimiter that is *
* passed in to break the string by. *
* Parameters: *
* - s The string to be split *
* - len The length of the string *
* - c The size of the returned char * array *
* - delim The delimiter by which the string s is to be broken up by *
* Return Value: *
* char ** A pointer to an array of c number of strings, or NULL on *
* failure to allocate enough space *
*************************************************************************/
char **str_split (char *s, unsigned int len, unsigned int c, const char delim) {
char **result;
unsigned int i, j, k = 0, l;
if (s == NULL || len == 0) {return NULL;}
/* Allocate memory for c number of strings/char pointers */
result = calloc(c + 1, sizeof(char *));
if (result != NULL) {
for (i = 0; i < c + 1; i++) {
/* Allocate space for the actual chars for each string */
result[i] = calloc(len, sizeof(char));
if (result[i] == NULL) {return NULL;}
}
/***************************************************************
* i holds index of advancing pointer *
* j holds index of following pointer *
* k holds index of string in returning array of char pointers *
* l holds index of char in string *
***************************************************************
* Loop over and copy contents of s into the each index of *
* result *
***************************************************************/
for (i = 0, j = 0; i < len; i++) {
l = 0;
if (s[i] == delim) {
for (; j < i; j++) {result[k][l++] = s[j];}
k++;
j = i + 1;
}
}
for (; j < i; j++) {result[k][l++] = s[j];}
}
return result;
}
/*************************************************************************
* Function Name: *
* cin *
* Purpose: *
* The function below allows for "continuous" user input, based on *
* how much the user has already entered, by dynamically allocating *
* more memory whenever the input buffer is maxed out. *
* Parameters: *
* - cur_size The initial allocation size for the input buffer *
* Return Value: *
* char * A string/pointer to an array of characters or NULL *
* if allocation failed *
*************************************************************************/
char *cin (unsigned int cur_size) {
char *input = (char *)malloc(cur_size);
int i = 0, c = EOF;
if (input != NULL) {
// cook keyboard input until NL or EOF
while ((c = getchar()) != '\n' && c != EOF) {
if ((char)c == 8 || (char)c == 127) {
printf("\b \b\b \b\b \b");
fflush(stdout);
i = (i > 0) ? i - 1 : i;
}
else {
input[i++] = (char)c;
if (i == cur_size) {
/* Attempt to double the current size of the buffer */
cur_size *= 2;
input = realloc(input, cur_size);
if (input == NULL) {
printf("Unable to allocate enough memory for input\n");
break;
}
}
}
}
// null-terminate user input
input[i] = '\0';
}
return input;
}
...
int main (void) {
char *input, **s, t, delim = ' ';
unsigned int len, i, count, result;
bool valid;
/* Allocate space for both stacks */
init_stacks(); // DO NOT MOVE OR DUPLICATE CALL
while (true) {
/* Continuously grab null-terminated user input string */
input = cin(INIT_BUF_SIZE);
if (input != NULL) {
len = string_length(input);
/* Get the number of times the char delim appears in input */
count = num_in_string(input, delim);
/***********************************************************
* First word in user input should either be: *
* - "add" *
* - "remove" *
* - "print" *
* - Any of the exit commands ("q", "quit", or "exit"). *
***********************************************************/
t = input[0];
/* Exit loop if first token is an exit command */
if (user_exit(input)) {break;}
/*******************************************************
* Only process user input if there's at least 2 words *
* Eg: add 1 <-- Valid *
* add <-- Invalid *
* remove 1 <-- Valid *
* foobar <-- Invalid *
*******************************************************/
if (count > 0) {
/* Get the user input split up by spaces */
s = str_split(input, len, ++count, delim);
if (s != NULL) {
/*******************************************
* Look at each token entered by the user, *
* except the first one *
*******************************************/
for (i = 1; i < count; i++) {
/***********************************************
* Assuming the token is a number, convert the *
* token to an integer *
***********************************************/
result = string_to_int(s[i], &valid);
if (same_string(s[0], "print")) {
if (same_string(s[1], "stack")) {
printf("size = %u\n", stack.size);
printf("max_size = %u\n", stack.max_size);
}
else {printf("%s\n", s[i]);}
fflush(stdout);
}
else if (valid) {
if (same_string(s[0], "add")) {
add_node(result);
}
else if (same_string(s[0], "remove")) {
remove_node(result);
}
}
}
/* Free up memory allocated for split user input */
for (i = 0; i < count; i++) {free(s[i]);} // DO NOT MOVE
free(s); // DO NOT MOVE
}
else {printf(ERROR_MSG);} // couldn't allocate heap space
}
/* Free up memory allocated for cin */
free(input); // DO NOT MOVE
}
}
/* Free up memory allocated for both stacks */
delete_stacks(); // DO NOT MOVE
return 0;
}
所以当我运行它时,程序运行正常,因为我在下面的树中添加(按顺序): 15, 7, 23, 3, 11, 19, 27
然后,当我将数字5添加到树中时,程序会因名义错误而崩溃。我已尝试使用gdb
(和lldb)进行调试,但即使打开-g标志,我也认为这不是很有效。
这对我来说没有意义,因为realloc
和update_stack
中update_count_stack
的调用并未使先前失败而添加数字5
我也意识到,将动态分配的内存块的大小一遍又一遍地增加1并不是典型或好的做法,因为它效率低,但我只是想知道为什么这是在这里发生的,以及我怎么能够解决它 - 我觉得我只是错过了一些明显的东西并且正在把我的头发拉到一些微不足道的东西上来!
非常感谢。
编辑:感谢大家的反馈。很抱歉a)延迟更新,以及b)我最初发布的“代码墙”。我昨天在这里稍微抄袭了我的源文件,所以请原谅我此刻必须运行。我之后尝试将发布的代码最小化,仅显示相关功能,其中包括调用malloc
,realloc
或free
的功能。我也尝试在代码中添加一些注释,以帮助理解它。如果你想让我解决别的问题,请告诉我!再次感谢你!
答案 0 :(得分:0)
回答其中一个OP问题;
在增加分配时,
在每个增量处,增加量的两倍I.E. 1,2,4,9 ...
保持当前最大可用数量和实际使用的当前数量,
然后只有在没有可用空间时才调用`realloc()
答案 1 :(得分:0)
您的代码存在一些问题
你的delete_c_stack
和delete_stack
都是泄漏内存,你将指针c_stack.s/stack.s
设置为NULL,然后释放它,这样free就变成了NOP而且永远不会释放内存。
永远不会转换calloc/malloc
的返回值。如果由于编译器需要这样做,那么你正在使用C ++编译器编译,C ++代码应该使用new
检查calloc / malloc的返回值,你不要在add_node中这样做。
请注意为什么你这样做了str_split
,有一个名为strtok
的C运行时函数将字符串拆分成标记,使用它会更容易,并且可能更不容易出错,例如如果参数l
为0,则str_split中的len
不会被初始化,这可能会导致崩溃。
由str_split
if (result[i] == NULL) { return NULL; }
潜在内存泄漏
我会使用断言来填充代码,以使其更加健壮并且能够检测是否有一些索引有点过分,例如在cin
语句中输入[i] =&#39; \ 0&#39 ;;最好先跟assert(i < cur_size);
realloc
是一项昂贵的操作,最好一次分配比一个整数更大的块,然后当所有块用完时再分配另一个块。例如在你的函数update_c_stack中,你只用一个增加堆栈。
您的代码中有许多函数调用未显示可能有问题。一个简短的例子显示问题是最好的,当你这样做时,你可能会发现自己的问题是什么。这种情况一直发生在我身上。