0

I'm trying to understand a piece of C code which is as follows:

#define DIM2( basetype, name, w1 ) basetype (*name)[w1]
int mx = 10; //number of rows per processor
int my = 100; //number of cols
DIM2 (double, f, my);
f = (typeof (f)) malloc (2 * mx * sizeof (*f));

If I'm correct, with DIM2 a 1-d array of (size=100) pointers to double is created. I'm not able to understand what happens again with malloc? Is it necessary for two such statements?

Is there any alternative way to achieve what happens in the last two lines of code above in any other way?

Vandana
  • 127
  • 1
  • 1
  • 10

2 Answers2

4

The macro evaluates to:

double (*f)[my];

which is a pointer to array of double, not an array of pointer to double.

malloc allocates an array of 2 * mx * <whateverfpoints to> (i.e. an array of double). Not sure why it would allocate twice as many entries as given by mx, but that's what it does.

So, f points to the first array of double afterwards. It effectively allocates a true 2 dimensional array. (not the often confused array of pointers to double).

Note that the cast of malloc is bad practise in C.

Comment: As there is not less typing and the macro does not add specific information, it is actually bad practise. Worse is it hides the pointer semantics obfuscating the code. Recommendation is not to use it, but better be explicit; this is even not more typing.

Update: There is currently an argument if sizeof(*f) presents _undefined behaviour, becausef` is used uninitialized here. While I see a flaw in the standard here which should be more precise, you might better play safe and use an explicit expression:

f = malloc (2 * mx * my * sizeof (double))
Community
  • 1
  • 1
too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • What is difference between double f[my] and double (*f)[my]? Are these both the same? – Vandana Oct 06 '15 at 01:42
  • @Vandana: the first declares `f` as an array of `double`, the second declares `f` as a pointer to an array of `double`. Very different. – John Bode Oct 06 '15 at 02:23
  • @JohnBode: Even when we do, double f[my] wouldn't we get a pointer "f" that points to the first element? So how are the two different? – Vandana Oct 06 '15 at 02:44
  • @Olaf can you explain your answer in greater detail please? I really need to understand this. – Vandana Oct 06 '15 at 05:30
  • 1
    @Vandana: Yes, you **really** need to understand, because this is a fundamental concept in C and often missunderstood. An array is **not** a pointer. But it [**most times** decays](http://port70.net/~nsz/c/c11/n1570.html#6.3.2.1p3) to a pointer to the first element. Exceptions are `_Alignof`, `sizeof` and `&` operators. Try getting `sizeof(f)` for both types. You also cannot change the address of an array, but you can for a pointer. Try `char f[3] = `malloc(3)` and see the compiler error. – too honest for this site Oct 06 '15 at 10:32
0

double (*f)[my] is a VLA type because my is an int. So sizeof (*f) causes undefined behaviour because the argument of sizeof is evaluated if it has VLA type. For more discussion see here.

Unfortunately the sizeof *f idiom can only be used with pointer to array of fixed dimension (or pointer to non-array!). So this entire idea is bogus.

IMHO it is simpler and clearer to do away with the macro and write:

double (*f)[my] = malloc( sizeof(double[mx][my]) );
Community
  • 1
  • 1
M.M
  • 138,810
  • 21
  • 208
  • 365
  • 2
    No, it does not. `sizeof` does not dereference the pointer, but only deconstructs the type, detects the VLA and evaluates the expression. There is no need to dereference the pointer to get the type of its target. Do you think `int *i; sizeof(*i);` is UB, too? – too honest for this site Oct 06 '15 at 12:50
  • 1
    Please read the standard. The link I provided is very clear about this. `*i` is not dereferenced, because ot need not to determine the type. What with `int i = 5; char f[i]; sizeof(f)`? There is also no need to dereference. Note that in C arrays **never** include the dimensions in the array (as other languages do). So the compiler has to track how they are calculated anyway - be they VLA or not. Please link where the standard states `sizeof()` is UB. – too honest for this site Oct 06 '15 at 19:20
  • test.c: `... int main() { int i = 4, ar[i]; printf("%zu\n", sizeof(ar)); }` `$ gcc -std=c11 -Wall -O3 test.c` (no complaints) `$ /a.out` --> `16` – too honest for this site Oct 06 '15 at 19:27
  • Removed my rebuttal comments, there is now [a question](http://stackoverflow.com/questions/32985424/is-the-operand-of-sizeof-evaluated-with-a-vla) on this topic – M.M Oct 07 '15 at 07:29
  • Ok, I think I get your point now (assume I had to sleep it over). However, I still see no actual need to evaluate (in the sense of dereference the pointer) the whole operand, but only the index. However, of course the operand has to be parsed to detect the VLA type which includes the index-expression (i.e. the length). Only the latter actually has to be evaluated. – too honest for this site Oct 07 '15 at 11:13
  • 1
    I updated my answer. Could you please dummy-edit your answer, so I can remove the DV (it is currently locked in). – too honest for this site Oct 07 '15 at 11:31
  • @Olaf Edited now. BTW you can dummy-edit yourself for vote-changing purposes if you have enough rep (seems obvious but never occurred to me for ages) – M.M Oct 08 '15 at 08:30
  • Removed. I would have, just thought it was more polite to let you edit your text (unless It contained errors). Next time I know better :-) – too honest for this site Oct 08 '15 at 11:04