定义可变长度结构并将其强制转换为

问题描述

| 在C语言中,您有时会看到类似以下内容内容
struct foobar
{
    int size;
    int data[1];
};
data
成员实际上并不只有一个元素;而是长度可变。 如果您在D中执行类似的操作,是否会让您例如阅读
myfoobar.data[4]
? 我知道D有可变长度的数组,例如
int[] myvarlenintarray;
,但是,如果您要尝试与已经在内存中提供一种数据结构的某些代码进行接口,而这种代码可能要复杂得多,该怎么办?假设它在
int[3000] buffer;
的第一部分中。是否有一种简单的方法可以将其转换为可用的结构而无需在内存中移动它?如果不是,是否有一种简单的方法可以将数据放入相似的结构中,而无需手动解析出该结构的每个成员? 编辑: 我想我需要举一个实际的例子,以便您了解我的来历。
import std.c.windows.windows;
import std.utf;
import std.stdio;

public struct REPARSE_DATA_BUFFER
{
  ULONG  ReparseTag;
  USHORT ReparseDataLength;
  USHORT Reserved;
  union
  {
    struct SymbolicLinkReparseBuffer
    {
      USHORT SubstituteNameOffset;
      USHORT SubstituteNameLength;
      USHORT PrintNameOffset;
      USHORT PrintNameLength;
      ULONG Flags;
      WCHAR[1] PathBuffer;
    }
    SymbolicLinkReparseBuffer mySymbolicLinkReparseBuffer;
    struct MountPointReparseBuffer
    {
      USHORT SubstituteNameOffset;
      USHORT SubstituteNameLength;
      USHORT PrintNameOffset;
      USHORT PrintNameLength;
      WCHAR[1] PathBuffer;
    }
    MountPointReparseBuffer myMountPointReparseBuffer;
    struct GenericReparseBuffer
    {
        UCHAR[1] DataBuffer;
    }
    GenericReparseBuffer myGenericReparseBuffer;
  }
}
alias REPARSE_DATA_BUFFER* PREPARSE_DATA_BUFFER;
enum MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16*1024;

// Values for \'ReparseTag\' member of REPARSE_DATA_BUFFER:
enum : DWORD {
    IO_REPARSE_TAG_SYMLINK = 0xA000000C,IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003 // which also defines a Junction Point
}
enum DWORD FSCTL_GET_REPARSE_POINT = 0x000900a8;
enum FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;

public extern(Windows) BOOL function(HANDLE,DWORD,LPVOID,OVERLAPPED*) DeviceIoControl;

void main()
{
    DeviceIoControl = cast(BOOL function(HANDLE,OVERLAPPED*))GetProcAddress(LoadLibraryA(\"kernel32.dll\"),\"DeviceIoControl\");
    auto RPHandle = CreateFileW((r\"J:\\Documents and Settings\").toUTF16z(),FILE_SHARE_READ,null,OPEN_EXISTING,FILE_FLAG_OPEN_REPARSE_POINT + FILE_FLAG_BACKUP_SEMANTICS,null);
    if (RPHandle == INVALID_HANDLE_VALUE)
    {
        printf(\"CreateFileW Failed with error code %d.\",GetLastError());
        return;
    }
    BYTE[MAXIMUM_REPARSE_DATA_BUFFER_SIZE] reparsebuffer;
    uint reparsedatasize;
    auto getreparsepointresult = DeviceIoControl(RPHandle,FSCTL_GET_REPARSE_POINT,cast(void*) reparsebuffer.ptr,MAXIMUM_REPARSE_DATA_BUFFER_SIZE,&reparsedatasize,null);
    if (getreparsepointresult == 0)
    {
        printf(\"DeviceIoControl with FSCTL_GET_REPARSE_POINT Failed with error code %d.\",GetLastError());
        return;
    }
    // Now what?
    // If I do this:
    auto ReparseDataPtr = cast(REPARSE_DATA_BUFFER*) reparsebuffer.ptr;
    printf(\"%d == %d\\n\",reparsebuffer.ptr,ReparseDataPtr); // Alright,data hasn\'t been copied.
    // But what good is a pointer?  Can I use a pointer to a struct to access one of its members apart from dereferencing?
    printf(\"%d == %d\\n\",&reparsebuffer[0],&(*ReparseDataPtr)); // Here,I dereference ReparseDataPtr,but nothing moves.
    printf(\"%d == %d\\n\",&((*ReparseDataPtr).ReparseTag)); // Same here,so I can access members in a roundabout way.
    printf(\"%d == %d\\n\",&(ReparseDataPtr.ReparseTag)); // And thanks to Jim\'s comment,here\'s a less roundabout way.
    auto ReparseData = *ReparseDataPtr; // But if I assign a name to the dereferenced ReparseDataPtr,printf(\"%d != %d\\n\",&(ReparseData.ReparseTag)); // the data is copied to a new location,leaving most of PathBuffer behind.
    REPARSE_DATA_BUFFER ReparseDataFn() {return *ReparseDataPtr;} // Similarly,this way
    printf(\"%d != %d\\n\",&(ReparseDataFn().ReparseTag)); // copies stuff to a new location.

}
首先,我不明白为什么我不给
*ReparseDataPtr
一个名字的区别。 其次,没有办法拥有类型为REPARSE_DATA_BUFFER且其数据位于reparsebuffer.ptr的符号吗?     

解决方法

        您是否尝试过在D和C中做完全相同的事情?
struct foobar { int size; int data[1]; };
它可以工作...只需使用
data.ptr
而不是
data
即可访问元素,因为否则它将执行长度为1的边界检查。     ,        您可以通过辅助方法访问它:
struct foobar
{
 public:
  int[] Data() { return data.ptr[0..size]; }

 private:
  int size;
  int data[1];
}
您可能还想在使用
static assert
foobar
成员上放置一个静态
foreach
,以确保每个偏移量都小于
data
的偏移量。