在 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 位架构上,我强烈建议使用第一个变体。
我正在尝试寻找一种低延迟解决方案(最好是在一个或两个语句中),我可以在其中直接增加内存中的值。
假设这是我的字符串数据:
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 位架构上,我强烈建议使用第一个变体。