Hello.

I have been reading this forum for quite some time, and this is my first post.

I often have the need to dynamically allocate memory for arrays with multiple dimensions. In my opinion I find it quite ugly to litter the code with multiple for-loops and mallocs every time I need one.

I have created this macro to allocate memory for a three-dimensional array, regardless of type:

#define allocate_cube(type, var, x, y, z)                            \
    do {                                                             \
        (var) = malloc(sizeof(type **) * (x));                       \
        if ((var) == NULL) {                                         \
            perror("Error allocating memory.\n");                    \
        }                                                            \
        int i, j;                                                    \
        for (i = 0; i < x; i++) {                                    \
            (var)[i] = malloc(sizeof(type*) * (y));                  \
            if ((var)[i] == NULL) {                                  \
                perror("Error allocating memory.\n");                \
            }                                                        \
            for (j = 0; j < y; j++) {                                \
                (var)[i][j] = malloc(sizeof(type) * (z));            \
                if ((var)[i][j] == NULL) {                           \
                    perror("Error allocating memory.\n");            \
                }                                                    \
            }                                                        \
        }                                                            \
    } while(0)

The macro is used in the following way:

int ***array;
array = allocate_cube(int, array, 5, 10, 15);

Is this considered bad practice? I have used functions to do this before, but it makes it almost impossible to track the origin of the memory allocation, when debugging.

Any thoughts or comments are appreciated :)

Recommended Answers

All 7 Replies

That is C++ code, not C. To compile with a C compiler you have to move line 7 to immediately after line 2.

Why use a do loop ? It does nothing at all. You can achieve the same thing if you delete lines 2 and 20.

If malloc() fails on line 3 then the rest of the code must not be executed because it will cause an exception.

That macro would be better if it were a normal function because macros are expanded every time they are referenced, such will make your program a lot larger than it needs to be.

>>but it makes it almost impossible to track the origin of the memory allocation, when debugging.
You are not using your debugger very efficiently then.

No, it's C code. Legal C99 code, at least.

The do { .. } while(0) is to use the loop-variables in a new scope, so it won't mess up variables already defined in the function that calls the macro.

.

The do { .. } while(0) is to use the loop-variables in a new scope, so it won't mess up variables already defined in the function that calls the macro.

All you need for that is the braces, which do not cause extra code to be geenrated like a do-nothing do loop. Guess a good optimizing compiler will toss it out anyway.

I don't think they will be tossed out by the compiler. Consider the following case:

if (x > y)
allocate_cube(int, var, 1, 2, 3);
else
....

If the macro was written with only a {} block, and not a do{...} while(0), because of the trailing ; after the macro call, the if-statement is interpreted as having only one branch. ( if (x < y) {foo}; ) Therefore, the compiler will issue an error when the else occurs seemingly by itself.

Another issue (as I see it) with macro vs functions in this case is that my macro is type-neutral, i.e. the same macro can be used to allocate strings, doubles, structs etc, but I have to create separate functions for each type to do the same with functions. Or have I missed something completely in class? :p

> Why use a do loop ?
It's one of the standard techniques to ensure that a multi-line macro such as this always appears as a single statement.
See http://www.c-faq.com/cpp/multistmt.html

@OP

>#define allocate_cube(type, var, x, y, z)                            \
>    do {                                                             \
>        (var) = malloc(sizeof(type **) * (x));                       \

You don't need to specify the type as a parameter to the macro. All the size information is available via the var, eg.

#define allocate_cube(var, x, y, z)                                  \
    do {                                                             \
        (var) = malloc(sizeof(*var) * (x));                          \

The general form being p = malloc ( n * sizeof *p ); This fixes the problem of doing something dumb, but hard to trace if it happens to work first time around, like this.

int ***array;
array = allocate_cube(double, array, 5, 10, 15);

> Another issue (as I see it) with macro vs functions in this case is that my macro is type-neutral
Or type-unsafe, depending on your point of view.
In reality, how many types are you going to be dealing with, and how often do you need a new type?

AD's suggestion of a function could be implemented simply as

int ***allocate_int_cube ( int x, int y, int z ) {
    int ***result = alloc_cube( result, x, y, z );
    return result;
}

The macro still does all the donkey work, but it's use is tightly controlled to just a small part of the overall program. The bonus to you is something you can debug with, and a measure of type safety.

Hello.

I have been reading this forum for quite some time, and this is my first post.

I often have the need to dynamically allocate memory for arrays with multiple dimensions. In my opinion I find it quite ugly to litter the code with multiple for-loops and mallocs every time I need one.

I have created this macro to allocate memory for a three-dimensional array, regardless of type:

#define allocate_cube(type, var, x, y, z)                            \
    do {                                                             \
        (var) = malloc(sizeof(type **) * (x));                       \
        if ((var) == NULL) {                                         \
            perror("Error allocating memory.\n");                    \
        }                                                            \
        int i, j;                                                    \
        for (i = 0; i < x; i++) {                                    \
            (var)[i] = malloc(sizeof(type*) * (y));                  \
            if ((var)[i] == NULL) {                                  \
                perror("Error allocating memory.\n");                \
            }                                                        \
            for (j = 0; j < y; j++) {                                \
                (var)[i][j] = malloc(sizeof(type) * (z));            \
                if ((var)[i][j] == NULL) {                           \
                    perror("Error allocating memory.\n");            \
                }                                                    \
            }                                                        \
        }                                                            \
    } while(0)

The macro is used in the following way:

int ***array;
array = allocate_cube(int, array, 5, 10, 15);

Is this considered bad practice? I have used functions to do this before, but it makes it almost impossible to track the origin of the memory allocation, when debugging.

Any thoughts or comments are appreciated :)

I think, it is no need to mention like "array = allocate_cube(int, array, 5, 10, 15);"
I haven't executed or checked it out,
But, I feels, "allocate_cube(int, array, 5, 10, 15);" is also sufficient.

i wish threads that have had no activity for 90 days or more, would be automatically locked.

commented: I vote for that. +8
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.