Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the standard require that objects in automatic storage have the correct alignment for any type (e.g. as malloc does)?

I'm curious if allocating a buffer on the stack is required to have correct alignment for any type, similar to how malloc works, or if I would be forced to use something like std::aligned_storage.

Consider the following block of code:

typedef enum _KEY_VALUE_INFORMATION_CLASS {
    KeyValueBasicInformation            = 0,
    // Others
} KEY_VALUE_INFORMATION_CLASS;

typedef struct _KEY_VALUE_BASIC_INFORMATION {
    ULONG TitleIndex;
    ULONG Type;
    ULONG NameLength;
    WCHAR Name[1];
} KEY_VALUE_BASIC_INFORMATION, *PKEY_VALUE_BASIC_INFORMATION;

std::vector<std::wstring> RegistryKey::EnumerateValueNames() const
{
    std::vector<std::wstring> result;
    ULONG index = 0;
    const ULONG valueNameStructSize = 16384 * sizeof(wchar_t) +
        sizeof(KEY_VALUE_BASIC_INFORMATION);

    // Stack buffer here
    unsigned char buff[valueNameStructSize];
    // Casted here
    KEY_VALUE_BASIC_INFORMATION const* basicValueInformation =
        reinterpret_cast<KEY_VALUE_BASIC_INFORMATION const*>(buff);
    for(;;)
    {
        ULONG resultLength;
        NTSTATUS errorCheck = PNtEnumerateValueKeyFunc(
            hKey_,
            index++,
            KeyValueBasicInformation,
            buff,
            valueNameStructSize,
            &resultLength);
        if (NT_SUCCESS(errorCheck))
        {
            result.emplace_back(std::wstring(basicValueInformation->Name,
                basicValueInformation->NameLength / sizeof(wchar_t)));
        }
        else if (errorCheck == STATUS_NO_MORE_ENTRIES)
        {
            break;
        }
        else
        {
            Win32Exception::ThrowFromNtError(errorCheck);
        }
    }
    return result;
}

Note how the value buff is a character buffer put on the stack, sized to hold a given maximum amount of data. However, I'm concerned that the cast required to interpret the buffer as a string may cause an alignment fault if this code were to be ported to another (say ARM or IA64) platform.

EDIT: If anyone's curious, I redid this in terms of std::aligned_storage and std::alignment_of:

std::vector<std::wstring> RegistryKey::EnumerateValueNames() const
{
    std::vector<std::wstring> result;
    ULONG index = 0;
    const ULONG valueNameStructSize = 16384 * sizeof(wchar_t) +
        sizeof(KEY_VALUE_BASIC_INFORMATION);
    std::aligned_storage<valueNameStructSize,
        std::alignment_of<KEY_VALUE_BASIC_INFORMATION>::value>::type buff;
    auto basicValueInformation =
        reinterpret_cast<KEY_VALUE_BASIC_INFORMATION*>(&buff);
    for(;;)
    {
        ULONG resultLength;
        NTSTATUS errorCheck = PNtEnumerateValueKeyFunc(
            hKey_,
            index++,
            KeyValueBasicInformation,
            basicValueInformation,
            valueNameStructSize,
            &resultLength);
        if (NT_SUCCESS(errorCheck))
        {
            result.emplace_back(std::wstring(basicValueInformation->Name,
                basicValueInformation->NameLength / sizeof(wchar_t)));
        }
        else if (errorCheck == STATUS_NO_MORE_ENTRIES)
        {
            break;
        }
        else
        {
            Win32Exception::ThrowFromNtError(errorCheck);
        }
    }
    return std::move(result);
}
like image 595
Billy ONeal Avatar asked Mar 22 '12 22:03

Billy ONeal


1 Answers

The standard makes no requirements on the alignment of automatic variables (or variables with static storage for that matter), other than the compiler must make sure that accessing them works.

  • C++03 3.9/5 Types

Object types have alignment requirements (3.9.1, 3.9.2). The alignment of a complete object type is an implementation-defined integer value representing a number of bytes; an object is allocated at an address that meets the alignment requirements of its object type

Note: "object type" here means a type that's not a function, reference or void type, (i.e., it applies to unsigned char).

One way to get an aligned buffer might be to declare buff like so:

KEY_VALUE_BASIC_INFORMATION buff[valueNameStructSize/sizeof(KEY_VALUE_BASIC_INFORMATION) + 1];

And you'll be able to get rid of the reinterpret_cast<> to boot.

like image 117
Michael Burr Avatar answered Nov 13 '22 01:11

Michael Burr