Get nth Value of a linked list in C - c

Though after writing code and examining it so many times i am getting an assertion error. i dunno why. hope you pals can help me.This is the function
int GetNth(struct node* head, int index) {
Node* current = head;
int count = 0; // the index of the node we're currently looking at
while (current != NULL) {
if (count == index)
return(current->data);
count++;
current = current->next;
}
assert(0);
// if we get to this line, the caller was asking
// for a non-existent element so we assert fail.
}
this is the error i get.
GetNth: Assertion `0' failed.Aborted (core dumped)
My doubt is that if there is a hit with the position whose value is expected, why would Assertion test happen? As there is already a return statement of the while loop which exits the function.
If i comment that assertion test and if there is a hit/miss with position whose value is expected, it returns me 0 every time instead of value/null
Thanks in advance!

I debug code like this with good old fashion printf's
int GetNth(struct node* head, int index) {
Node* current = head;
int count = 0; // the index of the node we're currently looking at
if ( index < 0 ) {
printf( "invalid -ve index passes\n" );
assert(0);
}
printf( "starting ptr %p\n", current );
while (current != NULL) {
printf( "checking value %d against %d for ptr %p", count, index, current );
if (count == index) {
printf( "found\n" );
return(current->data);
}
count++;
current = current->next;
}
printf( "not found\n" );
assert(0);
// if we get to this line, the caller was asking
// for a non-existent element so we assert fail.
}
Run it and see what you get out. If needed change the printf for fprintf( stderr.

Aside from some input/output issues, your code looks correct, I think the problem is that the list you are passing in is either NULL, or it doesn't have enough items. And so you hit the assertion failure.
Output issue - assert(0) is not a good way to return an error to a caller of a function. C language provides no way for a caller function to detect the assertion failure. So your process will crash.
Also it is likely that the reason someone is calling your API is they don't know whether the item is in the list or not, so crashing the process is not a good design from that POV.
Input issue - you don't validate the input argument is positive. Or restrict it to being unsigned.

Related

Popping stack doesn't work right? (Stack from two queues)

I've been stewing over this one for a while, and I can't quite seem to figure out why at execution, when once the following function executes, and the test code in main calls to see if the stack is empty, for some reason it isn't. I can't seem to figure out the exact cause, though I have a feeling it has something to do with the "remove" function not deleting the last node, but for some reason, I can't figure out how to fix it.
TYPE listStackPop (struct Stack* stack)
{
/* FIXME: You will write this function */
assert (stack != NULL);
assert (!listQueueIsEmpty (stack->q1));
return listQueueRemoveFront (stack->q1);
}
---later calls to listQueueRemoveFront---
TYPE listQueueRemoveFront (struct Queue* queue)
{
/* FIXME: You will write this function */
assert (queue != 0);
assert (!listQueueIsEmpty (queue));
struct Link* toDelete;
toDelete = queue->head->next;
if (toDelete == queue->tail) {
queue->tail = queue->head;
}
else {
queue->head->next = toDelete->next;
}
int retVal = toDelete->value;
return retVal;
}
--For clarity, TYPE is defined as int--
I've tried going from simply return toDelete->value to copying toDelete's value to an int, and passing that int to be returned, as I thought maybe toDelete was being removed prematurely or something, but that isn't the case.
Unfortunately, google doesn't really have much info on this either. So far anyway.
Here is the full code on pastebin, in case anyone is interested to read it in full: https://pastebin.com/cDvdHmTu
I had expected it to pass, but for some reason, it failed even though all the other test cases passed.
results:
-------------------------------------------------
---- Testing stack from queue implementation ----
-------------------------------------------------
stack init...
stackIsEmpty == 1: PASSED
pushing 4, 5, -300...
stackIsEmpty == 0: PASSED
popping; val == -300: PASSED
popping; val == 5: PASSED
top val == 4 : PASSED
popping; val == 4: PASSED
stackIsEmpty == 1: FAILED
pushing 0-9...
top val == 9 : PASSED
C:\Users\Zedri\source\repos\Stack From Queues\Debug\Stack From Queues.exe (process 8928) exited with code 0.
Press any key to close this window . . .
Compiler/IDE Used: Visual Studio 2019
-~-~-~Edit 1~-~-~-
removed malloc call to the toDelete pointer
~-~-~-Edit 2-~-~-~
Fixed code on pastebin. Issue resolved.
Never mind, I simply needed to move queue->head->next = toDelete->next; outside of the else block, and remove the else block entirely.

Tricky Segmentation faults with BST recursion in C

I'm trying to add strings to a Binary Search Tree using a recursive insert method (the usual for BSTs, IIRC) so I can later print them out using recursion as well.
Trouble is, I've been getting a segmentation faults I don't really understand. Related code follows (this block of code is from my main function):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
// Stores the size of the C-strings we will use;
// Standardized to 100 (assignment specifications say
// ALL strings will be no more than 100 characters long)
// Please note that I defined this as a preprocessor
// directive because using the const keyword makes it
// impossible to define the size of the C-String array
// (C doesn't allow for static array struct members whose
// size is given as a variable)
#define STRING_SIZE 100
// The flags for case sensitivity
// and an output file
int cflag = 0, oflag = 0;
// These are intended to represent the boolean
// values true and false (there's no native bool
// data type in C, apparently)
const int TRUE = 1;
const int FALSE = 0;
// Type alias for the bool type
typedef int bool;
// This is the BST struct. A BST is basically just
// a Node with at most two children (left and right)
// and a data element.
typedef struct BST
{
struct BST *left;
struct BST *right;
char *key;
int counter;
} BST;
// ----- FUNCTION PROTOTYPES -----
void insert(BST **root, char *key);
int caseSenStrCmp(char *str1, char *str2);
int caseInsenStrCmp(char *str1, char *str2);
bool existsInTree(BST *root, char *key, int cflag);
void inOrderPrint(BST *root, int oflag, FILE *outFile);
void deallocateTree(BST *root);
int main(int argc, char **argv) {
extern char *optarg;
extern int optind;
int c, err = 0;
// Holds the current line in the file/user-provided string.
char currentLine[STRING_SIZE];
// This will store the input/output file
// directories
char fileDirectory[STRING_SIZE];
static char usage[] = "Usage: %s [-c] [-o output_file_name] [input_file_name]\n";
while ((c = getopt(argc, argv, "co:")) != -1)
switch (c)
{
case 'c':
cflag = 1;
break;
case 'o':
oflag = 1;
// If an output file name
// was entered, copy it
// to fileDirectory
if (argv[optind] != NULL)
{
strcpy(fileDirectory, argv[optind]);
}
break;
case '?':
err = 1;
break;
default:
err = 1;
break;
}
if (err)
{
// Generic error message
printf("ERROR: Invalid input.\n");
fprintf(stderr, usage, argv[0]);
exit(1);
}
// --- BST SORT CODE STARTS HERE ---
printf("This is BEFORE setting root to NULL\n");
// This is the BST. As the assignment instructions
// specify, it is initially set to NULL
BST *root = NULL;
// Pointer to the mode the files
// will be opened in. Starts as
// "w" since we're opening the output file
// first
printf("This is AFTER setting root to NULL\n");
char *mode = (char*)malloc(sizeof(char*));
strcpy(mode, "w");
printf("Wrote w to mode pointer");
// Pointer to the output file
FILE *outFile;
// Attempt to open output file
outFile = fopen(fileDirectory, mode);
printf("Opened outfile \n");
// Now update mode and fileDirectory so
// we can open the INPUT file
strcpy(mode, "r");
printf("Wrote r to mode\n");
// Check if we have an input file name
// If argv[optind] isn't NULL, that means we have
// an input file name, so copy it into fileDirectory
if (argv[optind] != NULL)
{
strcpy(fileDirectory, argv[optind]);
}
printf("Wrote input file name to fileDirectory.\n");
// Pointer to the input file
FILE *inFile;
// Attempt to open the input file
//printf("%d", inFile = fopen(fileDirectory, mode));
printf("Opened input file\n");
// If the input file was opened successfully, process it
if (inFile != NULL)
{
// Process the file while EOF isn't
// returned
while (!feof(inFile))
{
// Get a single line (one string)
//fgets(currentLine, STRING_SIZE, inFile);
printf("Wrote to currentLine; it now contains: %s\n", currentLine);
// Check whether the line is an empty line
if (*currentLine != '\n')
{
// If the string isn't an empty line, call
// the insert function
printf("currentLine wasn't the NULL CHAR");
insert(&root, currentLine);
}
}
// At this point, we're done processing
// the input file, so close it
fclose(inFile);
}
// Otherwise, process user input from standard input
else
{
do
{
printf("Please enter a string (or blank line to exit): ");
// Scanf takes user's input from stdin. Note the use
// of the regex [^\n], allowing the scanf statement
// to read input until the newline character is encountered
// (which happens when the user is done writing their string
// and presses the Enter key)
scanf("%[^\n]s", currentLine);
// Call the insert function on the line
// provided
insert(&root, currentLine);
} while (caseSenStrCmp(currentLine, "") != 0);
}
// At this point, we've read all the input, so
// perform in-order traversal and print all the
// strings as per assignment specification
inOrderPrint(root, oflag, outFile);
// We're done, so reclaim the tree
deallocateTree(root);
}
// ===== AUXILIARY METHODS ======
// Creates a new branch for the BST and returns a
// pointer to it. Will be called by the insert()
// function. Intended to keep the main() function
// as clutter-free as possible.
BST* createBranch(char *keyVal)
{
// Create the new branch to be inserted into
// the tree
BST* newBranch = (BST*)malloc(sizeof(BST));
// Allocate memory for newBranch's C-string
newBranch->key = (char*)malloc(STRING_SIZE * sizeof(char));
// Copy the user-provided string into newBranch's
// key field
strcpy(newBranch->key, keyVal);
// Set newBranch's counter value to 1. This
// will be incremented if/when other instances
// of the key are inserted into the tree
newBranch->counter = 1;
// Set newBranch's child branches to null
newBranch->left = NULL;
newBranch->right = NULL;
// Return the newly created branch
return newBranch;
}
// Adds items to the BST. Includes functionality
// to verify whether an item already exists in the tree
// Note that we pass the tree's root to the insert function
// as a POINTER TO A POINTER so that changes made to it
// affect the actual memory location that was passed in
// rather than just the local pointer
void insert(BST **root, char *key)
{
printf("We made it to the insert function!");
// Check if the current branch is empty
if (*root == NULL)
{
// If it is, create a new
// branch here and insert it
// This will also initialize the
// entire tree when the first element
// is inserted (i.e. when the tree is
// empty)
*root = createBranch(key);
}
// If the tree ISN'T empty, check whether
// the element we're trying to insert
// into the tree is already in it
// If it is, don't insert anything (the
// existsInTree function takes care of
// incrementing the counter associated
// with the provided string)
if (!existsInTree(*root, key, cflag))
{
// If it isn't, check if the case sensitivity
// flag is set; if it is, perform the
// checks using case-sensitive string
// comparison function
if (cflag) {
// Is the string provided (key) is
// greater than the string stored
// at the current branch?
if (caseSenStrCmp((*root)->key, key))
{
// If so, recursively call the
// insert() function on root's
// right child (that is, insert into
// the right side of the tree)
// Note that we pass the ADDRESS
// of root's right branch, since
// the insert function takes a
// pointer to a pointer to a BST
// as an argument
insert(&((*root)->right), key);
}
// If not, the key passed in is either less than
// or equal to the current branch's key,
// so recursively call the insert()
// function on root's LEFT child (that is,
// insert into the left side of the tree)
else
{
insert(&((*root)->left), key);
}
}
// If it isn't, perform the checks using
// the case-INsensitive string comparison
// function
else {
// The logic here is exactly the same
// as the comparisons above, except
// it uses the case-insensitive comparison
// function
if (caseInsenStrCmp((*root)->key, key))
{
insert(&((*root)->right), key);
}
else
{
insert(&((*root)->left), key);
}
}
}
}
// CASE SENSITIVE STRING COMPARISON function. Returns:
// -1 if str1 is lexicographically less than str2
// 0 if str1 is lexicographically equal to str2
// 1 if str2 is lexicographically greater than str1
I'm using getopt to parse options that the user enters. I've been doing a little bit of basic debugging using printf statements just to see how far I get into the code before it crashes, and I've more or less narrowed down the cause. It seems to be this part here:
do
{
printf("Please enter a string (or blank line to exit): ");
// Scanf takes user's input from stdin. Note the use
// of the regex [^\n], allowing the scanf statement
// to read input until the newline character is encountered
// (which happens when the user is done writing their string
// and presses the Enter key)
scanf("%[^\n]s", currentLine);
// Call the insert function on the line
// provided
insert(&root, currentLine);
} while (caseSenStrCmp(currentLine, "\n") != 0);
Or rather, calls to the insert function in general, since the printf statement I put at the beginning of the insert function ("We made it to the insert function!) gets printed over and over again until the program finally crashes with a segmentation fault, which probably means the problem is infinite recursion?
If so, I don't understand why it's happening. I initialized the root node to NULL at the beginning of main, so it should go directly into the insert functions *root == NULL case, at least on its first call.
Does it maybe have something to do with the way I pass root as a pointer to a pointer (BST **root in parameter list of insert function)? Am I improperly recursing, i.e. is this statement (and others similar to it)
insert(&((*root)->right), key);
incorrect somehow? This was my first guess, but I don't see how that would cause infinite recursion - if anything, it should fail without recursing at all if that was the case? Either way, it doesn't explain why infinite recursion happens when root is NULL (i.e. on the first call to insert, wherein I pass in &root - a pointer to the root pointer - to the insert function).
I'm really stuck on this one. At first I thought it might have something to do with the way I was copying strings to currentLine, since the line
if(*currentLine != '\0')
in the while (!feof(inFile)) loop also crashes the program with a segmentation fault, but even when I commented that whole part out just to test the rest of the code I ended up with the infinite recursion problem.
Any help at all is appreciated here, I've been trying to fix this for over 5 hours to no avail. I legitimately don't know what to do.
**EDIT: Since a lot of the comments involved questions regarding the way I declared other variables and such in the rest of the code, I've decided to include the entirety of my code, at least until the insert() function, which is where the problem (presumably) is. I only omitted things to try and keep the code to a minimum - I'm sure nobody likes to read through large blocks of code.
Barmar:
Regarding fopen() and fgets(): these were commented out so that inFile would remain NULL and the relevant conditional check would fail, since that part of the code also fails with a segmentation fault
createBranch() does initialize both the left and right children of the node it creates to NULL (as can be seen above)
currentLine is declared as an array with a static size.
#coderredoc:
My understanding of it is that it reads from standard input until it encounters the newline character (i.e. user hits the enter button), which isn't recorded as part of the string
I can already see where you were going with this! My loop conditional was set for the do/while loop was set to check for the newline character, so the loop would never have terminated. That's absolutely my fault; it was a carryover from a previous implementation of that block that I forgot to change.
I did change it after you pointed it out (see new code above), but unfortunately it didn't fix the problem (I'm guessing it's because of the infinite recursion happening inside the insert() function - once it gets called the first time, it never returns and just crashes with a segfault).
**
I managed to figure it out - turns out the problem was with the insert() function after all. I rewrote it (and the rest of the code that was relevant) to use a regular pointer rather than a pointer to a pointer:
BST* insert(BST* root, char *key)
{
// If branch is null, call createBranch
// to make one, then return it
if (root == NULL)
{
return createBranch(key);
}
// Otherwise, check whether the key
// already exists in the tree
if (!existsInTree(root, key, cflag))
{
// If it doesn't, check whether
// the case sensitivity flag is set
if (cflag)
{
// If it is, use the case-sensitive
// string comparison function to
// decide where to insert the key
if (caseSenStrCmp(root->key, key))
{
// If the key provided is greater
// than the string stored at the
// current branch, insert into
// right child
root->right = insert(root->right, key);
}
else
{
// Otherwise, insert into left child
root->left = insert(root->left, key);
}
}
// If it isn't, use the case-INsensitive string
// comparison function to decide where to insert
else
{
// Same logic as before. If the key
// provided is greater, insert into
// current branch's right child
if (caseInsenStrCmp(root->key, key))
{
root->right = insert(root->right, key);
}
// Otherwise, insert into the left child
else
{
root->left = insert(root ->left, key);
}
}
}
// Return the root pointer
return root;
}
Which immediately solved the infinite recursion/seg fault issue. It did reveal a few other minor semantic errors (most of which were probably made in frustration as I desperately tried to fix this problem without rewriting the insert function), but I've been taking care of those bit by bit.
I've now got a new problem (albeit probably a simpler one than this) which I'll make a separate thread for, since it's not related to segmentation faults.

C: Segmentation fault: GDB: <error reading variable>

I have a function shortestPath() that is a modified implementation of Dijkstra's algorithm for use with a board game AI I am working on for my comp2 class. I have trawled through the website and using gdb and valgrind I know exactly where the segfault happens (actually knew that a few hours ago), but can't figure out what undefined behaviour or logic error is causing the problem.
The function in which the problem occurs is called around 10x and works as expected until it segfaults with GDB:
"error reading variable: cannot access memory"
and valgrind:
"Invalid read of size 8"
Normally that would be enough, but I can't work this one out. Also any general advise and tips are appreciated... thanks!
GDB: https://gist.github.com/mckayryan/b8d1e9cdcc58dd1627ea
Valgrind: https://gist.github.com/mckayryan/8495963f6e62a51a734f
Here is the function in which the segfault occurs:
static void processBuffer (GameView currentView, Link pQ, int *pQLen,
LocationID *buffer, int bufferLen, Link prev,
LocationID cur)
{
//printLinkIndex("prev", prev, NUM_MAP_LOCATIONS);
// adds newly retrieved buffer Locations to queue adding link types
appendLocationsToQueue(currentView, pQ, pQLen, buffer, bufferLen, cur);
// calculates distance of new locations and updates prev when needed
updatePrev(currentView, pQ, pQLen, prev, cur); <--- this line here
qsort((void *) pQ, *pQLen, sizeof(link), (compfn)cmpDist);
// qsort sanity check
int i, qsortErr = 0;
for (i = 0; i < *pQLen-1; i++)
if (pQ[i].dist > pQ[i+1].dist) qsortErr = 1;
if (qsortErr) {
fprintf(stderr, "loadToPQ: qsort did not sort succesfully");
abort();
}
}
and the function whereby after it is called everything falls apart:
static void appendLocationsToQueue (GameView currentView, Link pQ,
int *pQLen, LocationID *buffer,
int bufferLen, LocationID cur)
{
int i, c, conns;
TransportID type[MAX_TRANSPORT] = { NONE };
for (i = 0; i < bufferLen; i++) {
// get connection information (up to 3 possible)
conns = connections(currentView->gameMap, cur, buffer[i], type);
for (c = 0; c < conns; c++) {
pQ[*pQLen].loc = buffer[i];
pQ[(*pQLen)++].type = type[c];
}
}
}
So I thought that a pointer had been overridden to the wrong address, but after a lot of printing in GDB that doesn't seem to be the case. I also rotated through making reads/writes to the variables in question to see which trigger the fault and they all do after appendLocationsToQueue(), but not before (or at the end of that function for that matter).
Here is the rest of the relevant code:
shortestPath():
Link shortestPath (GameView currentView, LocationID from, LocationID to, PlayerID player, int road, int rail, int boat)
{
if (!RAIL_MOVE) rail = 0;
// index of locations that have been visited
int visited[NUM_MAP_LOCATIONS] = { 0 };
// current shortest distance from the source
// the previous node for current known shortest path
Link prev;
if(!(prev = malloc(NUM_MAP_LOCATIONS*sizeof(link))))
fprintf(stderr, "GameView.c: shortestPath: malloc failure (prev)");
int i;
// intialise link data structure
for (i = 0; i < NUM_MAP_LOCATIONS; i++) {
prev[i].loc = NOWHERE;
prev[i].type = NONE;
if (i != from) prev[i].dist = INF;
else prev[i].dist = LAST;
}
LocationID *buffer, cur;
// a priority queue that dictates the order LocationID's are checked
Link pQ;
int bufferLen, pQLen = 0;
if (!(pQ = malloc(MAX_QUEUE*sizeof(link))))
fprintf(stderr, "GameView.c: shortestPath: malloc failure (pQ)");
// load initial location into queue
pQ[pQLen++].loc = from;
while (!visited[to]) {
// remove first item from queue into cur
shift(pQ, &pQLen, &cur);
if (visited[cur]) continue;
// freeing malloc from connectedLocations()
if (cur != from) free(buffer);
// find all locations connected to
buffer = connectedLocations(currentView, &bufferLen, cur,
player, currentView->roundNum, road,
rail, boat);
// mark current node as visited
visited[cur] = VISITED;
// locations from buffer are used to update priority queue (pQ)
// and distance information in prev
processBuffer(currentView, pQ, &pQLen, buffer, bufferLen, prev,
cur);
}
free(buffer);
free(pQ);
return prev;
}
The fact that all your parameters look good before this line:
appendLocationsToQueue(currentView, pQ, pQLen, buffer, bufferLen, cur);
and become unavailable after it tells me that you've stepped on (wrote 0x7fff00000000 to) the $rbp register (all local variables and parameters are relative to $rbp when building without optimization).
You can confirm this in GDB with print $rbp before and after call to appendLocationsToQueue ($rbp is supposed to always have the same value inside a given function, but will have changed).
Assuming this is true, there are only a few ways this could happen, and the most likely way is a stack buffer overflow in appendLocationsToQueue (or something it calls).
You should be able to use Address Sanitizer (g++ -fsanitize=address ...) to find this bug fairly easily.
It's also fairly easy to find the overflow in GDB: step into appendLocationsToQueue, and do watch -l *(char**)$rbp, continue. The watchpoint should fire when your code overwrites the $rbp save location.

How do we handle freeing a BST when malloc fails in the middle of our recursive build?

I've done some looking around and can't really find a good source that even addresses the idea.
First: It's well known that we should always check if malloc() and realloc() return null. This is commonly done in some way similar to:
Word* temp;
if ((temp = (Word*)malloc(sizeof(Word))) == NULL) {
fprintf(stderr, "unable to malloc for node.\n");
exit(EXIT_FAILURE);
}
However, we also generally build binary search trees in a recursive manner, like so:
void buildTree(Word** tree, const char* input) {
//See if we have found the spot to insert the node.
//Do this by checking for NULL
if (!(*tree)) {
*tree = createNode(input);
return;
}
//else, move left or right accordingly.
if (strcmp(input, (*tree)->data) < 0)
buildTree(&(*tree)->left, input);
else
buildTree(&(*tree)->right, input);
return;
}
So, what do we do if we start working with massive data sets and malloc() fails to allocate memory in the middle of that recursive buildTree function? I've tried a number of things from keeping track of a "global error" flag and a "global head" node pointer and it just seems to be more and more messy the more I try. Examples working with building BSTs rarely seem to give any thought to malloc() failing, so they aren't really helpful in this regard.
I can logically see that one answer is "Don't use recursion and return the head of the tree each time." and while I can see why that would work, I'm an undergraduate TA and one of the things we use BSTs to teach is recursion. So, saying "don't use recursion" to my students when we are TEACHING recursion would be self-defeating.
Any thoughts, suggestions, or links would be greatly appreciated.
We usually use a return error and let the caller free it, after all it could very well free other non critical resources and try to insert the node again.
#define BUILD_OK 0
#define BUILD_FAILED 1
int buildTree(Word** tree, const char* input) {
int res;
//See if we have found the spot to insert the node.
//Do this by checking for NULL
if (!(*tree)) {
if (!(*tree = createNode(input)))
return BUILD_FAILED;
//Maybe other checks
return BUILD_OK;
}
//else, move left or right accordingly.
if (strcmp(input, (*tree)->data) < 0)
res = buildTree(&(*tree)->left, input);
else
res = buildTree(&(*tree)->right, input);
return res;
}

Whether code is read from top to bottom

I am creating a program in c which is based on a linked list, where every node (of struct) holds an integer and a pointer to the next node.
I use dynamic allocation (malloc) and deallocation (free) as new nodes are added and old nodes are deleted.
when a node is deleted a function named delete is called.
I discovered that the program crashes sometimes when this delete-function is called and I KNOW that its something with the pointers in the method but I dont know WHERE in the code (row number) and WHY this happends.
I am used to high-level languages such as Java and I am used to encircle the problem by putting print-syntax at certain places in the method just to reveal WHERE it crashes.
I thought I could do the same with c and with pointer because to my knowledge I beleive the code is read from top to bottom that is 1, 2, 3, 4, and so on. (maybe interrupt handlers behave another way?)
So in this function named delete I have gone so far by putting this printf() at the very beginning of the delete-function - and all the same the program crashes.
So my Question - is it really possible that its some syntax in the delete-function (when I loop pointers for instance) that causes the crash WHEN not even the printf() is printing?
Am I wrong when I believe that the program is executed from to to bottom - that is 1, 2, 3 ....
You can se my printf-function in the very beginning of delete-function
And by the way - how could I solve this problem when I get this cryptic crash message from windows? See the bitmap!!
Greatful for answers!!!
int delete(int data) {
printf("IN THE BEGINNING OF DELETE!!!");
int result = 0;
if (queueref.last != NULL) {
node *curr_ptr;
node *prev_ptr;
node *temp_ptr;
if (queueref.first->data == data) {
temp_ptr = queueref.first;
queueref.first = queueref.first->next;
destroy_node(temp_ptr);
result = 1;
if (queueref.first == NULL) {
queueref.last = NULL;
puts("queue is now empty!!!");
}
} else {
prev_ptr = queueref.first;
curr_ptr = queueref.first->next;
printf("prev_ptr: %d\n", prev_ptr);
printf("curr_ptr: %d\n", curr_ptr);
while(curr_ptr != NULL) {
if (curr_ptr->data == data) {
result = 1;
if (curr_ptr->next != NULL) {
temp_ptr = curr_ptr;
destroy_node(temp_ptr);
prev_ptr->next = curr_ptr->next;
} else {
temp_ptr = curr_ptr;
queueref.last = prev_ptr;
prev_ptr->next = NULL;
destroy_node(temp_ptr);
}
}
curr_ptr = curr_ptr->next;
prev_ptr = prev_ptr->next;
}
}
}
return result;
}
Common mistake, here's the deal. This
printf("IN THE BEGINNING OF DELETE!!!");
needs to be
printf("IN THE BEGINNING OF DELETE!!!\n");
^^ note the newline
The reason is because stdio does not flush stdout until it sees a newline. If you add that newline, you should see the printf when the code enters the function. Without it, the program could crash, the stdout buffer would not have been flushed and would not see the printf.
Your code seems to have lots of implementation flaws. As a general advice I would recommend using some standard well-tested queue support library and static code analyzers (in this case you would even find dynamic analyzer valgrind very helpful, I guess).
For example, if implementation of destroy_node(ptr) is equivalent to free(ptr), then your code suffers from referencing destroyed data (or ,in other words, garbage) in this code snippet:
while(curr_ptr != NULL) {
if (curr_ptr->data == data) {
result = 1;
if (curr_ptr->next != NULL) {
temp_ptr = curr_ptr;
destroy_node(temp_ptr);
prev_ptr->next = curr_ptr->next; //<- curr_ptr is still in stack
//or register, but curr->next
//is garbage
// what if curr_ptr is first node? did you forget to update queueref.first?
} else {
temp_ptr = curr_ptr;
queueref.last = prev_ptr;
prev_ptr->next = NULL;
destroy_node(temp_ptr);
}
// if you you need to destroy only one node - you can leave the loop here with break;
}
curr_ptr = curr_ptr->next; /// assigning garbage again if node is found
prev_ptr = prev_ptr->next;
The reason why using destroyed data can work in * most * (if I can say that, basically this is unpredictable) cases is that the chances that this memory can be reused by other part of program for dynamically allocated data can vary on timings and code flow.
PS
Regarding cryptic messages in the Windows box - when program crashes OS basically generates crashdump and prints registers (and dumps some relevant memory parts). Registers and memory dumps can show the place of crash and immediate register/stack values but you have to now memory map and assembler output to understand it. Crashdump can be loaded to debugger (WinDbg) together with unstripped binary to check stactrace and values of local variables at the moment of crash. All these I described very very briefly, you could find tons of books / guides searching for "windows crash or crashdump analysis"

Resources