在C中一次在char数组中设置多个值(重新实现strcpy)



如何逐字复制信息,而不是像苹果(或任何其他enterprice的C库)那样逐字节复制信息?(他们的memcpy供参考,其中他们使用一个结构"单词"来复制一组大小的信息。他们的strcpy只调用memcpy)

当我做这样的事情时(mystrcpy逐字节复制):

char *src = "Hi";
char *dst = malloc(3); // or 99, just so the focus is method instead
mystrcpy(dst, src);    // of safety or this very specific case

dst将为:? ? ?->'H' ? ?->'H' 'i' ?->'H' 'i' 0
为了澄清这个问题,使dst一步从? ? ?直接到达'H' 'i' 0mystrcpy最简单的代码是什么
我之所以这么问,是因为我很难理解苹果的源代码,而谷歌搜索却一无所获。如果有人能简化并解释苹果的代码,我们将不胜感激。

更新以包括当前mystrcpy:

char    *mystrcpy(char *dst, const char *src)
{
char    *tmp;
tmp = dst;
while (*src)
*tmp++ = *src++;
*tmp = 0;
return (dst);
}

通过尽可能多地复制寄存器大小的块来实现strcpy()的完全优化的实现通常是复杂的且不可移植的。特别是,它们往往是用特定平台的汇编语言编写的。它们使用单程设计,而不使用strlen()

下面我展示了半可移植的C代码,它应该适用于所有的小端64位平台,如x64、ARM64、Power 8,包括那些需要所有负载和存储自然对齐的平台。基本策略是执行单字节移动,直到源指针对齐8字节。从那时起,源数据总是以对齐的8字节块加载,而目标数据则以所需的最小块数写入,前提是每个块都需要以自然对齐的方式写入(2n-字节对象在2n-字节边界上访问)。

注意,8字节块中的读取可能会读取超出源字符串的分配内存的字节。如果加载对齐,这是无害的,因为它们永远不会越过页面边界,因此永远不会接触属于不同进程的内存。因此,这在C的"好像"规则下是允许的,也就是说,没有观察到与抽象机器语义的偏离。然而,内存检查器工具通常会抱怨这一点,因为访问相对于分配的对象是越界的。

当加载源字符串的每个8字节块时,将执行快速检查以查看它是否包含指示字符串末尾的零字节。如果是这种情况,那么最后一个块将按字节写入,直到到达源字符串的末尾。空字节的快速检查使用了Alan Mycroft于1987年4月8日发布在新闻组comp.lang.c上的技术。他为检测32位字中的空字节定义了以下内容,该字可扩展到64位操作数。

#define has_nullbyte_(x) ((x - 0x01010101) & ~x & 0x80808080)

