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.
Related
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.
I have a struct The_Word that has a variable char word[WORD_LENGTH]
I have the following
typedef struct The_Word
{
char word[WORD_LENGTH];
int frequency;
struct The_Word* next;
} The_Word;
int someFunc(char* word)
{
/*Rest of method excluded*/
struct The_Word *newWord = malloc(sizeof(struct The_Word));
newWord->word = word; // error here. How can I assign the struct's word to the pointer word
}
You need to use strncpy to copy an string:
#include <string.h>
int someFunc(char* word)
{
/*Rest of method excluded*/
struct The_Word *newWord = malloc(sizeof(struct The_Word));
strncpy(newWord->word, word, WORD_LENGTH);
newWord->word[WORD_LENGTH - 1] = '\0';
}
You should be careful to check if the string fits in the array. That's it, when the length of the parameter char* word is longer than WORD_LENGTH.
You don't assign pointers directly. Instead you should use strncpy() function.
strncpy(newWord->word,word,strlen(word));
strcpy(), memcpy() all work similarly.
typedef struct The_Word
{
char word[WORD_LENGTH];
int frequency;
struct The_Word* next;
} The_Word;
int someFunc(char* word)
{
/*Rest of method excluded*/
struct The_Word *newWord = malloc(sizeof(struct The_Word));
memset(newWord->word,0,WORD_LENGTH);
strcpy(newWord->word,word);
/*return something*/
}
This gives an incompatible types error because in C arrays are treated as constant pointers. Arrays and pointers are not exactly the same thing. Although they behave identically in most other circumstances, you cannot reassign what an array points to.
It looks like you intend to copy the string from the function argument into the newly allocated struct. If this is the case use strncpy() or memcpy() as others have suggested.
typedef struct
{
int id;
char* first;
char* last;
}* person;
person* people;
Hi.
How can I use this above, all set globally, to fill people with different "person"s? I am having issues wrapping my head regarding the typedef struct pointer.
I am aware pointers are like arrays, but I'm having issues getting this all together...
I would like to keep the above code as is as well.
Edit 1: char first should be char* first.
Ugly as sin. You really should redefine person to not be a pointer. Also don't use anonymous structs.
#include <stdio.h>
typedef struct {
int id;
char* first;
char* last;
}* person;
person* people = (person[]){
(person)&(struct {int id;char* first;char* last;}){0,"me","foo"},
(person)&(struct {int id;char* first;char* last;}){0,"you","foo"},
NULL
};
int main(void) {
while(*people) {
printf("%s %s\n", (*people)->first, (*people)->last);
people++;
}
return 0;
}
Don't bother with typedefs for structs. It's much clearer if you use structs with tags and then do your thing:
struct PERSON {
int id;
char *first;
char *last;
};
struct PERSON *people; /* people is a pointer to a struct PERSON. */
/* Allocate array of 42 struct PERSONS. */
people = malloc (42 * sizeof *people);
/* Now use people[0] to people[41]. */
You could simply create an array of structures of type person using something like following:
people = malloc (num_person * sizeof(person));
for (i = 0; i < num_person; i++) {
people[i]->first = malloc (size * sizeof(char));
people[i]->last = malloc (size * sizeof(char));
}
Following this, you could fill up each people struct with different parameters.
Typedefs that hide the real type are a bad idea.
If you really want the double indirection, I guess something like:
people = malloc(sizeof(person));
*people = malloc(SOME_NUMBER * sizeof(**people));
Will allocate what you need. You'd get an individual person structure out like:
(*people)[INDEX]
When using a particular person structure, you'll need to allocate memory for the first and last strings as well:
(*people)[INDEX].first = malloc(STRING_SIZE);
(*people)[INDEX].last = malloc(STRING_SIZE);
But simply removing the crazy double indirection will really clean things up and make everything a lot easier to use and understand:
people = malloc(SOME_NUMBER * sizeof(person));
people[INDEX].first = malloc(STRING_SIZE);
people[INDEX].last = malloc(STRING_SIZE);
I am a newbie in C..I am trying to make some sense of how dynamic memory allocation works in case of structures and arrays..So like for example I have a code like..
struct Person
{
int id;
char *name;
char *place;
};
struct Database
{
struct Person *data_rows;
};
I want to dynamically allocate memory for both the character arrays name and place..and the array of struct data_rows..take their size as input..So what should ideally be the order of allocations and the proper syntax for the same? Thanks.
Well, "obviously" you need to get "struct Database" filled in first:
struct Database MyDatabase;
MyDatabase.data_rows=malloc(sizeof(MyDatabase.data_rows[0])*NumberOfPeople);
Ignoring the fact that I didn't check the malloc() for failure, this will give you an array of "struct Person", all uninitialized. So, most likely, you'll want to initialize them:
int i;
for (i=0; i<NumberOfPeople; i++)
{
struct Person* MyPerson;
MyPerson=&MyDatabase.data_rows[i];
MyPerson->id=i;
MyPerson->name=malloc(...);
/* Do something to store the name in MyPerson->name */
MyPerson->place=malloc(...);
/* Do something to store the place in MyPerson->name */
}
Now, the problem here is the "..." I put on the malloc. It's easy if you use a fixed size, but then you could have just declared your struct to be something like
struct Person
{
int id;
char name[100];
char place[200];
};
Basically, I just can't tell what the length of the names should be, hence I just typed it as "...".
Also, I just guessed what the "id" might be. Using the array index is actually somewhat pointless :-)
Of course, you don't have to do it all now. You could just set the name and place pointers to NULL and fill them in later, like when you're reading the data from a file, or whatever you're planning to do. Or you could just not initialize it here at all, if you're confident that your code always "knows" which fields are initialized and which ones are not.
I would highly recommend to write a functions person_new and person_free that would take care of structure memory management:
struct Person* person_new(char *name, char* place) {
struct Person* person = malloc(sizeof(struct Person));
person->name = strdup(name);
person->place = strdup(place);
return person;
}
void person_free(struct Person* person) {
free(person->name);
free(person->place);
free(person);
}
The best thing would be to convert your structs to classes, the following works also for structs...
You define a constructor and destructor in Database and in Person as following:
struct Person
{
Person(){
name = new char[256];
place = new char[256];
};
~Person(){
delete name;
delete place;
}
int id;
char *name;
char *place;
};
struct Database
{
Database(int nPersons){
data_rows = new Person[nPersons];
};
~Database(){
delete data_rows;
};
struct Person *data_rows;
};
or you can do this without a constructor and destructor and allocate all the stuff sequentially in your code, which is a very ugly way to do this!
as:
Database myData;
myData.data_rows = new Persons[40];
for(int i=0; i < 40; i++){
myData.data_rows[i].name = new char[256];
myData.data_rows[i].place = new char[256];
}
Note that data_rows[i] is nothing more than -> *(data_rows + i) which shifts the address of the pointer i times and then dereferences it!
I have the following structure:
struct hashItem {
char userid[8];
char name[30];
struct hashItem *next;
};
In the function below I take a char pointer (char array) argument that I wish to assign to the struct.
void insertItem(struct hashItem *htable[], char *userid, char *name)
{
int hcode = hashCode(userid);
struct hashItem *current = htable[hcode];
struct hashItem *newItem = (struct hashItem*) malloc(sizeof(struct hashItem));
newItem->userid = userid;
newItem->name = name;
[...]
}
Instead I get the following error:
hashtable.c: In function ‘insertItem’:
hashtable.c:62: error: incompatible types in assignment
hashtable.c:63: error: incompatible types in assignment
Line 62 and 63 are the `newItem->..." lines.
You almost certainly don't want to just assign the char* to the char[] - as the compiler points out, the types are incompatible, and the semantics are not what you think. I assume you want the struct members to contain the values of the two char* strings - in which case, you want to call strncpy.
strncpy(target, source, max_chars);
You should chang your struct in
struct hashItem {
char userid[8];
char *name;
struct hashItem *next;
};
to assign a char pointer to a name. In the struct you defined
char name[30] are just 30 chars.
You can't assign a pointer to a string to a character array like you are trying to. Instead you need to copy the contents of the string with strncpy as Adam indicated:
strncpy (newItem->userid, userid, 8);
When you declare the struct with a character array in it, you are allocating memory inside the structure itself to store a string of the given length.
When you pass a pointer into your function, you are passing a memory address (an integer) that indicates where a null-terminated string can be found.
To assign the pointer to the array doesn't make sense. The array has memory allocated for it already -- it can't be made to "point" to another location.
While you can use pointers in your structure, you need to be very careful that when you assign them, you are telling them to point to something that is going to be valid for the duration of the time you will use the structure. For example, this code is bad, because the string passed to insertItem no longer exists after fillStructure returns:
struct hashItem
{
char * userid;
};
void insertItem (struct hashItem * item, char * userid)
{
item->userid = userid;
}
void fillStructure (struct hashItem * item)
{
const char string[] = "testing";
insertItem (item, string);
}
int main(void)
{
struct hashItem item;
fillStructure (&item);
/* item->userid is now a dangling pointer! */
}
For more, I would recommend reading the "Arrays and Pointers" chapter of the C FAQ -- start with Question 6.2 and keep reading from there.