所以让我们从头开始。您从文件中读取字符串的键、数据对。您按照阅读它们的顺序构建这些对的链接列表。然后呢?
/*
compile with:
gcc -std=c89 -pedantic -Wall -O2 -o so_10185705 so_10185705.c
test with:
valgrind ./so_10185705
*/
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
struct node_t {
char key[32];
char value[64];
struct node_t *next;
};
static void print_list(const struct node_t *curr)
{
while (curr) {
printf("%s, %s\n", curr->key, curr->value);
curr = curr->next;
}
}
static void free_list(struct node_t **currp)
{
struct node_t *curr = *currp;
*currp = NULL; /* don't leave dangling pointers */
while (curr) {
struct node_t *next = curr->next;
curr->next = NULL;
free((void *)curr);
curr = next;
}
}
/* O(n) but useful */
static const struct node_t *
find_in_list(const struct node_t *curr, const char *key)
{
while (curr) {
if (!strncmp(curr->key, key, sizeof(curr->key)))
return curr;
curr = curr->next;
}
return NULL;
}
/* Same as find_in_list but less useful */
static int is_in_list(const struct node_t *curr, const char *key)
{
while (curr) {
if (!strncmp(curr->key, key, sizeof(curr->key)))
return 1;
curr = curr->next;
}
return 0;
}
int main()
{
struct node_t *head = NULL;
FILE *f = fopen("foo", "r");
if (!f) exit(1);
while (!feof(f)) {
struct node_t *new_node = malloc(sizeof(struct node_t));
fscanf(f, "%s %s", new_node->key, new_node->value);
new_node->next = head;
head = new_node;
}
fclose(f);
print_list(head);
const struct node_t *node = find_in_list(head, "abcd2");
if (node) {
printf("found! key = %s, value = %s\n", node->key, node->value);
} else {
printf("not found!\n");
}
if (is_in_list(head, "abcd3")) {
printf("found key in list but now I don't know the value associated with this key.\n");
} else {
printf("not found!\n");
}
free_list(&head);
return 0;
}
/* explanation of bugs in OP's code */
struct node_t_2 {
char *key;
char *value;
struct node_t_2 *next;
};
void build_list(FILE *f, struct node_t_2 *curr)
{
while (!feof(f)) {
/*
These variable are allocated on the stack.
Their lifetime is limited to the current scope.
At the closing curly brace of the block in which they are declared,
they die, the information in them is lost and pointer to them become
invalid garbage.
*/
key char[32];
value char[64];
/*
Of course, you can only read keys up to 31 bytes long and values up to 63 bytes long.
Because you need an extra byte for the string's NUL terminator.
fscanf puts that NUL terminator for you.
If it didn't, you would not be able to use the data:
you would not know the lenth of the string.
If you need 32-byte long keys, declare the variable key to be 33 bytes long.
*/
fscanf(f, "%s %s", key, value);
/* You can use key and value here */
struct node_t_2 bad_new_node;
/*
You cannot add bad_new_node to the list, because as soon as you
reach '}' it will die.
You need a place for the node that will not disappear
(be reused on the next iteration of the loop).
So it must be on the heap.
How many bytes do you need for the node? Enough to hold the three pointers:
12 bytes on 32bit, 24 bytes on 64bit.
The compiler knows how many bytes a struct needs.
*/
struct node_t_2 *new_node = malloc(sizeof(struct node_t_2));
/*
You can add new_node to the list, because it is on the heap and will
exist until either passed to free() or the process (program) exits.
*/
new_node->key = key;
new_node->value = value;
/*
That was a bug, because we stored pointers to garbage.
Now new_node has a pointer to a place that will cease to exist
as soon as we reach '}'.
*/
new_node->key = strdup(key);
new_node->value = strdup(value);
/*
strdup is a standard function that can be implemented like this:
char * strdup(const char *s)
{
int len = strlen(s)
char *p = malloc(len);
memcpy(p, s, len);
return p;
}
Now new_node has pointers to memory on the heap that will continue to
exist until passed to free or the process terminates.
*/
new_node->next = curr;
curr = new_node;
/*
At the next line, memory for key and value is disappears and
is re-used if we re-enter the loop.
*/
}
}
/*
If your list nodes have pointers instead of arrays, you need to free
the strings pointed to by those pointers manually, becuause freeing
the list node wont free stuff it points to.
*/
free_list(struct node_t_2 **currp)
{
struct node_t_2 *curr = *currp;
*currp = NULL;
while (curr) {
struct node_t_2 *next = curr->next;
free((void *)curr->key);
curr->key = NULL;
free((void *)curr->value);
curr->value = NULL;
curr->next = NULL;
free((void *)curr);
curr = next;
}
}