13

in C, I believe the following program is valid: casting a pointer to an allocated memory buffer to an array like this:

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

#define ARRSIZE 4

int *getPointer(int num){
    return malloc(sizeof(int) * num);
}

int main(){
    int *pointer = getPointer(ARRSIZE);
    int (*arrPointer)[ARRSIZE] = (int(*)[ARRSIZE])pointer;

    printf("%d\n", sizeof(*arrPointer) / sizeof((*arrPointer)[0]));

    return 0;
}

(this outputs 4).

However, is it safe, in C99, to do this using VLAs?

    int arrSize = 4;
    int *pointer = getPointer(arrSize);
    int (*arrPointer)[arrSize] = (int(*)[arrSize])pointer;

    printf("%d\n", sizeof(*arrPointer) / sizeof((*arrPointer)[0]));

    return 0;

(also outputs 4).

Is this legit, according to the C99 standard?

It'd be quite strange if it is legit, since this would mean that VLAs effectively enable dynamic type creation, for example, types of the kind type(*)[variable].

  • 2
    Note: I confirm that the OP's code [compiles on IDEONE](http://ideone.com/AI4yYB) and works as the OP says for array sizes different than 4, meaning that it outputs whatever you set `ARRSIZE` or `arrSize` to. – Mike Nakis Feb 15 '15 at 10:03
  • It's legit according to the C11 draft spec. I don't have a copy of C99, so I can't confirm it was legit in the previous century. – user3386109 Feb 15 '15 at 10:07
  • @user3386109: I am asking about C99, since in C11 VLAs are *optional* –  Feb 15 '15 at 10:09
  • OT: Printing as `%d` is inappropriate here, as `sizeof` returns at least `unsigned` if not also `long`. – alk Feb 15 '15 at 10:20
  • @alk: you mean I should use `%zd` or `%zu`? I thought they were considered a discouraged practice. –  Feb 15 '15 at 10:23
  • Just use what is available on the target platform. – alk Feb 15 '15 at 10:25
  • @alk `sizeof` yields `size_t`. (This may or may not be a typedef for a standard unsigned type). – M.M Feb 15 '15 at 10:33
  • @Mints97 `%zu` is correct practice for `size_t`. If not on a C99-compatible platform you will have to cast to a known unsigned type and use the correct modifier for that type. There's no such thing as `%zd`. – M.M Feb 15 '15 at 10:34
  • @hyde: thank you, I understand your concern. I'll try to follow your advice in the future. I'm not exactly a seasoned SO user, you see =) –  Feb 15 '15 at 10:46
  • 1
    @MattMcNabb, yes there is `%zd`. It is for the signed type corresponding to `size_t`. – Jens Gustedt Feb 15 '15 at 12:24
  • 1
    @Mints97, also consider dropping your allocation function completely. It does nothing a simple `malloc(sizeof(int[n][m]))` couldn't achieve and you would avoid a superflouous cast. Casts are bad. – Jens Gustedt Feb 15 '15 at 12:27

2 Answers2

10

Yes, this is legit, and yes, the variably-modified type system is extremely useful. You can use natural array syntax to access a contiguous 2-D array both of whose dimensions were not known until runtime.

It could be called syntactic sugar as there's nothing you can do with these types that you couldn't do without them, but it makes for clean code (in my opinion).

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Thank you, this is an interesting answer. I never considered using VLAs as 2D arrays. –  Feb 15 '15 at 10:20
  • 2
    @Mints97 in case it didn't occur to you already, you can use the SO-approved [malloc idiom](http://stackoverflow.com/a/605858/1505939) `ptr = malloc(N * sizeof *ptr);` here too, e.g. `int (*ptr)[cols] = malloc(rows * sizeof *ptr);` – M.M Feb 15 '15 at 10:24
  • thanks, I know that =) I was just interested in this intricacy of C's type system. –  Feb 15 '15 at 10:26
  • I use this frequently to take a 1-D array of floats (example [20] floats), and typecast it into either 5x4 matrix, a 4x5 matrix, a 2x10 matrix, etc, etc. – abelenky Apr 26 '22 at 19:12
2

I would say it is valid. The Final version of the C99 standard (cited on Wikipedia) says in paragraph 7.5.2 - Array declarators alinea 5 : If the size is an expression that is not an integer constant expression: ... each time it is evaluated it shall have a value greater than zero. The size of each instance of a variable length array type does not change during its lifetime.

It even explicitely says that it can be used in a sizeof provided the size never changes : Where a size expression is part of the operand of a sizeof operator and changing the value of the size expression would not affect the result of the operator, it is unspecified whether or not the size expression is evaluated.

But the standard also says that this is only allowed in a block scope declarator or a function prototype : An ordinary identifier (as defined in 6.2.3) that has a variably modified type shall have either block scope and no linkage or function prototype scope. If an identifier is declared to be an object with static storage duration, it shall not have a variable length array type.

And an example later explains that it cannot be used for member fields, even in a block scope :

...
void fvla(int m, int C[m][m]); // valid: VLA with prototype scope
void fvla(int m, int C[m][m]) // valid: adjusted to auto pointer to VLA
{
    typedef int VLA[m][m]; // valid: block scope typedef VLA
    struct tag {
        int (*y)[n]; // invalid: y not ordinary identifier
        int z[n]; // invalid: z not ordinary identifier
    };
...
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252