在 C 中直接操作字符串中的 long 值

direct manipulation of long value in string in C

我正在尝试寻找一种低延迟解决方案(最好是在一个或两个语句中),我可以在其中直接增加内存中的值。

假设这是我的字符串数据:

123ABCD456DEFABABCDCD.,.,?!!X

我想要做的是让程序读取第一个 ABCD 作为实际整数值,它应该等于:

 68 (D) + 67 (C) * 256 (in 3rd position) + 66 (B) * 65536 (in 2nd position) + 65 (A) * 65536* 256 (in 1st position)
 which equals a 4-byte value of: 1,094,861,636 or 41424344 hex.

然后我想将我的字符串设置为:

123ABCE456DEFABABCDCD.,.,?!!X

这意味着 4 字节的值需要是 1,094,861,637 或 41424345 十六进制。

这是我尝试使用的代码:

char* pointer=data; //data pointer has been set to valid memory space containing the data
long *offset=(long*)pointer+3;
*offset++;

我知道这段代码会有点成功:

    char* pointer=data; //data pointer has been set to valid memory space containing the data
char *offset=pointer+3;
long number=(long)*offset;
number++;
sprintf(offset,"%d",number);

但是我想在做递增的时候操作多于一个字节的内存

在不使用可能产生高延迟的函数的情况下,最简单的方法是什么?

只需对您的代码稍作调整:

char* pointer=data; //data pointer has been set to valid memory space containing the data
long *offset=(long*)pointer+3;
(*offset)++;

区别在于(*offset)++中的括号。如果没有括号,您将递增指针。使用括号,您正在递增指针指向的值。

现在,如果您 运行 在 big-endian 体系结构上,并且 long 是 32 位类型,这可能只会执行您期望的操作。如果没有,那么您将不得不做一些稍微不同的事情。

如果架构需要对齐,那将不起作用。 即使不是,这也取决于字节顺序。它只适用于大端,现在越来越少了。 x86/64 并且大多数 ARM 实际上是交换字节的小端。

所以,最好使用合规的方式:

uint8_t *data;
uint32_t val = ((uint32_t)data[0] << 24) | ((uint32_t)data[1] << 16) | ...
val++;
data[3] = (uint8_t)val; val >>= 8;
data[2] = (uint8_t)val; val >>= 8;
data[1] = (uint8_t)val; val >>= 8;
data[0] = (uint8_t)val; val >>= 8;

注意使用 uint8_t 和 uint32_t,但不是 char 和 long:不能保证两者都恰好有 8 位和 32 位(char 在当前 CPU 上很有可能,但 long 可以有很很好 64 位。请注意,我们需要无符号类型。long 是有符号的,char 可以是或。 类型转换至少不会在 ARM 和 Intel 上花费任何代码(但不确定其他 32 位 CPU)。在 8 位上,以下会更快。

代码可能会被编译器优化为一个简单的 load/store,但您真的不应该让您的代码依赖于此。

8 种苦味酒的替代品是:

if ( !++data[3] )
    if ( !++data[2] )
        if ( ...
            ;

这在 8 位架构上比首先加载所有 32 位架构更快。实际上,即使在汇编程序中,你也无法在这些方面做得更快(除非你的编译器搞砸了)。由于环绕(未定义签名),它也只能保证在无符号上工作。您还可以使用预递减,从 `data += 4 开始,这可能会在某些架构上节省代码。

然而,在 >=32 位架构上,我强烈建议使用第一个变体。