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.

Share Improve this question asked Nov 21, 2024 at 11:01 Aditya KurroduAditya Kurrodu 4613 silver badges10 bronze badges 3
  • 1 Undefined behavior (eg: because of a strict aliasing violation) means that the standard imposes no requirements on the behavior of the program. – Paul Hankin Commented Nov 21, 2024 at 11:29
  • 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 a struct cmplx { double components[2]; } with no padding, and the legality is the same as struct 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
  • 1 This question is similar to: How to assign real and imaginary parts of complex variables in C. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem. – Ian Abbott Commented Nov 21, 2024 at 12:04
Add a comment  | 

3 Answers 3

Reset to default 2

While 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