9

I used malloc to define an index because each line had a different number of elements (simplified example below):

int** Index=malloc(2*sizeof(int*));
Index[0]=malloc(2*sizeof(int));
Index[1]=malloc(3*sizeof(int));

But I did not want to write each element one by one (as follows) because it takes a lot of space and time:

Index[0][0]=1000;
Index[0][1]=1300;
Index[1][0]=1500;
Index[1][1]=2000;
Index[1][2]=2900;

But rather wanted to use a way to do it in a single line of code as follows:

Index[0]={1000,1300};
Index[1]={1500,2000,2900};

As I've been taught to do on "regular" indexes. Only problem is that my compiler doesn't seem to like such notations with an index defined with the help of a malloc. Is there another way rather than to write dozens of lines of code?

I searched for a similar question on this website but was unable to find one.

4
  • 3
    There is no syntax in C that lets you assign multiple values to an array object. What is the larger goal? There are many ways to do something similar to what you want, but evaluating relative merits depends on the details of what you want to do. You could use memcpy and compound literals (@ikegami), you could write a macro to streamline this approach, you could use a struct with array members, you could write data in a file and read it into memory at runtime,.... Commented Sep 21 at 20:06
  • 1
    @Anonymus Anonyma, with C, initialization only happens when object is defined. After definition, int a; ... a = b;, the a = b; is an assignment, not an initialization, even if is the first assignment. Commented Sep 22 at 2:00
  • 2
    " because it takes a lot of space and time" You chose to make a jagged pointer-to-pointer array, those are flexible but at the expense of performance. Your way of initializating them has nothing to do with that however. Commented Sep 22 at 6:56
  • @AnonymusAnonyma Just out of curiosity, did any of the answers answer your question? You are allowed, and even encouraged, to ask for clarification if an answer doesn't fully answer your question in a way you understand or if you disagree with it. Commented Oct 10 at 16:47

4 Answers 4

10

You can't assign to an array with an initializer list but you can copy whole arrays if you place them in structs and since the sizes are hardcoded 2 and 3, you could put both arrays in a struct that you initialize with an initializer list and then use compound literal assignment to the dynamically allocted struct.

Example:

#include <stdlib.h>

struct Indices {
    int a[2];
    int b[3];
};

int main() {
    struct Indices* Index = malloc(sizeof *Index);

    *Index = (struct Indices){ // compound literal assignment
        {1000, 1300}, 
        {1500, 2000, 2900}
    };

    free(Index);
}

You can also use designated initializers if you want to make it even clearer:

    *Index = (struct Indices){
        .a = {1000, 1300}, 
        .b = {1500, 2000, 2900}
    };

If you for example still would like to access both your arrays via index, you could simply put the pointers to the first elements in a temporary array.

Iterating over this will probably (not measured) be more efficient than having the arrays potentially allocated in such a way that cache locality goes out the window.

Example:

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

#define SIZE(x) (sizeof(x) / sizeof *(x))

enum : size_t { Asize = 2, Bsize = 3 };
constexpr size_t Sizes[] = {Asize, Bsize};

struct Indices {
    int a[Asize];
    int b[Bsize];
};

int main() {
    struct Indices* Index = malloc(sizeof *Index);

    *Index = (struct Indices){  // compound literal assignment
        {1000, 1300},
        {1500, 2000, 2900},
    };

    int* arrs[] = {Index->a, Index->b};

    for (size_t i = 0; i != SIZE(arrs); ++i) {
        for (size_t j = 0, end = Sizes[i]; j != end; ++j) {
            printf("%zu,%zu = %d\t", i, j, arrs[i][j]);
//                                         ^^^^^^^^^^
        }
        putchar('\n');
    }

    free(Index);
}

Demo

Sign up to request clarification or add additional context in comments.

11 Comments

I'm not sure why dynamic allocation would be needed here unless there is a very large number of elements to store.
@adabsurdum I assume OP uses dynamic allocation because he/she creates and destroys these on the fly and possibly don't want them to get destroyed at the end of the scope in which they are created.
@adabsurdum In that case, those values could be replaced by the variables that carries them.
Probably the best and most maintainable solution And it preserves the ability to use sizeof individual array members to know the size. The struct can also be embedded in a union with a single int[] in case there is a need to access all data as one single array.
@JeremyP No, but that doesn't make it into a non-answer. If you ever need that type of indexing, it's just a matter of doing int *arrs[] = {Index->a, Index->b}; before looping and access arrs[0][x] to access Index->a or arrs[1][x] to access Index->b. It'll probably have better performance because of cache locality too.
If you add that to the answer, it will turn it from a non answer to a great answer and I'll remove my downvote.
|
9

If you want to initialize the content of a dynamically allocated memory block with the content of an array containing hardcoded values, then I suggest that you declare a static constexpr array containing these values, and then copy these values into the dynamically allocated array using memcpy.

Using static constexpr has the advantage that the array with the source data will be put into the read-only data section of the executable, so that it does not have to be created every time you need it.

If you are using a compiler that does not yet support C23, you can use static const instead of static constexpr.

When creating the arrays with the source data, you can use the desired array initialization syntax.

Here is an example:

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