下面的代码是为了合理清晰而编写的,并没有完全优化。特别是,仍然存在用于按字节处理可以展开的结束情况的循环。在实际应用程序中,传递给strcpy()的字符串通常非常短,因此以尽可能高的速度处理最终情况通常是至关重要的。该代码使用了许多辅助函数,并依赖于编译器优化来内联这些函数,因此应该在高优化级别进行编译。

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#define M0 0x0101010101010101ULL // Mycroft's first mask (LSBs)
#define M1 0x8080808080808080ULL // Mycroft's second mask (MSBs)
#define BM 0x00000000000000ffULL // mask for byte
#define HM 0x000000000000ffffULL // mask for half-word
#define WM 0x00000000ffffffffULL // mask for word
#define HAS_NULLBYTE(x) (((x) - M0) & ~(x) & M1)
void store_byte (uintptr_t a, uint8_t b)
{
*((uint8_t *)(void *)a) = b;
}
void store_half (uintptr_t a, uint16_t b)
{
assert ((a & 1) == 0);
*((uint16_t *)(void *)a) = b;
}
void store_word (uintptr_t a, uint32_t b)
{
assert ((a & 3) == 0);
*((uint32_t *)(void *)a) = b;
}
void store_dword (uintptr_t a, uint64_t b)
{
assert ((a & 7) == 0);
*((uint64_t *)(void *)a) = b;
}
uint8_t load_byte (uintptr_t a)
{
return *((uint8_t *)(void *)a);
}
uint64_t load_dword (uintptr_t a)
{
assert ((a & 7) == 0);
return *((uint64_t *)(void *)a);
}
void store_last_dword (uintptr_t d, uint64_t t)
{
uint8_t b;
do {
b = t & 0xff;
store_byte (d, b);
d++;
t = t >> 8;
} while (b);
}
void store_8 (uintptr_t d, uintptr_t diff)
{   
uint64_t t, zero_byte;
do {
t = load_dword (d + diff);
zero_byte = HAS_NULLBYTE (t);
d += 8;
if (!zero_byte) {
store_dword (d - 8, t);
}
} while (!zero_byte);
store_last_dword (d - 8, t);
}
void store_44 (uintptr_t d, uintptr_t diff)
{
uint64_t t, zero_byte;
do {
t = load_dword (d + diff);
zero_byte = HAS_NULLBYTE (t);
d += 8;
if (!zero_byte) {
store_word (d - 8, (uint32_t)(t >>  0) & WM);
store_word (d - 4, (uint32_t)(t >> 32) & WM); 
}
} while (!zero_byte);
store_last_dword (d - 8, t);
}
void store_242 (uintptr_t d, uintptr_t diff)
{
uint64_t t, zero_byte;
do {
t = load_dword (d + diff);
zero_byte = HAS_NULLBYTE (t);
d += 8;
if (!zero_byte) {
store_half (d - 8, (uint16_t)((t >>  0) & HM));
store_word (d - 6, (uint32_t)((t >> 16) & WM));
store_half (d - 2, (uint16_t)((t >> 48) & HM));
}
} while (!zero_byte);
store_last_dword (d - 8, t);
}
void store_1421 (uintptr_t d, uintptr_t diff)
{
uint64_t t, zero_byte;
do {
t = load_dword (d + diff);
zero_byte = HAS_NULLBYTE (t);
d += 8;
if (!zero_byte) {
store_byte (d - 8, (uint8_t )((t >>  0) & BM));
store_word (d - 7, (uint32_t)((t >>  8) & WM));
store_half (d - 3, (uint16_t)((t >> 40) & HM));
store_byte (d - 1, (uint8_t )((t >> 56) & BM));
}
} while (!zero_byte);
store_last_dword (d - 8, t);
}
void store_1241 (uintptr_t d, uintptr_t diff)
{
uint64_t t, zero_byte;
do {
t = load_dword (d + diff);
zero_byte = HAS_NULLBYTE (t);
d += 8;
if (!zero_byte) {
store_byte (d - 8, (uint8_t )((t >>  0) & BM));
store_half (d - 7, (uint16_t)((t >>  8) & HM));
store_word (d - 5, (uint32_t)((t >> 24) & WM));
store_byte (d - 1, (uint8_t )((t >> 56) & BM));
}
} while (!zero_byte);
store_last_dword (d - 8, t);
}
char* my_strcpy (char *dst, const char* src)
{
uintptr_t s = (uintptr_t)(void *)src;
uintptr_t d = (uintptr_t)(void *)dst;
uintptr_t diff = s - d;
uint8_t b = 0xff;
// align source pointer to next 8-byte boundary
int unaligned_bytes = (s & 7) ? (8 - (s & 7)) : 0;
while (unaligned_bytes && (b != 0)) {
b = load_byte (d + diff);
store_byte (d, b);
d++;
unaligned_bytes--;
}
// source now 8-byte aligned, write destination according to its alignment
if (b) {
switch (d & 7) {
case 0: store_8    (d, diff);
break;
case 1: store_1241 (d, diff);
break;
case 2: store_242  (d, diff);
break;
case 3: store_1421 (d, diff);
break;
case 4: store_44   (d, diff);
break;
case 5: store_1241 (d, diff);
break;
case 6: store_242  (d, diff);
break;
case 7: store_1421 (d, diff);
break;
}
}
return dst;
}
int main (void)
{
const char a[] = "0123456789 the quick brown fox jumps over the lazy dog";
char* src =  malloc (sizeof(a));
int buffer_len = sizeof(a) + 16;
char* res =  malloc (buffer_len); 
char* ref =  malloc (buffer_len);
printf ("src=%p  res=%p  ref=%pn", a, res, ref);
for (int srcofs = 0; srcofs < 8; srcofs++) {
for (int dstofs = 0; dstofs < 8; dstofs++) {
for (size_t len = 0; len < sizeof(a); len++) {
memcpy (src, a, sizeof(a));
src[len] = 0;
memset (res, 0xff, buffer_len);
memset (ref, 0xff, buffer_len);
my_strcpy (res + dstofs, src + srcofs);
strcpy    (ref + dstofs, src + srcofs);
if (memcmp (res, ref, buffer_len) != 0) {
printf ("error @ srcofs=%d  dstofs=%d  len=%llun", 
dstofs, srcofs, len);
}
}
}
}
printf ("Test passedn");
return EXIT_SUCCESS;
}

我以前也遇到过类似的情况,需要用位翻转来复制大块数据(所以我不能只使用memcpy())。基本计划是尽可能多地复制八个字节的块,然后在最后清除任何奇数字节。但是,如果源或目标都不是八字节对齐的,我们必须小心。这是一个简化版本,可以改进对不一致数据的处理。

#include <stdio.h>
#include <stdint.h>
#include <string.h>
// Try to copy a string 8 bytes at a time
void myStrcpy(char *dest, const char *src) {
size_t n = 1 + strlen(src);
// Check we're aligned
if ((((uintptr_t) dest) % 8) || (((uintptr_t) src) % 8)) {
strcpy(dest, src);
return;
}
// Copy eight-byte chunks as far as possible
const uint64_t *s = (uint64_t *) src;
uint64_t *d = (uint64_t *) dest;
for(; n >= 8; n -= 8) {
fprintf(stderr, "Long copyn");
*d++ = *s++;
}
// Now mop up any remaining bytes
src = (const char *) s;
dest = (char *) d;
while(n-- > 0) {
*dest++ = *src++;
fprintf(stderr, "Short copyn");
}
}
int main() {
char s[] ="3r78cfjkcu8cdecowfcjefj0fj6d4j0e89j6rgffjk34kk4kik3f--f?";
char d[sizeof s];
myStrcpy(d, s);
puts(s);
puts(d);
}

约翰·

最新更新