Intel® C++ Compiler
Community support and assistance for creating C++ code that runs on platforms based on Intel® processors.

Bug or undefined behaviour?

Erik_P_
Beginner
442 Views

Hi,

In integrating a third party C library into our product, I came across a fragment of code where inside a function, a struct is defined that includes a variable length array, the length of which depends on a parameter of that function. Incrementing a pointer s to such a struct works differently depending on whether you write ++s or s += 1 on the one hand, or s = s + 1 on the other hand. The code is similar to the following:

[cpp]#include <stdio.h>

typedef struct {
    int   ndim;
    void  *firstmember, *lastmember;
} Container;

#define N_MEMBERS 5

#define TYPEDEFMEMBER(t)       \
    typedef struct {           \
        int    n;              \
        double data[t->ndim];  \
    } Member


void init(Container *t) {
    TYPEDEFMEMBER(t);

    Member *first, *last;
    first = malloc(N_MEMBERS * sizeof(Member));
    last  = first + N_MEMBERS - 1;
    t->firstmember = first;
    t->lastmember  = last;
}

void process(Container *t) {
    TYPEDEFMEMBER(t);

    Member *s, *first, *last;
    first = t->firstmember;
    last  = t->lastmember;
    printf("t->ndim = %d, with ++s:\n", t->ndim);
    for (s = first; s <= last; ++s) {
        printf("s: %p\n", s);
    }

    printf("t->ndim = %d, with s += 1:\n", t->ndim);
    for (s = first; s <= last; s += 1) {
        printf("s: %p\n", s);
    }

    printf("t->ndim = %d, with s = s + 1:\n", t->ndim);
    for (s = first; s <= last; s = s + 1) {
        printf("s: %p\n", s);
    }
}

void destroy(Container *t) {
    free(t->firstmember);
    t->firstmember = 0;
    t->lastmember  = 0;
}

int main(void) {
    Container container;
    Container *containerp = &container;
    container.ndim = 1;
    init(containerp);
    process(containerp);
    destroy(containerp);
    container.ndim = 3;
    init(containerp);
    process(containerp);
    destroy(containerp);

    return 0;
}[/cpp]

On linux on x86_64, both ++s and s += 1 increase s by 8 independent of the value of t->ndim, whereas s = s + 1 increases s by sizeof(Member) == 8 + t->ndim * 8. Clearly the latter is intended. This was compiled icc Version 13.0.1.117 Build 20121010 by the way, without any further options - just icc foo.c && ./a.out.

I'm not entirely sure this code is legal; but I feel that either icc should refuse to compile it, or be consistent between the ways of incrementing s.

Thanks

Erik Postma
Maplesoft

0 Kudos
3 Replies
Erik_P_
Beginner
442 Views

Not sure if I can edit my original post - I notice that the line continuation characters in the macro definition of TYPEDEFMEMBER do not show up. There should be a backslash at the end of four lines there - the definition ends with the line "} Member". Similarly but less crucially, there should be backslashes in the printf format strings. Sorry for the confusion.

Erik.

0 Kudos
Mark_S_Intel1
Employee
442 Views

What result do you get with the gcc compiler?

The compiler you are using is a bit old.  The latest one that you could try is icc 13.1.1.163 Build 20130313.

--mark

0 Kudos
Erik_P_
Beginner
442 Views

Hi Mark,

Thanks for your response. gcc does the intended thing: it increases s by sizeof(Member) for each of the methods of increasing s.

Indeed, I have found out since yesterday that this is an undocumented (mis?)feature of gcc specifically; clang, for example, describes it at http://clang.llvm.org/docs/UsersManual.html#intentionally-unsupported-gcc-extensions as:

clang does not support the gcc extension that allows variable-length arrays in structures. This is for a few reasons: one, it is tricky to implement, two, the extension is completely undocumented, and three, the extension appears to be rarely used.

I guess this means that the obvious solution for you guys is to just refuse to compile it (which wouldn't solve the issue for me, but that's life I guess).

Erik.

0 Kudos
Reply