基本内存布局
struct Point3D {
int32_t x;
int32_t y;
int32_t z;
};
int main() {
std::cout << sizeof(Point3D) << ' ';
std::cout << offsetof(Point3D::x) << ' '
<< offsetof(Point3D::y) << ' '
<< offsetof(Point3D::z) << std::endl;
}
在典型的 64-bit 机器上,输出为:
12 0 4 8
struct PaddingInBetween {
int32_t x;
int64_t y;
};
16 0 8
struct PaddingAtTheEnd {
int64_t y;
int32_t x;
};
16 0 8
柔性数组
柔性数组实际上并不在 C++ 标准中,但是主流的 C++ 编译器都提供了支持。
struct Packet {
uint16_t src_port;
uint16_t dst_port;
uint32_t len : 4;
uint8_t ext[0];
static std::unique_ptr<Packet> make(size_t len) {
assert(len % 4 == 0 && len <= 60);
void *mem = ::operator new(len);
auto pkt = new (mem) Packet{};
pkt.len = static_cast<uint32_t>(len / 4);
return std::unique_ptr<Packet>(pkt);
}
};
更进一步
柔性数组实际上意味着我们可以一定程度上操控内存布局:
假设有这样一个类:
template <typename T>
class ArrayRef {
public:
using value_type = T;
using pointer_type = T*;
using size_type = std::size_t;
ArrayRef(pointer_type arr, size_type len): data_{arr}, n_{len} {}
// Other ctors and dtor are defaulted.
private:
pointer_type data_;
std::size_t n_;
};
我们甚至可以这样:
class Dangerous {
public:
static std::unique_ptr<Dangerous> make(std::size_t n) {
void *mem = ::operator new(sizeof(Dangerous) + n * sizeof(uint32_t));
auto ans = new (mem) Dangerous{};
ans->xs_ = ArrayRef<uint32_t>{
reinterpret_cast<uint32_t*>(
reinterpret_cast<uintptr_t>(mem) + sizeof(Dangerous)),
n
};
return ans;
}
private:
ArrayRef<uint32_t> xs_;
int y_;
};
在 C 语言中实现单继承
typedef struct {
double (*area)(void *self);
} shape_vtbl_t;
extern shape_vtbl_t rectangle_vtbl;
extern shape_vtbl_t circle_vtbl;
shape_vtbl_t rectangle_vtbl = {
.area = ...
};
typedef struct {
shape_vtbl_t *vtbl;
} shape_t;
typedef struct {
shape_t super;
double height;
double width;
} rectangle_t;
rectangle_t *make_rectangle(double h, double w);
typedef struct {
shape_t super;
double radius;
} circle_t;
#define STATIC_CAST(typ, obj) ((typ)(obj))
void example() {
rectangle_t *rect = make_rectangle(2, 3);
shape_t *a_shape = STATIC_CAST(shape_t, rect);
a_shape->vtbl->area(a_shape);
}