问题引入
在编译下面一段代码时,无意中发现了这样一个错误:
.....
typedef nx_struct test_msg { nx_uint8_t data[]; } test_msg_t
.....
错误:
van9ogh@WSN:Git$ gcc test.c test.c:4: error: flexible array member in otherwise empty struct
但如果换成这样:
..... typedef nx_struct test_msg { nx_uint8_t counter; nx_uint8_t data[]; } test_msg_t .....
编译是没有问题的,现在我们再换一下,
..... typedef nx_struct test_msg { nx_uint8_t counter; nx_uint8_t data[]; nx_uint8_t data2[]; } test_msg_t .....
编译如下:
van9ogh@WSN:Git$ gcc test.c test.c:5: error: flexible array member not at end of struct
这...,其实仔细想想,对阿,这不是让编译器为难吗,到底data2[]的首地址怎么办阿?!
TinyOS
好了,在tinyos中我们发现了很多这样的data[],没有指定具体的数组大小,
比如,串口通信中/tos/lib/Serial.h
typedef nx_struct serial_header { nx_am_addr_t dest; nx_am_addr_t src; nx_uint8_t length; nx_am_group_t group; nx_am_id_t type; } serial_header_t; typedef nx_struct serial_packet { serial_header_t header; nx_uint8_t data[]; } serial_packet_t;
那么这个data[]在这里到底起到什么作用呢?
其实就是保存上层数据帧地址的作用,我们举一个demo,具体代码请去
tinyos-2.x-contrib项目的tinyos-programming/ReliableTestSerial去查看;
说明:ReliableTestSerial是tinyos源码包中apps/tests/TestSerial的升级版,旨在提供一个可靠的PC-Mote串口通信,
主要组件:TestSerialC.nc:提供Ack机制的AMSend,Receive,底层通过Timer,SubSend,AckSend, SubReceive, AckReceive实现
客户端应用程序调用该组件提供的AMSend和Receive就可以可靠的进行串口通信.
ReliableSerialC.nc
/* * Copyright (c) 2007-2009 Intel Corporation * All rights reserved. * This file is distributed under the terms in the attached INTEL-LICENS * file. If you do not find this file, a copy can be found by writing to * Intel Research Berkeley, 2150 Shattuck Avenue, Suite 1300, Berkeley, CA, * 94704. Attention: Intel License Inquiry. */ #include "reliableserial.h" module ReliableSerialC { provides { interface AMSend; interface Receive; } uses { interface AMSend as SubSend; interface AMSend as AckSend; interface Receive as SubReceive; interface Receive as AckReceive; interface Timer<TMilli>; } } implementation { message_t ackmsg; bool ackBusy; message_t *sending; uint8_t sendLen; am_addr_t sendAddr; uint8_t dropCount; uint8_t sendCookie = 1, recvCookie; void done(error_t ok) { message_t *sent = sending; sending = NULL; sendCookie *= 3; /* New cookie time! Cycles every 64 cookies. */ signal AMSend.sendDone(sent, ok); } default event void AMSend.sendDone(message_t *msg, error_t error) { } command error_t AMSend.send(am_addr_t addr, message_t* msg, uint8_t len) { error_t ok = EBUSY; if (!sending) { reliable_msg_t *header = call SubSend.getPayload(msg, sizeof(reliable_msg_t)); header->cookie = sendCookie; ok = call SubSend.send(addr, msg, len + sizeof(reliable_msg_t)); if (ok == SUCCESS) { sending = msg; sendAddr = addr; sendLen = len; } } return ok; } event void SubSend.sendDone(message_t *msg, error_t error) { if (error == SUCCESS) call Timer.startOneShot(ACK_TIMEOUT); else done(error); } event void Timer.fired() { error_t ok = call SubSend.send(sendAddr, sending, sendLen + sizeof(reliable_msg_t)); if (ok != SUCCESS) done(ok); } event message_t *AckReceive.receive(message_t *m, void *payload, uint8_t len) { ack_msg_t *ack = payload; #ifdef TESTING if (++dropCount == 5) { dropCount = 0; return m; } #endif if (ack->cookie == sendCookie) { call Timer.stop(); done(SUCCESS); } return m; } event message_t *SubReceive.receive(message_t *m, void *payload, uint8_t len) { reliable_msg_t *header; ack_msg_t *ack = call AckSend.getPayload(&ackmsg, sizeof(ack_msg_t)); #ifdef TESTING if (++dropCount == 5) { dropCount = 0; return m; } #endif if (len < sizeof(reliable_msg_t)) return m; header = payload; if (ack && !ackBusy) { /* We ack the received packet. We don't care if that fails (we'll just try the ack again on the sender's retry...) */ ack->cookie = header->cookie; if (call AckSend.send(TOS_BCAST_ADDR, &ackmsg, sizeof(ack_msg_t)) == SUCCESS) ackBusy = TRUE; } /* Duplicate packet - ignore */ if (header->cookie == recvCookie) return m; recvCookie = header->cookie; return signal Receive.receive(m, header->data, len - sizeof(reliable_msg_t)); } event void AckSend.sendDone(message_t *msg, error_t error) { ackBusy = FALSE; } default event message_t *Receive.receive(message_t *m, void *payload, uint8_t len) { return m; } command error_t AMSend.cancel(message_t* msg) { if (msg == sending) { call Timer.stop(); return SUCCESS; } else return FAIL; } command uint8_t AMSend.maxPayloadLength() { return call SubSend.maxPayloadLength() - sizeof(reliable_msg_t); } command void* AMSend.getPayload(message_t* msg, uint8_t len) { reliable_msg_t *header = call SubSend.getPayload(msg, len + sizeof(reliable_msg_t)); return header ? header + 1 : NULL;//这边的header+1正好跳过了一个reliable_msg_t个长度,到达data区域
//但问题是data[]不是在reliable_msg_t里面的吗,而用户数据在
//reliable_msg_t之后的吗?其实不是这样的,我们可以检测一下是否
//sizeof(reliable_msg_t)== sizeof(reliable.cookie),对的,
//data[]没有任何空间
} }
上层应用传入的packet会被加入cookies传输,我们可以看到data的作用.
下面我们针对上面的问题做一个奇怪的实验,然后我们就可以理解什么叫做data[]没有任何空间了!
test.c
#include <stdio.h> typedef struct test_t { int count; int *data1; int data2[]; } test_t; int main() { test_t test; printf("%d\n", sizeof(test_t)); printf("%d\n", sizeof(test)); printf("0x%x\n", &test.count); printf("0x%x\n", &test.data1); printf("0x%x\n", &test.data2); return 0; }
我们来看一下输出,
van9ogh@WSN:Git$ ./test 8 8 0xbf9f66b8 0xbf9f66bc 0xbf9f66c0
奇怪了,怎么会只有八个字节呢?
如果改成这样呢?
#include <stdio.h> typedef struct test_t { int count; // int *data1; int data2[]; } test_t; int main() { test_t test; printf("%d\n", sizeof(test_t)); printf("%d\n", sizeof(test)); printf("0x%x\n", &test.count); // printf("0x%x\n", &test.data1); printf("0x%x\n", &test.data2); return 0; }
输出:
van9ogh@WSN:Git$ ./test 4 4 0xbf8e2d3c 0xbf8e2d40
很犀利阿~
转载于:https://www.cnblogs.com/van9ogh/archive/2012/05/06/2486135.html