C struct initialization with char array - c

I have a C struct defined as follows:
struct Guest {
int age;
char name[20];
};
When I created a Guest variable and initialized it using the following:
int guest_age = 30;
char guest_name[20] = "Mike";
struct Guest mike = {guest_age, guest_name};
I got the error about the second parameter initialization which tells me that guest_name cannot be used to initialize member variable char name[20].
I could do this to initialize all:
struct Guest mike = {guest_age, "Mike"};
But this is not I want. I want to initialize all fields by variables. How to do this in C?

mike.name is 20 bytes of reserved memory inside the struct. guest_name is a pointer to another memory location. By trying to assign guest_name to the struct's member you try something impossible.
If you have to copy data into the struct you have to use memcpy and friends. In this case you need to handle the \0 terminator.
memcpy(mike.name, guest_name, 20);
mike.name[19] = 0; // ensure termination
If you have \0 terminated strings you can also use strcpy, but since the name's size is 20, I'd suggest strncpy.
strncpy(mike.name, guest_name, 19);
mike.name[19] = 0; // ensure termination

mike.name is a character array. You can't copy arrays by just using the = operator.
Instead, you'll need to use strncpy or something similar to copy the data.
int guest_age = 30;
char guest_name[20] = "Mike";
struct Guest mike = { guest_age };
strncpy(mike.name, guest_name, sizeof(mike.name) - 1);
You've tagged this question as C++, so I'd like to point out that in that case you should almost always use std::string in preference to char[].

Actually you can statically initialise this struct:
struct Guest {
int age;
char name[20];
};
Guest guest = { 30, {'M','i','k','e','\0'}};
Each element of the array must be set explictly and this cannot be done using c-strings. If the struct is defined with a char* name then we can do this:
struct Guest {
int age;
char* name;
};
Guest guest = { 30, "Mike"};

You can statically allocate a struct with a fixed char[] array in C. For example, gcc allows the following:
#include <stdio.h>
typedef struct {
int num;
char str[];
} test;
int main(void) {
static test x={.num=sizeof("hello"),.str="hello"};
printf("sizeof=%zu num=%d str=%s\n",sizeof(x),x.num,x.str);
return 0;
}
And it does the right thing (though beware of the sizeof(x): it returns 4 on my machine; not the length of the total statically allocated memory).
This does not work for structs allocated from the stack, as you might suspect.

Related

