30

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.

Adam Liss
  • 47,594
  • 12
  • 108
  • 150
fleuracia
  • 301
  • 1
  • 3
  • 5

5 Answers5

66

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;
}
Adam Liss
  • 47,594
  • 12
  • 108
  • 150
  • ah so basically i dont need to literally use the sting type and modify it into pointer if i want to use it as a pointer of char. thank you this is really eyeopening. but i got a couple of questions here: (a) say i want to insert a record for a new patient, do i have to make a new fuction like void insert() ? got a bit confused with the struct patient for the typedef struct above (b) or do i have to insert a new (say a node) that will contain the patient structure in the list structure, by inserting let say, a new node to the list? is this understandable .. thanks a lot . – fleuracia Apr 15 '12 at 15:45
  • Linked lists are one of the fundamental, general-purpose data structures that will serve you well once you understand them. It would be a worthwhile investment to find a more comprehensive explanation than you'll get here on SO. Plenty of good web sites and books are available. – Adam Liss Apr 15 '12 at 20:04
5

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.

Pakpoom Tiwakornkit
  • 2,601
  • 1
  • 20
  • 19
  • This helped me solve the issues. I was getting errors when defining strings like: `struct Student { char name[24]; } student;` – user8581607 Jan 31 '21 at 09:26
  • 2
    patient.gender = 'M' will be there instead of "M" as it is char. Please correct it. Although It'll compile, but result will not be as expected. – Kapil Apr 28 '21 at 16:22
  • As a newbie, this answer is very helpful for me. Does it have any drawbacks compared to the (seemingly more complicated) accepted answer? – Dave Jan 14 '22 at 16:07
  • 1
    @Dave There are no drawbacks using this method AFAIK, but if you can understand the accepted answer's coding style, then it is a plus, as there are many developers using that style. – Pakpoom Tiwakornkit Jan 19 '22 at 15:44
  • @Kapil Thank you for pointing that out. The answer is corrected. – Pakpoom Tiwakornkit Jan 19 '22 at 15:47
1

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 *'...

Gwyn Evans
  • 1,361
  • 7
  • 17
0

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.

Attila
  • 28,265
  • 3
  • 46
  • 55
  • 1
    You [should not typecast](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc/605858#605858) while using malloc. – Alok Save Apr 15 '12 at 12:56
  • No. `patient` is not globally declared, it's a typdef. But even if it was global, you wouldn't initialize it like this. – kralyk Apr 15 '12 at 13:04
0

You could just use an even simpler typedef:

typedef char *string;

Then, your malloc would look like a usual malloc:

string s = malloc(maxStringLength);
Richard J. Ross III
  • 55,009
  • 24
  • 135
  • 201