在C中的字符缓冲区中存储一个int,然后检索相同的int
我正在编写一个socket客户机-服务器应用程序,其中服务器需要向客户机发送一个大的缓冲区,并且所有缓冲区都应该单独处理,因此我想将缓冲区长度放入缓冲区中,以便客户机可以从缓冲区读取数据长度并进行相应的处理 要输入长度值,我需要将一个整数值除以一个字节,并将其存储在缓冲区中,以便通过套接字发送。我可以将整数分解为四个部分,但在合并时,我无法检索正确的值。为了演示我的问题,我编写了一个示例程序,其中我将int划分为四个char变量,然后将其合并为另一个整数。我们的目标是,加入后我应该得到同样的结果 这是我的小程序在C中的字符缓冲区中存储一个int,然后检索相同的int,c,bit-manipulation,bit-shift,C,Bit Manipulation,Bit Shift,我正在编写一个socket客户机-服务器应用程序,其中服务器需要向客户机发送一个大的缓冲区,并且所有缓冲区都应该单独处理,因此我想将缓冲区长度放入缓冲区中,以便客户机可以从缓冲区读取数据长度并进行相应的处理 要输入长度值,我需要将一个整数值除以一个字节,并将其存储在缓冲区中,以便通过套接字发送。我可以将整数分解为四个部分,但在合并时,我无法检索正确的值。为了演示我的问题,我编写了一个示例程序,其中我将int划分为四个char变量,然后将其合并为另一个整数。我们的目标是,加入后我应该得到同样的结果
#include <stdio.h>
int main ()
{
int inVal = 0, outVal =0;
char buf[5] = {0};
inVal = 67502978;
printf ("inVal: %d\n", inVal);
buf[0] = inVal & 0xff;
buf[1] = (inVal >> 8) & 0xff;
buf[2] = (inVal >> 16) & 0xff;
buf[3] = (inVal >> 24) & 0xff;
outVal = buf[3];
outVal = outVal << 8;
outVal |= buf[2];
outVal = outVal << 8;
outVal |= buf[1];
outVal = outVal << 8;
outVal |= buf[0];
printf ("outVal: %d\n",outVal);
return 0;
}
#包括
int main()
{
int inVal=0,outVal=0;
char buf[5]={0};
无效=67502978;
printf(“无效:%d\n”,无效);
buf[0]=无效&0xff;
buf[1]=(无效>>8)&0xff;
buf[2]=(无效>>16)&0xff;
buf[3]=(无效>>24)&0xff;
outVal=buf[3];
outVal=outVal对inVal
和outVal
使用unsigned char buf[5]={0};
和unsigned int
,它应该可以工作
使用有符号整数类型时,会出现两类问题:
首先,如果buf[3]
为负,则由于outVal=buf[3]
变量outVal
变为负;随后outVal
上的位移位运算符为未定义行为:
对于有符号和正a,a的值一个问题是对有符号的数字使用位运算符。这总是一个坏主意,几乎总是不正确。请注意,char
具有实现定义的有符号性,而int
总是有符号的
因此,您应该将int
替换为uint32\u t
,将char
替换为uint8\u t
。使用这种无符号类型,您可以消除在负数上使用位移位的可能性,这将是一个错误。同样,如果将数据移位到有符号数的符号位,您将得到错误
不用说,如果整数不是4字节大,则代码将不起作用。C++标准N3936引用了移位运算符:
E1的值由于架构之间的endian差异,最佳做法是将数值转换为网络顺序
,即大端。收到后,可以将其转换为本机主机顺序。我们可以通过使用htonl()
(主机到网络“long”=uint32\t)以可移植的方式进行此操作,并在收到时使用ntohl()
转换为主机订单。示例:
#include <stdio.h>
#include <arpa/inet.h>
int main(int argc, char **argv) {
uint32_t inval = 67502978, outval, backinval;
outval = htonl(inval);
printf("outval: %d\n", outval);
backinval = ntohl(outval);
printf("backinval: %d\n", backinval);
return 0;
}
虽然有符号值的位移位可能是个问题,但这里的情况并非如此(所有左侧值均为正值,所有结果均在32位无符号整数的范围内)
语义有些不直观的问题表达式是最后一个按位OR:
outVal |= buf[0];
buf[0]
是一个(在您和我的体系结构上)值为-126的有符号字符,原因很简单,因为67502978的最低有效字节中的最高有效位已设置。在C中,算术表达式中的所有操作数都要进行算术转换。具体来说,它们进行整数提升,即:“如果整数可以表示原始类型[…]的所有值,则该值将转换为整数”。相应地,有符号字符buf[0]
将转换为(有符号的)int
,保留其值-126。负有符号int设置了符号位。使用另一个有符号int对其进行排序也会设置结果的符号位,使该值为负。这正是我们所看到的
使字节无符号字符
s修复此问题,因为无符号字符转换为的临时整数的值是一个简单的8位值130。您的方法具有潜在的实现定义行为和未定义行为:
- 将值存储到类型
char
的数组中,超出类型char
的范围,具有实现定义的行为:buf[0]=inVal&0xff;
和接下来的3条语句(inVal&0xff
如果char
类型默认签名,则可能大于char\u MAX
)
- 左移负值调用未定义的行为:如果数组中前3个字节中的任何一个变为负值,作为将大于
CHAR_MAX
的值存储到数组中的实现定义结果,结果outVal
变为负值,则左移未定义
在您的特定示例中,您的体系结构对负值使用2的补码表示,并且类型char
是有符号的。存储在buf[0]
中的值是67502978&0xff=130
,成为-126
。最后一条语句outVal |=buf[0];
设置outVal
的第7位到第31位,结果是-126
您可以通过使用无符号字符
数组和无符号int
类型的值来避免这些问题:
#include <stdio.h>
int main(void) {
unsigned int inVal = 0, outVal = 0;
unsigned char buf[4] = { 0 };
inVal = 67502978;
printf("inVal: %u\n", inVal);
buf[0] = inVal & 0xff;
buf[1] = (inVal >> 8) & 0xff;
buf[2] = (inVal >> 16) & 0xff;
buf[3] = (inVal >> 24) & 0xff;
outVal = buf[3];
outVal <<= 8;
outVal |= buf[2];
outVal <<= 8;
outVal |= buf[1];
outVal <<= 8;
outVal |= buf[0];
printf("outVal: %u\n", outVal);
return 0;
}
#包括
内部主(空){
无符号int-inVal=0,outVal=0;
无符号字符buf[4]={0};
无效=67502978;
printf(“无效:%u\n”,无效);
buf[0]=无效&0xff;
buf[1]=(无效>>8)&0xff;
buf[2]=(无效>>16)&0xff;
buf[3]=(无效>>24)&0xff;
outVal=buf[3];
outVal这可能是一个糟糕的主意,但出于兴趣,我会将其张贴在这里-您可以使用:
您的体系结构是否可能具有64位int
s?您在何处看到溢出整数操作?所有移位都在(afaics none overflow)int
s上完成。这不是整数溢出,而是符号的直接操作
#include <stdio.h>
int main(void) {
unsigned int inVal = 0, outVal = 0;
unsigned char buf[4] = { 0 };
inVal = 67502978;
printf("inVal: %u\n", inVal);
buf[0] = inVal & 0xff;
buf[1] = (inVal >> 8) & 0xff;
buf[2] = (inVal >> 16) & 0xff;
buf[3] = (inVal >> 24) & 0xff;
outVal = buf[3];
outVal <<= 8;
outVal |= buf[2];
outVal <<= 8;
outVal |= buf[1];
outVal <<= 8;
outVal |= buf[0];
printf("outVal: %u\n", outVal);
return 0;
}
union my_data
{
uint32_t one_int;
struct
{
uint8_t byte3;
uint8_t byte2;
uint8_t byte1;
uint8_t byte0;
}bytes;
};
// Your original code modified to use union my_data
#include <stdio.h>
int main(void) {
union my_data data;
uint32_t inVal = 0, outVal = 0;
uint8_t buf[4] = {0};
inVal = 67502978;
printf("inVal: %u\n", inVal);
data.one_int = inVal;
// Populate bytes into buff
buf[3] = data.bytes.byte3;
buf[2] = data.bytes.byte2;
buf[1] = data.bytes.byte1;
buf[0] = data.bytes.byte0;
return 0;
}
union my_data
{
uint32_t one_int;
uint8_t bytes[4];
};