Ok, so for my homework I have a function that creates a linked list from file input and it's like a calendar, so it reads in a date and title from a file. I'm using a head node that is globally declared to make it
easier to work with other functions.
This is my node struct:
typedef struct event_t{
char title[20];
event_date_t date;
struct event_t *next;
}event_t;
event_date_t is just a simple struct for the date
Here's the function:
void insert_events_linked_list(FILE *file, int n){
//printf("LL function started\n"); //self-explanatory test line
event_t last;
head.next = &last;
int i;
//This loop will create a ll of the specified length
for(i=0; i<n; i++){
event_t *last = malloc(sizeof(event_t));
int title_test = fscanf(file, "%20s%*c", last->title);
printf("%s\n", last->title); //test line to make sure names are grabbed properly
//This skips the rest of the event and prints error message if title is too long
if(title_test != 1){
fscanf(file, "%*s %*d %*d");
printf("Error: LL event %d title too long\n", i++);
continue;
}
else{
fscanf(file, "%d %d", &last->date.month, &last->date.day);
last = last->next;
}
}
printf("Loop exited");
}
The test line prints all the titles, but it shows a segmentation fault and aborts before printing "Loop exited"
This line of your code,
event_t *last = malloc(sizeof(event_t));
will declare a brand new last every time the for loops back. Declare it as static and adjust your code accordingly, and there should no longer be any problem.
Demo
for (i=0;i<5;i++){
int x=6;
x++;
printf("%d ",x);
}
Output
7 7 7 7 7
You should see the problem.
The test line prints all the titles, but it shows a segmentation fault and aborts before printing "Loop exited"
There are many bugs in your program. You should read this post. You should also learn to use a debugger.
I suspect that the reason Loop exited is not printed is that you didn't terminate it with the newline, and so printf is waiting for you to complete a line or call fflush. By default, printf output is line buffered. That is one of the reasons you should always use fprintf(stderr, ...) instead. stderr (by default) is unbuffered.
On to bugs.
You have two variables called last in the same scope. This is almost never a good idea.
This statement:
head.next = &last;
assigned an address of a local variable to a global head pointer. After the current function returns, that address will become invalid, and this is almost certainly not what you intended. In particular, head.next never points to any memory you malloc (and leak!) in the loop.
Your loop looks like this:
for (...) {
event_t *last = malloc(sizeof(event_t)); // last->next is uninitialized
...
last = last->next; // leak the memory allocated above
// by overwriting the pointer with garbage
}
That is also clearly not what you intended.
Here is one way to build a linked list incrementally:
event_t **link = &head.next;
for (...) {
event_t *ev = calloc(...); // Initializes ev->next to NULL.
// Fill the rest of ev ...
// Link it into the list:
*link = ev;
link = &ev->next;
}
Related
When I call the print_linklist function I am getting a segmentation fault. Here is the function definition:
//will display the node in a nice string
char * term_to_string(term_t * term){
int exp = term->exponent;
int coef = term->coefficient;
return ("%dx^%d", coef, exp);
}
**//will print the list using the nodde_to_string method
void print_linklist(node_t * curr){
printf("entering print to list!!!");
node_t * current = curr;
while(current != NULL){
printf("%s +", term_to_string(curr->term));
current = current->next_node;
}
}**
And here is the main method where it is being called:
/* This is your main file */
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"common.h"
#include"buildlinklist.h"
#include"printandcombine.h"
int main() {
node_t * node_ptr = NULL;
node_t * new_node_ptr=NULL;
printf("NAME: SAMPLE OUTPUT\n");
/* Build linklist */
read_objects(&node_ptr);
/* Print the link list */
printf("Original: \n");
print_linklist(node_ptr);
/* Combine like terms in the link list and craeate a new link list */
new_node_ptr=combine_like_terms(node_ptr);
printf("\nCombined: : ");
/* Print new combine linklist */
print_linklist(new_node_ptr);
printf("\nNAME: SAMPLE OUTPUT\n");
free(node_ptr);
free(new_node_ptr);
return 0;
}
After the function is called I get "zsh: segmentation fault ./project1". I don't even get the "entering print to list!!!" to print from the print_linklist method.
return ("%dx^%d", coef, exp);
That's not doing what you seem to think it's doing (what I think you think it's doing is to return a string created by some sort of printf functionality).
However, the comma operator in something like a, b evaluates both a and b, but the result is b. The n-variant, like a, b, c, d, evaluates everything and returns the last one (d).
Hence you are returning exp as if it was a character pointer. It almost certainly isn't (since you're trying to printf it with %d) so, if you treat it as such, hilarity may ensue.
Well, less hilarity and more crashing/weirdness, but you get the idea :-)
You could create strings in heap memory and pass them around but it's sometimes difficult for newcomers to the language to do that safely. Instead, I would suggest simply printing the thing within the function, with something like:
void print_term(const term_t *term, const char *after){
printf("%dx^%d%s", term->coefficient, term->exponent, after);
}
void print_linklist(node_t *curr){
puts("entering print to list!!!");
node_t *current = curr;
while (current != NULL){
print_term(curr->term, " +");
current = current->next_node;
}
}
You'll notice my "entering print to list" statement is subtly different to yours in that it uses puts, which appends a newline character to the end (you could do that explicitly, with \n, if you wanted to stick with printf).
Since standard output is line-buffered for terminal devices, the reason you're not seeing that is almost certainly due to the fact your code is crashing (as per this answer) before it flushes. In that case, unflushed data is likely to just disappear.
There could be a number of reasons you are getting a segmentation fault but trying to print the list is not one of them. If your read_objects() declaration is like this read_objects(node_t * curr) then you shouldn't pass as an argument the address of the pointer
read_objects(&node_ptr);
This should be read_objects(node_ptr);
Also, if you want to iterate through the list and print it you should create a pointer to the head of the list in the main() function node_t *head = NULL;
and pass it as a parameter in the print_linklist(head);.
After that, make *current point at the head of the list node_t * current = head;.
That way you are starting printing the nodes from the start. You should also check if the list is empty.
I don't even get the "entering print to list!!!" to print from the print_linklist method.
printf doesn't cause anything to be printed to the screen. It just copies a string into a buffer to be printed at some later time. If you want to force the buffer to be printed, you can call fflush. As mentioned in comments, if stdout is directed to an interactive device, including a \n in the output string will generally also cause the buffer to be flushed to the actual output device.
After the function is called I get "zsh: segmentation fault ./project1".
This probably happens in term_to_string if you call it with an invalid pointer argument, when it tries to dereference its argument.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I'm trying to create a program in C that is able to create a list of characters (using a linked list) input by the user, and follow these commands: insert (adds a new character to the list), remove (clears the list and deletes all elements), print (prints the current contents of the list) and exit (closes the program). In order for it to work, the user must enter the command to execute first. For instance, if they type 'insert', they will be prompted to enter a character, if they type 'print', the program will print the list, and so on.
To approach this, I basically declared a char[] variable called command. Which represents the user's text input. After receiving the input, the program will compare the command string to the following strings: insert, remove, print, and exit, and if the strings match, then the program will execute the command that the string corresponds to (refer to the main below):
#include <stdio.h>
#include <stdlib.h>
struct linked_list
{
char data;
struct linked_list* next;
};
void insert(struct linked_list** head, char input);
void removed(struct linked_list** head);
void printed(struct linked_list* head);
void insert(struct linked_list** head, char input) {
int x;
x = ("%d",input);
if (x < 97 || x > 122) {
printf("The character you entered is invalid, please use the insert command and try again\n");
}
else {
struct linked_list* newNode = (struct linked_list*) malloc(sizeof(struct linked_list));
struct linked_list* lastNode = *head;
newNode->data = input;
newNode->next = NULL;
if (*head == NULL) {
*head = newNode;
return;
}
while (lastNode->next != NULL) {
lastNode = lastNode->next;
}
lastNode->next = newNode;
}
}
void removed(struct linked_list** head) {
struct linked_list* current = *head;
struct linked_list* next;
while(current != NULL) {
next = current->next;
free(current);
current = next;
}
*head = NULL;
}
void printed(struct linked_list* head) {
while (head != NULL) {
printf("%c ", head->data);
head = head->next;
}
printf("\n");
}
int main(int argc, char *argv[]) {
char command[] = "";
char inserts[] = "insert";
char removes[] = "remove";
char prints[] = "print";
char exits[] = "exit";
char input;
struct linked_list* head = NULL;
int done = 1; //Variable used to keep the loop going until the user types 'exit'
printf("Welcome, please use the commands 'insert', 'remove', 'print' or 'exit' to use the program\n");
while (done == 1) {
scanf("%s", &command);
int result1 = strcmp(command,inserts);
int result2 = strcmp(command,removes);
int result3 = strcmp(command,prints);
int result4 = strcmp(command,exits);
if (result1 == 0) {
printf("Enter a char: ");
scanf("%s", &input);
insert(&head, input);
result1 = 1;
}
else if (result2 == 0) {
removed(&head);
result2 = 1;
}
else if (result3 == 0) {
printed(head);
result3 = 1;
}
else if (result4 == 0) {
printf("The program will close now");
done = 0;
}
else {
printf("Unrecognized command, please try again\n");
}
}
return 0;
}
The solution I came up with works perfectly for the commands insert, remove, and print. However, for some reason I can't quite understand it doesn't work for exit, it will only work if and only if exit is the first command you enter (which doesn't make sense, since why would you exit a program without even using it). There are no typos or other apparent errors. You can compile the code and see for yourself. What might be causing this issue? Why does it work for three of the command words but not for one?
Thanks in advance
UPDATE: After messing around with the code a little bit, I realized none of the suggestions provided were the issue. I simply changed my loop statement to be : else if (strcmp(command,"exit")==0) and it worked perfectly
x = ("%d",input);
involves the comma operator. The left operand "%d" is evaluated (to itself, since a literal string, then discarded) and the right operand input is evaluated and result of that comma operator.
So x becomes input
You may want to spend a dozen of minutes reading about scanf or about getchar. It can fail. You should handle both failure and success (with some test).
My personal recommendation : enable all warnings and debug info in your compiler. With GCC, compile with gcc -Wall -Wextra -g. Improve your code to get no warnings. Then use your debugger (e.g. GDB) to understand the behavior of your program.
Of course you will spend some time reading the documentation of your compiler and of your debugger.
Don't forget to spend some time reading a good book on the C programming language. Refer also to this C reference. Study also for inspiration the source code of existing free software (like GNU bash) coded in C. You will learn a lot by reading existing reviewed C code.
You may also want to read something about parsing techniques. You could consider (if your teacher allows that) using parser generators like GNU bison (which generates C code).
Be aware that in 2021 UTF-8 is used everywhere.
Be scared and avoid undefined behavior.
In some cases, a program appears to work, but does not. Read some C standard (like n1570) explaining it. I guess your program has several buffer overflows.
You could also (if your teacher allows it) use tools like the Clang static analyzer, valgrind or the address sanitizer
The reason your commands aren't working as intended is that you declare zero-length string literal:
char command[] = "";
then read user input into it:
scanf("%s", &command);
The user input isn't going to fit into zero characters, so it's going to overwrite something else in memory, likely something important.
You need to declare an actual buffer that's large enough to hold the largest expected input, something like this:
char command[10];
You also need to clear or initialize that buffer, perhaps using memset(command, 0, sizeof(command));
Something similar happens with input. It's declared as char but then you read a string (%s) into it:
scanf("%s", &input);
Even if you only type one character, scanf still has to store a terminating NUL, so it will clobber something else in memory.
With all these buffer overwrites going on, what the program actually does, and why exit only works as the first command, is anybody's guess.
So I wrote some code to implement the shell (Linux, GCC, C language) and it all works but from some reason the code crashes when I added the history option:
It really doesn't (the other code) so I'll put here only what you need.
The problem is when I type quit after one old command that need to be saved in the history and when I type quit it just crushes with segmentation fault (core dumped).
The history is saved in a structure of a linked list, string of command and the node for the next node, also I saved the head in the main. The point is that I want to save only 15 last commands, and I don't care about the others, so every time I want to print the list I just moved on the first 15 nodes in the loop.
When I debugged with GDB I saw that the line the code crashes is the line after he add the first command the the history but the current line is really not related to the history:
main:
int main()
{
history_ll* top;
char userInput [CHOICE_LENGTH];
char buff[PATH_MAX];
int flag=1;
cmdLine * head;
while (flag)
{
getcwd(buff, PATH_MAX);
printf("%s:~$ ",buff);
fgets(userInput,MAX_INPUT, stdin);
userInput[strlen(userInput)-1]=0;
historyAdder(userInput,&top);
if(strcmp(userInput,QUIT_OPTION)==0) //segmentation fault here!
{
flag=0;
}
else
{
//doesn't matter
}
}
return 0;
}
the historyAdder looks like this:
void historyAdder(const char *command,history_ll** top)
{
history_ll* node;
strcpy(node->command,command);
node->command[strlen(command)]=0;
if(historyLength!=0)
{
node->next= *top;
}
else
{
node->next= NULL;
}
*top = node;
historyLength++;
}
NOTE: historyLength is a global variable
This is the structure:
typedef struct history_ll{
char command[CHOICE_LENGTH];
struct history_ll *next;
}history_ll;
Thank you helpers!
There are at least two important issues in this code.
One is that buffer length might ne too short when you read from stdin:
definition:
char userInput [CHOICE_LENGTH];
but usage is:
fgets(userInput,MAX_INPUT, stdin);
You should use same buffer size or assert MAX_INPUT is less or equal CHOICE_LENGTH.
Second, you trigger undefined behavior by dereferencing uninitialised pointer here:
void historyAdder(const char *command,history_ll** top)
{
history_ll* node;
strcpy(node->command,command); /* bad... */
Here (and in the lines following)
void historyAdder(const char *command,history_ll** top)
{
history_ll* node;
strcpy(node->command,command);
...
the code dereferences an uninitialised pointer: node Doing so invokes UB and mostly likely crashes the program.
This is probably your problem:
char userInput [CHOICE_LENGTH];
...
fgets(userInput,MAX_INPUT, stdin);
Unless CHOICE_LENGTH is greater than or equal to MAX_INPUT, fgets can write past the end of the userInput array, which will corrupt memory, leading to a crash. However, since you did not show a complete program that I could compile for myself and watch crash, I can't be sure.
Two pieces of advice for you:
You're on Linux, so you have getline. Use it instead of fgets and you won't have to worry about input buffer sizes.
Whenever you have a program that is crashing with segmentation faults, the first thing you reach for should be valgrind. Very often, valgrind will reveal that the real bug is nowhere near where you thought it was.
So I have this program that I put together from head first c. It is from Chapter 6 - the data structures chapter... My problem is that the output displays all previous listed entries as well as the last entered name to the standard input. So instead showing everything printed once the program prints everything almost twice. It is hard for me to describe. If you just copy and paste it into a text-editor on your machine and run the code you will see what I mean.
The book shows the program taking a file of island names using the < redirection tool. When I try this it prints the first name the second name and the first name. Then the next name and the second and first name...Then the next name and the third, second, and first name...etc depending how many names there are. This behavior also occurs when entering text in the terminal in standard input.
IF I change the code to say display(next) it works closer to what I would expect but it still prints out an extra blank line and there are probably memory leaks
This code is pretty much over my head can someone figure out why it is printing like this?
I would ask at the head first c discussion board but I wanted to ask stackoverflow first and get an immediate answer.
My code is below. If you copy and paste it into a text editor it should not look like a wall of text.
Happy coding.
#include <stdio.h> // basic input output
#include <stdlib.h> // for obtaining and releasing heap memory malloc and free...
#include <string.h> // for the stringdup method
typedef struct island {
char *name;
char *opens;
char *closes;
struct island *next;
} island;
void display(island *madonna);
island* create(char *name);
void release(island *start);
int main()
{
/* create islands */
island *start = NULL;
island *i = NULL;
island *next = NULL;
char name[80];
puts("enter island name...");
for(; fgets(name, 80, stdin) != NULL; i = next) {
next = create(name);
if(start == NULL)
start = next;
if (i != NULL)
i -> next = next;
display(start);
}
release(start);
}
// display method
void display(island *start)
{
island *i = start;
if (i == NULL)
puts("i equals NULL ");
for(;i != NULL; i = i ->next) {
printf("Name: %s open: %s-%s\n", i->name, i->opens, i->closes);
}
}
// create method
island* create(char *name)
{
island *i = malloc(sizeof(island));
i->name = strdup(name);
i->opens = "09:00";
i->closes = "17:00";
i->next = NULL;
return i;
}
// release method
void release(island *start)
{
island *i = start;
island *next = NULL;
for(; i != NULL; i = next) {
next = i-> next;
free(i->name); // must free this first because of strdup uses heap memory
free(i);
}
}
The code is working-as-designed (WAD). It is designed to print the complete list after each entry is read — that's what the display(start) does in the loop. You could help yourself by either echoing the input (printf("Read: %s", name); where there's no newline in the format because the name still includes the newline) or by tagging the display with printf("Printing list:\n"); before the call to display() (or both). If you get rid of the newline from the name, you'll need to adjust the 'echo' operation.
Learning how to create helpful diagnostic messages is a valuable technique; one of the key points is to ensure that the output lines end with a newline so there's a decent chance you'll see the printing as it occurs, rather than some indeterminate time later. Another key point is printing inputs so that you know what the code is working on rather than thinking you know what the code is working on. Printing the complete list on each iteration also helps ensure that the list is being constructed correctly. You can find examples on SO where the list was not constructed correctly (e.g. First address of struct). Had the complete list been printed on each iteration, the problem would have been more obvious.
The trouble, therefore, seems to be that your expectations do not match what the code is designed to do.
"My problem is that the output displays all previous listed entries as
well as the last entered name to the standard input."
for(;i != NULL; i = i ->next) {
printf("Name: %s open: %s-%s\n", i->name, i->opens, i->closes);
by the way you told us your problem but you did not tell us what your program should do
here is the function where im getting the segmentation fault
void searchcity()
{
struct city *ptr=citylist;
printf("Which city would you like me to search?: ");
scanf("%s",searchedcity);
// printf("%s",searchedcity);
while(ptr)
{
if(!strcmp(searchedcity,ptr->name))
printf("name= %s, statecode = %s,population = %s,region = %s,zipcode = %s\n",ptr->name,ptr->statecode,ptr->population,ptr->region,ptr->zipcode);
else
printf("sorry, couldnt find that city");
ptr=ptr->next;
}
}
not sure what can be causing this to happen.
Based on that code (a), here's what you need to check at a bare minimum:
That searchedcity has enough space for the input (b).
That all the strings held in the citylist linked list are properly constructed (null-terminated).
That all those fields in the structure are actually character arrays (or equivalent pointers) rather than integers (population, for example).
That the list itself is properly built (no dangling or invalid pointers).
You do have one other problem, though nothing to do with the segfault.
Your code will print "sorry, couldnt find that city" for every node in the list that doesn't match your city so, if you have New York, Moscow and London, and you look for London, you'll get that message printed twice before it finds it.
A better solution (one of many variants) would be something like:
struct city *ptr = citylist;
while (ptr != NULL)
if (strcmp (searchedcity, ptr->name) == 0)
break;
if (ptr == NULL)
printf ("Sorry, couldnt find that city.\n");
else
printf ("%s, state = %s, pop = %s,r egion = %s, zip = %s\n",
ptr->name, ptr->statecode, ptr->population, ptr->region, ptr->zipcode);
That way, the loop is responsible for either finding the correct pointer or setting it to NULL. After the loop is the correct time to decide what should be printed.
(a) That code itself seems okay other than the dangerous scanf but it does depend on quite a lot of other stuff not shown.
(b) In fact, scanf with an unbounded %s is a serious hole in your code that can easily lead to buffer overflow. See here for details and solution. In any case, scanf("%s") is not a good way to scan strings with spaces in them since something like Los Angeles would end up as Los :-)
Code below works, I did minor changes you can walk through it. Few small mistakes you had was your ptr->next was never executed due to missing bracket. The rest I wrote in the code.
Thanks hope we helped.
#include <stdio.h>
struct city {
char name[100];
char statecode[100];
char population[100];
char region[100];
char zipcode[100];
struct city* next;
};
int main() // this is your searchcity function
{
char searchedcity[100]; // make sure you initialize this. YOu haven't done it in the code you gave us.
// Assume citylist is in your main function or initialized as a global var
// I initialized it here for simplicity
struct city* citylist = (struct city*) malloc(sizeof( struct city));
strcpy(citylist->statecode,"statecode");
strcpy(citylist->population,"population");
strcpy(citylist->region,"region");
strcpy(citylist->zipcode,"zipcode");
citylist->next = NULL;
//end of citylist
struct city *ptr = citylist;
printf("Which city would you like me to search?: ");
scanf("%s",searchedcity);
// printf("%s",searchedcity);
while(ptr) {
printf("while \n");
if(!strcmp(searchedcity,ptr->name)){
printf("name= %s, statecode = %s,population = %s,region = %s,zipcode = %s\n",ptr->name,ptr->statecode,ptr->population,ptr->region,ptr->zipcode);
}else{
printf("sorry, couldnt find that city");
ptr=ptr->next;
}
}
return 0;
}