Why are pointers expected in structures? - c

I was just testing structures using C. The code is:
#include <stdio.h>
struct {
int age;
char name[]; //just an array
char gender; //just a character (M for male and F for female)
}person;
int main()
{
person.age=10;
person.name="John";
person.gender="M";
printf("Person's age: %d", person.age);
printf("Person's name: %s", person.name);
printf("Person's gender: %c", person.gender);
return 0;
}
However, what the compiler returns is:
warning: initialization of 'char' from 'char *' makes integer from
pointer without a cast [-Wint-conversion]|
I have no idea what this means or why it appears.

This code has multiple issues.
First:
person.name="John";
You cannot assign a char* to a char array. The two types are not compatible.
You must either change your struct to:
char* name; //just an char pointer
Or use strcpy() to copy the name to this array.
strcpy(person.name, "John");
Also, you need to specify the size of this array in your struct. Depending on your compiler, funny things can happen with an undefined array size like this.
Second:
person.gender="M";
You also cannot assign a char* to a char.
Either change gender to be of type char*, or change your assignment to use a char.
person.gender = 'M';

Why are pointers expected in structures?
Pointers are not expected, nor are they dis-allowed.
"warning: initialization of 'char' from 'char *' makes integer from pointer without a cast [-Wint-conversion]|"
person.gender="M"; errantly attempts to take a pointer (the address of the stirng literal "M") and assign it to a char (.gender). Pointers do not fit in a char.
OP's code is invalid. OP is relying on a code extension.
A member like char name[]; must be the last member in a struct. There it is a flexible array member.
struct {
int age;
// char name[]; // move to the end
char gender;
char name[];
}person;
There are no pointers in OP's struct. With repair, member .name[] is an array.
Example usage
#include <stdio.h>
#include <stdlib.h>
// Define a type
typedef struct {
int age;
char gender;
char name[]; // last member
} person;
// Allocate and populate data
person* person_initialize(int age, const char *name, char gender) {
size_t len = strlen(name);
// Size needed is for the `person` and the name as a _string_.
person *p = malloc(sizeof *p + len + 1);
// If successful ....
if (p) {
p->age = age;
p->gender = gender;
strcpy(p->name, name);
}
return p;
}
int main(void) {
person *p = person_initialize(10, "John", 'M'); // Note 'M', not "M".
if (p) {
printf("Person's age: %d", p->age);
printf("Person's name: %s", p->name);
printf("Person's gender: %c", p->gender);
free(p);
}
return 0;
}

"Why are pointers expected in structures?"
They are not. They are also not unexpected.
This construction is however:
struct {
int age;
char name[]; //just an array <- A BOMB
char gender; //just a character (M for male and F for female)
} person;
You here have an instance (person) of an anonymous struct with a flexible array. Old-school structs with this setup had their last member declared as type identifier[1];.
[1] was historically used because a zero element array has never been valid in standard C (although accepted as a language extension by some compilers).
The C99 standard made char name[] legal by allowing indexing such an array if it's made the last element in a struct. The name[] member in your struct is not.
The purpose of the flexible array was to let writing to such an array be legal to let the programmer (or a called function) pre-allocate memory and have functions return an unknown amount of elements by writing their result in that memory.
If your struct is accepted by a compiler, a funcion using it will overwrite gender when writing more than zero chars to name as an answer.
This would be a definition (and typedef) with the potential of being used properly:
typedef struct person_ {
int age;
char gender;
struct person_ *next; // points somewhere after the '\0' in name[] or NULL
char name[]; // Pre C99 "char name[1];" was often seen
} person; // a typedef, not an instance here

Related

ANSI C: Declare a pointer of type "array of string"

