我正在尝试实施memset()
。我的代码很实用,但我想知道我对演员的使用是好还是坏。
void* __memset(void *b, int c, size_t len){
while (len--)
*((unsigned char*)(b++)) = (unsigned char)c;
return (b);
}
我的代码更长,但我决定投射void*
以缩短代码。这没关系,还是代码会中断。
答案 0 :(得分:2)
警告(这实际上非常有趣):
重新标记标准名称是一个坏主意,因为它会调用未定义的行为'。这不仅仅是一些毛茸茸的概念。它的非常糟糕因为编译器会假设您不会调用未定义的行为而做出决定。
考虑这个c文件:
#include <stdlib.h>
void* memset(void *b, int c, size_t len){
unsigned char* p = (unsigned char*)b;
while (len--) {
*p++ = c;
}
return b;
}
现在使用gcc5.3 -O3
编译:
产生这个汇编代码:
memset:
testq %rdx, %rdx
je .L6
subq $8, %rsp
movzbl %sil, %esi
call memset
addq $8, %rsp
ret
.L6:
movq %rdi, %rax
ret
结果:BOOM!
答案 1 :(得分:1)
鉴于b
的类型为(void*), then
,所以b ++`甚至不是有效的C或C ++代码。因此,这是一个百分之百的坏事,因为代码不会在符合标准的编译器上编译。
请注意,例如gcc具有非标准扩展,允许对void指针进行算术运算。您可以使用正确的编译器选项将gcc转换为符合标准的编译器:
gcc -std=c11 -pedantic-errors
。
C中memset的典型实现看起来像这样:
void* memset(void* s, int c, size_t n)
{
uint8_t* ptr = (uint8_t*) s;
while(n != 0)
{
*ptr = (uint8_t)c;
ptr++;
n--;
}
return s;
}
不,不要试图让它成为一个肮脏的单行内乱。写可读代码。无论如何,生成的二进制文件都是相同的。
(此代码假定任何理智的编译器都将uint8_t
视为字符类型,用于指针别名。)
答案 2 :(得分:0)
如何减少代码并获得泛型函数的好处?
#include <stdio.h>
#include <string.h>
void *g_memset(void *dst, size_t dstSize, void *val, size_t valSize);
int main(void)
{
int val = 4;
int dst[5];
size_t valSize = sizeof(val);
size_t dstSize = sizeof(dst) / sizeof(dst[0]);
g_memset(dst, dstSize, &val, sizeof(val));
for (size_t n = 0; n < dstSize; n++) {
printf("%d ", dst[n]);
}
putchar('\n');
return 0;
}
void *g_memset(void *dst, size_t dstSize, void *val, size_t valSize)
{
char *ptr = (char *)dst;
while (dstSize-- > 0) {
memcpy(ptr, val, valSize);
ptr += valSize;
}
return dst;
}