开发者

Concise way to initialize a block of memory with magic numbers

A few examples of what I'm referring to:

typedef struct SOME_STRUCT {
  unsigned int x1;
  unsigned int x2;
  unsigned int x3;
  unsigned int x4;

  // What I expected would work, but doesn't; the 2nd parameter gets
  // turned into an 8-bit quantity at some point within memset
  SOME_STRUCT() { memset( this, 0xFEEDFACE, sizeof( *this ) ); }

  // Something that worked, but seems hokey/hackish
  SOME_STRUCT() {
    unsigned int *me = (unsigned int *)this;
    for( int ii = 0; ii < sizeof(*this)/sizeof(*me); ++ii ) {
      me[ii] = 0xFEEDFACE;
    }
  }

  // The far-more-verbose-but-C++-way-of-doing-it
  // This works, but doesn't lend itself very well
  // to bein开发者_运维知识库g a drop-in way to pull this off on
  // any struct.
  SOME_STRUCT() :  x1( 0xFEEDFACE )
                 , x2( 0XFEEDFACE )
                 , x3( 0XFEEDFACE )
                 , x4( 0XFEEDFACE ) {}

  // This would work, but I figured there would be a standard
  // function that would alleviate the need to do it myself
  SOME_STRUCT() { my_memset( this, 0xFEEDFACE, sizeof(*this) ); }
}

I can't use valgrind here, and my options are limited as far as various debugging libraries I have access to -- which is why I'm doing it myself for this one-off case.


Here’s a partial example of using std::generate() safely:

#include <algorithm>

struct Wizard {
    size_t i;
    static unsigned char magic[4];
    Wizard() : i(0) {}
    unsigned char operator()() {
        size_t j = i++;
        i %= sizeof(magic); // Not strictly necessary due to wrapping.
        return magic[j];
    }
};

unsigned char Wizard::magic[4] = {0xDE,0xAD,0xBE,0xEF};

std::generate(reinterpret_cast<unsigned char*>(this),
              reinterpret_cast<unsigned char*>(this) + sizeof(*this),
              Wizard());

(Of course, the endianness may or may not be right, depending on how you’re looking and what you’re expecting to see when you do!)


I would declare this constructor:

SOME_STRUCT( unsigned int magic) : x1 (magic), x2 (magic), x3 (magic), x4 (magic) {}

This is very similar to your third option, and seems to be the natural C++ way of doing it.


A point not made by others is this:

I think it is unsafe to do this for Non-POD types. Ironically, adding the initialization into a constructor makes it non-pod. Therefore I propose a freestanding function that checks for POD-ness statically (sample uses c++0x type_traits but you could use Boost as well)

#include <iostream>
#include <type_traits>

template <typename T>
    typename std::enable_if<std::is_pod<T>::value>::type* FeedFace(T& v)
{
    static const unsigned char MAGIC[] = { 0xFE, 0xED, 0xFA, 0xCE };
    unsigned char *me = reinterpret_cast<unsigned char *>(&v);
    for( size_t ii = 0; ii < sizeof(T)/sizeof(unsigned char); ++ii ) 
        me[ii] = MAGIC[ii % sizeof(MAGIC)/sizeof(unsigned char)];
}

struct Pod { char data[37]; };
struct NonPod : Pod { virtual ~NonPod() { } };

int main()
{
    Pod pod;
    FeedFace(pod);

    NonPod nonpod;
    // FeedFace(nonpod); // fails to compile (no matching function call)

    return 0;
}


I assume this allows for nasty hacky stuff, like this:

#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
int main(void)
{
    struct SOME_STRUCT {
        unsigned int x1;
        unsigned int x2;
        unsigned int x3;
        unsigned int x4;
    } foo;
    fill(reinterpret_cast<unsigned int *>(&foo),
         reinterpret_cast<unsigned int *>(&foo) + sizeof(foo) / sizeof(unsigned int),
         (unsigned int)0xDEADBEEF);
    cout << foo.x1 << endl;
    cout << foo.x2 << endl;
    cout << foo.x3 << endl;
    cout << foo.x4 << endl;
    return (0);
}

Basically abusing std::fill() with pointer casts.


You could reinterpret_cast this as a char* and then use std::generate with a predicate that rotates through the values you care about. If I get time later I'll try to sketch the code.

Also have you considered for example an LD_PRELOAD memory checking malloc library?


Here's another hacky method.

SOME_STRUCT() {
    x1 = 0xFEEDFACE;
    memmove(&(this->x2), this, sizeof(*this)-sizeof(x1));
}


Even if your memset() attempt did work, it makes an assumption about the structure packing and is therefore not guaranteed to be correct. There is no programmatic way to iterate through the members of a struct and assign them in C or C++. You will therefore need to be content with assigning the members individually. Having said that, if you feel that you are comfortable with the memory layout of the structure and don't need to worry about portable code, you can just as easily initialize it with a for loop.

unsigned int i, *ar = (unsigned int *)&my_struct;
for (i = 0; i < sizeof(my_struct) / sizeof(unsigned int); i++) {
  ar[i] = 0xdeadbeef;
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