tinyos:data[]

原文链接:http://www.cnblogs.com/van9ogh/archive/2012/05/06/2486135.html

问题引入

在编译下面一段代码时,无意中发现了这样一个错误:

.....

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

上一篇:keil 编译器V6 定义函数在ram中运行-和在指定地址定义常量


下一篇:基于STM32的LTC6804驱动代码解析