#include<stdio.h>#include<stdint.h>// 定义 uintN_t 型别#include<inttypes.h>// 定义 PRIx macrosintmain(void){float f =3.1415926, f2;double d =3.14159265358979323, d2;uint32_t fi;uint64_t di; fi =pack754_32(f); f2 =unpack754_32(fi); di =pack754_64(d); d2 =unpack754_64(di);printf("float before : %.7f\n", f);printf("float encoded: 0x%08" PRIx32 "\n", fi);printf("float after : %.7f\n\n", f2);printf("double before : %.20lf\n", d);printf("double encoded: 0x%016" PRIx64 "\n", di);printf("double after : %.20lf\n", d2);return0;}
上面的代码会产生下列的输出:
float before : 3.1415925float encoded: 0x40490FDAfloat after : 3.1415925double before : 3.14159265358979311600double encoded: 0x400921FB54442D18double after : 3.14159265358979311600
你可能遭遇的另一个问题是你该如何封装 struct 呢?
对你来说没有问题的,编译器会自动将一个 struct 中的全部空间填入。[你不会病到听成 "不能这样做"丶"不能那样做"?抱歉!引述一个朋友的话:"当事情出错了,我都会责怪 Microsoft。"这次固然可能不是 Microsoft 的错,不过我朋友的陈述完全符合事实。]
回到这边,透过网路送出 struct 的最好方式是将每个栏位独立封装,并接着在它们抵达另一端时,将它们解封装到 struct。
你正在想,这样要做很多事情。是的,的确是。一件你能做的事情是写一个有用的函数来帮你封装数据,这很好玩!真的!
在 Kernighan 与 Pike 着作的 "The Practice of Programming"[31] 这本书,他们实作类似 printf() 的函数,名为 pack() 与 unpack(),可以完全做到这件事。我想要连结到这些函数,但是这些函数显然地无法从网路上取得。
[The Practice of Programming 是值得阅读的好书,Zeus saves a kitten every time I recommend it。]
此时,我正要舍弃指向我从未用过的 BSD 授权类型的参数语言 C API(BSD-licensed Typed Parameter Language C API)[32] 的指针,可是看起来整个很可敬。Python 与 Perl 程序设计师想要找出他们语言的 pack() 与 unpack() 函数,用来完成同样的事情。而 Java 有一个能用於同样用途的 big-ol' Serializable interface。
不过如果你想要用 C 写你自己的封装工具,K&P 的技巧是使用变动参数列(variable argument list),来让类似 printf() 的函数建立数据包。我自己所编造的版本 [33] 希望足以供你了解这样的东西是如何运作的。
[这段代码参考到上面的 pack754() 函数,packi*() 函数的运作方式类似 htons() 家族,除非它们是封装到一个 char 数组(array)而不是另一个整数。]
#include<ctype.h>#include<stdarg.h>#include<string.h>#include<stdint.h>#include<inttypes.h>// 供浮点数型别的变动比特// 随着架构而变动typedeffloatfloat32_t;typedefdoublefloat64_t;/*** packi16() -- store a 16-bit int into a char buffer (like htons())*/voidpacki16(unsignedchar*buf,unsignedint i){*buf++= i>>8; *buf++= i;}/*** packi32() -- store a 32-bit int into a char buffer (like htonl())*/voidpacki32(unsignedchar*buf,unsignedlong i){*buf++= i>>24; *buf++= i>>16;*buf++= i>>8; *buf++= i;}/*** unpacki16() -- unpack a 16-bit int from a char buffer (like ntohs())*/unsignedintunpacki16(unsignedchar*buf){return (buf[0]<<8) | buf[1];}/*** unpacki32() -- unpack a 32-bit int from a char buffer (like ntohl())*/unsignedlongunpacki32(unsignedchar*buf){return (buf[0]<<24) | (buf[1]<<16) | (buf[2]<<8) | buf[3];}/*** pack() -- store data dictated by the format string in the buffer**** h - 16-bit l - 32-bit** c - 8-bit char f - float, 32-bit** s - string (16-bit length is automatically prepended)*/int32_tpack(unsignedchar*buf,char*format, ...){ va_list ap;int16_t h;int32_t l;int8_t c;float32_t f;char*s;int32_t size =0, len;va_start(ap, format);for(; *format !='\0'; format++) {switch(*format) {case'h': // 16-bit size +=2; h = (int16_t)va_arg(ap,int); // promotedpacki16(buf, h); buf +=2;break;case'l': // 32-bit size +=4; l =va_arg(ap,int32_t);packi32(buf, l); buf +=4;break;case'c': // 8-bit size +=1; c = (int8_t)va_arg(ap,int); // promoted*buf++= (c>>0)&0xff;break;case'f': // float size +=4; f = (float32_t)va_arg(ap,double); // promoted l =pack754_32(f); // convert to IEEE 754packi32(buf, l); buf +=4;break;case's': // string s =va_arg(ap,char*); len =strlen(s); size += len +2;packi16(buf, len); buf +=2;memcpy(buf, s, len); buf += len;break; } }va_end(ap);return size;}/*** unpack() -- unpack data dictated by the format string into the buffer*/voidunpack(unsignedchar*buf,char*format, ...){ va_list ap;int16_t*h;int32_t*l;int32_t pf;int8_t*c;float32_t*f;char*s;int32_t len, count, maxstrlen=0;va_start(ap, format);for(; *format !='\0'; format++) {switch(*format) {case'h': // 16-bit h =va_arg(ap,int16_t*);*h =unpacki16(buf); buf +=2;break;case'l': // 32-bit l =va_arg(ap,int32_t*);*l =unpacki32(buf); buf +=4;break;case'c': // 8-bit c =va_arg(ap,int8_t*);*c =*buf++;break;case'f': // float f =va_arg(ap,float32_t*); pf =unpacki32(buf); buf +=4;*f =unpack754_32(pf);break;case's': // string s =va_arg(ap,char*); len =unpacki16(buf); buf +=2;if (maxstrlen >0&& len > maxstrlen) count = maxstrlen -1;else count = len;memcpy(s, buf, count); s[count] ='\0'; buf += len;break;default:if (isdigit(*format)) { // track max str len maxstrlen = maxstrlen *10+ (*format-'0'); } }if (!isdigit(*format)) maxstrlen =0; }va_end(ap);}