Yes, you are breaking the strict aliasing rules. Use a union - it may not maintain your original layout, however it reflects your intent much better and is generally cleaner:
#include <stdio.h>
union value
{
double d;
float f;
};
void dpromote (const int n, value* x)
{
for (int i=0; i < n; ++i)
x[i].d = x[i].f;
}
void test_dpromote()
{
value x[] = {{.f=1}, {.f=2}, {.f=3}, {.f=4}, {.f=5}, {.f=6}, {.f=7}};
const int n = sizeof(x) / sizeof(x[0]);
for (int i=0; i < n; ++i)
printf("float: %f\n", x[i].f);
dpromote(n, x);
for (int i=0; i < n; ++i)
printf("double: %f\n", x[i].d);
}
int main ()
{
test_dpromote();
return 0;
}
If you must maintain the original layout then you'll need to manage the block of memory manually to satisfy strict aliasing rules:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
double* dpromote (const int n, char* x)
{
for (int i=n; i-- > 0; )
{
float f;
memcpy(&f, &x[i*sizeof(float)], sizeof(f));
double d = f;
memcpy(&x[i*sizeof(double)], &d, sizeof(d));
}
return (double*)x;
}
void test_dpromote()
{
int const n = 7;
char* block = (char*)malloc(n*sizeof(double));
for (int i=0; i < n; ++i)
{
float const x = i+1;
memcpy(&block[i*sizeof(float)], &x, sizeof(x));
}
// It is now safe to access block through x
float* x = (float*)block;
for (int i=0; i < n; ++i)
printf("float: %f\n", x[i]);
double* y = dpromote(n, block);
for (int i=0; i < n; ++i)
printf("double: %f\n", y[i]);
// It is now safe to access block through y, however
// subsequent access through x will violate strict aliasing rules
}
int main ()
{
test_dpromote();
return 0;
}