我有以下三個structs。
struct A {
int a;
int bOffset;
int cOffset;
};
struct B {
long long b;
int other[];
};
struct C {
long long c;
int other[];
};
main函式如下:
int main(void) {
int otherSize = 0;
scanf("%d", &otherSize);
int aSize = sizeof(struct A);
int bSize = sizeof(struct B) sizeof(int) * otherSize;
int cSize = sizeof(struct C) sizeof(int) * otherSize;
int totalSize = aSize bSize cSize;
struct A *a = malloc(totalSize);
a->bOffset = aSize;
a->cOffset = aSize bSize;
struct B *b = (struct B*)((char*)a a->bOffset);
struct C *c = (struct C*)((char*)a a->cOffset);
......
}
的空間struct A和struct B一起struct C分配以顯示更好的快取行為。我的問題是,根據之前關于 SO 的帖子,演員表
struct B *b = (struct B*)((char*)a a->bOffset);
struct C *c = (struct C*)((char*)a a->cOffset);
是 C 中未定義的行為,因為struct B并且struct C具有比 更嚴格的對齊要求struct A。那么我該怎么做才能在 C 中明確定義演員表呢?
我現在能想到的是添加一個long long變數,struct A如下所示。
struct A {
int a;
int bOffset;
int cOffset;
long long unused;
};
另一個問題是如果我取消參考bor c,它也是一個 UB。有沒有辦法解決這個問題?
uj5u.com熱心網友回復:
那么我該怎么做才能在 C 中明確定義演員表呢?
要正確計算應該放置的位置,您應該將之前的尺寸填充到必要的對齊位置struct B。struct CC 提供了_Alignof運算子來提供一個型別的對齊要求。所以這段代碼將完成這項作業:
/* Calculate how many bytes are required to add to size s to make it be a
multiple of alignment a. If s is a multiple of a, this is zero.
Otherwise, we need to add a-r bytes, where r is the remainder of s divided
by a.
Omitting the parentheses used for macro parameters, the following code is
a - (s-1)%a - 1. To see it works, consider two cases:
s is a multiple of a. Then s-1 is a-1 modulo a, and the expression
evaluates to a - (a-1) - 1 = 0.
s has some non-zero remainder r modulo a. Then (s-1)%a evaluates to
r-1, and the expression evaluates to a - (r-1) - 1 = a-r.
*/
#define PadToAlignment(s, a) ((a) - ((s)-1) % (a) - 1)
…
// Add padding needed to align struct B and struct C correctly.
aSize = PadToAlignment(aSize, _Alignof (struct B));
bSize = PadToAlignment(aSize bSize, _Alignof (struct C));
筆記
您通常應該使用size_t而不是int用于尺寸。此外,當使用帶有sizeofand的型別時_Alignof,我不喜歡將它們寫成函式呼叫,sizeof(int)因為它們不是函式呼叫。相反,出于語法原因,它們是運算元為括號中的型別名的運算子,因此sizeof (int)有助于提醒讀者 C 代碼的含義。
這是包含這些的完整程式:
#include <stdio.h>
#include <stdlib.h>
/* Calculate how many bytes are required to add to size s to make it be a
multiple of alignment a. If s is a multiple of a, this is zero.
Otherwise, we need to add a-r bytes, where r is the remainder of s divided
by a.
Omitting the parentheses used for macro parameters, the following code is
a - (s-1)%a - 1. To see it works, consider two cases:
s is a multiple of a. Then s-1 is a-1 modulo a, and the expression
evaluates to a - (a-1) - 1 = 0.
s has some non-zero remainder r modulo a. Then (s-1)%a evaluates to
r-1, and the expression evaluates to a - (r-1) - 1 = a-r.
*/
#define PadToAlignment(s, a) ((a) - ((s)-1) % (a) - 1)
struct A {
int a;
int bOffset;
int cOffset;
};
struct B {
long long b;
int other[];
};
struct C {
long long c;
int other[];
};
int main(void)
{
int otherSize = 0;
if (1 != scanf("%d", &otherSize))
{
fprintf(stderr, "Error, scanf failed.\n");
exit(EXIT_FAILURE);
}
size_t aSize = sizeof (struct A);
size_t bSize = sizeof (struct B) sizeof (int) * otherSize;
size_t cSize = sizeof (struct C) sizeof (int) * otherSize;
// Add padding needed to align struct B and struct C correctly.
aSize = PadToAlignment(aSize, _Alignof (struct B));
bSize = PadToAlignment(aSize bSize, _Alignof (struct C));
size_t totalSize = aSize bSize cSize;
unsigned char *RawMemory = malloc(totalSize);
if (!RawMemory)
{
fprintf(stderr, "Error, unable to allocate memory.\n");
exit(EXIT_FAILURE);
}
struct A *a = (struct A *) RawMemory;
a->bOffset = aSize;
a->cOffset = aSize bSize;
struct B *b = (struct B *) (RawMemory a->bOffset);
struct C *c = (struct C *) (RawMemory a->cOffset);
printf("a is at %p.\n", (void *) a);
printf("b is at %p.\n", (void *) b);
printf("c is at %p.\n", (void *) c);
free(RawMemory);
}
另一個問題是如果我取消參考 b 或 c,它也是一個 UB。
Memory allocated by malloc has no effective type. It may be used as any type by storing data there through an lvalue of that type.
The C standard’s rules about effective types in dynamically allocated memory involving structures are incomplete; the natural language wording is insufficient to write a formal semantic description. Certainly it is clear that if a struct S has members a, b, and c and no others, and we do:
struct S *p = malloc(sizeof *p);
p->a = 3;
p->b = 4;
p->c = 5;
then, for aliasing considerations, there should be a struct S at the memory address p even though the memory has only been written in parts, never with a full struct S lvalue. But the standard’s rules about effective type do not make this clear; they are are simply inadequate.
Putting multiple structures in the memory further complicates this. However, the purpose of the standard’s aliasing rules is to specify when objects can or cannot be aliased (and hence what optimizations the compiler can do regarding these). For practical purposes, as long as you use these structures in normal ways (the memory you designate for struct A as a struct A, the memory you designate for struct B as a struct B, and the memory you designate for struct C as a struct C), then you are not aliasing the memory with other types, and compilers are not going to perform unexpected optimizations that break that. I expect it is safe to use the allocated memory in this way.
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/434171.html
上一篇:為什么我的A[]函式引數的大小是8個位元組而不是4個?
下一篇:訪問特定指標時出現分段錯誤
