2011-08-24 38 views
0

我对C写作相对比较陌生。我已经自学自己使用我在网上和印刷中找到的资源。这是我在C编程中第一个真正的项目。喜欢在职培训。在内存中依次写入不同数据类型的值?或者,具有多种数据类型的数组?

我正在使用德州仪器C6701数字信号处理器的C语言编写一些代码。具体来说,我正在编写一组通信函数来通过串口进行连接。

我所在的项目有一个现有的数据包协议,用于通过串行端口发送数据。这是通过将指针传递给要传输的数据及其以字节为单位的长度来工作的。我所要做的就是将待传输的字节写入内存中的“数组”中(发送器将该字节序列复制到缓冲区并发送)。

我的问题与如何最好地格式化要传输的数据有关,我必须发送的数据由几种不同的数据类型(无符号字符,无符号整数,浮点等)组成。我无法将所有内容扩展到float(或int),因为我的通信带宽有限,需要尽可能小的数据包。

我原想用数组对数据进行格式化,

unsigned char* dataTx[10]; 
dataTx[0]=char1; 
dataTx[1]=char2; 
etc... 

这只是不是所有的工作,我的数据是char,有些是unsigned int类型或unsigned short。

为了处理short和int我使用了位移(现在让我们忽略little-endian vs big-endian)。

unsigned char* dataTx[10]; 
dataTx[0]=short1>>8; 
dataTx[1]=short1; 
dataTx[2]=int1>>24; 
dataTx[3]=int1>>16; 
etc... 

但是,我相信另一个(和更好的?)的方法是使用指针和指针算术。

unsigned char* dataTx[10] 
*(dataTx+0) = int1; 
*(dataTx+4) = short1; 
*(dataTx+6) = char1; 
etc... 

我的问题最后)是,是该方法(比特移位或指针运算)是更可接受的方法?另外,运行速度更快吗? (我也有运行时间限制)。

我的要求:数据按顺序位于内存中,没有间隙,中断或填充。

我对结构知之甚少,不知道结构是否可以作为解决方案。具体来说,我不知道一个结构是否总是按顺序分配内存位置并且没有中断。我读了一些表明它们分配在8字节块中的内容,并可能引入填充字节。

现在我倾向于指针方法。感谢您阅读这篇长长的文章。

回答

0

通常,使用的比特移位的方法,因为许多芯片不允许你复制,例如,一个4字节整数为奇数字节地址(或者,更精确地,以一组的4个字节处开始一个奇数字节地址)。这被称为对齐。如果可移植性是一个问题,或者如果您的DSP不允许错位访问,则需要移位。如果您的DSP在访问不对齐时发生显着性能下降,您可能会担心。

然而,如图所示我不会写与排班时间做速记不同类型的代码。我期望使用函数(可能是内联)或宏来处理数据的序列化和反序列化。例如:

unsigned char dataTx[1024]; 
unsigned char *dst = dataTx; 

dst += st_int2(short1, dst); 
dst += st_int4(int1, dst); 
dst += st_char(str, len, dst); 
... 

在函数形式,这些功能可能是:

size_t st_int2(uint16_t value, unsigned char *dst) 
{ 
    *dst++ = (value >> 8) & 0xFF; 
    *dst = value & 0xFF; 
    return 2; 
} 

size_t st_int4(uint32_t value, unsigned char *dst) 
{ 
    *dst++ = (value >> 24) & 0xFF; 
    *dst++ = (value >> 16) & 0xFF; 
    *dst++ = (value >> 8) & 0xFF; 
    *dst = value & 0xFF; 
    return 4; 
} 

size_t st_char(unsigned char *str, size_t len, unsigned char *dst) 
{ 
    memmove(dst, str, len); 
    return len; 
} 

当然,这些功能使代码镗;另一方面,他们也减少了犯错的机会。您可以决定名称是否应该是st_uint2()而不是st_int2() - 实际上,您可以决定长度是以字节为单位(如此处)还是以位为单位(如参数类型中)。只要你一贯和无聊,你可以按照你的意愿去做。您也可以将这些功能组合成更大的封装整个数据结构的功能。

现代编译器可能不需要掩码操作(& 0xFF)。很久以前,我似乎记得,他们有必要避免在某些平台上与某些编译器偶尔出现问题(所以,我的代码可以追溯到20世纪80年代,包括这种掩蔽操作)。说平台可能已经平静下来了,所以我认为它们可能是纯粹的偏执狂。

