问题
Please consider the following structs from the WinAPI:
typedef struct _PROCESS_MEMORY_COUNTERS {
DWORD cb;
DWORD PageFaultCount;
SIZE_T PeakWorkingSetSize;
SIZE_T WorkingSetSize;
SIZE_T QuotaPeakPagedPoolUsage;
SIZE_T QuotaPagedPoolUsage;
SIZE_T QuotaPeakNonPagedPoolUsage;
SIZE_T QuotaNonPagedPoolUsage;
SIZE_T PagefileUsage;
SIZE_T PeakPagefileUsage;
} PROCESS_MEMORY_COUNTERS;
typedef struct _PROCESS_MEMORY_COUNTERS_EX {
DWORD cb;
DWORD PageFaultCount;
SIZE_T PeakWorkingSetSize;
SIZE_T WorkingSetSize;
SIZE_T QuotaPeakPagedPoolUsage;
SIZE_T QuotaPagedPoolUsage;
SIZE_T QuotaPeakNonPagedPoolUsage;
SIZE_T QuotaNonPagedPoolUsage;
SIZE_T PagefileUsage;
SIZE_T PeakPagefileUsage;
SIZE_T PrivateUsage;
} PROCESS_MEMORY_COUNTERS_EX;
As you can see, PROCESS_MEMORY_COUNTERS_EX
"inherits from" PROCESS_MEMORY_COUNTERS
- the former starts with the exact definition of the latter, and adds an additional field.
Consider a function:
BOOL GetProcessMemoryInfo(
HANDLE Process,
PPROCESS_MEMORY_COUNTERS ppsmemCounters,
DWORD cb
);
As you can see, it takes a PPROCESS_MEMORY_COUNTERS
. However I would like to pass it a pointer to a PROCESS_MEMORY_COUNTERS_EX
.
According to the standard, correct me if I'm wrong, I should be able to cast a PROCESS_MEMORY_COUNTERS_EX*
to a PPROCESS_MEMORY_COUNTERS*
, since the former starts with the exact definition of the latter.
In C it would be done like so:
PROCESS_MEMORY_COUNTERS_EX pmc;
GetProcessMemoryInfo(process, (PROCESS_MEMORY_COUNTERS*) &pmc, sizeof(pmc));
What is the correct way to do this in C++ code, if any? Should I use reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc)
, static_cast<PROCESS_MEMORY_COUNTERS*>(&pmc)
, or something else?
回答1:
Documentation states that GetProcessMemoryInfo
accepts
A pointer to the PROCESS_MEMORY_COUNTERS or PROCESS_MEMORY_COUNTERS_EX structure that receives information about the memory usage of the process.
So, reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc)
would be the answer to your question.
But you must set cb
to the size of object being passed.
回答2:
As types are unrelated by inheritance rules, and none of them is void, you should use reinterpret_cast
.
As explained by @eerorika, it will indeed be a violation of the non aliasing rule.
It will work anyway, with common compilers (and specifically with MSVC) because the types are trivially copyable. This would be perfectly legal:
PROCESS_MEMORY_COUNTERS_EX a = ...; // initialized a object
PROCESS_MEMORY_COUNTERS b; // uninitialized b object;
memcpy(&b, &a, sizeof(b)); // copy the REPRESENTATION
The representation is just a series of bytes so it is legal to copy it. The beginning of the representation of a
is a valid representation for b
, so you get a valid b
object.
This is an intended usage for reinterpret_cast
. We know that this does happen when dealing with legacy code, of when mixing C and C++ functions. So reinterpret_cast
allow to use the representation of an object as if is was the representation of a different object. It cannot be anything other than UB on a standard point of view, because the representations of types are willingly not defined by the standard beyond some trivial cases.
But if you know that it will used on a specific architecture with a specific set of compilers, it will work.
回答3:
According to the standard, correct me if I'm wrong, I should be able to cast a B* to a A*
Cast, yes. This would be a reinterpret_cast
. But depending on how the pointer is going to be used, it might not be practical. In particular, you couldn't access B
through the pointer to A
.
Casting the pointer back is one thing that can be done, so this would be well-defined:
void f(A* a) {
B* b = reinterpret_cast<B*>(a);
// ...
}
extern b* b;
f(reinterpret_cast<A*>(b));
Another thing that can be done with the reinterpreted pointer is to reinterpret it again as pointer to char and for example memcpy
or memcmp
the memory.
If you do need to access the "common initial sequence", then you could change the function. Because accessing common initial sequence of inactive standard layout struct member is an allowed special case of unions, this would work:
union AB {
A a;
B b;
};
void f(AB& ab) {
// can access all members of ab.a
// regardless of which member is active
}
AB ab {
.b = {},
};
f(ab);
来源:https://stackoverflow.com/questions/65196623/what-is-the-correct-way-to-cast-a-struct-to-a-type-that-matches-its-first-set-of