19

Here's a short C program that prompts the user for a number, creates a variable-length array of ints of that size, and then uses pointer arithmetic to step over the elements that were allocated:

#include <stdio.h>

int main() {
    /* Read a size from the user; inhibits compiler optimizations. */
    int n;
    scanf("%d", &n); // Yes, I should error-check. :-)

    /* We now have a VLA. */
    int arr[n];

    /* What is the type of &arr? */
    void* ptr = (&arr) + 1;

    /* Seems like this skipped over things properly... */
    printf("%p\n", arr);
    printf("%p\n", ptr);
}

You can try this on ideone if you'd like. The output suggests that the line

void* ptr = (&arr) + 1;

takes the address of arr and, in a size-aware way, steps over all n of the elements in the variable-length array.

If this were not a variable-length array, I'd be completely comfortable with how this works. The compiler would know the type of arr (it would be int (*) [K] for some constant K), so when we add one to &arr it could skip over the right number of bytes.

It's clear how, at runtime, we could evaluate (&arr) + 1. The compiler stashes the size of arr somewhere on the stack, and when we add one to (&arr) it knows to load that size in order to compute how many bytes to skip over.

However, what I don't know is what the language says the type of the expression &arr is. Is it assigned some static type that indicates it's a variable-length array (something like int (*) [??])? Does the spec say "the type of the expression is int (*) [K], where K is whatever size is assigned to the array at runtime?" Does the spec disallow taking the address of a variable-length array, and the compiler just happens to allow it?

12
  • Forming a pointer to a variable-length array is definitely allowed; among other things, VLAs of VLAs wouldn't work if you couldn't do that. Commented Sep 29, 2017 at 0:05
  • See the example at &sect;6.5.6 para 10 Commented Sep 29, 2017 at 0:09
  • The example rici is referring to. Commented Sep 29, 2017 at 0:10
  • 1
    It will be a pointer to variable length array type. The sizeof operator evaluates the operand to determine the size of the variable length array object, so + must do the same thing. See C.2011 - 6.5.6/10. Commented Sep 29, 2017 at 0:10
  • 1
    @savram My question is less about how that works - the mechanism is pretty clear to me - and more about how the C spec assigns types to the expressions here. I don’t think disassembling things would provide any extra insight. Commented Sep 29, 2017 at 0:57

1 Answer 1

13

With a VLA, the sizeof operator is not a compile-time constant. It is evaluated at run-time. In your question, the type of &arr is int (*)[n]; — a pointer to an array of n values of type int, where n is a run-time value. Hence, as you note, &arr + 1 (parentheses not needed except around parenthetical comments noting that the parentheses are not needed) is the start of the array after arr — the address is sizeof(arr) bytes beyond the value of arr.

You could print the size of arr; it would give you the appropriate size (printf() modifier z). You could print the difference between &arr + 1 and arr and you'd get the size as a ptrdiff_t (printf() modifier t).

Hence:

#include <stdio.h>

int main(void)
{
    int n;
    if (scanf("%d", &n) == 1)
    {
        int arr[n];

        void* ptr = (&arr) + 1;

        printf("%p\n", arr);
        printf("%p\n", ptr);
        printf("Size: %zu\n", sizeof(arr));
        printf("Diff: %td\n", ptr - (void *)arr);
    }
    return 0;
}

And two sample runs (program name vla59):

$ vla59
23
0x7ffee8106410
0x7ffee810646c
Size: 92
Diff: 92
$ vla59
16
0x7ffeeace7420
0x7ffeeace7460
Size: 64
Diff: 64
$

No recompilation — but sizeof() is correct each time the program is run. It is calculated at run-time.

Indeed, you can even use a loop with a different size each time:

#include <stdio.h>

int main(void)
{
    int n;
    while (printf("Size: ") > 0 && scanf("%d", &n) == 1  && n > 0)
    {
        int arr[n];

        void* ptr = (&arr) + 1;

        printf("Base: %p\n", arr);
        printf("Next: %p\n", ptr);
        printf("Size: %zu\n", sizeof(arr));
        printf("Diff: %td\n", ptr - (void *)arr);
    }
    return 0;
}

Sample run of vla11:

$ vla11
Size: 23
Base: 0x7ffee3e55410
Next: 0x7ffee3e5546c
Size: 92
Diff: 92
Size: 16
Base: 0x7ffee3e55420
Next: 0x7ffee3e55460
Size: 64
Diff: 64
Size: 2234
Base: 0x7ffee3e53180
Next: 0x7ffee3e55468
Size: 8936
Diff: 8936
Size: -1
$
Sign up to request clarification or add additional context in comments.

2 Comments

Yup. §6.5.3.4 The sizeof and _Alignof operators. (C11 n1570 draft)
Interesting. So the type is “an array of whatever size it ends up being.” That’s cool! I didn’t know that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.