why won't %s print from a linked list [duplicate] - c

This question already has answers here:
How can I correctly assign a new string value?
(4 answers)
Closed 3 years ago.
I created a linked list that contains a char array. Then I tried to print it using %s and it would not print. I know I have to transform the char by adding in '\0' so that it can print using '/0'. But I'm not sure why.
For example if I input:
Bob 20
It should print:
New student created: Bob is 20 years old.
BUT: it's printing (without "Bob"):
New student created: is 20 years old.
#include <stdio.h>
#include <stdlib.h>
struct student {
char name[50];
int age;
struct student *next;
};
struct student *createStudent(char studentName[], int studentAge);
int main(void) {
struct student *studptr;
int myAge;
char myName[50];
scanf("%s %d", myName, &myAge);
studptr = createStudent(myName, myAge);
printf("New student created: %s is %d years old.\n", studptr->name, studptr->age);
free(studptr);
return 0;
}
struct student *createStudent(char studentName[], int studentAge){
struct student * ptr;
ptr = (struct student *)malloc(sizeof(struct student));
ptr->name[50] = studentName[50];
ptr->age = studentAge;
ptr->next = NULL;
return ptr;
}
NOTE: I understand the code below will work and print the correct name, where I add an additional method to change the char array called copyStr(), but I'm not sure why....
#include <stdio.h>
#include <stdlib.h>
struct student {
char name[50];
int age;
struct student *next;
};
struct student *createStudent(char studentName[], int studentAge);
void copyStr(char *source, char *target);
int main(void) {
struct student *studptr;
int myAge;
char myName[50];
scanf("%s %d", myName, &myAge);
studptr = createStudent(myName, myAge);
printf("New student created: %s is %d years old.\n", studptr->name, studptr->age);
free(studptr);
return 0;
}
struct student *createStudent(char studentName[], int studentAge){
struct student * ptr;
ptr = (struct student *)malloc(sizeof(struct student));
//we need to translate the char into a string
copyStr(studentName, ptr->name);
ptr->name[50] = studentName[50];
ptr->age = studentAge;
ptr->next = NULL;
return ptr;
}
void copyStr(char *source, char *target){
int i = 0;
while(source[i] != '\0')
{
target[i] = source[i];
i++;
}
target[i] = '\0';
}

Arrays do not have the assignment operator.
This statement
ptr->name[50] = studentName[50];
tries to assign the non-existent element with the index 50 of the array pointed to by the pointer studentName to the non-existent element of the array ptr->name.
Instead you should use the standard C function strcpy declared in the header <string.h>.
For example
strcpy( ptr->name, studentName );

Adding to #Vlad's answer, Right way to copy arrays is by iterating over them, thereby, picking each element in char array and copying it to new location. strncpy does exactly that along with buffer overflow checks, which makes it better than strcpy.
Please have a look at strcpy documentation, which says : "ensure size of the array pointed by destination shall be long enough to contain the same C string as source (including the terminating null character), and should not overlap in memory with source"
On a completely different side note, if you're looking for one-liner assignment, you're allowed to do this:
char sample[] = {"randomText"};
char sample2[11] = {"randomText"};

Related

initialise constructor values of structure using pointer in c [duplicate]

This question already has answers here:
How do malloc() and free() work?
(13 answers)
Closed 3 years ago.
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
struct Person {
char name[50];
int year_of_birth;
char sex[7];
char father[50];
char mother[50];
char significant_other[50];
char children[50];
};
struct Person* person_constructor(char *name, int year_of_birth, char *sex);
int main(){
struct Person* p1 = person_constructor("Abbas", 1970, "male");
}
struct Person* person_constructor(char *name, int year_of_birth, char *sex) {
struct Person *p;
printf("%s",*name);
printf("%s",*sex);
printf("%d",&year_of_birth);
// how to initalise these here and return name, age and sex everytime , can you tell me in print function
}
i want to do :
Person* person_constructor(char *name, int year_of_birth, char *sex);
A person with the given arguments and return it.
Also allocate memory.
In example code bellow you can find one of possible solutions to your question.
In C language is not possible to return more then one variable, but you can return pointer to constructed structure object and access structure members using notation stuct_ptr->struct_member.
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
struct Person {
char name[50];
int year_of_birth;
char sex[7];
char father[50];
char mother[50];
char significant_other[50];
char children[50];
};
struct Person* person_constructor(char *name, int year_of_birth, char *sex);
int main(){
struct Person* p1 = person_constructor("Abbas", 1970, "male");
/* it is not possible to return more variables in C */
/* you can use pointer to access members from constructed structure: */
printf("print from main:\n %s %d %s \n", p1->name, p1->year_of_birth, p1->sex);
if( p1 != NULL) free(p1); /* do not forget do deallocate something taht is allocated */
return 0;
}
struct Person* person_constructor(char *name, int year_of_birth, char *sex) {
struct Person *p = calloc(1, sizeof(struct Person));
if( p == NULL ) return p; /* memory alocation failed! */
strcpy(p->name, name);
p->year_of_birth = year_of_birth;
strcpy(p->sex, sex);
printf("print from constructor:\n");
printf("%s ",p->name);
printf("%s ",p->sex);
printf("%d \n",p->year_of_birth);
return p;
}

