6

I was surprised when gcc -Wall compiled this without warning. Is this really legitimate C? What are the risks of writing code like this?

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

typedef struct {
  int a;
  int b;
} MyStruct;

int main(void) {
  MyStruct *s = malloc(sizeof(*s)); // as opposed to malloc(sizeof(MyStruct))
  s->a = 5;
  printf("%d\n", s->a);
}
countunique
  • 4,068
  • 6
  • 26
  • 35

3 Answers3

5

Not only it's legitimate, it's even preferable to the alternative. This way you let the compiler deduce the actual type instead of doing it manually.

SomeWittyUsername
  • 18,025
  • 3
  • 42
  • 85
2

sizeof is evaluated at compile time. In this context, *s resolves to the type of *s, it does not dereference the pointer.

This is the canonical way of using sizeof. If you used sizeof(int) you leave an opening for an error should the type change (in this case, probably unlikely, but still.)

Ed S.
  • 122,712
  • 22
  • 185
  • 265
  • Note that given: `void function(const char *n) { short a[atoi(n)]; size_t s = sizeof(a)/sizeof(a[0]); …code using a and s… }`, the value of `sizeof(a)` is a run-time computation because `a` is a VLA — variable length array. (Note that `sizeof(a[0])` is still a compile-time computation, though.) I make no claims for the quality of the code except for this narrow pedagogical purpose (it is awful). – Jonathan Leffler Apr 24 '14 at 20:47
  • @JonathanLeffler: That's true, VLA's are the exception. – Ed S. Apr 24 '14 at 21:14
1

Writing

MyStruct *s = malloc(sizeof(*s));

has exactly the same effect as

MyStruct *s = malloc(sizeof(MyStruct));

except that now you only write MyStruct once. That is, the object you're allocating has its source type determined automatically, which reduces the chances of errors.

For example - it has happened to me - you start with a MyStruct. Then you decide you need also a different MyStruct for different purposes. So you end up with two different structures, MyStruct and AnotherStruct.

Then you refactor your code and change some variables from MyStruct to AnotherStruct, and end up with

AnotherStruct *s = malloc(sizeof(MyStruct));

which might actually work in several circumstances, or for a long time, until you make another little and, at that point, completely unrelated change in either structure. At that point your code goes kaboom.

E.g.

typedef struct {
        int a;
        int b;
} StructA;

typedef struct {
        int a;
        int b;
        int c;
} StructB;

int main() {
        // No problem here except wasted space
        StructA *s = malloc(sizeof(StructB));

        // t->c dwells in undefined country
        StructB *t = malloc(sizeof(StructA));
}
LSerni
  • 55,617
  • 10
  • 65
  • 107