void my_func()
{
    // declare the source arrays
    static constexpr int source_array_0[] = { 1000, 1300 };
    static constexpr int source_array_1[] = { 1500, 2000, 2900 };

    // calculate the number of elements in the source arrays
    static constexpr int source_array_0_num_elements = sizeof source_array_0 / sizeof *source_array_0;
    static constexpr int source_array_1_num_elements = sizeof source_array_1 / sizeof *source_array_1;

    // allocate memory for the array of pointers
    int **Index = malloc( 2 * sizeof *Index );
    if ( Index == NULL )
    {
        fprintf( stderr, "Memory allocation failure!\n" );
        exit( EXIT_FAILURE );
    }

    // allocate memory for the copies of the source arrays
    Index[0] = malloc( sizeof source_array_0 );
    Index[1] = malloc( sizeof source_array_1 );
    if ( Index[0] == NULL || Index[1] == NULL )
    {
        fprintf( stderr, "Memory allocation failure!\n" );
        exit( EXIT_FAILURE );
    }

    // initialize the content of the dynamically allocated arrays from the source arrays
    memcpy( Index[0], source_array_0, sizeof source_array_0 );
    memcpy( Index[1], source_array_1, sizeof source_array_1 );

    // print the content of the dynamically allocated arrays, in order to verify that they
    // were initialized properly
    for ( int i = 0; i < source_array_0_num_elements; i++ )
    {
        printf( "%d\n", Index[0][i] );
    }
    printf( "\n" );
    for ( int i = 0; i < source_array_1_num_elements; i++ )
    {
        printf( "%d\n", Index[1][i] );
    }

    // cleanup
    free( Index[1] );
    free( Index[0] );
    free( Index );
}

Calling the function my_func above will print the following output:

1000
1300

1500
2000
2900

Here is a demonstration.

As you can see, the dynamically allocated arrays were correctly initialized with the hardcoded data from the source arrays.

3 Comments

Nice. Yet a minor curious question, why lower case instead of sentence case for code comments as they are meant for humans?
When the comments consist of whole sentences, I usually do write the first letter of the sentences in capital-case. However, in this case, I don't consider the comments to be whole sentences, so I wrote them in lower-case.
They look like whole sentences to me.
3

Here is a simplified version of Costantino Grana's proposal:

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

#define malloc_array(...) memcpy(malloc(sizeof((int[]){__VA_ARGS__})), \
                                 (int[]){__VA_ARGS__}, \
                                 sizeof((int[]){__VA_ARGS__}))

int main(void) {
    int **index = malloc(2 * sizeof(*index));

    index[0] = malloc_array(1000, 1300);
    index[1] = malloc_array(1500, 2000, 2900);

    printf("%i %i\n", index[0][0], index[0][1]);
    printf("%i %i %i\n", index[1][0], index[1][1], index[1][2]);

    free(index[0]);
    free(index[1]);
    free(index);

    return 0;
}

Sub array sizes could be handled automatically with a sub_array structure:

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

typedef struct sub_array {
    size_t count;
    int *a;
} sub_array;

#define malloc_sub_array(...) \
     (sub_array){ \
         sizeof((int[]){__VA_ARGS__}) / sizeof(int), \
         memcpy(malloc(sizeof((int[]){__VA_ARGS__})), \
                       (int[]){__VA_ARGS__}, \
                       sizeof((int[]){__VA_ARGS__})) }

int main(void) {
    size_t index_len = 2;
    sub_array *index = calloc(index_len, sizeof(*index));

    index[0] = malloc_sub_array(1000, 1300);
    index[1] = malloc_sub_array(1500, 2000, 2900);
    // ...

    for (size_t i = 0; i < index_len; i++) {
        printf("{");
        for (size_t j = 0; j < index[i].count; j++) {
            printf(" %i", index[i].a[j]);
        }
        printf(" }\n");
    }
    for (size_t i = 0; i < index_len; i++) {
        free(index[i].a);
    }
    free(index);

    return 0;
}

Output:

{ 1000 1300 }
{ 1500 2000 2900 }

3 Comments

What happens when malloc() returns a null pointer? It's hard to add the necessary recovery path when it's embedded in another expression.
This kind of coding style should probably use xalloc(): a wrapper on malloc that aborts with an explicit message on failure. This answer merely illustrates how to abuse the C preprocessor :)
I love the malloc_array() macro. Honestly, much better than mine.
2

Ikegami's solution is a great idea. I just packed both allocation and initialization in a single macro.

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

#define MALLOC_SET(dest, ...) do { \
    (dest) = malloc( sizeof((int[]){__VA_ARGS__}) ); \
    memcpy( dest, (int[]){__VA_ARGS__}, sizeof((int[]){__VA_ARGS__}) ); \
} while(0)

int main()
{
    int** index = malloc(2 * sizeof(int*));
    
    MALLOC_SET(index[0], 1000,1300);
    MALLOC_SET(index[1], 1500,2000,2900);

    printf("%i %i\n", index[0][0], index[0][1]);
    printf("%i %i %i\n", index[1][0], index[1][1], index[1][2]);

    free(index[0]);
    free(index[1]);
    free(index);

    return 0;
}

My only problem is in how do you plan to store the number of elements per line.

1 Comment

A plain loop would be much more readable however.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.