C programming question on the implementation of a hash table - c

I have a C programming question on the implementation of a hash table. I have implemented the hash table for storing some strings.
I am having a problem while dealing with hash collisons. I am following a chaining linked-list approach to overcome the problem but, somehow, my code is behaving differently. I am not able to debug it. Can somebody help?
This is what I am facing:
Say first time, I insert a string called gaur. My hash map calculates the index as 0 and inserts the string successfully. However, when another string whose hash also, when calculated, turns out to be 0, my previous value gets overrwritten i.e. gaur will be replaced by new string.
This is my code:
struct list
{
char *string;
struct list *next;
};
struct hash_table
{
int size; /* the size of the table */
struct list **table; /* the table elements */
};
struct hash_table *create_hash_table(int size)
{
struct hash_table *new_table;
int i;
if (size<1) return NULL; /* invalid size for table */
/* Attempt to allocate memory for the table structure */
if ((new_table = malloc(sizeof(struct hash_table))) == NULL) {
return NULL;
}
/* Attempt to allocate memory for the table itself */
if ((new_table->table = malloc(sizeof(struct list *) * size)) == NULL) {
return NULL;
}
/* Initialize the elements of the table */
for(i=0; i<size; i++)
new_table->table[i] = '\0';
/* Set the table's size */
new_table->size = size;
return new_table;
}
unsigned int hash(struct hash_table *hashtable, char *str)
{
unsigned int hashval = 0;
int i = 0;
for(; *str != '\0'; str++)
{
hashval += str[i];
i++;
}
return (hashval % hashtable->size);
}
struct list *lookup_string(struct hash_table *hashtable, char *str)
{
printf("\n enters in lookup_string \n");
struct list * new_list;
unsigned int hashval = hash(hashtable, str);
/* Go to the correct list based on the hash value and see if str is
* in the list. If it is, return return a pointer to the list element.
* If it isn't, the item isn't in the table, so return NULL.
*/
for(new_list = hashtable->table[hashval]; new_list != NULL;new_list = new_list->next)
{
if (strcmp(str, new_list->string) == 0)
return new_list;
}
printf("\n returns NULL in lookup_string \n");
return NULL;
}
int add_string(struct hash_table *hashtable, char *str)
{
printf("\n enters in add_string \n");
struct list *new_list;
struct list *current_list;
unsigned int hashval = hash(hashtable, str);
printf("\n hashval = %d", hashval);
/* Attempt to allocate memory for list */
if ((new_list = malloc(sizeof(struct list))) == NULL)
{
printf("\n enters here \n");
return 1;
}
/* Does item already exist? */
current_list = lookup_string(hashtable, str);
if (current_list == NULL)
{
printf("\n DEBUG Purpose \n");
printf("\n NULL \n");
}
/* item already exists, don't insert it again. */
if (current_list != NULL)
{
printf("\n Item already present...\n");
return 2;
}
/* Insert into list */
printf("\n Inserting...\n");
new_list->string = strdup(str);
new_list->next = NULL;
//new_list->next = hashtable->table[hashval];
if(hashtable->table[hashval] == NULL)
{
hashtable->table[hashval] = new_list;
}
else
{
struct list * temp_list = hashtable->table[hashval];
while(temp_list->next!=NULL)
temp_list = temp_list->next;
temp_list->next = new_list;
hashtable->table[hashval] = new_list;
}
return 0;
}

I haven't checked to confirm, but this line looks wrong:
hashtable->table[hashval] = new_list;
This is right at the end of the last case of add_string. You have:
correctly created the new struct list to hold the value being added
correctly found the head of the linked list for that hashvalue, and worked your way to the end of it
correctly put the new struct list at the end of the linked list
BUT then, with the line I quote above, you are telling the hash table to put the new struct list at the head of the linked list for this hashvalue! Thus throwing away the whole linked list that was there before.
I think you should omit the line I quote above, and see how you get on. The preceding lines are correctly appending it to the end of the existing list.

The statement hashtable->table[hashval] = new_list; is the culprit. You insrted the new_list ( I think better name would have been new_node) at end of the linked list. But then you overwrite this linked list with new_list which is just a single node. Just remove this statement.

