-2

I'm having some trouble wrapping my head around pointer assignments in C. The asterisk character appears in many different locations and I don't understand why I would choose to use one way over the other.

Specifically in the code below:

why would I choose:

conn->db->rows = (struct Address**)malloc(sizeof(struct Address *) * conn->db->MAX_ROWS);

over:

conn->db->rows = (struct **Address)malloc(sizeof(struct Address *) * conn->db->MAX_ROWS);

and within sizeof, what does the asterisk indicate?

This is the origin of the code above. Not the entire program.

struct Address {
  int id;
  int set;
  char *name;
  char *email;
};

struct Database {
  int MAX_DATA;
  int MAX_ROWS;
  struct Address **rows; // USE ARRAY OF POINTERS
};

struct Connection {
  FILE *file;
  struct Database *db;
};

void die(const char *message) {
  if(errno) {
    perror(message);
  } else {
    printf("ERROR: %s\n", message);
  }

  exit(1);
}

void Address_print(struct Address *addr) {
  printf("%d %s %s\n", addr->id, addr->name, addr->email);
}

void Database_load(struct Connection *conn) {
  size_t i=0;

  // Each database will have two `int` values. read
  // those first.
  assert(conn->db && conn->file);
  if (!(conn->db && conn->file))
      die("Database load : Invalid Connection info");
  if (fread(&conn->db->MAX_DATA, sizeof(conn->db->MAX_DATA), 1, conn->file) != 1)
      die("Database load : Couldn't read MAX_DATA");
  if (fread(&conn->db->MAX_ROWS, sizeof(conn->db->MAX_ROWS), 1, conn->file) != 1)
      die("Database load : Couldn't read MAX_ROWS");
  conn->db->rows = (struct Address**)malloc(sizeof(struct Address *) * conn->db->MAX_ROWS);

  assert(conn->db->rows);
  if (!(conn->db->rows)) {
    die("Database_load : Could not MAX_ROWS Address  structures");
  }
dablyputs
  • 3
  • 3
  • 3
    `struct **Address` is not valid syntax. In a type specifier, `*` goes after the type name to indicate that you're specifying a pointer to the type. `struct` is not a type, but `struct Address` is. – Barmar Jul 22 '20 at 16:06
  • The asterisk in `sizeof` indicates that you want the size of a pointer. Without the asterisk, `sizeof` would return the size of the structure. – user3386109 Jul 22 '20 at 16:06
  • Welcome to Stack Overflow! [dont cast malloc](https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc) – Barmar Jul 22 '20 at 16:07
  • 1
    @dablyputs Before asking your qustion you should at least check whether this record struct **Address compiles. – Vlad from Moscow Jul 22 '20 at 16:17
  • @VladfromMoscow why? You (or someone else) will do it for him. – 0___________ Jul 22 '20 at 16:24
  • @VladfromMoscow I know it doesn't compile but I didn't understand why. I'm a beginner at C and I am struggling with this section of Learn C The Hard Way. The code sample this is from is a solution to a problem in the book written by someone other than the author but it is using techniques that are beyond what has been covered so far. There is no way I would have known to search the topic of type casting. – dablyputs Jul 22 '20 at 22:23

1 Answers1

0

struct **Address is illegal syntax. The base type is struct Address and there should be nothing but whitespace (comments count as whitespace) between the struct keyword and the tag, Address.

struct Address * is a type that is a pointer to a struct Address.

struct Address ** is a type that is a pointer to a pointer to a struct Address.


A sizeof expression determines the size of a type in bytes. There are two forms:

  • sizeof(type) (for some type, type) specifies the type directly.
  • sizeof expression (for some expression, expression) uses the resulting type of expression without evaluating expression.

In the case of sizeof(struct Address *), the first form is used. This is evaluated as the size of the struct Address * type (pointer to struct Address) in bytes.

In the statement:

conn->db->rows = (struct Address**)malloc(sizeof(struct Address *) * conn->db->MAX_ROWS);

the result of the call to malloc is a generic pointer type, void * and it is being cast to the struct Address** type by the cast operator (struct Address**) to match the type of the rows member being assigned to.

In C, it is actually the usual practice to omit the cast when assigning a void * value to something of another object pointer type, or vice versa. The statement can be rewritten, omitting the cast, as:

conn->db->rows = malloc(sizeof(struct Address *) * conn->db->MAX_ROWS);

Additionally, the type operand of sizeof(type) can be replaced with an expression that has the same type. Since conn->db->rows has type struct Address**, then *conn->db->rows or conn->db->rows[0] will have type struct Address *. Either of those could be substituted into the statement, rewriting it as, for example:

conn->db->rows = malloc(sizeof(*conn->db->rows) * conn->db->MAX_ROWS);

That can be less error-prone than specifying the type directly in the sizeof expression, and is common practice.

Ian Abbott
  • 15,083
  • 19
  • 33