Array of structs in c: giving all the strings same values (with the int it works well). What sould I do?

When I run the program and give values to the id, name, surname it gives them all the value of the last student. For instance if the last students name is Anna then all the other names of the array are Anna. With the grades it works well! I tried and without the 'constructor' function and happenden the same thing.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student{ /*struct for student info*/
char *id;
char *name;
char *surname;
int grade;
};
struct Student* Student_new(char* id, char* name, char* surname, int grade);
/*fuction: Student 'constructor'*/
int main(int argc, char *argv[]) {
int no; /*number of students*/
printf("Welcome! \n\nNumber of students: ");
scanf("%d", &no);
struct Student *studentArray[no]; /*arary of struct students*/
int i; /*counter*/
for(i=0; i<no; i++){
char id[10], name[10], surname[10];
int grade;
printf("\n\nStudent(%d)\n", i+1);
printf("id: ");
scanf("%s", id);
printf("name: ");
scanf("%s", name);
printf("surname: ");
scanf("%s", surname);
printf("grade: ");
scanf("%d", &grade);
studentArray[i] = Student_new(id, name, surname, grade); /*using Student
'constructor' to initialize the array*/
}
for(i=0; i<no; i++){
printf("%s %s %s %d \n", studentArray[i]->id, studentArray[i]-
>name, studentArray[i]->surname, studentArray[i]->grade);
}
return 0;
}
struct Student* Student_new(char* id, char* name, char* surname, int grade)
{
struct Student* st = malloc(sizeof(struct Student));
st->id = id;
st->name = name;
st->surname = surname;
st->grade = grade;
return st;
}
PLEASE HELP!!
The issue is that the loop variables go out of scope after each iteration, and you're left with dangling pointers in the Student instances. What you're seeing is the result of undefined behavior.
What's probably happening is that the same char array is being passed into every student instance. Then you modify the same char array, overwriting the previous values.
You'll need to make copies of the strings. Remember to create a function like Student_free where you free the dynamically allocated copies.
struct Student* Student_new(char* id, char* name, char* surname, int grade)
{
struct Student* st = malloc(sizeof(struct Student));
st->id = strndup(id, 10);
st->name = strndup(name, 10);
st->surname = strndup(surname, 10);
st->grade = grade;
return st;
}
You should reserve memory for the string attributes. As a hint, use a struct similar to:
#define MAX_LEN 32
struct Student{ /*struct for student info*/
char id[MAX_LEN];
char name[MAX_LEN];
char surname[MAX_LEN];
int grade;
};
Define MAX_LEN to something that is reasonable for you, and check that the entered values aren't any longer. Also make sure to strcpy the input values to the struct variables.
The student structure only holds pointers to char arrays.
Now the actual space in memory for the strings is allocated in your for cycle an its lifetime is limited by the scope of the for cycle, accessing it afterwards is undefined behaviour. In your case the space where the strings are has not been reused and overridden yet, so coincidentally you are able to get the last value stored (cases like this can also raise segmentation fault).
You should have a look on some basic tutorial about pointers and c memory model. Allocating the arrays in the struct is a good solution (nucleon's answer). Also the scanf function can overflow you should limit the number of retrieved characters to match the size of allocated array.

Why give "stop working" error message when work with structure in c?

I need to understand why give this error message "stop working". Here is my code.
#include <stdio.h>
struct student{
int id;
char name[20];
char *title;
};
int main()
{
struct student *st;
st->id = 23;
//st->name = "shaib";
st->title = "title";
printf ("%d", st->id);
printf ("%s", st->title);
return 0;
}
You define a pointer but it is not init, so is Undefined Behavior.
You can create a space in heap memory using malloc function.
int main()
{
struct student *st = malloc(sizeof(struct student));
if ( st != NULL)
{
st->id = 23;
..
}
else
{
fprintf(stderr, "No space for variable\n");
}
free(st);
return 0;
}
As you can see, each time you allocate memory with malloc you are responsible for freeing it. Otherwise you have memory leak
Second problem is that
st->name = "shaib";
is not the way you fill an array with a C-String.
You can achieve it with:
strcpy(st->name, "shaib");
So, you need to allocate some memory for your new struct:
1) Add a #include in order to...
2) Replace the struct declaration for a malloc()
Tip: use a better format for your print outputs with \t and \n
Here's the working code:
#include <stdio.h>
#include <stdlib.h>
struct student{
int id;
char name[20];
char *title;
};
int main()
{
struct student *st = malloc(sizeof (struct student));
st->id = 23;
//st->name = "shaib";
st->title = "title";
printf ("%d\t", st->id);
printf ("%s\n", st->title);
return 0;
}
You're creating a pointer to the student structure, but you're not setting it to point to anything.
You'll need to allocate memory for st (the student structure) before you can use it.
You are not allocating memory for st.
You can do it like this: st = malloc(sizeof (student));

