1

How can I use malloc() for these structures in C:

typedef struct {
    char *Name;
    char *Email;
} Person;

typedef struct {
    unsigned int len;
    Value *pVal;
} Person;

How can I read the these structures' values using fread()?

Person *p;
p->pVal = (Value *)malloc(p->len * sizeof(Value));
p->pVal.Name = (char *)malloc(sizeof(char));
p->pVal.Email = (char *)malloc(sizeof(char));

Is this the correct method?. The size of Name and Email will be read from file and is of variable size.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Aysha
  • 39
  • 2
  • You can't have 2 types with same name. First `Person` type should probably be `Value` – Gerhardh Aug 19 '20 at 09:52
  • Where does `p` point to? – Gerhardh Aug 19 '20 at 09:53
  • `(char *)malloc(sizeof(char));` does not make much sense. In C you should not cast the return value of `malloc`. And you will only allocate 1 byte which cannot take any character except the terminating 0 byte. – Gerhardh Aug 19 '20 at 09:55
  • The other comments don't mention it yet, but since you mention `fread`, you'll need to handle serialization (or at least be aware in which way things can break when you don't). https://stackoverflow.com/questions/6002528/c-serialization-techniques has some useful info on the topic. – domen Aug 19 '20 at 10:04
  • @Aysha: you can accept one of the answers by clicking on the grey checkmark below its score. – chqrlie Sep 06 '20 at 05:53

2 Answers2

0

If you do not know how long your strings will be, you have to read in some temporary buffer and then parse the content.

It would be useful to define some maximum length for the fields:

// TODO: Check for NULL result for all calls to malloc

#define MAX_FIELD_LEN 500

char temp_name[MAX_FIELD_LEN] = {0};
char temp_email[MAX_FIELD_LEN] = {0};

Person *p = malloc(sizeof (*p));
p->len = ...; 
p->pVal = malloc(p->len * sizeof(*p->pVal));
for (int i = 0; i < p->len; i++) {
    // read file content into temp_name and temp_email
    p->pVal[i].Name = malloc(strlen(temp_name)+1);
    strcpy(p->pVal[i].Name, temp_name);
    p->pVal[i].EMail = malloc(strlen(temp_email)+1);
    strcpy(p->pVal[i].Email, temp_email);
}

This is based on code from your question. Without more details about how you get the data, no optimum solution can be provided.

You need to define the structure of your file. It must be defined how you store strings and how you can separate them when reading back.

You might read about "serialization/deserialization" for more details.

Gerhardh
  • 11,688
  • 4
  • 17
  • 39
0

There is typo in your code: the first type should be Value, not Person:

typedef struct {
    char *Name;
    char *Email;
} Value;

typedef struct {
    unsigned int len;
    Value *pVal;
} Person;

To allocate a structure from the heap, you can use malloc() an provide the size in bytes of the object to allocate:

Person *p = malloc(sizeof(Person));

Note that you can get the appropriate size from the pointer type, so you can use a more general syntax for all types:

Person *p = malloc(sizeof(*p));

The pVal pointer seems to point to an array of Value structures, so your allocation is correct, but the len member must be initialized to the appropriate number of elements. You can also write:

p->len = count;
p->pVal = malloc(p->len * sizeof(*p->pVal));

Block of memory allocated by malloc() are not initialized, so you must either initialize all member manually to the appropriate values or use calloc() that returns a block of memory initialized to all bits zero, which on all modern systems means all members are initialized to their 0 value: integer members to 0, floating point members to 0.0 and pointers to NULL:

p->len = count;
p->pVal = calloc(p->len, sizeof(*p->pVal));  // members Name and Email are set to NULL

Note also that the C Standard does not specify if an allocation request for 0 bytes will return a null pointer or a pointer to an object with a zero size. You should avoid trying to allocate zero bytes:

p->len = count;
if (p->len == 0) {
    p->pVal = NULL;
} else {
    p->pVal = calloc(p->len, sizeof(*p->pVal));  // members Name and Email are set to NULL
}

Note also that any allocation attempt can fail and return a null pointer, so you should test the return value and handle allocation failures gracefully.

For the variable sized strings, you can read the file with fgets() and use strdup() to allocate copies of the strings:

char Name[100];
char Email[100];

if (fgets(Name, sizeof Name, fp) != NULL &&
    fgets(Email, sizeof Email, fp) != NULL) {
    Name[strcspn(Name, "\n")] = '\0';  // strip the trailing newline
    Email[strcspn(Email, "\n")] = '\0';  // strip the trailing newline
    p->pVal->Name = strdup(Name);
    p->pVal->Email = strdup(Email);
} else {
    /* handle invalid input */
}

Here is a function that combines the above to read a record from the file:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct {
    char *Name;
    char *Email;
} Value;

typedef struct {
    unsigned int len;
    Value *pVal;
} Person;

Person *read_person(FILE *fp, int count) {
    Person *p = malloc(sizeof(*p));
    if (p == NULL)
        return NULL;
    p->len = count;
    if (p->len == 0) {
        p->pVal = NULL;
    } else {
        p->pVal = calloc(p->len, sizeof(*p->pVal));  // members Name and Email are set to NULL
        if (p->pVal == NULL) {
            free(p);
            return NULL;
        }
        for (unsigned int i = 0; i < len; i++) {
            char Name[100];
            char Email[100];
 
            if (fgets(Name, sizeof Name, fp) != NULL &&
                fgets(Email, sizeof Email, fp) != NULL) {
                Name[strcspn(Name, "\n")] = '\0';  // strip the trailing newline
                Email[strcspn(Email, "\n")] = '\0';  // strip the trailing newline
                p->pVal[i].Name = strdup(Name);
                p->pVal[i].Email = strdup(Email);
            } else {
                /* handle invalid input */
                while (i-- > 0) {
                    free(p->pVal[i].Name);
                    free(p->pVal[i].Email);
                }
                free(p->pVal);
                free(p);
                return NULL;
            }
        }
    }
    return p;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189