I have a structure like this
struct {
int id;
char str00[10];
char str01[10];
char str03[10];
char str04[10];
...
} myStructure;
All strXX have the same size. I would like to access them via an array (strArray), like this :
strcpy(strArray[i], strValue);
How to declare strArray ?
I used this:
char (*strArray)[][10] = (void *)&myStructure.str00;
It's working, but I have to code the strcpy like this
strcpy((*strArray)[i], strValue);
... and I don't like this :-)
Is there another way to declare strArray ?
Thanks for your ideas and help.
You almost had it, the correct pointer type is simply char (*ptr)[10].
Naively, you could use this pointer type to iterate over the struct members, but doing so would invoke undefined behavior. Because strictly speaking, a pointer can only point at single item or an array, and if we use pointer arithmetic to go beyond that single item/array, we invoke undefined behavior.
I'll include an example still, for the sake of demonstrating array pointer arithmetic:
// BAD CODE, it relies on undefined behavior!
#include <stdio.h>
#include <string.h>
typedef struct {
int id;
char str00[10];
char str01[10];
char str02[10];
char str03[10];
} myStructure;
int main(void)
{
myStructure ms = { 0 };
char (*ptr)[10] = &ms.str00;
for(size_t i=0; i<4; i++)
{
strcpy(ptr[i], "hello ");
strcat(ptr[i], (char[]){i+'0', '\0'});
}
puts(ms.str00);
puts(ms.str01);
puts(ms.str02);
puts(ms.str03);
return 0;
}
The proper solution is to instead use a union, so that you can access the members either individually, or as an array:
typedef union {
struct // anonymous struct, requires a standard C compiler
{
char str00[10];
char str01[10];
char str02[10];
char str03[10];
};
char array[4][10];
} str_t;
typedef struct {
int id;
str_t str;
} myStructure;
strcpy(ms.str.array[i], ...); // access as array
puts(ms.str.str00); // access as individual item
The cleanest way to define strArray as requested would be to make it an array of pointers to the (first elements of the) arrays in myStructure:
char *strArray[] = { myStructure.str00, myStructure.str01, myStructure.str03, myStructure.Str04, … };
With this definition, strArray[i] is initialized to the corresponding member of the structure, such as myStructure.str01. Note that myStructure.str01 will be automatically converted to a pointer to its first element, so strArray[i] is a pointer to the first char in one of the arrays.
Then strArray[i][j] is char j of array i.
(Incidentally, you skip str02 in your sample code. I do not know why but have retained that in the code above.)
An alternative method would be to use a union, which can be done in various ways, one of which is:
struct
{
int id;
union
{
struct
{
char str00[10];
char str01[10];
char str03[10];
char str04[10];
...
};
char strArray[number of arrays][10];
};
} myStructure;
That is generally a poor design, as it is needlessly confusing. (While this has a technical possibility of failure due to padding between the individually defined arrays, assertions can be used to ensure this does not occur, or rather to detect when it does.)
Most often, we would simply define the strings as an array of arrays:
struct
{
int id;
char str[number of arrays][10];
} my Structure;
Then the members would always be referred to by index, such as myStructure.str[1], and not by individual names, such as myStructure.str01.

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;
}

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 do i get values stored in struct?

I am trying to retrieve values from an array of structs. I do not know the correct ways to retrieve them.
Here is my struct:
struct entry{
char name[NAME_SIZE];
int mark;
};
typedef struct entry Acct;
Acct dism2A03[MAX_ENTRY];
How i assigned values:
void add_new(char *name,int mark){
printf("%s,%d",name,mark);
int v=0;
v=entry_total;
strcpy(dism2A03[v].name,name);
dism2A03[v].mark = mark;
}
What i tried (DOES NOT WORK):
int m=0;
for(m=0;m<MAX_ENTRY;m++){
char name[NAME_SIZE] = dism2A03[m].name;
line 75 >> int mark = dism2A03[m].mark;
printf("\nEntry %d",m);
printf("%s",name);
printf("%d",mark);
}
ERROR:
p9t2.c: In function ‘main’:
p9t2.c:75:5: error: invalid initializer
Your first attempt implies existence of getfield function that takes a struct and a multicharacter char literal and gets the field; there is no such function in C.
Your second attempt is much closer: rather than trying to assign the name to an array, assign it to a char pointer, like this:
int m=0;
for(m=0;m<MAX_ENTRY;m++){
// Since you aren't planning on modifying name through pointer,
// you can declare the pointer const to make your intentions clear.
const char *name = dism2A03[m].name;
int mark = dism2A03[m].mark;
printf("\nEntry %d",m);
printf("%s",name);
printf("%d",mark);
}

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