confused with structs and pointers [closed]

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 6 years ago.
Improve this question
I am trying to fill a struct with strings.
struct person {
char *name
char age
};
int record_values(struct person *dude, const char *his_name, char his_age)
{
dude->name = malloc(strlen(his_name)*sizeof(char)); //Get space for name
strcpy(dude->name, his_name); //Set name
strcpy(dude->age, his_age); //Set age
}
However this doesn't work. Any help?
dude->name = malloc(strlen(his_name)*sizeof(char)); //Get space for name
strcpy(dude->name, his_name); //Set name
strcpy(dude->age, his_age); //Set age
Your first line doesn't allocate enough space. You need one byte for the string terminator.
Your last line calls strcpy, but his_age is not a string.
int record_values(struct person *dude, const char *his_name, char his_age)
{
dude->name = strdup(his_name); // Duplicate name
dude->age = his_age; // Set age (Simple assignment!)
}
Instead of giving pointers on what you should change in your code here an complete working example that hopefully shows the required differences. The code below first creates an struct person but when the function record_values() is called you'll see that it first has to check if dude-name is not pointing to an char array already. Not having this check would create a memory leak in your code and the previous dude->name will never be freed. Also it allocates one extra char space in the char array for the string terminator (strcpy will copy it as well). This will avoid overflow errors. The function returns nothing, so make it an void functions rather than an function returning an int. The value of dude->age is not a pointer to memory space, therefore strcpy should not be used but dude->age = his-age which copies the values of variables.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct person {
char *name;
char age;
};
struct person * record_alloc(const char *his_name, char his_age);
void record_values(struct person *dude, const char *his_name, char his_age);
void record_free(struct person * dude);
struct person * record_alloc(const char *his_name, char his_age){
struct person * dude;
dude = malloc(sizeof(struct person));
if (his_name != NULL){
record_values(dude, his_name, his_age);
} else {
dude->age = 0;
dude->name = NULL;
}
return dude;
}
void record_values(struct person *dude, const char *his_name, char his_age)
{
size_t nameSize;
if (dude->name)
free(dude->name);
nameSize = (strlen(his_name) + 1) * sizeof(char);
dude->name = malloc(nameSize);
strcpy(dude->name, his_name);
dude->age = his_age;
return;
}
void record_free(struct person * dude){
if (dude->name)
free(dude->name);
free(dude);
return;
}
int main(int argc, const char * argv[]) {
struct person * p;
// allocate and set values
p = record_alloc("John Smith", 32);
printf("%s is %i years old\n", p->name, p->age);
// set new values for p
record_values(p, "John Doe", 37);
printf("%s is %i years old\n", p->name, p->age);
// free p
record_free(p);
return 0;
}
You might prefer to use designated initializers:
#include <stdio.h>
#include <string.h>
typedef struct {
char *name;
char age;
} person;
int record_values(person *dude, const char *his_name, char his_age)
{
*dude = (person) {.name = strdup(his_name), .age = his_age};
}
int main()
{
person p;
record_values(&p, "bob", 27);
printf("Hello, I am %s and I am %d years old!\n", p.name, p.age);
return 0;
}
Hello, I am Bob and I am 27 years old!
Since strdup is not a part of c99 your compiler might generate warnings, to get rid of them you need to compile it with -std=gnu99:
gcc -std=gnu99 -o main *.c
Or use your own version of this, which might look like this:
#include <stdlib.h>
char * strdup(const char *in) {
char *out = malloc(sizeof(in) + 1);
int i;
for (i = 0; in[i] != '\0'; ++i)
out[i] = in[i];
return out;
}

Segmentation fault when I try to printf

