代码之家  ›  专栏  ›  技术社区  ›  Dennis Miller

关于联合和堆分配内存的问题

  •  4
  • Dennis Miller  · 技术社区  · 14 年前

    问题: 1) 有没有一种方法可以静态地在页面边界上声明fieldB alligned。基本上和valloc做同样的事情,但是在堆栈上?

    2) 当在堆上分配字段B或任何字段时,是否可以进行并集?。不知道这是否合法。

    #include <iostream>
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    class Test
    {
       public:
          Test(void)
          {
             // field B must be alligned to page boundary
             // Is there a way to do this on the stack???
             this->field.fieldB = (unsigned char*) valloc(10);
          };
    
          //I know this is bad, this class is being treated like 
          //a global structure. Its self contained in another class.
          unsigned char* PointerToFieldA(void)
          {
             return &this->field.fieldA[0];
          }
    
          unsigned char* PointerToFieldB(void)
          {
             return this->field.fieldB;
          }
    
          unsigned char* PointerToFieldC(void)
          {
             return &this->field.fieldC[0];
          }
    
          unsigned char* PointerToAllFields(void)
          {
             return &this->allFields[0];
          }
    
       private:
          // Is this union possible with field B being 
          // allocated on the heap?
          union
          {
             struct
             {
                unsigned char  fieldA[10];
    
                //This field has to be alligned to page boundary
                //Is there way to be declared on the stack
                unsigned char* fieldB;
                unsigned char  fieldC[10];
             } field;
    
             unsigned char allFields[30];
          };
    };
    
    
    int main()
    {
       Test test;
    
       strncpy((char*) test.PointerToFieldA(), "0123456789", 10);
       strncpy((char*) test.PointerToFieldB(), "1234567890", 10);
       strncpy((char*) test.PointerToFieldC(), "2345678901", 10);
    
       char dummy[11];
       dummy[10] = '\0';
    
       strncpy(dummy, (char*) test.PointerToFieldA(), 10);
       printf("%s\n", dummy);
    
       strncpy(dummy, (char*) test.PointerToFieldB(), 10);
       printf("%s\n", dummy);
    
       strncpy(dummy, (char*) test.PointerToFieldC(), 10);
       printf("%s\n", dummy);
    
       char allFields[31];
       allFields[30] = '\0';
       strncpy(allFields, (char*) test.PointerToAllFields(), 30);
       printf("%s\n", allFields);
    
       return 0;
    }
    
    2 回复  |  直到 12 年前
        1
  •  2
  •   Mark Wilkins    14 年前

    我想你不能申报 fieldB 作为指针并获得所需的行为(假设我正确理解问题)。为了使联合在使用时有意义,您需要将它声明为联合中的数组。

    我有点好奇,是否有可能重载新操作符,使类强制特定成员位于页面边界上。我很快就拼凑了超负荷的操作符来做这件事。它会导致每次分配一个完整的额外页面。它找到该字段所在位置的偏移量,然后按该量调整地址。因为额外的内存是分配的(假设我的计算正确),所以它是安全的。不过,很难看。

    它将分配偏移量填充到类中的成员中,以便知道释放指针所需的“取消偏移”量。这真是可怕的代码。作为一个实验,它看起来还不错,但在生产代码中就不那么好了。

    #define PAGE_SIZE 0x1000
    
    class test
    {
    public:
       int allocoffset;
       void* operator new( size_t );
       void operator delete( void* );
        union
          {
             __declspec( align(4096)) struct
             {
                unsigned char  fieldA[10];
    
                //This field has to be alligned to page boundary
                //Is there way to be declared on the stack
                unsigned char  fieldB[10];
                unsigned char  fieldC[10];
             } field;
    
             unsigned char allFields[30];
          };
    };
    
    void* test::operator new(size_t size)
    {
       // Allocate an entire extra page so we can offset it by any amount
       // less than the page size to ensure alignment of fieldB
       unsigned char *p = (unsigned char*)malloc( sizeof( test ) + PAGE_SIZE );
       uintptr_t addr;
       uintptr_t diff;
    
       std::cout << "new " << (void*)p << std::endl;
    
       // now offset the returned memory by the amount needed to align
       // fieldB on a page boundary.
       addr = (uintptr_t)p + (uintptr_t)( offsetof( test, field.fieldB ));
    
       diff = PAGE_SIZE - ( addr & (PAGE_SIZE - 1 ));
    
       p += diff;
    
       ((test*)p)->allocoffset = diff;
    
       return p;
    }
    
    void test::operator delete( void *p )
    {
       // offset by appropriate amount that we allocated it by
       p = (void*)( (unsigned char*)p - ((test*)p)->allocoffset );
       std::cout << "delete " << p << std::endl;
       free(p);
    }
    
    int main()
    {
       test *t;
    
       t = new test;
    
       std::cout << "allocation offset " << t->allocoffset << std::endl;
       std::cout << "address of fieldB " << (void*)&t->field.fieldB << std::endl;
    
       delete t;
    }
    

    new 00353FA0
    allocation offset 86
    address of fieldB 00355000
    delete 00353FA0
    
        2
  •  1
  •   gbjbaanb    14 年前

    但是,有些编译器有pragma来对齐结构,MSVC有' __declspec align '其中可以指定数据成员的对齐方式,编译器将插入适当的字节数。

    可以在堆上分配1个成员的情况下进行联合-联合将像往常一样包含所有字段,但堆分配的字段将只是一个指针。