定义可变长度结构并向其投射

时间:2011-06-10 01:56:20

标签: d

在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, DWORD, LPVOID, DWORD, LPVOID, OVERLAPPED*) DeviceIoControl;

void main()
{
    DeviceIoControl = cast(BOOL function(HANDLE, DWORD, LPVOID, DWORD, LPVOID, DWORD, LPVOID, OVERLAPPED*))GetProcAddress(LoadLibraryA("kernel32.dll"), "DeviceIoControl");
    auto RPHandle = CreateFileW((r"J:\Documents and Settings").toUTF16z(), 0, 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, null, 0, 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", &reparsebuffer[0], &((*ReparseDataPtr).ReparseTag)); // Same here, so I can access members in a roundabout way.
    printf("%d == %d\n", &reparsebuffer[0], &(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", &reparsebuffer[0], &(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", &reparsebuffer[0], &(ReparseDataFn().ReparseTag)); // copies stuff to a new location.

}

首先,我不明白为什么我不给*ReparseDataPtr一个名字的情况有所不同。

其次,有没有办法让一个符号的类型为REPARSE_DATA_BUFFER且其数据位于reparsebuffer.ptr?

2 个答案:

答案 0 :(得分:3)

您是否尝试在D中完成与在C中完全相同的操作?

struct foobar { int size; int data[1]; };

它有效......只需使用data.ptr而不是data来访问元素,否则它将执行长度为1的边界检查。

答案 1 :(得分:1)

您可以通过辅助方法访问它:

struct foobar
{
 public:
  int[] Data() { return data.ptr[0..size]; }

 private:
  int size;
  int data[1];
}

您可能还希望在使用foreach的{​​{1}}成员上放置一个静态foobar,以确保每个成员的偏移量小于{{1}的偏移量}}