这行C代码的意思是什么?

4
我正在将一些C代码转换为Delphi。请问有人可以解释一下这行代码的含义吗?
nResult = ( (pBuffer[ 0 ] << 8) & 0xFF00 )
 | ( pBuffer[ 1 ] & 0x00FF );

以下是上下文的其余代码:

这里是上下文的其余代码:

USHORT UTIL_htons( USHORT hostshort )
{
 PUCHAR pBuffer;
 USHORT nResult;

 nResult = 0;
 pBuffer = (PUCHAR )&hostshort;

 nResult = ( (pBuffer[ 0 ] << 8) & 0xFF00 )
  | ( pBuffer[ 1 ] & 0x00FF );

 return( nResult );
}

USHORT UTIL_ntohs( USHORT netshort )
{
 return( UTIL_htons( netshort ) );
}
ULONG UTIL_htonl( ULONG hostlong )
{
 PUCHAR pBuffer;
 ULONG nResult;
 UCHAR c, *pResult;

 pBuffer = (PUCHAR )&hostlong;

 if( !pBuffer )
 {
  return( 0L );
 }

 pResult = (UCHAR * )&nResult;

 c = ((UCHAR * )pBuffer)[ 0 ];
 ((UCHAR * )pResult)[ 0 ] = ((UCHAR * )pBuffer)[ 3 ];
 ((UCHAR * )pResult)[ 3 ] = c;

 c = ((UCHAR * )pBuffer)[ 1 ];
 ((UCHAR * )pResult)[ 1 ] = ((UCHAR * )pBuffer)[ 2 ];
 ((UCHAR * )pResult)[ 2 ] = c;

 return( nResult );
}
ULONG UTIL_ntohl( ULONG netlong )
{
 return( UTIL_htonl( netlong ) );
}

感谢您的提前帮助,Bojan。


3
你目前尝试过什么?你卡在哪里了?我们喜欢帮助那些自己先努力的人,而不是只是替他们完成工作的人。 - Oded
好的。重新表述问题,有人能解释一下这行代码的含义吗?nResult = ((pBuffer [0] << 8) & 0xFF00) | (pBuffer [1]&0x00FF); - bojan gavrilovic
1
我认为这行代码很容易理解。它从一个缓冲元素中获取8位,然后从另一个缓冲元素中获取8位,最后将它们连接在一起。 - Vovanium
1
只有当你知道如何阅读C语言时,它才是不言自明的,@Vovanium。这意味着要了解十六进制数字的格式,并知道几个运算符的含义。 - Rob Kennedy
4个回答

4

显然(全部使用大写字母的定义使其难以阅读),这些函数正在交换占用2或4个字节的值的内部字节顺序。例如:

UTIL_htons(0x1234); /* returns 0x3412 */
UTIL_htonl(0x12345678); /* returns 0x78563412 */

我不知道如何在Delphi中编写它们...

希望已经有人将它们编写好了,而且Delphi使用的库已经给它们指定了一些名称。请查看您的文档。


编辑

nResult = ( (pBuffer[ 0 ] << 8) & 0xFF00 ) | ( pBuffer[ 1 ] & 0x00FF );

在这行中

pBuffer[0] 是数组 pBuffer 的第一个元素。
pBuffer[0] << 8 将该值左移8位(0x12变成了0x1200)
(...) & 0xFF00 是多余的:它重置了最右边的8位

pBuffer[1] & 0x00FF 中,只保留最右边的8位(因此0x1234变成了0x0034)

另一个操作符 | 是按位或

( ... & 0xFF00) | ( ... & 0xFF00) 是第一部分的最左边的8位和第二部分的最右边的8位。


编辑:hto* / *toh 命名

C语言中的函数 htonlhtonsntohlntohs 用于在主机字节顺序和网络字节顺序之间转换值。

字节顺序不一定不同(网络字节顺序是大端序),因此函数的第一部分应该检查主机字节顺序是小端序还是大端序,然后再进行交换……或者在使用您发布的函数的程序中先进行检查。


感谢您的回答。在调试C代码和Delphi代码时,我得到了一些类似的结果:例如nIPOffset = UTIL_ntohs(64),使用VC调试器显示值为16384。在Delphi中使用ntohs(64)也得到了相同的结果。但是printf("nIPOffset %d\n", nIPOffset);却显示20。 - bojan gavrilovic
nIPOffset = UTIL_ntohs(ip->ip_off); //调试器显示16384,Delphi的结果也是如此 printf("nIPOffset %d\n", nIPOffset); //屏幕上的结果是20。这是我需要读取新数据的偏移量。数据长度约为40 - bojan gavrilovic
printf 中使用 "%d" 告诉 printf 从参数中使用一个 int(通常为4个字节)。但是 nIPOffset 似乎是一个 longlong long(8个字节)。如果我的假设是正确的,printf 将忽略该值的4个字节。 - pmg
要输出类型为long的值,请使用printf("%ld", value);;对于类型为long long的值,请使用printf("%lld") - pmg
请问你能解释一下吗?unsigned char ihl: 4, version: 4; - bojan gavrilovic
@bojan:最好再提一个问题。这是在struct定义中,对吧?它定义了结构成员ihlversion都使用4位且类型为unsigned char:所以它们的值从0到15(将它们设为unsigned char是C标准的扩展)。 - pmg

3
你不必逐一翻译,只需包含WinSock,它具备全部四个功能!

2

看起来只是字节交换,您可以非常简单地实现:

function Swap32(const Value: DWORD): DWORD; assembler;
asm
  bswap eax;
end;

function Swap16(const Value: WORD): WORD; assembler;
asm
  xchg al, ah;
end;

有一个标记为backward compatibility only的笨拙内置Swap()过程,用于16位字节交换(内联,可节省CALL/RET)。此外,什么是const限定符?您真的想要交换参数的地址吗? - Free Consulting
不,我不想交换地址,const 只是因为我们不需要复制。 - Remko

0

相当晦涩的位运算表达式

nResult = ( (pBuffer[ 0 ] << 8) & 0xFF00 )
 | ( pBuffer[ 1 ] & 0x00FF );

implies 什么意思:

var 
  pBuffer: PByteArray; 
  nResult: Word;

所以,Pascal的结构化清晰度会看起来像:

WordRec(nResult).Hi := pBuffer^[0];
WordRec(nResult).Lo := pBuffer^[1];

即使是隐式的字节交换在这里也非常明显


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接