admin管理员组文章数量:1122846
Suppose I have a following code
#include <complex.h>
#include <stdio.h>
int main()
{
double _Complex a = 1.0;
void * b = &a;
double * c = b;
c[0] = 3;
c[1] = 2;
printf("%f %f\n",creal(a), cimag(a));
return 0;
}
Does the C standard mandate that a
is changed in this case (Can compilers assume a
is unchanged in this case)? I see most compilers are fine but not sure if this is allowed by standard.
Suppose I have a following code
#include <complex.h>
#include <stdio.h>
int main()
{
double _Complex a = 1.0;
void * b = &a;
double * c = b;
c[0] = 3;
c[1] = 2;
printf("%f %f\n",creal(a), cimag(a));
return 0;
}
Does the C standard mandate that a
is changed in this case (Can compilers assume a
is unchanged in this case)? I see most compilers are fine but not sure if this is allowed by standard.
3 Answers
Reset to default 2While c[0] = 3;
and c[1] = 2;
nominally violate the strict aliasing rules in C 2024 6.5.1 because they access the double _Complex
object a
with an lvalue of type double
, 6.2.5 states:
… Each complex type has the same representation and alignment requirements as an array type containing exactly two elements of the corresponding real type; the first element is equal to the real part, and the second element to the imaginary part, of the complex number…
This phrasing about the “same representation” is used multiple times in the standard with the intent, according to notes, that it makes the two types interchangeable in some vague way that the standard does not define. This conflicting information between aliasing and interchangeability is a long-standing defect in the C standard, and one might interpret the interchangeability as overriding or coming before the aliasing rules.
However, it is safer to take the “same representation” requirement to mean you can rely on the memory layout of the double _Complex
type to have the same layout as two double
objects which can be aliased through character type:
memcpy(&c[0], & (double) {3}, sizeof c[0]);
memcpy(&c[1], & (double) {2}, sizeof c[1]);
or by using a union:
union { double _Complex a; double b[2]; } u = { 1.0 };
u.b[0] = 3;
u.b[1] = 2;
Does the C standard mandate that a is changed in this case (Can compilers assume a is unchanged in this case)? I see most compilers are fine but not sure if this is allowed by standard.
You invoke undefined behavior (UB), which means the program's behavior cannot be predicted from the perspective of the C standard.
Undefined behavior occurs when the C standard does not define what should happen in a particular situation. The standard does not prohibit such behavior; it merely states that the program's observable behavior is not specified and can vary depending on the compiler, platform, or runtime conditions.
Why is it UB: The C standard specifies that a double _Complex object is represented as two adjacent double values in memory:
- The first double stores the real part.
- The second double stores the imaginary part.
While this memory layout allows direct manipulation of the real and imaginary parts using pointers (like c[0]
and c[1]
), it still violates the strict aliasing rule.
By specifying the representation of complex numbers, the authors of the C Standard described the behavior of code which would e.g. use sscanf
to separately read both halves of one. The Standard, however, deliberately allows implementations to deviate from what would otherwise be defined behavior in ways that don't interfere with what programs neeed to do, so as to facilitate optimization. For example, given something like:
double d;
double test(double _Complex *p)
{
d = 1.0;
*p = 2.0;
return d;
}
the following function in another compiilation unit would have two possible correct outcomes:
double d2;
extern double d;
double test2(void)
{
if (&d+1 == &d2)
return test((double _Complex*)&d);
else
return 3.0;
}
If d2
happens to immediately follow d
, the correct behavior for test2
would be to set d
to 2.0, d2
to 0.0, and return 2.0; in all other cases, the correct behavior would be to return 3.0 with no side effects. I don't think the authors of the Standard intended to require that implementations make heroic efforts to recognize and accommodate such scenarios.
On the other hand, the authors of the Standard also recognized that the statement about how double _Complex
values are stored would be sufficient to tell any compiler writer who was making a good faith effort to process programs usefully how to handle something like:
double _Complex arr[10];
double _Complex test(int i, int j)
{
if (arr[i])
((double*)(arr+j))[1]=0;
return arr[i];
}
I suspect many if not all members of the Committee would have tought it obvious that quality implementations should accommodate the case where i
and j
are equal, without regard for whether the Standard would treat failure to do so as an excusable mistake. Code which needs to be compatible with compiler configurations that are more interested in exploiting allowable excuses than correctly processing useful programs would need to jump through hoops to accomplish tasks which were intended to be easy, but would only be required if people insist upon using such configurations.
本文标签: Strict aliasing between Complex numbers and real numbers in CStack Overflow
版权声明:本文标题:Strict aliasing between Complex numbers and real numbers in C - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736311569a1934786.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
double _Complex
is not an array type, but it has the same size and alignment as an array type, so it is a bit like astruct cmplx { double components[2]; }
with no padding, and the legality is the same asstruct cmplx a = {1.0};
void * b = &a;
double * c = b;
c[0] = 3;
c[1] = 2;
. I think it is OK. I get that you are really asking if it is OK to modify the real and imaginary components of a complex number individually. – Ian Abbott Commented Nov 21, 2024 at 12:03