0

I am allocating memory for my float3x3 matrix as such:

typedef float float3x3[3][3];
float3x3 *g = malloc(sizeof g);
g = &(float3x3){
    { 1, 0, 0 },
    { 0, 1, 0 },
    { 0, 0, 1 }
};
printfloat3x3(*g);

The above compiles, however I am allocating space for *g then setting g's pointer to a static 3x3 matrix. Not exactly what I want to do if I want to free g.

How can I initialize g using a compound literal after allocation? I tried this but it wont compile:

typedef float float3x3[3][3];
float3x3 *g = malloc(sizeof g);
g = (float3x3){
    { 1, 0, 0 },
    { 0, 1, 0 },
    { 0, 0, 1 }
};
printfloat3x3(*g);
free(g);
cc -fsanitize=address -Wall -Wextra -pedantic -O2 -o test-gmath test-gmath.c gmath.c -lm
test-gmath.c: In function ‘GMATH_Tests’:
test-gmath.c:69:11: warning: assignment to ‘float (*)[3][3]’ from incompatible pointer type ‘float (*)[3]’ [-Wincompa
tible-pointer-types]
   69 |         g = (float3x3){
      |           ^
test-gmath.c:75:9: warning: ‘free’ called on unallocated object ‘({anonymous})’ [-Wfree-nonheap-object]
   75 |         free(g);
      |         ^~~~~~~
test-gmath.c:69:23: note: declared here
   69 |         g = (float3x3){
      |                       ^

(3 of 11): warning: assignment to ‘float (*)[3][3]’ from incompatible pointer type ‘float (*)[3]’ [-Wincompatible-poi
nter-types]
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 3
    "I am allocating space for `*g`". No, you request the size of the pointer `g` instead of the type pointed to: `float3x3 *g = malloc(sizeof g);` ==> `float3x3 *g = malloc(sizeof *g);` – Weather Vane Jan 20 '23 at 17:46
  • It's not an initialization, it's an assignment, but all you're doing is assigning to the pointer. If you want to assign to the elements of the array, you need to use a loop. – Tom Karzes Jan 20 '23 at 17:47
  • 2
    `g = malloc();` followed by `g = anything` is a memory leak. – KamilCuk Jan 20 '23 at 17:48
  • Your first example is imprecise. Up until C23, compound literals created inside a block scope have automatic storage duration, which means that you are **not** setting g to point to a static array and thus the pointer will be invalidated when the function returns. With C23, you can create `static` compound literals in a block scope, but you have to do so explicitly: `(static float3x3 { ... })`. – rici Jan 20 '23 at 18:18

3 Answers3

2

First, you need to allocate the size of the structure, not the pointer:

float3x3 *g = malloc(sizeof *g);

Then to initialize the contents, you can use memcpy()

memcpy(g, &(float3x3){
    { 1, 0, 0 },
    { 0, 1, 0 },
    { 0, 0, 1 }
}, sizeof *g);

Your code causes a memory leak because you're reassigning the pointer to point to the memory of the compound literal. That's also why free() fails, because it no longer points to the memory returned by malloc().

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
Barmar
  • 741,623
  • 53
  • 500
  • 612
2

Typedef arrays are confusing. It's not possible to assign an array. Yoyu can't do:

float a[3][3];
float b[3][3];
a = b;

The same way you can't:

typedef float float3x3[3][3];
float3x3 *a = ...
*a = anything

Do not use typedef arrays. Use a structure.

struct float3x3 {
     float v[3][3];
};
struct float3x3 *g = malloc(sizeof(*ga));
*g = (struct float3x3){
   .v = {
       { 1, 0, 0 },
       { 0, 1, 0 },
       { 0, 0, 1 },
   }
};
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • sizeof(*g) - a is undefined, however I agree with you if I put the array into a struct then I can assign the array member with a CL. Not sure why I cant do the same with typedef array unless its static. Memcpy is also a valid method that works with typedef arrays. – will rabbermann Jan 20 '23 at 18:13
0

In this code snippet

typedef float float3x3[3][3];
float3x3 *g = malloc(sizeof g);

The pointer g has the type float ( * )[3][3].

And sizeof( g ) is equivalent to sizeof( float ( * )[3][3] ). So you are allocating memory for a pointer not for an array of the type float3x3.

And this statement

g = &(float3x3){
    { 1, 0, 0 },
    { 0, 1, 0 },
    { 0, 0, 1 }
};

results in a memory leak because at first the pointer pointed to a dynamically allocated memory and then it was reassigned with the address of the compound literal. So the address of the allocated memory is lost.

Instead of using a pointer of the type float ( * )[3][3] you need to use a pointer of the type float ( * )[3].

#include <string.h>

//...

typedef float float3x3[3][3];

float ( * g )[3] = malloc( sizeof( float3x3 ) );
memcpy( g, (float3x3){ { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } }, sizeof( float3c3 ) ); 
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Yeah although -fsanitize=address didn't say anything about a memleak because at first it wouldnt compile. Would have, which is why you dont assign to a pointer until freeing what was allocated earlier. float ( * g )[3] = malloc( 3 * sizeof( *g ) ); would assign the same space as malloc( sizeof( float3x3 ) ); works too – will rabbermann Jan 20 '23 at 18:30
  • @ultimatr I have updated the line with the memory allocation.:) – Vlad from Moscow Jan 20 '23 at 18:43