请注意,这些函数以big-endian顺序传递数据。这些函数可以在big-endian和little-endian机器上“按原样”使用,并且这两种类型的数据都可以正确解释,因此您可以使用此代码在线路上进行多种硬件通话,并且会有没有沟通不畅。如果要传达浮点值,则必须更多关注线路上的表示。尽管如此,您也许应该旨在将数据以平台无关的格式传输,以便芯片类型之间的交互尽可能简单。 (这也是为什么我使用带有数字的字体大小;'int'和'long'在不同平台上可能意味着不同的事物,但4字节有符号整数仍然是4字节有符号整数,即使您是不幸的是 - 或者幸运的 - 足够拥有一个8字节整数的机器。)

+0

谢谢你的回应!我有一个问题,在编写长手和写子函数来处理它之间会有什么运行时差异吗?如果这是一个棘手的问题来回答我明白,我只是想知道是否有明显的差异。 –

+0

可能有函数调用/返回的代价;这不太可能是一个严重的问题。如果函数是内联的,那么你可能会离开而没有这个成本。您需要(a)测量它,并(b)评估维护代码的成本与运行代码的成本。如果最坏的情况出现,请考虑使用宏而不是函数;但那绝对是最后的选择。如果这是您应用程序的瓶颈,我会很惊讶。你应该证明它是放弃优秀软件工程原理之前的瓶颈。 –

0

您可能想要使用一个联合数组。

+0

根据我对如何分配内存的理解,如果我创建了一个联合数组,每个数组元素将是最长类型的大小工会。这是有问题的,因为适合1字节的值会占用4字节的内存,因为我也有int(4字节)。 纠正我,如果我错了 –

+0

你是对的。工会和结构由编译器对齐 - 这在某种程度上在我的答案中提到过(在某处) –

0

来处理你的问题,最简单和最传统的方法是设置你要发送的数据,然后将指针传递给你的数据上的传输程序。最常见的例子是POSIX send()程序:

ssize_t send(int socket, const void *buffer, size_t length, int flags); 

这对于你的情况,你可以简化为:

ssize_t send(const void *buffer, size_t length); 

然后使用类似:

send(&int1, sizeof int1); 
send(&short1, sizeof short1); 

将其发送出。你的情况的一个例子(但很幼稚)实现可能是:

ssize_t send(const void *buffer, size_t length) 
{ 
    size_t i; 
    unsigned char *data = buffer; 

    for (i = 0; i < length; i++) 
    { 
    dataTx[i] = data[i]; 
    } 
} 

换句话说,使用自动转换为void *,然后回到char *让您的数据字节方式访问,然后发送适当地出来。

+0

我可能做了一个糟糕的工作来提出我的问题。 我的问题不在数据的**传输**中,那里已经存在遗留代码。我的问题是将存储在内存中的值写入缓冲区。 –

+0

@Alex - 您可以使用完全相同的技术。如果你想让''''静态调用'send',你也可以让它静止。 –

+0

啊,好吧。我认为这有帮助。我会继续努力的。谢谢! –

0

冗长的问题,我会尽量缩短答案。

不要继续*(dataTx + 4)= short1;等等,因为这种方法可能会失败,因为大多数芯片可能只在一些对齐的位置上读/写。您可以通过16位访问由2对齐的位置和由4对齐的位置上的32位,但以“int32 char8 int32”为例 - 第二个int32的位置为(dataTx + 5) - 不是4字节对齐,你可能会得到“总线错误”或类似的东西(取决于你将使用的CPU)。希望你了解这个问题。

月1日 - 这样你可以试试结构,如果声明:

struct 
{ 
    char a; 
    int b; 
    char c; 
    short d; 
}; 

你现在外的麻烦,因为compilator本身会照顾约结构对齐。当然,在你的编译器中阅读关于对齐相关的选项(如果这是gcc,那么这就是所谓的对齐),因为可能有一个设置强制结构字段对齐或结构字段打包。 GCC甚至可以定义每个结构的对齐方式(更多here)。

另一种方法是使用一些“缓冲式的方法” - 像卡尔Norum时的回答,后(我不会重复那个答案),但也考虑到使用了memcpy()调用时更数据被复制(例如long long或string),因为这可能比逐字节复制更快。

+0

好的,我想我现在对此有了更好的了解。 (仍然有点模糊,但我必须自己理解,否则我什么都没学习。)谢谢! –