C, Expression must be a modifiable lvalue (changing the value of a struct's member) [duplicate]

This question already has answers here:
Assigning strings to arrays of characters
(10 answers)
Closed 4 years ago.
I'm an extreme newbie, I'm just trying to learn.. this is my simple struct that I've created
struct Student{
char FirstName[20];
char LastName[20];
char StudentID[10];
char Password[20];}
Then I'm creating an array of pointers;
struct Student *StudentList[10];
I am then calling my "Register" function and passing the first element in the array as a parameter, for the reason of changing values to that specific struct element in the array, for example I want to change the student's details;
Register(&StudentList[0]);
Further on, my function;
void Register(struct Student *student);
void Register(struct Student *student) {student->FirstName = "John";}
This is a very simplified example and sorry for not being able to correctly paste in the code here.
But why am I getting an "expression must be a modifiable lvalue", when I try to assign a value.
You can't assign array types like that in C, and "John" is an array of type char[5].
strcpy(student->FirstName, "John");
would do it or, better still, something of the form
strncpy(student->FirstName, "John", 20);
so you avoid overrunning the char buffer.
The firstName field is an array, and arrays cannot be assigned to as a whole. That's what the error message is telling you.
Since you're copying a string into this array, you should use strcpy:
strcpy(student->FirstName, "John");
In C, you do not set strings using = (and you do not compare them using == either).
You must use the strcpy function:
strcpy( student->firstName, "John" );
First thing you forget to put semicolon at the end of struct declaration struct Student { };
Secondly, you are passing &StudentList[0], instead just pass StudentList[0] and allocate memory dynamically for that first.
Finally, student->FirstName = "John"; because student->FirstName is one char buffer and "John" also one buffer so you can't do A = B where A and B both are char buffer, instead use strcpy(A,B);
Here is sample example
struct Student{
char FirstName[20];
char LastName[20];
char StudentID[10];
char Password[20];
}; /* you forget to put semicolon */
void Register(struct Student *student) {
strcpy(student->FirstName,"John"); /* use strcpy() */
printf("%s\n",student->FirstName);
}
int main() {
struct Student *StudentList[10];
for(int index = 0;index < 10;index++) {
StudentList[index] = malloc(sizeof(struct Student)); /* allocate memory for each Student */
}
Register(StudentList[0]);
/* free the dynamically allocated memory */
return 0;
}

Dynamic memory allocation for arrays and strings in struct in C

I want to be create a struct, but I also want to write its array or string elements with dynamic memory allocation.
struct st {
char *name[40];
int age;
};
For "name" string should I use malloc before struct, or can I use it in struct also.
1)
char *name = malloc(sizeof(char)*40);
struct st {
char *name;
int age;
};
2)
struct st {
char *name = malloc(sizeof(char)*40);
int age;
};
Are both of them true or is there any mistake? And if both of them are true, which one is more useful for other parts of code?
You need to create an instance of the structure, an actual variable of it. Then you need to initialize the members of the structure instance in a function.
For example, in some function you could do
struct st instance;
instance.name = malloc(...); // Or use strdup if you have a string ready
instance.age = ...;
An option would be to have a pointer in the struct however allocate memory outside of the struct within the function you use it.
e.g.
struct st {
char* n;
int a;
};
void foo() {
char* name = malloc(sizeof(char) * 40);
int age = 0;
struct st s = (struct st) {
.n = name,
.a = age,
};
/* TO DO */
free(name);
}
Declaring a type does not create lvalues (like variable). It defines the format of the lvalue. In 1), you have correctly declared a struct type, but you seem to have assumed the similarity of the "name" variable in struct declaration will associate the pointer "name" to struct member "name". It does not work that way.
2) is syntactically/symantically wrong. You simply cannot assign a malloc() to non-lvalue (as you are declaring a type struct)
So, create a variable out of struct type as you have created in 1) and allocate memory in the struct variable member.
typedef struct st {
char *name;
int age;
} st_type;
st_type st_var;
st_var.name = (char *) malloc(sizeof(char) * 40); // This is how you will allocate the memory for the struct variable.
Remember, before you can do dynamic allocation, you need to have a lvalue just like you did for standalone "name" variable.

How to correctly define an array of structures?

