首页 > 解决方案 > 为什么 char 缓冲区内容大小为 4 个字节?

问题描述

我调用从套接字接收数据的 recv() 并以十六进制打印缓冲区内容的结尾

char nbuff[BUFSZ];
while ((r_n=recv(sfd,rbuff,B_BUF,MSG_EOF))>-1)
{
    printf("r_n:%d eob_p:%x\n",r_n,rbuff[r_n-1]);
    if (r_n==0)
    {
        break;
    }
    memset(rbuff,0,B_BUF);
}

结果是

r_n:1674 eob_p:3c
r_n:1228 eob_p:76
r_n:2456 eob_p:ffffff81
r_n:1228 eob_p:4b
r_n:1228 eob_p:49
r_n:2456 eob_p:57
r_n:1417 eob_p:ffffff82

我对为什么结果是 4 个字节感到困惑。我创建另一个代码来打印从 buff 保存的文件

int main ()
{
    char buff[11686];
    memset(buff,0,11686);
    FILE *in =fopen("web/www.sse.com.cn.html","r");
    fread(buff,11686,1,in);
    for (int i = 0; i <  11686 ; i++)
    {
        printf("%x\n",buff[i]);
    }
}

结果是

....
buff[11684]:60
buff[11685]:ffffff82

为什么 char buff 的内容大小是 4 字节buff[11685]:ffffff82

标签: cbuffer

解决方案


诊断

在第二个示例中,buff是一个char缓冲区,plainchar是您机器上的有符号类型,并且您在 中存储负值buff,因此当它们在int对 的调用中转换printf()为时,它们是负整数(幅度很小) ,以十六进制打印。

ISO/IEC 9899:2018

实际上,这些链接指向 C11 的在线草案,而不是 C18,HTML 允许链接到标准中的相关段落。AFAIK,这些细节在 C90、C99、C11 和 C18 之间并没有改变。

标准说普通char类型等同于signed charor unsigned char

§6.2.5 类型¶15

charsigned char和三种类型unsigned char统称为字符类型。实现应定义为具有与orchar相同的范围、表示和行为。45)signed charunsigned char

45) CHAR_MIN,在 中定义<limits.h>,将具有值 0 或 之一SCHAR_MIN,这可用于区分这两个选项。无论做出何种选择,char它都是与其他两种不同的类型,并且与任何一种都不兼容。

§6.3.1.1 布尔值、字符和整数¶2,3

2 在可以使用 int 或 unsigned int 的表达式中,可以使用以下内容:

  • 具有整数类型(除intor之外unsigned int)的对象或表达式,其整数转换等级小于或等于intand的等级unsigned int
  • _Bool, int,signed int或类型的位域unsigned int

如果 anint可以表示原始类型的所有值(受宽度限制,对于位域),则该值将转换为int; 否则,将其转换为unsigned int. 这些被称为整数促销58)整数提升不会改变所有其他类型。

3 整数促销保留价值,包括符号。如前所述,“plain”char是否被视为已签名是实现定义的。

58)整数提升仅适用于:作为通常算术转换的一部分,适用于某些参数表达式,适用于一元+、、-~运算符的操作数,以及移位运算符的两个操作数,由它们各自的子类指定。

§6.5.2.6 函数调用¶6,7

6 如果表示被调用函数的表达式具有不包含原型的类型,则对每个参数执行整数提升,并将具有类型的参数float提升为double。这些被称为默认参数提升。如果参数的数量不等于参数的数量,则行为未定义。如果函数是使用包含原型的类型定义的,并且原型以省略号 (, ...) 或提升后的参数类型与参数类型不兼容,则行为未定义。如果函数定义的类型不包含原型,并且提升后的参数类型与提升后的参数类型不兼容,则行为未定义,但以下情况除外:

  • 一种提升类型是有符号整数类型,另一种提升类型是对应的无符号整数类型,并且值可以在两种类型中表示;
  • 这两种类型都是指向字符类型或 void 的限定或非限定版本的指针。

7 如果表示被调用函数的表达式具有包含原型的类型,则参数将隐式转换为相应参数的类型,就像通过赋值一样,将每个参数的类型作为其非限定版本声明的类型。函数原型声明器中的省略号会导致参数类型转换在最后一个声明的参数之后停止。默认参数提升是在尾随参数上执行的。

注释

请注意 §6.5.2.6 ¶7 的最后两句——当char值被“整数提升”提升时,它们被提升为 (signed) int,并且负值保持为负。由于 anint有 4 个字节,并且您可能使用的所有机器都使用补码算法,因此该值的最重要 3 个字节将是 0xFF。

处方

要始终为字符打印 2 位十六进制,请使用%.2X(或者%.2x,如果您愿意;您也可以使用%02Xor %02x)并将其中一个(unsigned char)rbuff[r_n-1]orrbuff[r_n-1] & 0xFF作为参数传递(使用第一个示例中的变量)。或者,使用第二个示例中的变量:

printf("%.2X\n", (unsigned char)buff[i]);
printf("%.2X\n", buff[i] & 0xFF);

推荐阅读