2015-08-13 21 views
4

另一个问题的答案是:Strict aliasing rule and 'char *' pointers表示使用char*检查T对象的二进制内容是可以的。但是使用T*覆盖char缓冲区并不好。如何在不破坏严格别名规则的情况下有效地从char缓冲区中有效地复制小数据?

现在我有一个函数,它将二进制数据带到char缓冲区。并在阅读时做这样的事情:

// unsigned char *pData used to walk through the buffer. 
uint32_t value = *(unit32_t*)pData; 
pData += 4; 

如果我通过这样做打破严格的别名,还有哪些更有效的方法可用?编译器在用少量字节调用时会优化memcpy调用吗?

+3

是的,只需使用'memcpy()',它是大多数现代(<10年)编译器的内在功能。 –

回答

2

如果我打破通过这样的严格别名...

是的,你做

什么其他的,更有效的方式提供?

如果缓冲区必须是char,你需要使用memcpyuint32_t访问前值。当然,如果你所有的值都是uint32_t s,你可以制作一个缓冲区为uint32_t s,并将它传递给函数,用char s填充它,因为严格的别名是单向禁止,即使用uint32_t*作为char*是允许。

当编译器使用少量字节进行调用时,编译器会优化memcpy调用吗?

许多CPU都有内置指令memcpy。现代编译器使用这些指令来提高效率。

1

编译器在用少量字节调用时会优化memcpy调用吗?

对于此示例的代码:

#define _CRT_SECURE_NO_WARNINGS // To allow usage of scanf in vc++2015 
#include <stdlib.h> 
#include <stdio.h> 
#include <string.h> 

int main() 
{ 
    // printf and scanf to prevent code elimination 
    char bytes[ 4 ]; 
    scanf("%s", bytes); 
    char buffer[ 4 ]; 
    memcpy(buffer, bytes, 4); 
    printf("%s", buffer); 

    return 0; 
} 

的Visual C++ 2015生成此组件的输出(发布版本,X64):

; memcpy was replaced by a simple register move 
mov eax, DWORD PTR bytes$[rsp] 
lea rdx, QWORD PTR buffer$[rsp]     ; setting arguments 
lea rcx, OFFSET FLAT:[email protected][email protected][email protected] ; for printf call 
; at this point copied array was actually stored in memory 
mov DWORD PTR buffer$[rsp], eax  
call printf 

所以,是现代编译器甚至不会调用该过程。

+0

在多大程度上可以确信'memcpy(&temp,p,sizeof * p); temp | = 0x80808080; memcpy(p,&temp,sizeof * p);''最​​终会像禁用严格别名一样高效并使用'*((uint32_t *)p)| = 0x80808080;' – supercat

+0

@supercat,我试过用msvc,它结束是一个单独的''或dword ptr [esp + 8],80808080h'指令,不需要memcpy调用,不需要单个移动。现代优化器做得很好。当然,与任何优化一样,没有强有力的保证,特定的编译器会对特定的代码片段进行特定的优化,但是memcpy函数非常基础,可以直接或间接地使用很多次,您可能会认为它是高度的针对所有经常使用的情况进行了优化...... –

+0

@supercat,当涉及到性能时,我不会禁用严格别名,因为它存在的原因是为了允许更多优化,通过这样做可能会让代码变得更慢。 –

相关问题