As others have already pointed out, you are walking to the end of the list with temp_list, appending new_list to it, then throwing away the existing list.
Since the same value NULL is used to indicate an empty bucket and the end of the list, it's quite a bit easier to put the new item at the head of the list.
You also should do any test which would result in the new item not being added before creating it, otherwise you will leak the memory.
I would also have an internal lookup function that takes the hash value, otherwise you have to calculate it twice
int add_string(struct hash_table *hashtable, char *str)
{
unsigned int hashval = hash(hashtable, str);
/* item already exists, don't insert it again. */
if (lookup_hashed_string(hashtable, hashval, str))
return 2;
/* Attempt to allocate memory for list */
struct list *new_list = malloc(sizeof(struct list));
if (new_list == NULL)
return 1;
/* Insert into list */
new_list->string = strdup(str);
new_list->next = hashtable->table[hashval];
hashtable->table[hashval] = new_list;
return 0;
}

The hash function must be a function which take your data in entry and return delimited id (eg: integer between 0 and HASH_MAX)
Then you must stock your element in a list in the Hash(data) index of a hash_table array. if a data have the same hash, it will be stock in the same list as the previous data.
struct your_type_list {
yourtype data;
yourtype *next_data;
};
struct your_type_list hash_table[HASH_MAX];

Related

Why does my hash table program keep crashing?

