12

I have project I'm porting from from 32 bit Windows to 64 bit, which includes code that can be simplified as follows:

void FuncA(double &x)
{
    x = 0;
}

void FuncB(double *x)
{
   *x = 0;
}

#pack(1)

struct 
{
   char c;
   double x;
} MyStruct;

#pack();

void MyFunc()
{
   MyStruct M;
   FuncA(M.x);  // This is OK
   FuncB(&M.x); // This generates a warning C4366
}

When compiling under VS2010 SP1 targeting 64 bit, calling FuncB with a member of a packed struct generates the following warning:

warning C4366: The result of the unary '&' operator may be unaligned

Whereas calling FuncA does not. I would have thought that both cases would have compiled down to pretty much the same code. Are references somehow safer from alignment problems than the equivalent pointer? Or is it that MSVC is simply not issuing a warning where it should? The project requires the struct packing is maintained, so my choices are to either change FuncB to

void FuncB(__unaligned double *x)
{
   *x = 0;
}

or to simply use FuncA in all such cases. The latter would be preferable as it is more portable, but I'm wondering is it going to work or is the lack of a warning in the reference case simply a shortcoming in the compiler?

Edit: The Microsoft help entry for this error is here. The __unaligned help suggests that failing to heed this warning will cause exceptions to be thrown on Itanium processors. Further trawling around MSDN suggests there may be issues surrounding unaligned references. While this may not cause problems for my current users, if the Itanium architecture becomes more widely used in future, I could be setting up a support nightmare. Plan is now to add specific wrappers for all the functions that use the packed structures, to avoid the pointers and the __unaligned keyword.

Andreas
  • 5,393
  • 9
  • 44
  • 53
SmacL
  • 22,555
  • 12
  • 95
  • 149
  • See also (for other compilers): [c++ - Compiler Warning when using Pointers to Packed Structure Members - Stack Overflow](https://stackoverflow.com/questions/28859127/compiler-warning-when-using-pointers-to-packed-structure-members) – user202729 Dec 25 '21 at 07:25

1 Answers1

9

A reference and a pointer is pretty much the same thing (under the hood, so to speak), so from a safety perspective, it's no different. It's probably more of an oversight and/or that the compiler is less concerned by references, as they can't be passed outside of a C++ environment (although I'm inclined to think that it's simply an oversight).

It may also be that the compiler is more concerned by pointers because it is more likely that pointers are used for performance, where you want to iterate over a range of double values (e.g. by allocating more space than the struct itself uses, and storing further double values after it) - you can't do that with a reference. Since unaligned access is at least slower than an aligned access, this can have consequences for performance, and in some systems, it will cause a OS trap, which will either "fix up" the unaligned access (at a pace of a couple of orders of magnitued slower than an ordinary aligned access), or the OS simply says "Your program caused an unaligned access, I'm killing it".

There are also issues with multithreading, as unaligned access may not be atomically updating the data. Of course, you should use std::atomic or similar for data shared between threads.

The x86 is perfectly able to read a double from an unaligned address. I think Itanium isn't but I'm suspecting you don't use that processor anyway, statistically speaking. Other, older architectures, such as Alpha may have problems reading unaligned memory.

unwind
  • 391,730
  • 64
  • 469
  • 606
Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • 3
    Unaligned access is slower than aligned access, and in multithreading environments it can have other unwanted effects (an aligned read/write in intel is atomic, but that it not the case for unaligned access: with two threads writing values 1 and 2^31 to the same variable and a thread reading the value, if the value is aligned it is guaranteed to read either 1 or 2^31; with unaligned access that is not the case). Other platforms (SPARC) cannot handle unaligned reads/writes and the processor will trap. – David Rodríguez - dribeas Jun 17 '13 at 12:00
  • I have updated the "here's why I think it matters more for pointers than references" to explain that unaligned access is slower. I'm not aware of any version of Windows for SPARC, so I don't think its behaviour matters, and 64-bit ARM is also not yet available for Windows. Which leaves X86 and Itanium... – Mats Petersson Jun 17 '13 at 12:04
  • It was just a comment regarding C++ compiler warnings and unaligned access, but if you want consider alpha instead of sparc. There have been multiple Windows OS running in alpha, not sure if the newer versions do. Regarding the edit... ouch. The answer was better before! – David Rodríguez - dribeas Jun 17 '13 at 12:51
  • I tend to agree that it is an oversight / compiler shortcoming, but without an Itanium windows box to test it on, it's a tough one to prove. The MSDN link suggests that unaligned references are a potential problem, so I'll run with a macro that expands to __unaligned for Itanium releases, but can also be easily removed where not required. – SmacL Jun 21 '13 at 14:26