Why am I getting a segmentationfault when it tries to print the second member in the list?
After printing the first element of the list, the debugger opens the stdio.h and says:
At C:\TDM-GCC-32\include\stdio.h:255
At C:\TDM-GCC-32\include\stdio.h:256
At C:\TDM-GCC-32\include\stdio.h:258
At C:\TDM-GCC-32\include\stdio.h:259
Here is the code.
#include <stdio.h>
#include <stdlib.h>
struct Student {
char *Name;
char *Adresse;
unsigned long Mtnr;
short Kurse;
struct Student *next;
struct Student *previous;
};
typedef struct Student Student;
Student *liste = NULL, *ende = NULL;
void add(char Name, char Adresse, unsigned long Mtnr, short Kurse) {
Student *add;
ende->next = malloc(sizeof(Student));
add = ende->next;
add->Name = Name;
add->Adresse = Adresse;
add->Mtnr = Mtnr;
add->Kurse = Kurse;
add->previous = ende;
add->next = NULL;
ende = ende->next;
}
void Ausgabe(Student *Anfang) {
while (Anfang != NULL) {
printf("%s %s %d %d \n", Anfang->Name, Anfang->Adresse, Anfang->Mtnr, Anfang->Kurse);
Anfang = Anfang->next;
}
}
int main() {
liste = malloc(sizeof(Student));
ende = liste;
liste->Name = "Anna Musterfrau";
liste->Adresse = "Am Schwarzberg-Campus 3";
liste->Mtnr = 22222;
liste->Kurse = 2;
liste->next = NULL;
liste->previous = NULL;
add("Hans Peter", "Kasernenstrasse 4", 4444, 4);
Ausgabe(liste);
return 0;
}
The error is in the declaration of the add() function. The strings should be char pointers, not chars.
void add(char *Name, char *Adresse, unsigned long Mtnr, short Kurse){
The signature of the function add is inconsistent to the declaration and usage to the members of Student. Change the signature as follows.
void add(char* Name, char* Adresse, unsigned long Mtnr, short Kurse)
On the long run, it might also be necessary to create copies of Name and Adresse in add, as the caller of add might deallocate them, perhaps causing undesired behaviour.
While Marc Is correct with his observation, there is one more thing you may want to fix here.
When you add a record, you allocate it, but you do not allocate the content of it's pointers (Specifically = for the name and address pointers). The add function just point them to the input's address. this is a problem because the data in the address supplied to the add function is likely to change if, for example, it's a user input, or some other external buffer.
in the code snipped below I fixed the 'name', but left the address as is. please run it and see what happens. (assaf's record displays david's address)
hope this helps
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Student {
char *Name;
char *Adresse;
unsigned long Mtnr;
short Kurse;
struct Student *next;
struct Student *previous;
};
typedef struct Student Student;
Student *liste = NULL, *ende = NULL;
void add(char *Name, char *Adresse, unsigned long Mtnr, short Kurse) {
Student *add;
ende->next = malloc(sizeof(Student));
add = ende->next;
add->Name = malloc(strlen(Name)+1);
strcpy(add->Name, Name);
add->Adresse = Adresse;
add->Mtnr = Mtnr;
add->Kurse = Kurse;
add->previous = ende;
add->next = NULL;
ende = ende->next;
}
void Ausgabe(Student *Anfang) {
while (Anfang != NULL) {
printf("%s %s %d %d \n", Anfang->Name, Anfang->Adresse, Anfang->Mtnr, Anfang->Kurse);
Anfang = Anfang->next;
}
}
int main() {
char name_buf[100];
char address_buf[100];
liste = malloc(sizeof(Student));
ende = liste;
liste->Name = "Anna Musterfrau";
liste->Adresse = "Am Schwarzberg-Campus 3";
liste->Mtnr = 22222;
liste->Kurse = 2;
liste->next = NULL;
liste->previous = NULL;
add("Hans Peter", "Kasernenstrasse 4", 4444, 4);
sprintf(name_buf,"assaf stoler");
sprintf(address_buf,"maria 8");
add(name_buf, address_buf, 8888, 8);
sprintf(name_buf,"david david");
sprintf(address_buf,"some street 9");
add(name_buf, address_buf, 9999, 9);
Ausgabe(liste);
return 0;
}
EDIT: Op asked some questions, and the comment space is limited, so I'll add below:
A pointer is just an object pointing somewhere in memory. it's size is fixed. the content it's pointing to will vary.
When you include a pointer to a string in a structure, the space where the string is kept need to be allocated / accounted for. it is not part of the sizeof(struct).
In your original example the pointers were pointing to constant strings (which reside in the static code, usually the data section, allocated by the compiler), which is why your original code was able to access the strings.
In a more realistic case, the input data is not part of the program data, but is received by some input method (which my *buf was to emulate). as such, pointing your name and address at it would break the program, as the pointer you point to may have it's content changed. therefor copying of the data (string / array) is needed, and since we copy the data, we need to allocate space for it, and point our (name/address) pointer at it.
Alternate option is to use non-pointer array for name and address as in:
struct Student {
char Name[20];
char Adresse[60];
unsigned long Mtnr;
...
}
In this scenario, sizeof (struct Student) would actually include all the space for those fields. you still need to use strcpy or memcpy, as well as check for and handle strings that are too long to fit in your pre-defined length.
Hope this helps

Resources