2

I know the best solution would be to just use standard C or even better C++, but unfortunately I can't cause I have to use a function which I can't change and it requires the data to be in this format. The struct tComplex_R AmpLinRx[1]; is using something called "struct hack" and I can't change this. Therefore I can't change the 2 structs and the format of the data, which have to be a pointer of tOutputVRx_R.

I have the following 2 structs:

typedef struct tComplex_R {
    float real;
    float imag;
}tComplex_R;

typedef struct tOutputVRx_R {
    float range;
    float vel;
    tComplex_R AmpLinRx[1];
} tOutputVRx_R;

Now I want to have the struct tOutputVRx_R with length 3 and in there the struct tComplex_R with length 32. Like output_vrx[3].AmplLinRx[32]

int n_antennas = 32;
int n_max_detections = 3;
size_t n_out_vrx = n_max_detections * (sizeof(tOutputVRx_R) + n_antennas * sizeof(struct tComplex_R);
tOutputVRx_R*  output_vrx = (tOutputVRx_R*)malloc(n_out_vrx);

output_vrx have to be a pointer, no pointer pointer (I found a solution for that but can't use it with the existing code) and I can't change the 2 structs.

Later I fill it with Data like this, where a is an array with numbers from 0-31:

for(int j = 0; j < n_antennas; j++){
    output_vrx_device_temp[0].AmpLinRx[j].real = a[j];
    output_vrx_device_temp[0].AmpLinRx[j].imag = b[j];
} 
output_vrx_device_temp[0].range = 8;
output_vrx_device_temp[0].vel = 5;

for(int j = 0; j < n_antennas; j++){
    output_vrx_device_temp[1].AmpLinRx[j].real = c[j];
    output_vrx_device_temp[1].AmpLinRx[j].imag = d[j];
} 

Now I acces the data with a simple printf:

for(int i = 0; i < 32; i++){
    printf("Real: %e, Imag: %e \n", output_vrx_device_temp[0].AmpLinRx[i].real, output_vrx_device_temp[0].AmpLinRx[i].imag);
}

But the Data is not the same as I specified in the variables a, b, c or d. The output looks like:

Real: 0.000000e+00, Imag: 0.000000e+00 
Real: 8.000000e+00, Imag: 5.000000e+00 
Real: 0.000000e+00, Imag: 0.000000e+00 
Real: 8.000000e+00, Imag: 5.000000e+00 
Real: 0.000000e+00, Imag: 0.000000e+00 
Real: 1.000000e+00, Imag: 1.000000e+00 
Real: 2.000000e+00, Imag: 2.000000e+00 
Real: 3.000000e+00, Imag: 3.000000e+00 
Real: 4.000000e+00, Imag: 4.000000e+00 
Real: 5.000000e+00, Imag: 5.000000e+00 
Real: 6.000000e+00, Imag: 6.000000e+00 

Anybody know how to solve this?

Dominik
  • 35
  • 5
  • "Now I want to have the struct tOutputVRx_R with length 3 and in there the struct tComplex_R with length 32. Like output_vrx[3].AmplLinRx[32]" That looks wrong. If an array has length 3 you can't access array[3] – Support Ukraine Sep 16 '22 at 11:59
  • 1
    OT: ".... or even better C++" well, that's very opinion based – Support Ukraine Sep 16 '22 at 12:00
  • I know that I can't access array[3]. I was to help to clarify how it should look. [3] is the length of the array following the standard declaration of an array. E.g. int ar[3]. – Dominik Sep 16 '22 at 12:01
  • The title says "flexible array" I assume you mean "flexible array member". But I don't see that in this question. I'm confused... – Support Ukraine Sep 16 '22 at 12:03
  • You can't use the `[]` operator to access your structs. As far as the compiler cares, that array member has one element. To actually know where the struct is, you have to manually keep track of the *actual* size and do pointer manipulation. However in doing this, you might not be respecting the expected alignment. – Thomas Jager Sep 16 '22 at 12:03
  • The declaration tComplex_R AmpLinRx[1]; is flexible array. See: https://stackoverflow.com/questions/16553542/c-struct-hack-at-work – Dominik Sep 16 '22 at 12:08
  • 2
    You can never make this work. `tOutputVRx_R.AmpLinRx` is an array with **one element**. You can't code as-if it has more elements. The start point for `output_vrx_device_temp[1]` will be wrong – Support Ukraine Sep 16 '22 at 12:08
  • 2
    @Dominik No. That is not a standard flexible array member. It's a hack with several limitations. – Support Ukraine Sep 16 '22 at 12:10
  • I never said its a standard flexible array member. I just said the struct hack is used to create a flexible array. – Dominik Sep 16 '22 at 13:58
  • What your *existing code documentation* says about arrays with elements of type `tOutputVRx_R`? Is there any function that accepts such an array as a parameter? Can you quote what its documentation says? – n. m. could be an AI Sep 17 '22 at 08:15

1 Answers1

1

Disclaimer: This is a hack, and is not guaranteed to work in all cases, however it should work in practice. Alignment also shouldn't be an issue in this particular case given that all members have the same alignment.

Indexing output_vrx_device_temp won't work as since pointer arithmetic will be done in increments of sizeof(tOutputVRx_R) instead of that plus the size of the 31 instances of tComplex_R. So you need to do the pointer arithmetic manually on a converted pointer.

We can first create a function that can index into the array:

// a: start of "array" with struct hack / flexible member
// i: index of array element
// n: the size of the flexible member
tOutputVRx_R *idx(tOutputVRx_R *a, int i, int n)
{
    // use an unsigned char * to perform single byte pointer arithmetic.
    unsigned char *p = (unsigned char *)a;
    // get the size of the flexible member, subtracting 1 since it's part of the parent struct
    int fa_size = sizeof(tComplex_R) * (n-1);
    // calculate the offset
    int offset = i * (sizeof(tOutputVRx_R) + fa_size);
    // offset the char array and cast back
    return (tOutputVRx_R *)(p + offset);
}

Then we can modify the original code to use this function instead of indexing output_vrx_device_temp:

for(int j = 0; j < n_antennas; j++){
    idx(output_vrx_device_temp,0,n_antennas)->AmpLinRx[j].real = a[j];
    idx(output_vrx_device_temp,0,n_antennas)->AmpLinRx[j].imag = b[j];
}
idx(output_vrx_device_temp,0,n_antennas)->range = 8;
idx(output_vrx_device_temp,0,n_antennas)->vel = 5;

for(int j = 0; j < n_antennas; j++){
    idx(output_vrx_device_temp,1,n_antennas)->AmpLinRx[j].real = c[j];
    idx(output_vrx_device_temp,1,n_antennas)->AmpLinRx[j].imag = d[j];
}

for(int i = 0; i < n_antennas; i++){
    printf("Real: %e, Imag: %e \n", idx(output_vrx_device_temp,0,n_antennas)->AmpLinRx[i].real, 
                                    idx(output_vrx_device_temp,0,n_antennas)->AmpLinRx[i].imag);
}
for(int i = 0; i < n_antennas; i++){
    printf("Real: %e, Imag: %e \n", idx(output_vrx_device_temp,1,n_antennas)->AmpLinRx[i].real, 
                                    idx(output_vrx_device_temp,1,n_antennas)->AmpLinRx[i].imag);
}

Note that we're still indexing past the end of an array of size 1 for the AmpLinRx member, although it probably shouldn't be an issue given that the memory it's extending into is dynamically allocated and not being accessed in any other way.

dbush
  • 205,898
  • 23
  • 218
  • 273