使用函数是否有任何合法的方法来解释给定长度的char *
参数作为不同整数类型的指针,然后访问所述转换后的指针?似乎有很多非法(UB)方法可以做到这一点......
例如给出以下函数原型:
int32_t sum_32(char *a, int len);
我想知道是否有一种方法可以合法地编写与以下代码等效的内容:
int32_t sum_32(char *a, int len) {
assert(len % 4 == 0);
int32_t total = 0;
for (int i = 0; i < len / 4; i++) {
total += ((int32_t *)a)[i];
}
return total;
}
当然,这样做的一种方法就是将访问分解为字符大小的访问,转而重新组合成一个更大的值(有一些关于字节序的假设,这里假设为LE):
int32_t sum_32(char *a, int len) {
assert(len % 4 == 0);
int32_t total = 0;
for (int i = 0; i < len; i += 4) {
int32_t val = (int32_t)
(a[i+0] << 0) +
(a[i+1] << 8) +
(a[i+2] << 16) +
(a[i+3] << 24) ;
total += val;
}
return total;
}
...但是我在这里寻找一次访问基础数组int32_t
的解决方案。
如果答案是&#34;它不可能&#34;,如果我知道char *a
的来源是分配函数,答案是否会改变 - 或者更广泛地说,是我可以对a
施加任何其他限制,以便将其作为更大的类型进行访问是合法的吗?
答案 0 :(得分:2)
如果内存最后写为int32_t
或任何兼容类型,则有效类型变为int32_t
,您可以使用简单的强制转换来阅读它。否则,如果不打破别名规则就不可能。
答案 1 :(得分:2)
为避免严格的别名问题,total += ((int32_t *)a)[i];
可以替换为:
int32_t temp;
memcpy(&temp, a+i*4, sizeof temp);
total += temp;
编译器将优化其中不实际调用memcpy
库函数。当然,只有在想要预期的字节序含义时才使用它;否则使用位移版本。
(注意:正如问题中所写,由于char
被签名的可能性,位移版本是错误的 - 您需要将函数更改为unsigned char *
或使用等效函数投射)。
我使用compiler explorer并发现对于此代码,gcc将测试a
是否对齐,如果是,则使用XMM指令,否则使用旧指令。