I've posted a question similar to this, but I was able to further narrow down the issue. I'm pretty sure I know what the problem is, but I've been stuck figuring out how to solve it.
//assuming typedef struct person person;
struct person{
char first_n[100];
char last_n[100];
char middle_n[100];
struct person* next;
};
void open_file_and_read(char* file){
FILE* fp = fopen(file_name, "r");
if (fp != NULL){
while (!feof(fp)){
person* person = malloc(sizeof(person));
person->next = NULL;
while (fscanf(fp, "%s %s %s", person->first_n, person->last_n, person->middle_n) == 3){
add_to_contacts(person);
}
}
}
}
void open_write_file(char* file){
FILE* filep = fopen(file, "w");
person* copy;
for (copy = front; copy != NULL; copy=copy->next){
fprintf(filep, "%s %s %s\n", copy->last_n, copy->first_n, copy->middle_n);
}
fclose(filep);
}
void add_to_contacts(person* person){
printf("last_name: %s", person->last_name);
if (head == NULL){
person->next = head;
head = person;
else{
person->next = head;
head = person;
}
}
int main(int argc, char * argv[])
{
char* inputFilename = argv[1];
if (inputFilename == NULL){
inputFilename = "myRolodex";
}
open_read_file(inputFilename);
char command;
while (command != 'Q' && command != 'q'){
printf("STARTING TO READ COMMAND\n");
command = read_command(inputFilename);
evaluate_command(command);
}
open_write_file(inputFilename);
clear_rolodex();
The printf prints out everything in the file as one node. Ex: file contains:
Bob Lee Steve
Mike Steven Noel first, last, middle
James Nguyen Lee
the printf prints out LeeStevenNguyen. These should be separate.
I believe the problem has to do with malloc only being called once, when what I really need is for it to be called each time I scanf. However I can't move person* person = malloc(sizeof(person)); under the while...fscanf loop because fscanf is dependent on having a person already created. So how do I go about mallocing each new person. A similar assignment is due in a couple of hours and I've spent the last 10 hours trying to get everything to work and I believe this is the big part.
Thank you
EDIT for more detail: I have a print statement in my add_to_contacts function that prints out the node->lasts_name. I also added a print statement before my sorting function(not posted) and it prints infinitely
There are several ways to resolve this.
The one that I think most directly matches what you want is:
person* person = malloc(sizeof(person));
person->next = NULL;
while (fscanf(fp, "%s %s %s", person->first_n, person->last_n, person->middle_n) == 3){
add_to_contacts(person);
person = malloc(sizeof(person));
person->next = NULL;
}
free(person);
This assumes that add_to_contacts inserts the malloc'd
pointer into a data structure, so we need the next fscanf to put its
data in a different location.
Having done this, there will always be one person* allocated
at the end that is not actually ever passed to add_to_contacts, so we
can and should free that pointer when the loop exits.
A slight variation is
person* person = malloc(sizeof(person));
while (fscanf(fp, "%s %s %s", person->first_n, person->last_n, person->middle_n) == 3){
person->next = NULL;
add_to_contacts(person);
person = malloc(sizeof(person));
}
free(person);
(The idea there is that the only thing that needs person->next = NULL;
here is presumably add_to_contacts(person), and by postponing
person->next = NULL; until the very last moment we don't have to call
it outside the loop as well as inside the loop.)
An alternative (quite different) approach is to
declare local variables, let fscanf write to them,
and copy them to a newly allocated person inside the loop.
A third approach is to make person a stack-allocated (not malloc'd) variable,
and make add_to_contacts make its own malloc'd copy of it in order
to insert the copy in the data structure.
But these last two options involve fscanf writing the data one place
and then having to copy it to another in order to put it in the larger structure. It seems you may want to avoid the copy.
regarding this part of the question:
believe the problem has to do with malloc only being called once, when what I really need is for it to be called each time I scanf. However I can't move person* person = malloc(sizeof(person)); under the while...fscanf loop because fscanf is dependent on having a person already created
suggest fscanf() into local variables (remember to use length modifiers on the format parameters to avoid buffer overflow.
Then, within the loop, malloc, error check, copy variables, call function to insert malloc memory into linked list.
Related
I know questions of this form have been asked before, but I'm having difficulty finding one that suits my situation.
As someone trying to get used to C, I'm having issues with pointers, specifically Strings. I have a small number of errors that keep cropping up, and I can't wrap my head around what I keep doing wrong.
I'm writing a program that will read username:password key/values and then compare them against given values. For that, I use a userpass struct:
typedef struct {
char user[BUF_MAX];
char pass[BUF_MAX];
} userpass;
And the following code:
char *authThread(char *filename, char *request){
List_t logins;
char user[BUF_MAX];
char pass[BUF_MAX];
char *saveptr = NULL;
char *listptr = NULL;
char *username = strtok_r(request, ":", &saveptr);
char *password = strtok_r(NULL, ":", &saveptr);
char *failCode = malloc(sizeof (char)*BUF_MAX);
sprintf(failCode, "0:%s:0", username);
char *successCode = malloc(sizeof (char)*BUF_MAX);
sprintf(successCode, "1:%s:%s", username, ticketSecret);
if (List_init(&logins)){
//Retrieve all the user:pass pairs from the auth file
FILE *fp = fopen(filename, "r");
while(fscanf(fp, "%s:%s", user, pass) != EOF){
userpass new;
//PROBLEM LINES BELOW+++++++++++++++++++++++++++++++++
strcpy(new->user, user);
strcpy(new->pass, pass);
List_add_tail(&logins, &new);
}//while
fclose(fp);
//See if the username/pass combination provided exists in the auth file
for (;;){
userpass *next = NULL;
//PROBLEM LINE BELOW+++++++++++++++++++++++++++++++++
List_next_node(&logins, &listptr, &next);
if (next == NULL) break;
//Match found, return required auth string
if (strcmp(next->user, username) == 0 && strcmp(next->pass, password) == 0){
return successCode;
}//if
}//for
return failCode;
}//if
else{
printf("Error creating auth list\n");
}//else
}//authThread
List_t is a linked list implementation. The header of the List_next_node function is this:
int List_next_node ( List_t *list, void **context, void **data );
I've labelled the two lines where I get an error above. When I try to compile, I get two errors. On the first two lines, I get: invalid type argument of â->â.
One the second line, I get: passing argument 2(3) of âList_next_nodeâ from incompatible pointer type
I can see that both of these issues are caused by variables not being the correct type, but I can't see how that's possible. In the first case, new->user should be a char array, as should user.
In the second case, List_next_node accepts three parameters; a pointer to a list, a pointer to a context pointer, and a pointer to a data pointer. As far as I can tell, everything is the type it should be. I can only imagine it's some problem with how Strings (i.e. char arrays) work in C.
new is declared locally on the stack. It is not a pointer so you need to change
userpass new;
strcpy(new->user, user);
strcpy(new->pass, pass);
to
userpass new;
strcpy(new.user, user);
strcpy(new.pass, pass);
Depending on the implementation of List_add_tail (I can't see it in your question), this may not be the only problem. new goes out of scope after List_add_tail returns so, unless adding an item to the list takes a copy, the list will be left with a pointer to memory that is liable to be reused.
If List_add_tail doesn't create a copy of the userpass* in its second argument, you should change your code to something like
userpass* new = malloc(sizeof(*new));
strcpy(new->user, user);
strcpy(new->pass, pass);
List_add_tail(&logins, &new);
(Note that in this last example, new is a pointer so we have to use the dereference operator -> to access its members again.)
Error 1
You have defined the variable new as a userpass structure yet you access its internal attributes like a pointer.
userpass new;
...
strcpy(new->user, user); // new->user is equivalent to (*new).user
strcpy(new->pass, pass);
Instead, use the . operator:
strcpy(new.user, user);
strcpy(new.pass, pass);
You should dynamically allocate your userpass structures so that they do not go out of scope.
Error 2 (It's a warning)
Your argument &listptr is of type char ** yet the function is expecting void **. You can cast the argument to remove the warning:
List_next_node(&logins, (void **) &listptr, (void **) &next);
Similarly, &next is userpass ** and it is expecting void **
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;
}
I'm having some trouble with the following:
void BuildList(cs460hwp hw)
{
FILE* fp;
fp = fopen("HW2input.dat", "r");
if(fp == NULL)
{
printf("Couldn't open the file.");
return;
}
int numStudents;
int i;
bool success;
char* dueDate = malloc(9*sizeof(char));
char* course = malloc(7*sizeof(char));
char* wsuid = malloc(9*sizeof(char));
char* subDate = malloc(9*sizeof(char));
double points1 = 0;
double points2 = 0;
cs460hwp stuInsert = NULL;
fscanf(fp, "%d", &numStudents);
fscanf(fp, "%s", dueDate);
for(i = 0; i < numStudents; i++)
{
stuInsert = malloc(sizeof(cs460hwp));
fscanf(fp, "%s %s %s %lf", course, wsuid, subDate, &points1);
strcpy(stuInsert->course, course);
strcpy(stuInsert->wsuid, wsuid);
strcpy(stuInsert->subdate, subDate);
stuInsert->points1 = points1;
stuInsert->points2 = CalculatePoints(dueDate, subDate, points1);
stuInsert->nextPtr = NULL;
if(hw == NULL)
{
hw = stuInsert;
}
else
{
stuInsert->nextPtr = hw;
hw = stuInsert;
}
}
free(course);
free(wsuid);
free(subDate);
free(dueDate);
PrintGrades(hw);
fclose(fp);
}
struct hwpoints
{
char course[7];
char wsuid[9];
char subdate[9];
double points1;
double points2;
struct hwpoints *nextPtr;
};
typedef struct hwpoints *cs460hwp;
My goal here is to insert every entry to the top of the list. However, whenever I try to assign anything to nextPtr (such as in the else clause), it gets filled with garbage values. They're mostly truncated versions of old data, which leads me to believe they're being taken from the heap. I've been reading (a lot), but I'm having trouble finding advice on this particular problem.
nextPtr always becomes junk, and nextPtr->nextPtr causes a segfault. For every iteration of the loop. hw remains fine, but its pointer value never gets updated properly.
Even when I've attempted to move the memory allocation for the struct into a function, I've had the same (or similar) issues.
Can anyone point me in the right direction?
Two problems.
1) As pb2q mentioned, you are passing a pointer to a struct and trying to assign what the arg points to. That's allowed by the compiler, but it doesn't do anything for you outside the function. It might be OK in your case if:
void main()
{
cs460hwp hw = NULL;
BuildList(hw);
return;
}
Is the whole of your function. I don't know the assignment so you need to figure out if that's acceptable to you or not.
2) The much bigger problem:
stuInsert = malloc(sizeof(cs460hwp));
Did you check what sizeof(cs460hwp) comes out to be? it's 4. You're allocating enough memory for the size of a pointer, not the size of your structure. I'm pretty sure this is not what you want to do and this is what is killing you. Just for kicks, replace it with malloc(100) and see if your problem goes away. If so you just need to figure out what size you really want. ;)
A problem with your BuildList function is that you're passing a pointer to a struct hwpoints, and you're trying to re-assign what the argument points to. Since function arguments in C are pass-by-value you're only changing the copy of the pointer that your function receives, and those changes won't be reflected in the caller.
You can use this pointer to modify the contents of the list: you can change e.g. hw->course or hw->nextPtr, you can move elements around in your list. But you can't change what the head, hw points to, so you can't insert elements at the beginning of the list.
If you want to change your head pointer, as in these statements:
hw = stuInsert;
// ...
hw = stuInsert;
Then you'll need to pass a pointer to the pointer:
void BuildList(cs460hwp *hw)
And de-reference it as necessary in the body of the function.
I can't be sure that this is the cause of the output that you're observing, which may be due to other problems. But if, after some number of calls to BuildList, beginning with a head pointer equal to NULL, you're trying to print your list assuming that it has valid nodes, you could see garbage data.
Thanks to #Mike's answer, we see also that you're not allocating enough space for your list nodes:
stuInsert = malloc(sizeof(cs460hwp));
Will only allocate enough space for a pointer, since cs460hwp is typedef'd to be a pointer to struct hwpoints. You need to allocate enough space for the structure, not a pointer to it:
stuInsert = malloc(sizeof(struct hwpoints));
Alright I've been working on this for hours now and can't figure out my issue. I have a double linked list and when I try and add a new node to it it will change all the values in the linked list.
right now this is what I have:
void createSub(sibs *root, char *name, int size) {
if (root->subSibs == NULL) {
root->subSibs = (sibs *)malloc(sizeof(sibs));
root->subSibs->name = name;
root->subSibs->time_stamp = createTimeStamp();
root->subSibs->nextSib = NULL;
}
sibs *temp = root->subSibs;
if (temp != NULL) {
while(temp->nextSib != NULL)
temp = temp->nextSib;
}
sibs *t = (sibs *)malloc(sizeof(sibs));
t->name = name;
t->time_stamp = createTimeStamp();
t->nextSib = NULL;
if(temp != NULL)
temp->nextSib = t;
printf("root->subSibs->name = %s\n", root->subSibs->name);
(root->numSub)++;
}
this might not be perfect considering I've changed it a million times. Can someone tell me what I'm doing wrong here?
root is the root node I'm working with and subSibs is pointer to the linked list. What I am doing is adding a name and timestamp to each node in that linked list pointed to by root->subSibs.
What I get out is:
createSub(root, name1, size);
prints:
root->subSibs1: name1;
createSub(root, name2, size);
prints:
root->subSibs1: name2;
root->subSibs2: name2;
etc...
It will probably be some really really stupid mistake but any help would be amazing. I've been trying for hours and just need someone to tell me why it will change the names.
Also if I do strcpy(root->subSibsi->name, name); for i = 0-5; root->subSibs1->name prints garbage and then root->subSibs2->name prints correct name and then root->subSibs3->name prints garbage (same as subSibs1->name) and so on...
t->name = name;
You are copying pointers. Instead you likely want to duplicate the memory:
t->name = strdup(name);
Or:
t->name = malloc(strlen(name) + 1);
strcpy(t->name, name);
Without more information (like how you call the function) I guess that you have a single buffer (probably a char array) and that you pass the address to that same buffer for each call to createSubs. This will make every node you create to point to the same buffer, which will always contain the last string entered.
In your node, name is char *. In other words it can point to some arbitrary memory location. To begin with it has some garbage value, so it points to nothing from programmers point of view and cannot to be used to store value - which is what you did.
Now if we allocate a portion of memory, big enough to store string pointed by argument char *name , we can make your node's name to point to it. Once you do that you can copy argument name * to node name *