I'm trying to create a program that reads a dictionary and then stores the words into the hash table, then read another file checks every word of that file if it is in the hash table if it is not then it will be outputted as a misspelled word. I'm first trying to check if I can load the dictionary file into my hash table and then output the words in the hash table yet my code seems to crash whenever I try to run it. The hash function I use was taken from the Internet. I'm also still very new with data structures, and having a hard time understanding.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// file to read
#define dictionary "dictionary.txt"
// No. of buckets
const unsigned int N = 10;
typedef struct node
{
char* word;
struct node *next;
}
node;
node *table[10];
// hash function
unsigned int hash(char *word)
{
// TODO
unsigned int hash = 5381;
int c = 0;
while (c == *word++)
hash = ((hash << 5) + hash) + c;
return hash % 10;
}
int main(void)
{
// initialize array heads to NULL
for (int i = 0; i < N; i++)
{
table[i] = NULL;
}
// Open file to read
FILE *indata = fopen(dictionary, "r");
if (indata == NULL)
{
printf("cant open\n");
return 1;
}
// variable to store words read from the file
char *words = malloc(sizeof(char) * 20);
if (words == NULL)
{
printf("no memory\n");
return 1;
}
// While loop to read through the file
while (fgets(words, 20, indata))
{
// get the index of the word using hash function
int index = hash(words);
// create new node
node *newNode = malloc(sizeof(node));
if (newNode == NULL)
{
printf("here\n");
return 1;
}
// make the new node the new head of the list
strcpy(newNode->word, words);
newNode->next = table[index];
table[index] = newNode;
// free memory
free(newNode);
}
// free memory
free(words);
// loop to print out the values of the hash table
for (int i = 0; i < N; i++)
{
node *tmp = table[i];
while (tmp->next != NULL)
{
printf("%s\n", tmp->word);
tmp = tmp->next;
}
}
// loop to free all memory of the hash table
for (int i = 0; i < N; i++)
{
if (table[i] != NULL)
{
node *tmp = table[i]->next;
free(table[i]);
table[i] = tmp;
}
}
// close the file
fclose(indata);
}
At least three bugs that independently caused a segfault:
First, newNode->word is used unitialized, so it points to random memory, so the strcpy would segfault. Better to use strdup
Also, after you put newNode in the table, you do free(newNode) making what it points to invalid. This causes the second loop to segfault
Third, in the second loop, if table[i] is null, the while (tmp->next != NULL) will segfault
I've annotated and corrected your code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// file to read
#define dictionary "dictionary.txt"
// No. of buckets
const unsigned int N = 10;
typedef struct node {
char *word;
struct node *next;
} node;
node *table[10];
// hash function
unsigned int
hash(char *word)
{
// TODO
unsigned int hash = 5381;
int c = 0;
while (c == *word++)
hash = ((hash << 5) + hash) + c;
// NOTE: not a bug but probably better
#if 0
return hash % 10;
#else
return hash % N;
#endif
}
int
main(void)
{
// initialize array heads to NULL
for (int i = 0; i < N; i++) {
table[i] = NULL;
}
// Open file to read
FILE *indata = fopen(dictionary, "r");
if (indata == NULL) {
printf("cant open\n");
return 1;
}
// variable to store words read from the file
char *words = malloc(sizeof(char) * 20);
if (words == NULL) {
printf("no memory\n");
return 1;
}
// While loop to read through the file
while (fgets(words, 20, indata)) {
// get the index of the word using hash function
int index = hash(words);
// create new node
node *newNode = malloc(sizeof(node));
if (newNode == NULL) {
printf("here\n");
return 1;
}
// make the new node the new head of the list
// NOTE/BUG: word is never set to anything valid -- possible segfault here
#if 0
strcpy(newNode->word, words);
#else
newNode->word = strdup(words);
#endif
newNode->next = table[index];
table[index] = newNode;
// free memory
// NOTE/BUG: this will cause the _next_ loop to segfault -- don't deallocate
// the node you just added to the table
#if 0
free(newNode);
#endif
}
// free memory
free(words);
// loop to print out the values of the hash table
for (int i = 0; i < N; i++) {
node *tmp = table[i];
// NOTE/BUG: this test fails if the tmp is originally NULL (i.e. no entries
// in the given hash index)
#if 0
while (tmp->next != NULL) {
#else
while (tmp != NULL) {
#endif
printf("%s\n", tmp->word);
tmp = tmp->next;
}
}
// loop to free all memory of the hash table
for (int i = 0; i < N; i++) {
if (table[i] != NULL) {
node *tmp = table[i]->next;
free(table[i]);
table[i] = tmp;
}
}
// close the file
fclose(indata);
}
UPDATE:
I made a linked list program before that stores an integer in the list, int number; struct node *next; and I used newNode->number = 5; and it worked, why is it in this case it doesn't?? Is it because I am working with strings here??
The difference is that word is a pointer. It must be assigned a value before it can be used. strcpy does not assign a value to word. It tries to use the contents of word as the destination address of the copy.
But, the other two bugs happen regardless of word being a char * vs number being int.
If you had defined word not as a pointer, but as a fixed array [not as good in this usage], the strcpy would have worked. That is, instead of char *word;, if you had done (e.g.) char word[5];
But, what you did is better [with the strdup change] unless you can guarantee that the length of word can hold the input. strdup will guarantee that.
But, notice that I [deliberately] made word have only five chars to illustrate the problem. It means that the word to be added can only be 4 characters in length [we need one extra byte for the nul terminator character]. You'd need to use strncpy instead of strcpy but strncpy has issues [it does not guarantee to add the nul char at the end if the source length is too large].
Conincidentally, there is another question today that has an answer that may help shed some more light on the differences of your word struct member: Difference between memory allocations of struct member (pointer vs. array) in C
From a cursory glance I can see two problems:
You don't allocate space for your word in the node; you simply strcopy the word into an undefined pointer. You might want to use strdup instead.
You free the memory of the node after you added it to the list. The table is an array of pointers, so you store the point in the table and then throw away the memory that it points to.
Oh, three: and in the final loop you free the unallocated memory again...

C - Segfault when accessing struct member in a HashTable (insert function)

I am new to C and am having issues implementing an insert function for my HashTable.
Here are my structs:
typedef struct HashTableNode {
char *url; // url previously seen
struct HashTableNode *next; // pointer to next node
} HashTableNode;
typedef struct HashTable {
HashTableNode *table[MAX_HASH_SLOT]; // actual hashtable
} HashTable;
Here is how I init the table:
HashTable *initTable(){
HashTable* d = (HashTable*)malloc(sizeof(HashTable));
int i;
for (i = 0; i < MAX_HASH_SLOT; i++) {
d->table[i] = NULL;
}
return d;
}
Here is my insert function:
int HashTableInsert(HashTable *table, char *url){
long int hashindex = JenkinsHash(url, MAX_HASH_SLOT);
int uniqueBool = 2; // 0 for true, 1 for false, 2 for init
HashTableNode* theNode = (HashTableNode*)malloc(sizeof(HashTableNode));
theNode->url = url;
if (table->table[hashindex] != NULL) { // if we have a collision
HashTableNode* currentNode = (HashTableNode*)malloc(sizeof(HashTableNode));
currentNode = table->table[hashindex]->next; // the next node in the list
if (currentNode == NULL) { // only one node currently in list
if (strcmp(table->table[hashindex]->url, theNode->url) != 0) { // unique node
table->table[hashindex]->next = theNode;
return 0;
}
else{
printf("Repeated Node\n");
return 1;
}
}
else { // multiple nodes in this slot
printf("There was more than one element in this slot to start with. \n");
while (currentNode != NULL)
{
// SEGFAULT when accessing currentNode->url HERE
if (strcmp(currentNode->url, table->table[hashindex]->url) == 0 ){ // same URL
uniqueBool = 1;
}
else{
uniqueBool = 0;
}
currentNode = currentNode->next;
}
}
if (uniqueBool == 0) {
printf("Unique URL\n");
theNode->next = table->table[hashindex]->next; // splice current node in
table->table[hashindex]->next = theNode; // needs to be a node for each slot
return 0;
}
}
else{
printf("simple placement into an empty slot\n");
table->table[hashindex] = theNode;
}
return 0;
}
I get SegFault every time I try to access currentNode->url (the next node in the linked list of a given slot), which SHOULD have a string in it if the node itself is not NULL.
I know this code is a little dicey, so thank you in advance to anyone up for the challenge.
Chip
UPDATE:
this is the function that calls all ht functions. Through my testing on regular strings in main() of hash table.c, I have concluded that the segfault is due to something here:
void crawlPage(WebPage * page){
char * new_url = NULL;
int pos= 0;
pos = GetNextURL(page->html, pos, URL_PREFIX, &new_url);
while (pos != -1){
if (HashTableLookup(URLsVisited, new_url) == 1){ // url not in table
printf("url is not in table......\n");
hti(URLsVisited, new_url);
WebPage * newPage = (WebPage*) calloc(1, sizeof(WebPage));
newPage->url = new_url;
printf("Adding to LIST...\n");
add(&URLList, newPage); // added & to it.. no seg fault
}
else{
printf("skipping url cuz it is already in table\n");
}
new_url = NULL;
pos = GetNextURL(page->html, pos, URL_PREFIX, &new_url);
}
printf("freeing\n");
free(new_url); // cleanup
free(page); // free current page
}
Your hash table insertion logic violates some rather fundamental rules.
Allocating a new node before determining you actually need one.
Blatant memory leak in your currentNode allocation
Suspicious ownership semantics of the url pointer.
Beyond that, this algorithm is being made way too complicated for what it really should be.
Compute the hash index via hash-value modulo the table size.
Start at the table slot of the hash index, walking node pointers until one of two things happens:
You discover the node is already present
You reach the end of the collision chain.
Only in #2 above do you actually allocate a collision node and chain it to your existing collision list. Most of this is trivial when employing a pointer-to-pointer approach, which I demonstrate below:
int HashTableInsert(HashTable *table, const char *url)
{
// find collision list starting point
long int hashindex = JenkinsHash(url, MAX_HASH_SLOT);
HashTableNode **pp = table->table+hashindex;
// walk the collision list looking for a match
while (*pp && strcmp(url, (*pp)->url))
pp = &(*pp)->next;
if (!*pp)
{
// no matching node found. insert a new one.
HashTableNode *pNew = malloc(sizeof *pNew);
pNew->url = strdup(url);
pNew->next = NULL;
*pp = pNew;
}
else
{ // url already in the table
printf("url \"%s\" already present\n", url);
return 1;
}
return 0;
}
That really is all there is to it.
The url ownership issue I mentioned earlier is addressed above via string duplication using strdup(). Although not a standard library function, it is POSIX compliant and every non-neanderthal half-baked implementation I've seen in the last two decades provides it. If yours doesn't (a) I'd like to know what you're using, and (b) its trivial to implement with strlen and malloc. Regardless, when the nodes are being released during value-removal or table wiping, be sure and free a node's url before free-ing the node itself.
Best of luck.

Doubling an array that points to a linked list in C

I am implementing a hashset in C, where my array points to a linked list
this is the linked list:
typedef struct hashnode hashnode;
struct hashnode {
char *word;
// will hold our word as a string
hashnode *link;
//will be used only if chaining
};
and this is the Hashset:
struct hashset {
size_t size;
//size of entire array
size_t load;
//number of words total
hashnode **chains;
//linked list (if words have same index);
};
Now I am having a problem with my double array code
I believe there is a dangling pointer somewhere
here is the code:
void dbl_array(hashset *this) {
size_t newlen = this->size +1;
newlen *= 2;
//double siz
hashnode **new_array = malloc(newlen * sizeof(hashnode*));
//new array
int array_end = (int)this->size;//load;
//end of old array
for(int i = 0; i < array_end; i++) {
//loop through old
int index = i;
if(this->chains[index] == NULL) {
continue;
}
else {
hashnode *nod;
int i=0;
for(nod = this->chains[index]; nod != NULL; nod = nod->link) {
if(nod == NULL)
return;
size_t tmp = strhash(nod->word) % newlen;
//compute hash
hashnode *newnod;
newnod = malloc(sizeof(hashnode*));
newnod->word = strdup(nod->word);
newnod->link = NULL;
if(new_array[tmp] == NULL) {
//if new array does not already have a word at index
new_array[tmp] = newnod;
}
else {
//if word is here then link to old one
newnod->link = new_array[tmp];
new_array[tmp] = newnod;
}
printf("newarray has: %s # {%d} \n", new_array[tmp]->word, tmp);
//testing insertion
i++;
}
free(nod);
}
}
this->chains = new_array;
this->size = newlen;
free(new_array);
printf("new size %d\n", this->size);
}
So after running GDB, I am finding that there is something wrong when I add the new node
There is no reason at all to allocate new collision nodes for a hash table expansion. The algorithm for expanding your hash table is relatively straight forward:
compute new table size
allocate new table
enumerate all chains in old table
for each chain, enumerate all nodes
for each node, compute new hash based on new table size
move node to appropriate slot in new table
When the above is done, so are you. Just wire up the new table to the hashset and make sure to update the size member to the new size. The old table is discarded.
The following code assumes you have properly managed your hash table prior to doubling. With that:
All unused table slots are properly NULL
All collision lists are properly NULL-terminated.
If you can't guarantee both of those conditions, doubling the size of your hash table is the least of your worries.
void hashset_expand(hashset* hs)
{
size_t new_size = 2 * (1 + hs->size), i, idx;
hash node *next, *nod, **tbl = calloc(new_size, sizeof(*tbl));
// walk old table, and each chain within it.
for (i=0; i<hs->size; ++i)
{
next = hs->chains[i];
while (next)
{
nod = next;
next = next->link; // must be done **before** relink
idx = strhash(nod->word) % new_size;
nod->link = tbl[idx];
tbl[idx] = nod;
}
}
// finish up, deleting the old bed.
free(hs->chains);
hs->chains = tbl;
hs->size = new_size;
}
That is all there is to it. Don't make it more complicated than that.

Hashtable insertion/search in C

Hello i have a problem with my hash table its implemented like this:
#define HT_SIZE 10
typedef struct _list_t_ {
char key[20];
char string[20];
char prevValue[20];
struct _list_t_ *next;
} list_t;
typedef struct _hash_table_t_ {
int size; /* the size of the table */
list_t ***table; /* first */
sem_t lock;
} hash_table_t;
I have a Linked list with 3 pointers because i want a hash table with several partitions (shards), here is my initialization of my Hash table:
hash_table_t *create_hash_table(int NUM_SERVER_THREADS, int num_shards){
hash_table_t *new_table;
int j,i;
if (HT_SIZE<1) return NULL; /* invalid size for table */
/* Attempt to allocate memory for the hashtable structure */
new_table = (hash_table_t*)malloc(sizeof(hash_table_t)*HT_SIZE);
/* Attempt to allocate memory for the table itself */
new_table->table = (list_t ***)calloc(1,sizeof(list_t **));
/* Initialize the elements of the table */
for(j=0; j<num_shards; j++){
new_table->table[j] = (list_t **)calloc(1,sizeof(list_t *));
for(i=0; i<HT_SIZE; i++){
new_table->table[j][i] = (list_t *)calloc(1,sizeof(list_t ));
}
}
/* Set the table's size */
new_table->size = HT_SIZE;
sem_init(&new_table->lock, 0, 1);
return new_table;
}
Here is my search function to search in the hash table
list_t *lookup_string(hash_table_t *hashtable, char *key, int shardId){
list_t *list ;
int hashval = hash(key);
/* Go to the correct list based on the hash value and see if key is
* in the list. If it is, return return a pointer to the list element.
* If it isn't, the item isn't in the table, so return NULL.
*/
sem_wait(&hashtable->lock);
for(list = hashtable->table[shardId][hashval]; list != NULL; list =list->next) {
if (strcmp(key, list->key) == 0){
sem_post(&hashtable->lock);
return list;
}
}
sem_post(&hashtable->lock);
return NULL;
}
And my insert function:
char *add_string(hash_table_t *hashtable, char *str,char *key, int shardId){
list_t *new_list;
list_t *current_list;
unsigned int hashval = hash(key);
/*printf("|%d|%d|%s|\n",hashval,shardId,key);*/
/* Lock for concurrency */
sem_wait(&hashtable->lock);
/* Attempt to allocate memory for list */
new_list = (list_t*)malloc(sizeof(list_t));
/* Does item already exist? */
sem_post(&hashtable->lock);
current_list = lookup_string(hashtable, key,shardId);
sem_wait(&hashtable->lock);
/* item already exists, don't insert it again. */
if (current_list != NULL){
strcpy(new_list->prevValue,current_list->string);
strcpy(new_list->string,str);
strcpy(new_list->key,key);
new_list->next = hashtable->table[shardId][hashval];
hashtable->table[shardId][hashval] = new_list;
sem_post(&hashtable->lock);
return new_list->prevValue;
}
/* Insert into list */
strcpy(new_list->string,str);
strcpy(new_list->key,key);
new_list->next = hashtable->table[shardId][hashval];
hashtable->table[shardId][hashval] = new_list;
/* Unlock */
sem_post(&hashtable->lock);
return new_list->prevValue;
}
My main class runs some of tests by executing the insertion / reading / delete from the elements of the hash table the problem is when i have more than 4 partitions/shards the tests stop at the first reading element saying it returned the wrong value NULL on the search function, when its less than 4 it runs perfectly well and passes all the tests.
You can see my main.c in here if you want to give a look:
http://hostcode.sourceforge.net/view/1105
My complete Hash table code:
http://hostcode.sourceforge.net/view/1103
And other functions where hash table code is executed:
.c file http://hostcode.sourceforge.net/view/1104
.h file http://hostcode.sourceforge.net/view/1106
Thank for you time, i appreciate any help you can give to me this is a college important project that I'm trying to solve and I'm stuck here for 2 days.
Hi already solved this problem i was doing a bad allocation in my initialization:
new_table->table = (list_t ***)calloc(1,sizeof(list_t **));
it should be like this:
new_table->table = (list_t ***)calloc(num_shards,sizeof(list_t **));

Passing pointer to linked list from filler function does not work

I am goofing around with pointers and structures. I want to achieve the following:
(1) define a linked list with a structure (numberRecord)
(2) write a function that fills a linked list with some sample records by going thourgh a loop (fillList)
(3) count the number of elements in the linked list
(4) print the number of elements
I am now so far that the fillList function works well, but I do not succeed in handing over the filled linked list to a pointer in the main(). In the code below, the printList function only displays the single record that was added in main() instead of displaying the list that was created in the function fillList.
#include <stdio.h>
#include <stdlib.h>
typedef struct numberRecord numberRecord;
//linked list
struct numberRecord {
int number;
struct numberRecord *next;
};
//count #records in linked list
int countList(struct numberRecord *record) {
struct numberRecord *index = record;
int i = 0;
if (record == NULL)
return i;
while (index->next != NULL) {
++i;
index = index->next;
}
return i + 1;
}
//print linked list
void printList (struct numberRecord *record) {
struct numberRecord *index = record;
if (index == NULL)
printf("List is empty \n");
while (index != NULL) {
printf("%i \n", index->number);
index = index->next;
}
}
//fill the linked list with some sample records
void fillList(numberRecord *record) {
numberRecord *first, *prev, *new, *buffer;
//as soon as you add more records you get an memory error, static construction
new = (numberRecord *)malloc(100 * sizeof(numberRecord));
new->number = 0;
new->next = NULL;
first = new;
prev = new;
buffer = new;
int i;
for (i = 1; i < 11; i++) {
new++;
new->number = i;
new->next = NULL;
prev->next = new;
prev = prev->next;
}
record = first;
}
int main(void) {
numberRecord *list;
list = malloc(sizeof(numberRecord));
list->number = 1;
list->next = NULL;
fillList(list);
printf("ListCount: %i \n", countList(list));
printList(list);
return 0;
}
SOLUTION
Do read the posts below, they indicated this solution and contain some very insightful remarks about pointers. Below the adapted code that works:
#include <stdio.h>
#include <stdlib.h>
typedef struct numberRecord numberRecord;
//linked list
struct numberRecord {
int number;
struct numberRecord *next;
};
//count #records in linked list
int countList(struct numberRecord *record) {
struct numberRecord *index = record;
int i = 0;
if (record == NULL)
return i;
while (index->next != NULL) {
++i;
index = index->next;
}
return i + 1;
}
//print linked list
void printList (struct numberRecord *record) {
struct numberRecord *index = record;
if (index == NULL)
printf("List is empty \n");
while (index != NULL) {
printf("%i \n", index->number);
index = index->next;
}
}
//fill the linked list with some sample records
numberRecord *fillList() {
numberRecord *firstRec, *prevRec, *newRec;
int i;
for (i = 1; i < 11; i++) {
newRec = malloc(sizeof(numberRecord));
newRec->number = i;
newRec->next = NULL;
//initialize firstRec and prevRec with newRec, firstRec remains head
if (i == 1) {
firstRec = newRec;
prevRec = newRec;
}
prevRec->next = newRec;
prevRec = prevRec->next;
}
return firstRec;
}
int main(void) {
numberRecord *list;
list = fillList();
printf("ListCount: %i \n", countList(list));
printList(list);
return 0;
}
This statement in fillList
record = first;
has no effect on the list variable in main. Pointers are passed by value (like everything else) in C. If you want to update the list variable in main, you'll either have to pass a pointer to it (&list) and modify fillList accordingly, or return a numberRecord* from fillList. (I'd actually go with that second option.)
Here's a (bad) illustration:
When main calls fillList, at the starting point of that function, the pointers are like this:
main memory fillList
list ----> 0x01234 <---- record
A bit later in fillList, you allocate some storage for new (that's actually a bad name, it conflicts with an operator in C++, will get people confused)
main memory fillList
list ----> 0x01234 <---- record
0x03123 <---- new
At the last line of fillList you're left with:
main memory fillList
list ----> 0x01234 ,-- record
0x03123 <---- new
record and list are not the same variable. They start out with the same value, but changing record will not change list. The fact that they are both pointers doesn't make them any different from say ints in this respect.
You can change the thing pointed to by list in fillList, but you can't change what list points to (with your version of the code).
The easiest way for you to get around that is to change fillList like this:
numberRecord *fillList() {
....
return new;
}
And in main, don't allocate list directly, just call fillList() to initialize it.

Resources