i have the following structure:
typedef struct Course {
int course_id;
char* course_name;
int prior_course_id;
StudentTree* students;
} Course;
and the following function i need to implement:
void createReport(FILE* courses[], int numOfCourses, FILE* studentFile, char* reportFileName
as you can see i get an array of FILE*, each cell contains different file pointer.
my intention is to create an array that each cell is Course* type, and initialize each cell with a Course struct containing the data read from the courses files.
what is the correct way to declare it inside the function?
do i need to dynamically allocate memory for it, or it can be done in compilation?
i've tried
Course* course_array[numOfCourses] = {NULL};
Course* course_array[numOfCourses] = NULL;
but it won't compile.
thanks for your help
You declare an array of structs the same way you declare an array of ints or FILE *s:
Type variableName[numberOfElements];
Before C99 (and barring compiler specific extensions), creating an array with a variable number of elements on the stack wasn't supported. So make sure that you are targeting the correct standard. In your case, assuming C99 support, the following should work:
Course *course_array[numOfCourses];
Because you intend to initialize each of the elements in the array, there is no need to zero them out.
You would then access the elements like this:
course_array[0] = malloc(sizeof(Course))
course_array[0]->course_id = 2;
/* etc. */
Now if you can't assume C99 support, things get a bit more tricky but not much:
Course *course_array = malloc(sizeof(Course *) * numOfCourses);
After that you can access course_array with the same array notation:
course_array[0] = malloc(sizeof(Course))
course_array[0]->course_id = 42;
/* etc. */
Once you're doing with the array, you'll need to make sure that you free any of the memory that you allocated:
for (i = 0; i < numOfCourses; i++) {
free(course_array[i]);
}
/* If you malloc'd course_array, then you need this too */
free(course_array);
Course* course_array[numOfCourses] = {NULL};
This is good, but it creates array of Course *. So you need to allocate memory for each pointer in course_array before accessing it.
Something like
course_array[0] = malloc(sizeof(Course));
course_array[0]->course_id = someid;
When you define the array in the first place, you shouldn't need to allocate memory. You're defining the array on the stack, and the elements of the array are just pointers.
I think what you should do is first define the array, and then initialize each element with a malloc call. For example:
Course* course_array[numOfCourses];
for(int i = 0; i < numOfCourses, i++) {
course_array[i] = (Course*)malloc(sizeof(Course));
My favorite way:
typedef struct {
int a;
char b;
float c;
}DATA;
//then use typdef'ed DATA to create array (and a pointer to same)
DATA data[10], *pData;
//then, in function, you can initialize the pointer to first element of array this way:
int main(void)
{
pData = &data[0];
return 0;
}
Your example code would look like this:
typedef struct {
int course_id;
char* course_name;
int prior_course_id;
StudentTree* students;
} COURSE;
//then in function:
COURSE course[numOfCourses]

display a specified index of a pointer char*

I am trying to point on a specified character in a string contained on a structure
here my code
typedef struct{
char *name;
int age;
}PERSON, *person;
int main(){
person serenity;
serenity = (person)malloc(sizeof(PERSON));
strcpy(&(serenity->name),"Serenity");
printf("%c",*(&(serenity->name)+1));
}
here i wanted to display the second character which is 'e' but it shows 'n' instead
anyone can explain me what is wrong with this,
thank you
You have not allocated memory for name
typedef struct{
char *name;
int age;
}PERSON, *person;
int main(){
person serenity;
serenity = malloc(sizeof(PERSON));
serenity->name = malloc(sizeof("Serenity")); //<< Missing
strcpy((serenity->name),"Serenity");
printf("%c",*((serenity->name)+1)); // << Also you want the value in pointer name NOT its address
return 0;
}
Outputs e. Also since you tagged C there is no need to cast the return type of malloc.
Okay, okay... All of those answers aside, if you do not aim to change the characters inside the string "Serenity" in the future, you could just do the following:
#include <stdio.h>
typedef struct{
const char *name; // <-- added const
int age;
}PERSON, *person;
int main( ){
person serenity;
serenity = (person) malloc( sizeof( PERSON ) );
serenity->name = "Serenity"; // <-- simply assigned the pointer with the
// address to the array of constant characters
printf( "%c", *( serenity->name + 1 ) ); // <-- changed this
}
This statement
serenity = (person)malloc(sizeof(PERSON));
allocates the structure
typedef struct{
char *name;
int age;
}PERSON
however name is kept uninitialized and points somewhere in memory causing a crash when you copy to it.
So instead of
strcpy(&(serenity->name),"Serenity");
write
serenity->name = strdup("Serenity");
which is the same as
serenity->name = malloc(strlen("Serenity")+1);
strcpy(serenity->name,"Serenity");
don't forget to free that string as well later.
Try printf("%c",*(serenity->name+1));, also do strcpy(serenity->name,"Serenity");.
If you have a pointer char* name; you access the second element by doing name[1] or *(name+1). &name will give you the address where the pointer address of name is stored. This is not what you want here.
Another issue in your program is that you never allocate memory for the variable name. You need a serenity->name = (char*)malloc(128);. But using an arbitrary length like 128 is very dangerous in combination with strcpy. Use strncpy instead of strcpy to work around this.

How to work with string fields in a C struct?

I'm having trouble making a database based on a singly-linked list in C,
not because of the linked list concept but rather the string fields in the struct themselves.
This is an assignment in C and as far as I know (I'm a newbie), C doesn't recognize 'string' as a data type.
This is what my struct code looks like:
typedef struct
{
int number;
string name;
string address;
string birthdate;
char gender;
} patient;
typedef struct llist
{
patient num;
struct llist *next;
} list;
I was thinking of making a struct for the strings themselves so that I can use them in the struct, like this:
typedef struct string
{
char *text;
} *string;
Then I will malloc() each one of them when it is required to make new data of the string type (array of char).
typedef struct string
{
char *text;
} *string;
int main()
{
int length = 50;
string s = (string) malloc(sizeof string);
s->text = (char *) malloc(len * sizeof char);
strcpy(s->text, patient.name->text);
}
Can someone help me figure this out?
Thank you.
On strings and memory allocation:
A string in C is just a sequence of chars, so you can use char * or a char array wherever you want to use a string data type:
typedef struct {
int number;
char *name;
char *address;
char *birthdate;
char gender;
} patient;
Then you need to allocate memory for the structure itself, and for each of the strings:
patient *createPatient(int number, char *name,
char *addr, char *bd, char sex) {
// Allocate memory for the pointers themselves and other elements
// in the struct.
patient *p = malloc(sizeof(struct patient));
p->number = number; // Scalars (int, char, etc) can simply be copied
// Must allocate memory for contents of pointers. Here, strdup()
// creates a new copy of name. Another option:
// p->name = malloc(strlen(name)+1);
// strcpy(p->name, name);
p->name = strdup(name);
p->address = strdup(addr);
p->birthdate = strdup(bd);
p->gender = sex;
return p;
}
If you'll only need a few patients, you can avoid the memory management at the expense of allocating more memory than you really need:
typedef struct {
int number;
char name[50]; // Declaring an array will allocate the specified
char address[200]; // amount of memory when the struct is created,
char birthdate[50]; // but pre-determines the max length and may
char gender; // allocate more than you need.
} patient;
On linked lists:
In general, the purpose of a linked list is to prove quick access to an ordered collection of elements. If your llist contains an element called num (which presumably contains the patient number), you need an additional data structure to hold the actual patients themselves, and you'll need to look up the patient number every time.
Instead, if you declare
typedef struct llist
{
patient *p;
struct llist *next;
} list;
then each element contains a direct pointer to a patient structure, and you can access the data like this:
patient *getPatient(list *patients, int num) {
list *l = patients;
while (l != NULL) {
if (l->p->num == num) {
return l->p;
}
l = l->next;
}
return NULL;
}
I think this solution uses less code and is easy to understand even for newbie.
For string field in struct, you can use pointer and reassigning the string to that pointer will be straightforward and simpler.
Define definition of struct:
typedef struct {
int number;
char *name;
char *address;
char *birthdate;
char gender;
} Patient;
Initialize variable with type of that struct:
Patient patient;
patient.number = 12345;
patient.address = "123/123 some road Rd.";
patient.birthdate = "2020/12/12";
patient.gender = 'M';
It is that simple. Hope this answer helps many developers.
While Richard's is what you want if you do want to go with a typedef, I'd suggest that it's probably not a particularly good idea in this instance, as you lose sight of it being a pointer, while not gaining anything.
If you were treating it a a counted string, or something with additional functionality, that might be different, but I'd really recommend that in this instance, you just get familiar with the 'standard' C string implementation being a 'char *'...
You could just use an even simpler typedef:
typedef char *string;
Then, your malloc would look like a usual malloc:
string s = malloc(maxStringLength);
This does not work:
string s = (string)malloc(sizeof string);
string refers to a pointer, you need the size of the structure itself:
string s = malloc(sizeof (*string));
Note the lack of cast as well (conversion from void* (malloc's return type) is implicitly performed).
Also, in your main, you have a globally delcared patient, but that is uninitialized. Try:
patient.number = 3;
patient.name = "John";
patient.address = "Baker street";
patient.birthdate = "4/15/2012";
patient.gender = 'M';
before you read-access any of its members
Also, strcpy is inherently unsafe as it does not have boundary checking (will copy until the first '\0' is encountered, writing past allocated memory if the source is too long). Use strncpy instead, where you can at least specify the maximum number of characters copied -- read the documentation to ensure you pass the correct value, it is easy to make an off-by-one error.

Resources