EDN首页   博客首页

最新日志

发表于:2008/11/19 21:10:33
标签:嵌入式  

1

嵌入式Linux开发需要的参考资料

引导:

如需获得对 vmlinux zimage 之间区别的极好解释,请在 Alessandro Rubini 编写的Kernel Configuration: dealing with the unexpectedLinux Magazine)的一文中找到“Booting your kernel”一节。

有关内核、映像和引导过程的更多信息,请阅读中央昆士兰大学(Central Queensland University)的系统管理文本的第 13

要进一步了解引导过程的工作原理,请参阅 ROLO: A Developer's Guide,它讨论了在不利用 BIOS 的情况下引导 LinuxEmbedded Linux Works2001 6 月)。

 

小型分发版:

The Embedded Linux Distributions Quick Reference Guide 涵盖了许多商业的和开放源码的分发版(Linux Devices2001 8 月)。

请查看另一个详尽的分发版和有用的工具的清单(Linux-embedded.com)。

 

工具链:

Wiki 工具链页面包含到本文提到的所有三个工具链的链接,还有对它们的评论。

设备驱动程序:

Memory Technology Device (MTD) Subsystem for Linux 的目的是简化内存设备(特别是闪存设备)的驱动程序的创建。

Vipin Malik 编写的 The Linux MTD, JFFS HOWTO 将帮助您使 MTD JFFS2 一起工作。

Linux for PowerPC Embedded Systems HOWTO 有一个很好的设备驱动程序清单。

理解 Linux device drivers 有助于理解本篇介绍性文章(Penguin Magazine)。

要精通 Linux 设备驱动程序,请阅读 O'Reilly Linux Device Drivers,第 2 一书。

 

有用的工具:

请查看 LART 上的 Jflash-linux

BinutilsGCC Glibc 都可从 Free Software Foundation 下载获得。

许多有用的下载都可从 Netwinder.org 获得,这是一个致力于 NetWinder 平台上开发工作的志愿者站点。

请在 Mark Nielsen 写得非常棒的 How to use a Ramdisk for Linux 一文中阅读有关 Ramdisk 的所有信息。

FLNX 是以 FLTK(快速轻巧的工具箱)为基础的。

 

文件系统:

第二版扩展文件系统 Ext2fs 的主页在 SourceForge

Red Hat 英国公司的 David Woodhouse 概述了大量有关 JFFS2:日志闪存文件系统,第 2 的背景知识。

Vipin Malik JFFS - A practical guide 一文也详细讨论了 JFFS,包括垃圾收集的问题(Embedded Linux Works2001 5 月)。

您可以在 Linux HeadQuarters 阅读更多有关 tmpfs 的信息。

Cliff Brake Jeff Sutherland 编写的 Flash Filesystems for Embedded Linux Systems 一文论述了用于闪存设备的更多文件系统(Embedded Linux Journal)。

 

GUI

Xfree86 X 开发的主页。

Microwindows 站点上可以找到有关 Microwindows Nano-X 的信息。

请查看一篇对 Microwindows 的一些缺点GNOME gtk 开发人员的邮递列表)的讨论(时间比较长了)。

您将在 Microwindows Project Links 上找到丰富的 Microwindows/Nano-X 链接。

Trolltech 上查找有关 Qt/Embedded 的更多信息。

The Embedded Linux GUI/Windowing Quick Reference Guide 中有丰富的链接(Linux Devices2002 2 月)。

一般参考资料:

General Public License GPL 确保用户复制、分发和修改软件的权利。

ARM Linux 是您了解有关 Linux 用于 ARM 处理器的信息的一个非常好的站点。它由 ARM 的创建者 Russell King 来维护。

Penguinppc.org 是关于 Linux 用于 PowerPC 系列处理器的的主页。该站点上有一个关于为基于 PPC 的体系结构建立工具链的资料丰富的教程。

Linux Devices 是一个非常全面的站点,它包含有关 Linux 和嵌入式开发的出版发行、快速参考、新闻和特色报告等各种信息。

Silicon Penguin 列表站点上拥有嵌入式 Linux 参考资料的详尽集合。

ARMLinux - the book 可从 Aleph One 上获得。您可以定购一本,也可以在线阅读

嵌入式 Linux 协会(Embedded Linux Consortium是一个非赢利的互助协会,它欢迎致力于嵌入式 Linux 领域的开发人员成为会员。

访问 IBM 关于嵌入式 Linux 的主页,获得新闻、产品和开发人员参考资料。

IBM Linux wristwatch 是运行 Linux 的微型嵌入式设备的示例;本文的作者之一,Vishal Kulkarni 也参与了它的研发。请在 IBM's Linux Watch: The Challenge of MiniaturizationPDF 格式),

系统分类: 嵌入式   |    用户分类: 无分类    |    来源: 转贴

该用户于2008/11/19 21:10:40编辑过该文章

评论(0) | 阅读(415)
发表于:2008/11/5 16:13:20
标签:C语言  队列  

1

队列的c语言实现

队列是一种与栈相反的数据结构,它是先进先出(first in, first out),同样它也是一种运算受限的结构。

队列的抽象数据类型:

ADT QUEUE =

{

InitQueue(&Q)

//create a queue

DestroyQueue(&q)

//destroy a queue

clearQueue(&q)

//clear the queue

............

}

队列的c语言实现

1:

顺序队列:

/* ******************************************
* FileName:squeue.c
* Author:hufeng Date:2007/11/21
* Version:v1.0
* Description: The program creat a queue and lots
* of operating
* ******************************************/

/* header file */
#include <stdio.h>
#define MAX 20

/* creat a struct */
typedef struct squeue
{
    int date[MAX];      /*declartion a array */
    int front,rear;     /* declarion a front and a rear pointer */

}sq;

/* assign queue each elem */
void Assign_squeue(sq *p, int count)
{
    int i; /* declaration a cycle variable */

    if (p->rear > MAX - 1)
        {
            printf("Overflow!\n");
            return;
        }

    printf("Please enter each elem:\n");
    for (i = 0; i < count; i++)
        {
            scanf("%d", &p->date[++p->rear]);
        }
}


/* insert a new elem in the queue */
void Insert_squeue(sq *p, int num)
{
    if (p->rear > MAX - 1)
        {
            printf("Over flow!\n");
            return;
        }

    else
        {
            p->date[++p->rear] = num;
        }

}


/* Delete a elem */
void Delete_squeue(sq *p)
{
    int x;
    if (p->front == p->rear)
        {
            printf("Empty queue!\n");
            return;
        }
    else
        {
            x = p->date[++p->front];
            printf("P->front = %d\n",p->front);
            printf("The deleted number is:%d\n",x);
        }
}


/* main program */
int main()
{
    sq *p = NULL;      /* declaration a pointer */
    int num;    /* decalration a number of queue */

    p->front = -1;
    p->rear = p->front;

    printf("Please enter the length of queue:\n");
    scanf("%d", &num);

    Assign_squeue(p, num); /* assign array date */

    /* insert a number in the queue */
    printf("Please enter which number you want to insert:\n");
    scanf("%d", &num);
    Insert_squeue(p, num);

    /* delete a number */
    Delete_squeue(p);

    getch();
    return 0;

}

2:

循环队列:

/* ******************************************
* FileName:cycle_queue.c
* Author:hufeng Date:2007/11/21
* Version:v1.0
* Description:The program is a cycle queue
*********************************************/

/* header file */
#include <stdio.h>
#define MAX 20

/* creat a struct */
typedef struct cycle_queue
{
    int date[MAX];
    int front,rear;
}cq;

/* assign queue */
void Assign_CycleQueue(cq *p, int n)
{
    int i;
    int x;

    if (n < 1 || n > MAX - 1)
        {
            printf("Overflow!\n");
            return;
        }

   printf("Please enter each elem:\n");
   for (i = 0; i < n; i++)
        {
            scanf("%d", &x);
            p->date[p->rear++] = x;
        }
}

/* insert a number */
void Insert_CycleQueue(cq *p, int num)
{
    if ((p->rear + 1) % MAX == p->front)
        {
            printf("full queue!\n");
            return;
        }  
    else
        {
            p->date[++p->rear] = num;
        }
}

/* delete number */
void Delete_CycleQueue(cq *p)
{
    int x;
    if ((p->front + 1) % MAX == p->rear)
        {
            printf("empty queue!\n");
            return;
        }


   x = p->date[p->front];
   printf("p->front = %d\n", p->front);
   printf("The deleted number is:%d\n",x);
}

/* main program */
int main()
{

    int num;
    cq *p = NULL;
    p->front = 0;
    p->rear = p->front;

    printf("Please enter the length of queue:\n");
    scanf("%d", &num);

    Assign_CycleQueue(p,num);

    printf("Please enter the inserting number:\n");
    scanf("%d",&num);

    Insert_CycleQueue(p, num);
    Delete_CycleQueue(p);

    getch();
    return 0;
}

3.

链队:

/* ******************************************
* FileName:cycle_queue.c
* Author:hufeng Date:2007/11/21
* Version:v1.0
* Description:The program is a cycle queue
*********************************************/

/* header file */
#include <stdio.h>
#define MAX 20

/* creat a struct */
typedef struct cycle_queue
{
    int date[MAX];
    int front,rear;
}cq;

/* assign queue */
void Assign_CycleQueue(cq *p, int n)
{
    int i;
    int x;

    if (n < 1 || n > MAX - 1)
        {
            printf("Overflow!\n");
            return;
        }

   printf("Please enter each elem:\n");
   for (i = 0; i < n; i++)
        {
            scanf("%d", &x);
            p->date[p->rear++] = x;
        }
}

/* insert a number */
void Insert_CycleQueue(cq *p, int num)
{
    if ((p->rear + 1) % MAX == p->front)
        {
            printf("full queue!\n");
            return;
        }  
    else
        {
            p->date[++p->rear] = num;
        }
}

/* delete number */
void Delete_CycleQueue(cq *p)
{
    int x;
    if ((p->front + 1) % MAX == p->rear)
        {
            printf("empty queue!\n");
            return;
        }


   x = p->date[p->front];
   printf("p->front = %d\n", p->front);
   printf("The deleted number is:%d\n",x);
}

/* main program */
int main()
{

    int num;
    cq *p = NULL;
    p->front = 0;
    p->rear = p->front;

    printf("Please enter the length of queue:\n");
    scanf("%d", &num);

    Assign_CycleQueue(p,num);

    printf("Please enter the inserting number:\n");
    scanf("%d",&num);

    Insert_CycleQueue(p, num);
    Delete_CycleQueue(p);

    getch();
    return 0;
}

系统分类: 软件开发   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(773)
发表于:2008/11/5 16:11:03
标签:C语言  队列  

1

修改了一个抓包的程序(c语言队列)

修改了一个抓包的程序(c语言队列)
05 年底写了个抓包的小工具,做某些方面的测试用。但是在大流量的时候,存在一些问题,经常会漏掉一些包没有分析,即使接到了预定义好的包,也没有办法作出某些响应。周末没事,就做了一些改写,主要原理很简单,就是用一个队列(Queue)对高峰流量做一下缓冲,先存下来再慢慢分析。读了计算机专业的人应该就容易了,数据结构有讲。我比较土一点,学经济的,只能自己根据C#的接口YY一个类似的。

      整个程序代码比较长就不贴了,把队列代码贴在下面吧,放在网上的比硬盘的还是方便一点,随时可以查阅,以备不时之需。    



   1. #include <stdio.h>
   2.  
   3. #include <stdlib.h>
   4.  
   5. #include <malloc.h>
   6.  
   7. #include <string.h>
   8.  
   9.  
  10.  
  11. typedef struct _Node
  12.  
  13. {
  14.  
  15.     int     data_len;       // 存储在节点中的数据长度
  16.  
  17.     char    *data;          // 存储在节点中的数据
  18.  
  19.     struct  _Node *next;    // 队列中的下一个节点地址
  20.  
  21. }NODE;
  22.  
  23.  
  24.  
  25. typedef struct _Queue
  26.  
  27. {
  28.  
  29.     NODE    *head;          // 队列的头部
  30.  
  31.     NODE    *end;           // 队列的尾部
  32.  
  33.     int     count;          // 队列长度
  34.  
  35. }QUEUE;
  36.  
  37.  
  38.  
  39. bool InitQueue( QUEUE *queue )
  40.  
  41. {
  42.  
  43.     if( NULL == queue )
  44.  
  45.     {
  46.  
  47.         return false;
  48.  
  49.     }
  50.  
  51.  
  52.  
  53.     queue->head = NULL;
  54.  
  55.     queue->end = NULL;
  56.  
  57.     queue->count = 0;
  58.  
  59.  
  60.  
  61.     return true;
  62.  
  63. }
  64.  
  65.  
  66.  
  67. // 在队列中插入节点
  68.  
  69. bool Enqueue( QUEUE *queue, char *queue_data, int data_len )
  70.  
  71. {
  72.  
  73.     if( NULL == queue || NULL == queue_data )
  74.  
  75.     {
  76.  
  77.         return false;
  78.  
  79.     }
  80.  
  81.  
  82.  
  83.     // 开辟新节点
  84.  
  85.     NODE *new_node = (NODE *)malloc( sizeof(NODE) );
  86.  
  87.     if( NULL == new_node )
  88.  
  89.     {
  90.  
  91.         return false;
  92.  
  93.     }
  94.  
  95.  
  96.  
  97.     // 开辟空间存储数据
  98.  
  99.     new_node->data = (char *)malloc( data_len );
100.  
101.     if( NULL == new_node->data )
102.  
103.     {
104.  
105.         return false;
106.  
107.     }
108.  
109.  
110.  
111.     memcpy( new_node->data, queue_data, data_len );
112.  
113.     new_node->next = NULL;
114.  
115.     new_node->data_len = data_len;
116.  
117.  
118.  
119.     // 如果队列为空,则新节点即是头部,也是尾部
120.  
121.     if( queue->head == NULL )
122.  
123.     {
124.  
125.         queue->head = new_node;
126.  
127.         queue->end = new_node;
128.  
129.     }
130.  
131.     else
132.  
133.     {
134.  
135.         // 如果队列不为空,将此节点连接到队列的尾部
136.  
137.         queue->end->next = new_node;
138.  
139.  
140.  
141.         // 队列新尾部指向此节点
142.  
143.         queue->end = new_node;
144.  
145.     }
146.  
147.  
148.  
149.     queue->count ++;
150.  
151.     return true;
152.  
153. }
154.  
155.  
156.  
157. // 从队列中读出一个节点
158.  
159. NODE *Dequeue( QUEUE *queue )
160.  
161. {
162.  
163.     if( NULL == queue )
164.  
165.     {
166.  
167.         return NULL;
168.  
169.     }
170.  
171.  
172.  
173.     // 如果队列为空,则无数据可从数列读出,直接返回
174.  
175.     if( NULL == queue->head )
176.  
177.     {
178.  
179.         return NULL;
180.  
181.     }
182.  
183.  
184.  
185.     // 保存队列首节点
186.  
187.     NODE *node_tmp = queue->head;
188.  
189.  
190.  
191.     // 将首节点的下一个节点(第二个节点)设置为首节点,即删除了首节点
192.  
193.     queue->head = node_tmp->next;
194.  
195.  
196.  
197.     // 如果新首节点为空,则队列为空
198.  
199.     if( NULL == queue->head )
200.  
201.     {
202.  
203.         queue->end = NULL;
204.  
205.     }
206.  
207.  
208.  
209.     queue->count --;
210.  
211.     return node_tmp;
212.  
213. }
214.  
215.  
216.  
217. // 释放队列所有内存
218.  
219. void FreeQueue( QUEUE *queue )
220.  
221. {
222.  
223.     if( queue )
224.  
225.     {
226.  
227.         return;
228.  
229.     }
230.  
231.  
232.  
233.     NODE *tmp_node1 = queue->head;
234.  
235.     while( tmp_node1 )
236.  
237.     {
238.  
239.         NODE *tmp_node2 = tmp_node1;
240.  
241.  
242.  
243.         free( tmp_node1->data );
244.  
245.         free( tmp_node1 );
246.  
247.  
248.  
249.         tmp_node1->data = NULL;
250.  
251.         tmp_node1 = NULL;
252.  
253.  
254.  
255.         tmp_node1 = tmp_node2->next;
256.  
257.     }
258.  
259. }
260.  
261.  

      这样没抓到一个包,就压入到队列里面。流量较大的尖峰时刻时候队列会比较长,但是流量较小的时候队列就比较短了。服务器流量不会一直保持在顶峰,所以可以把部分过高的流量缓冲到流量较小的时候做分析,测试的时候发现确实稳定了很多。首先需要注意的是设置一个队列最大长度,必要的时候丢掉一些数据免得内存耗尽。另外一个是分析的时候可以多线程读取队列进行分析,这样速度会更快,只是做好线程的同步就好了,CreateMutex是最简单易行的了。我用5线程来分析数据包,基本足够了。

系统分类: 软件开发   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(409)
发表于:2008/11/5 16:08:01
标签:C语言  数据结构  队列  

1

数据结构C语言实现系列——队列

#include <stdio.h>
#include
<stdlib.h>

typedef
int elemType;
/************************************************************************/
/* 以下是关于队列链接存储操作的6种算法 */
/************************************************************************/
struct sNode{
elemType data;
/* 值域 */
struct sNode *next; /* 链接指针 */
};
struct queueLK{
struct sNode *front; /* 队首指针 */
struct sNode *rear; /* 队尾指针 */
};

/* 1.初始化链队 */
void initQueue(struct queueLK *hq)
{
hq
->front = hq->rear = NULL; /* 把队首和队尾指针置空 */
return;
}

/* 2.向链队中插入一个元素x */
void enQueue(struct queueLK *hq, elemType x)
{
/* 得到一个由newP指针所指向的新结点 */
struct sNode *newP;
newP
= malloc(sizeof(struct sNode));
if(newP == NULL){
printf(
"内存空间分配失败! ");
exit(
1 );

}
/* 把x的值赋给新结点的值域,把新结点的指针域置空 */
newP
->data = x;
newP
->next = NULL;
/* 若链队为空,则新结点即是队首结点又是队尾结点 */
if(hq->rear == NULL){
hq
->front = hq->rear = newP;
}
else{ /* 若链队非空,则依次修改队尾结点的指针域和队尾指针,使之指向新的队尾结点 */
hq
->rear = hq->rear->next = newP; /* 注意赋值顺序哦 */
}
return;
}

/* 3.从队列中删除一个元素 */
elemType outQueue(
struct queueLK *hq)
{
struct sNode *p;
elemType temp;
/* 若链队为空则停止运行 */
if(hq->front == NULL){
printf(
"队列为空,无法删除! ");
exit(
1);
}
temp
= hq->front->data; /* 暂存队尾元素以便返回 */
p
= hq->front; /* 暂存队尾指针以便回收队尾结点 */
hq
->front = p->next; /* 使队首指针指向下一个结点 */
/* 若删除后链队为空,则需同时使队尾指针为空 */
if (hq->front == NULL)
{

hq->rear = NULL;
}
free(p);
/* 回收原队首结点 */
return temp; /* 返回被删除的队首元素值 */
}

/* 4.读取队首元素 */
elemType peekQueue(
struct queueLK *hq)
{
/* 若链队为空则停止运行 */
if(hq->front == NULL){
printf(
"队列为空,无法删除! ");
exit(
1);
}
return hq->front->data; /* 返回队首元素 */
}

/* 5.检查链队是否为空,若为空则返回1, 否则返回0 */
int emptyQueue(struct queueLK *hq)
{
/* 判断队首或队尾任一个指针是否为空即可 */
if(hq->front == NULL){
return 1;
}
else{
return 0;
}
}

/* 6.清除链队中的任何元素 */
void clearQueue(struct queueLK *hq)
{
struct sNode *p = hq->front; /* 队首指针赋给p */
/* 依次删除队列中的每一个结点,最后使队首指针为空 */
while(p != NULL){
hq
->front = hq->front->next;
free(p);
p = hq->front;
}
/* 循环结束后队首指针已为空 */
hq
->rear = NULL; /* 置队尾指针为空 */
return;
}

/************************************************************************/

int main(int argc, char* argv[])
{
struct queueLK q;
int a[8] = {3, 8, 5, 17, 9, 30, 15, 22};
int i;
initQueue(
&q);
for(i = 0; i < 8; i ){
enQueue(
&q, a[i]);
}
printf(
"%d ", outQueue(&q)); printf("%d ", outQueue(&q));
enQueue(
&q, 68);
printf(
"%d ", peekQueue(&q)); printf("%d ", outQueue(&q));
while(!emptyQueue(&q)){
printf(
"%d ", outQueue(&q));
}
printf(
" ");
clearQueue( &q);

system("pause");
}
Word-BREAK: break-all; PADDING-TOP: 4px; BORDER-BOTTOM: windowtext 0.5pt solid">

#include <stdio.h>
#include
<stdlib.h>

typedef
int elemType;
/************************************************************************/
/* 以下是关于队列顺序存储操作的6种算法 */
/************************************************************************/

struct queue{
elemType
*queue; /* 指向存储队列的数组空间 */
int front, rear, len; /* 队首指针(下标),队尾指针(下标),队列长度变量 */
int maxSize; /* queue数组长度 */
};

void againMalloc(struct queue *q)
{
/* 空间扩展为原来的2倍,原内容被自动拷贝到p所指向的存储空间中 */
elemType
*p;
p
= realloc(q->queue, 2 * q->maxSize * sizeof(elemType));
/* 动态存储空间分配,若失败则退出运行 */
if(!p){
printf(
"空间分配失败! ");
exit(
1);
}
q
->queue = p; /* 使queue指向新的队列空间 */
/* 把原队列的尾部内容后移maxSize个位置 */
if(q->rear != q->maxSize - 1){

int i;
for(i = 0; i <= q->rear; i ){
q
->queue[i q->maxSize] = q->queue[i];
}
q
->rear = q->maxSize; /* 队尾指针后移maxSize个位置 */
}
q
->maxSize = 2 * q->maxSize; /* 把队列空间大小修改为新的长度 */
return;
}

/* 1.初始化队列 */
void initQueue(struct queue *q, int ms)
{
/* 检查ms是否有效,若无效则退出运行 */
if(ms <= 0){
printf(
"ms值非法! ");
exit(
1);
}
q
->maxSize = ms; /* 置队列空间大小为ms */
/* 动态存储空间分配,若失败则退出运行 */
q
->queue = malloc(ms * sizeof(elemType));
if(!q->queue){
printf(
"内存空间分配失败! ");
exit(
1);
}
q
->front = q->rear = 0; /* 初始置队列为空 */
return;
}

/* 2.向队列中插入元素x */
void enQueue(struct queue *q, elemType x)
{
/* 当队列满时进行动态生分配 */
if((q->rear 1) % q->maxSize == q->front){
againMalloc(q);
}
q
->rear = (q->rear 1) % q->maxSize; /* 求出队尾的下一个位置 */
q
->queue[q->rear] = x; /* 把x的值赋给新的队尾 */
return;
}

/* 3.从队列中删除元素并返回 */
elemType outQueue(
struct queue *q)
{
/* 若队列为空则终止运行 */
if(q->front == q->rear){
printf(
"队列为空,无法删除! ");
exit(
1);
}
q
->front = (q->front 1) % q->maxSize; /* 使队首指针指向下一个位置 */
return q->queue[q->front]; /*返回队首元素 */
}
/* 4.读取队首元素,不改变队列状态 */
elemType peekQueue(
struct queue *q)
{
/* 若队列为空则终止运行 */
if(q->front == q->rear){
printf(
"队列为空,无法删除! ");
exit(
1);
}
return q->queue[(q->front 1) % q->maxSize];/* 队首元素是队首指针的下一个位置中的元素 */
}

/* 5.检查一个队列是否为空,若是则返回1,否则返回0 */
int emptyQueue(struct queue *q)
{
if(q->front == q->rear){
return 1;
}
else{
return 0;
}
}

/* 6.清除一个队列,并释放动态存储空间 */
void clearQueue(struct queue *q)
{
if(q->queue != NULL){
free(q
->queue);
q
->queue = NULL; /* 配置队列空间指针为空 */
q
->front = q->rear = 0; /* 配置队列为空 */
q
->maxSize =0; /* 配置队列大小为0 */
}
return;
}

/************************************************************************/

int main(int argc, char* argv[])
{
struct queue q;
int a[8] = {3, 8, 5, 17, 9, 30, 15, 22};
int i;
initQueue(
&q, 5);
for(i = 0; i < 8; i ){
enQueue(
&q, a[i]);
}
printf(
"%d ", outQueue(&q)); printf("%d ", outQueue(&q));
enQueue(
&q, 68);
printf(
"%d ", peekQueue(&q)); printf("%d ", outQueue(&q));
while(!emptyQueue(&q)){
printf(
"%d ", outQueue(&q));
}
printf(
" ");
clearQueue(
&q);
system(
"pause");
return 0;
}

 

系统分类: 软件开发   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(335)
发表于:2008/10/22 11:45:22
标签:嵌入式  

1

嵌入式软件工程师的技能修炼

点穴: 
深入了解至少一种处理器/控制器的体系结构,熟悉其汇编指令。
    
掌法:
精通C语言,掌握加载技术和编译链接知识。

兵刃:
了解常用存储、通讯和人机接口设备,熟练编写设备驱动程序。

阵法:
嵌入式操作系统的理论和实践。

兵法:
程序设计思想的修炼。推荐阅读成熟的GUI和TCP/IP协议栈代码,体会其设计方法。
需求分析技术。
所在领域的专业技术。原理和算法,相关的国际和国家标准。

实战:
做项目,做大量的项目,前面所说才能真正为我所用。

如此,或可独当一面。

系统分类: 嵌入式   |    用户分类: 无分类    |    来源: 转贴

评论(0) | 阅读(416)
发表于:2008/10/15 11:42:10
标签:嵌入式  可移植性  

1

嵌入式系统硬件抽象层的建立及软件的可移植性设计

摘要:在阐述嵌入式系统软件设计方法的基础上,介绍嵌入式系统底层软件可移值性设计和硬件抽象层的建立;举例说明利用此思想的嵌入式软件的设计及测试过程。

    关键词:设备驱动程序 嵌入式系统 软件设计 可移植性

1 嵌入式系统设计

  由于嵌入式系统有着体积小、功能集中、可靠性高等优点,已被广泛地应用到日常生活的各个方面,如移动通信、工业控制、医疗器械,家用电器等。如何缩短嵌入式系统的开发周期,降低开发成本,以及提高产品的可靠性已成为嵌入式行业普遍关注的问题。在嵌入式系统设计中,通常采用以下设计方法。

(1)瀑布模式开发过程

  瀑布模式开发过程工作模式简单,任务的划分协调及人员安排、物质材料的分配管理都比较容易。如图1所示,开发过程为从硬件到软件的流水线式进行。此类开发方式有以下特点:

 ◇ 小系统,如利用8051控制的低速率信号采集等;

 ◇ 开发所需人力、物力资源有限,一般1个或几个人即可完成;

 ◇ 要求开发人员对软、硬件设计和制作都比较熟悉;

 ◇ 对开发周期要求不高,此类开发过程无疑会使用最长的开发周期;

 ◇ 在开发过程中,任一环节的阻塞都会影响其它环节的开发。

(2)V模式开发过程

  V模式开发过程为一种并行的工作方式,任务的划分协调及人员安排、物质材料的分配都必须考虑不同工作内容,如图2 所示。

  开发过程为硬件和软件同时进行,最后联合调试。此类开发方式有以下特点:

 ◇ 大系统,如利用PowerPC等处理器设计的网络交换/访问设备;

 ◇ 开发人力、物力资源比较丰富;

 ◇ 开发人员分工比较明确,软件开发者可不需了解太多的硬件信息,而硬件开发人员对软件也可不做太多了解;

 ◇ 有利于缩短开发周期;

 ◇ 在开发过程中,软、硬件设计独立进行。 硬件开发的阻塞不会影响软件开发过程,同样,软件开发的阻塞不会影响硬件的开发过程。

但在V模式开发过程中,仍存在以下问题:

 ◇ 设备驱动程序的可移值性差,与硬件和操作系统均有密切相关性;

 ◇ 软件测试需要等硬件完成以后才能进行;

 ◇ 对于每个设备驱动程序设计人员都需有软件和硬件的知识背景;

 ◇ 在测试过程中,很难判断错误是由硬件还是由软件造成的。

  为了克服V模式开发过程中的上述问题,本文将V模式开发过程稍作改进,增加了硬件抽象层,对系统软硬件起到隔离作用,从而提高系统软件的可移值性及有效地利用人力资源、缩短开发周期和提高产品的可靠性。

2 基于硬件抽象层的系统软件设计特性

(1)包含硬件抽象层的系统结构

  比较图3和图4,硬件抽象层完全把系统软件和硬件部分隔离开来,这样就使得系统的设备驱动程序与硬件设备无关,从而大大提高了系统的可移植性。从软硬件测试角度来看,软硬件的测试工作都可分别基于硬件抽象层来完成,使得软硬件的测试工作的并行进行成为可能。在抽象层的定义方面,需要规定统一的软硬件接口标准,其设计工作需要基于系统需求来做,代码工作可由对硬件比较熟悉的人员来完成。抽象层一般应包含相关硬件的初始化、数据的输入/输出操作、硬件设备的配置操作等功能。

(2)包含硬件抽象层的系统开发过程

  如图5给出的包含硬件抽象层V模式开发过程,在系统需求分析并定义了软硬件各自的设计要求以后,就需要花费一定的时间来定义硬件抽象层的接口,以确保硬件设计和测试与软件设计和测试工作能够在相同的接口上进行,从而有利于最终的软硬件集成测试。

  从图5可以看出,在基于硬件抽象层的V模式开发过程,软硬件的设计和调试具有无关性,并可完全地并行进行。硬件的错误不会影响到系统软件的调试,同样软件设计的错误也不会影响硬件的调试工作,这样就可大大缩短系统的测试周期和提高系统的可靠性。

(3)硬件抽象层的特点

  硬件抽象层接口的定义和代码设计应具有以下特点:

◇ 硬件抽象层具有与硬件密切相关性;

◇ 硬件抽象层具有与操作系统无关性;

◇ 接口定义的功能应包含硬件或系统所需硬件支持的所有功能;

◇ 接口定义简单明了,太多接口函数会增加软件模拟的复杂性;

◇ 具有可测性的接口设计有利于系统的软硬件测试和集成。

3 硬件抽象层的设计示例

  硬件抽象层接口的设计一般应包含以下几个步:

◇ 分析接口的数据传输特性(双向/单向数据传输,字节型/数据帧型传输模式);

◇ 分析接口配置属性;

◇ 定义接口所需的相关函数。

下面给出以字符为单位进行数据传输的UART接口硬件抽象层的接口定义内容:

◇ 设备初始化函数

BOOL InitDevice(Device_Register *regs, Device_Attribute *attr)

① 第一个参数为指向设备寄存器结构的指针,用来索引设备的相关寄存器。

② 第二个参数为一个设备属性的结构,用于描述设备初始化设置的属性(波特率、校验位等等)。

③ 函数返回一个布尔类型,用于描述初始化过程的正确性。

◇ 设备字符输入

BOOL ReadDevice(Device_Register *regs, unsigned char *c)

① 第一个参数为指向设备寄存器结构的指针,用来索引设备的相关寄存器。

② 第二个参数为指向字符的地址空间,用于保存设备输入的字符。

③ 函数返回一个布尔类型,用于描述设备字符输入的正确性。

◇ 设备字符输出

BOOL WriteDevice(Device_Register *regs, unsigned char c)

① 第一个参数为指向设备寄存器结构的指针,用来索引设备的相关寄存器。

② 第二个参数为设备所要输出的字符。

③ 函数返回一个布尔类型,用于描述设备字符输出的正确性。

◇ 设备属性设置

BOOL SetDevice(Device_Register *regs, Device_Attribute *attr)

① 第一个参数为指向设备寄存器结构的指针,用来索引设备的相关寄存器。

② 第二个参数为一个设备属性的结构,用于描述设备初始化设置的属性(波特率、校验位等等)。

③ 函数返回一个布尔类型,用于描述设备属性设置的正确性。

4 结 论

  以上所述的是作者在多年嵌入式系统开发中所总结出的开发流程,并在实践应用中起到了很好的效果。相信在一个较为复杂的嵌入式系统开发过程中,很好地利用上述开发流程,将会有利于提高系统的可移植性、减少产品的开发和测试周期,并能很好地保证产品的可靠性。

系统分类: 嵌入式   |    用户分类: 无分类    |    来源: 转贴

评论(0) | 阅读(285)
发表于:2008/9/19 18:34:06
标签:C语言  通信  数据包  

1

C语言对通讯数据包生成校验码的例子

在写Socket通讯程序的时候,为了校验传送的数据包的完整性,我一般会使用给数据包附加校验码

的方法,具体做法就是: 对要传输的数据从第一个字符到最后一个字符进行异或运算,最后回得到一个异或

结果字符,把这个字符转换为2进制字符串!附加在数据包后面,收取数据的一方,收到数据后,先根据预先定义

的格式拆分数据包,对收到的数据进行异或运算, 然后把结果和发送方的校验码比较,如果一致,说明数据传输

没有问题。

附上一段银行接口实际应用中的例子:

//定义报文结构
typedef struct tradePackage
{   
    char p_trade_code[4];
    char p_mobile_no[16];
    char p_agent_account[26];
    char p_our_account[26];   
   
    char p_trade_fee[16];    
    char p_trade_date[9];
    char p_trade_time[7];
    char p_bank_id[3];
    char p_trade_node[11];
    char p_operator_no[5];
   
    char p_bank_flow_no[13];
    char p_result_code[4];
       
    char p_our_flow_no[13];
    char p_check_code[9];               
}PACKAGE;

/*
功能描述:生成发送报文的校验码(显示为2进制的字符方式)
返回参数:checkcode为对input_buf串的每个字符做异或生成的校验码
*/

void make_XOR_checkcode(char * input_buf,char * checkcode)
{
    int m="strlen"(input_buf);
    int i,n;
       
    //保存异或的结果字符
    char ret;      
   
    char s[m];
    char b[8];
   
    int x="0x80";      
   
    strcpy(s,input_buf);
   
    //给准备返回的报文字符逐个做异或
    for(i=0;i<m-1;i++)
    {
    if(i==0)
    {
         ret="s"[i]^s[i+1];
    }
    else
    {
         ret="ret"^s[i+1];
    }
    }
   
    /*
    用异或的结果ret和x做位比较,做出ret的2进制校验码 start
    */
    for(n=0;n<8;n++)
    {
    if((ret&x)==0)
    {
         b[n]='0';
    }
    else
    {
         b[n]='1';
    }
   
    x="x">>1; //右移一位相当于除2   
    }
   
    b[8]='\0';
           
    /*
    用异或的结果ret和x做位比较,做出ret的2进制校验码 end
    */
   
    strcpy(checkcode,b); //返回生成的校验码
   
   
}

/*
功能描述:检验发来的数据报文的内容是否与校验码一致
返回参数:trade_package参数是由银行方传来的报文,用trade_package.p_check_code来
          对其他的字符串进行校验。校验成功返回1,否则返回0
*/

int packageXOR_parse(struct tradePackage trade_package)
{
       
    //报文格式:交易码3手机号码15代理商银行帐号25商户银行帐号25金额15交易日期8交易时间6银行编码2交易网点10
    //操作员4银行流水号12结果代码3商户流水号12校验码8           
    char chkcode[8];
    char strtmp[256];     
    char input_str[1024];     
    char checkcode[8];
       
    memset(input_str, 0x0, sizeof(input_str));
                         
    //1:交易码 3 不空
    strcpy(input_str,trade_package.p_trade_code);
            
    //2:手机号码 15 不空 右补空格                        
    memset(strtmp, 0x0, 15);
    sprintf(strtmp,"%-15s",trade_package.p_mobile_no); //显示字符串时限定15位(不足则补空格),并靠左端对齐
                                      
    strcat(input_str,strtmp);                      
                         
    //3:代理商银行帐号 25 不空 右补空格(如现金缴款,全空格)
    memset(strtmp, 0x0, 25);
    sprintf(strtmp,"%-25s",trade_package.p_agent_account); //显示字符串时限定25位(不足则右补空格),并靠左端对齐
            
    strcat(input_str,strtmp);            
            
    //4:商户银行帐号 25 不空
    memset(strtmp, 0x0, 25);
    sprintf(strtmp,"%-25s",trade_package.p_our_account); //显示字符串时限定25位(不足则右补空格),并靠左端对齐
            
    strcat(input_str,strtmp);
            
    //5:金额 15 不空 含小数点和两位小数,右补空格
    //sprintf(trade_package.p_trade_fee,"%0.2f",p_trade_fee);
                        
    memset(strtmp, 0x0, 15);
    sprintf(strtmp,"%-15s",trade_package.p_trade_fee); //显示字符串时限定15位(不足则右补空格),并靠左端对齐
            
    strcat(input_str,strtmp);
            
    //6:交易日期 8 不空 YYYYMMDD
    strcat(input_str,trade_package.p_trade_date);
    //7:交易时间 6 不空 HHMMSS
    strcat(input_str,trade_package.p_trade_time);
    //8:银行编码 2 不空
    strcat(input_str,trade_package.p_bank_id);
    //9:交易网点 10 不空 根据银行方规定
    strcat(input_str,trade_package.p_trade_node);
    //10:操作员   4 不空 柜台:操作员其它为设备号
    strcat(input_str,trade_package.p_operator_no);
            
    //11:银行流水号 12 不空 左补0做重复性检查   
    sprintf(strtmp,"%12s",trade_package.p_bank_flow_no); //显示字符串时限定12位(不足则左边补空格),并靠右端对齐
    strcat(input_str,strtmp);
            
    //12:结果代码 3 空格                   
    strcat(input_str,trade_package.p_result_code);
            
    //13:商户流水号 12 空格 左补0
    //显示字符串时限定12位(不足则左边补0),并靠右端对齐
    sprintf(strtmp,"%012s",trade_package.p_our_flow_no);
                         
    strcat(input_str,strtmp);

           
    //用发来的报文生成校验码chkcode,然后用chkcode和trade_package.p_check_code进行比较 start
    make_XOR_checkcode(input_str,chkcode);
         
    char str[128];
   
    sprintf(str,"%d",strcmp(trade_package.p_check_code,chkcode));
    strcat(str," comp");
           
    //如果比较
    if(strcmp(trade_package.p_check_code,chkcode)==0)
       return 1;
    else
       return 0;   
    //用发来的报文生成校验码chkcode,然后用chkcode和trade_package.p_check_code进行比较 end
}

系统分类: 软件开发   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(365)
发表于:2008/9/9 11:32:22
标签:无标签

2

C宏——智者的利刃,愚者的恶梦!

文章来自:http://blog.vckbase.com/smileonce/archive/2005/03/27/4081.html

水平不高不低的C++程序员最喜欢挂在嘴上的一句话就是:C宏,万恶之首,错误的开端,应该被废弃。

请注意,我用了一句不敬的修饰语“水平不高不低的”。为什么这么说?因为水平低都插不上话,都在在静静地听老前辈布道呢。水平高的,比如Bane Stroustrup老人家,也只是说若干种场合下C++语言能够提供比C macro更好的解决方案,而没有完全否定C macro的价值。但是话就怕传来传去,一传就走样。久而久之,就被传成上面那句话。其实说来也很好笑:java程序员经常说java比C++好,说C++手动释放内存老搞内存泄漏;C++程序员便反驳说,那是你水平低不会用。但是谈到C宏,水平不高不低的C++程序员居然也走java的老路了——明明是自己不会用,自己知道的少,却把责任推卸到C宏上。你自己笨我管不着,但是错误的言论如果误导后人就不好了吧。:)

本文就举几个简单的使用C宏的例子,如果这些例子用C++不用宏的语法能更好的解决,那么你一定要回复blog告诉我,这样下次我就不乱说话了。否则,笑笑很生气,后果很严重。:)


例一、用C宏,书写代码更简洁
这段代码写网络程序的朋友都很眼熟,是Net/3中mbuf的实现。

struct mbuf
{
    struct m_hdr mhdr;
    union {
        struct 
        {
            struct pkthdr MH_pkthdr; /* M_PKTHDR set */
            union 
            {
                struct m_ext MH_ext; /* M_EXT set */
                char MH_databuf[MHLEN];
            } MH_dat;
        } MH;
        char M_databuf[MLEN];        /* !M_PKTHER, !M_EXT*/
    } M_dat;
};

上面的代码,假如我想访问最里层的MH_databuf,那么我必须写M_dat.MH.MH_dat.MH_databuf; 这是不是很长,很难写呀?这样的代码阅读起来也不明了。其实,对于MH_pkthdr、MH_ext、MH_databuf来说,虽然不是在一个结构层次上,但是如果我们站在mbuf之外来看,它们都是mbuf的属性,完全可以压扁到一个平面上去看。所以,源码中有这么一组宏:

#define m_next      m_hdr.mh_next
#define m_len       m_hdr.mh_len
#define m_data      m_hdr.mh_data
... ...
#define m_pkthdr    M_dat.MH.MH_pkthdr
#define m_pktdat    M_dat.MH.MH_dat.MH_databuf
... ...

这样写起代码来,是不是很精练呢!


例二、用C宏,实现跨平台和编译器的需要
这方面的例子太好举了,一举一大摞,就从VC的库源码中随意copy一段出来吧。

#ifndef _CRTAPI1
#if _MSC_VER >= 800 && _M_IX86 >= 300
#define _CRTAPI1 __cdecl
#else  /* _MSC_VER >= 800 && _M_IX86 >= 300 */
#define _CRTAPI1
#endif  /* _MSC_VER >= 800 && _M_IX86 >= 300 */
#endif  /* _CRTAPI1 */

#ifndef _SIZE_T_DEFINED
typedef unsigned int size_t;
#define _SIZE_T_DEFINED
#endif  /* _SIZE_T_DEFINED */

#ifndef _MAC
#ifndef _WCHAR_T_DEFINED
typedef unsigned short wchar_t;
#define _WCHAR_T_DEFINED
#endif  /* _WCHAR_T_DEFINED */
#endif  /* _MAC */
 
#ifndef _NLSCMP_DEFINED
#define _NLSCMPERROR    2147483647  /* currently == INT_MAX */
#define _NLSCMP_DEFINED
#endif  /* _NLSCMP_DEFINED */

请问,这些指示宏如何取代呢?如果真的是没有了这些宏,实现起来就更麻烦了吧。


例三、用C宏,自动生成代码
这方面的例子也是多得很,不过有鉴于很多朋友不用很多编译器,不做嵌入式的开发,我就举个win平台的例子吧。我们知道MFC实现了windows的消息映射,比如:

ON_COMMAND(IDM_ABOUT, OnAbout)
ON_COMMAND(IDM_FILENEW, OnFileNew)

它是如何实现的IDM_ABOUT和OnAbout的关联的呢?这要用到几个宏。

#define DECLARE_MESSAGE_MAP() \
private: \
 static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
 static AFX_DATA const AFX_MSGMAP messageMap; \
 virtual const AFX_MSGMAP* GetMessageMap() const; \

#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
 const AFX_MSGMAP* theClass::GetMessageMap() const \
  { return &theClass::messageMap; } \
 AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \
 { &baseClass::messageMap, &theClass::_messageEntries[0] }; \
 AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
 { \

 #define ON_COMMAND(id, memberFxn) \
        { WM_COMMAND, 0, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)memberFxn },

 #define END_MESSAGE_MAP() \
  {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
 }; \
#define DECLARE_MESSAGE_MAP() \
private: \
 static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
 static AFX_DATA const AFX_MSGMAP messageMap; \
 virtual const AFX_MSGMAP* GetMessageMap() const; \

#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
 const AFX_MSGMAP* theClass::GetMessageMap() const \
  { return &theClass::messageMap; } \
 AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \
 { &baseClass::messageMap, &theClass::_messageEntries[0] }; \
 AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \
 { \

 #define ON_COMMAND(id, memberFxn) \
        { WM_COMMAND, 0, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)memberFxn },

 #define END_MESSAGE_MAP() \
  {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
 }; \

嘿嘿,就这么几个宏,就构造出一个消息数组来。


例四、用C宏,智者思维的火花
说了半天了,嘴皮子都干了,举个例子大家轻松一下——看看人家老外是怎么用宏的。
这个例子摘自《C专家编程》。

根据位模式构建图形
图标(icon)或者图形(glyph),是一种小型的位模式映射于屏幕产生的图像。一个位代表图像上的一个像素。如果一个位被设置,那么它所代表的像素就是“亮”的。如果一个位被清除,那么它所代表的像素就是“暗”的。所以,一系列的整数值能够用于为图像编码。类似Iconedit这样的工具就是用于绘图的,他们所输出的是一个包含一系列整型数的ASCII文件,可以被一个窗口程序所包含。它所存在的问题是程序中的图标只是一串十六进制数。在C语言中,典型的16X16的黑白图形可能如下:

static unsigned short stopwatch[] = {
0x07C6,
0x1FF7,
0x383B,
0x600C,
0x600C,
0xC006,
0xC006,
0xDF06,
0xC106,
0xC106,
0x610C,
0x610C,
0x3838,
0x1FF0,
0x07C0,
0x0000
};


正如所看到的那样,这些C语言常量并未有提供有关图形实际模样的任何线索。这里有一个惊人的#define定义的优雅集合,允许程序建立常量使它们看上去像是屏幕上的图形。

#define X )*2+1
#define _ )*2
#define s ((((((((((((((((0 /* For building glyphs 16 bits wide */


定义了它们之后,只要画所需要的图标或者图形等,程序会自动创建它们的十六进制模式。使用这些宏定义,程序的自描述能力大大加强,上面这个例子可以转变为:

static unsigned short stopwatch[] =
{
s _ _ _ _ _ X X X X X _ _ _ X X _ ,
s _ _ _ X X X X X X X X X _ X X X ,
s _ _ X X X _ _ _ _ _ X X X _ X X ,
s _ X X _ _ _ _ _ _ _ _ _ X X _ _ ,
s _ X X _ _ _ _ _ _ _ _ _ X X _ _ ,
s X X _ _ _ _ _ _ _ _ _ _ _ X X _ ,
s X X _ _ _ _ _ _ _ _ _ _ _ X X _ ,
s X X _ X X X X X _ _ _ _ _ X X _ ,
s X X _ _ _ _ _ X _ _ _ _ _ X X _ ,
s X X _ _ _ _ _ X _ _ _ _ _ X X _ ,
s _ X X _ _ _ _ X _ _ _ _ X X _ _ ,
s _ X X _ _ _ _ X _ _ _ _ X X _ _ ,
s _ _ X X X _ _ _ _ _ X X X _ _ _ ,
s _ _ _ X X X X X X X X X _ _ _ _ ,
s _ _ _ _ _ X X X X X _ _ _ _ _ _ ,
s _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
};


显然,与前面的代码相比,它的意思更为明显。标准的C语言具有八进制、十进制和十六进制常量,但没有二进制常量,否则的话倒是一种更为简单的绘制图形模式的方法。

如果抓住书的右上角,并斜这看这一页,可能会猜测这是一个用于流行窗口系统的“cursor busy”小秒表图形。我是在几年前从Usenet   comp.lang.c新闻组学到这个技巧的。

千万不要忘了在绘图结束后清除这些宏定义,否这很可能会给你后面的代码带来不可预测的后果。

好了,今天的废话就到这里了。水能载舟,亦能覆舟,把握好手中的双刃剑,让它好好的为你服务吧,别割破了手。:)

系统分类: 软件开发   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(271)
发表于:2008/8/27 9:12:02
标签:C语言  union  

2

C语言的联合(union)介绍

“联合”与“结构”有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。应该说明的是, 这里所谓的共享不是指把多个成员同时装入一个联合变量内, 而是指该联合变量可被赋予任一成员值,但每次只能赋一种值, 赋入新值则冲去旧值。如前面介绍的“单位”变量, 如定义为一个可装入“班级”或“教研室”的联合后,就允许赋予整型值(班级)或字符串(教研室)。要么赋予整型值,要么赋予字符串,不能把两者同时赋予它。联合类型的定义和联合变量的说明一个联合类型必须经过定义之后, 才能把变量说明为该联合类型。 

一、联合的定义 

定义一个联合类型的一般形式为:  
union 联合名  
{  
成员表  
}; 
成员表中含有若干成员,成员的一般形式为: 类型说明符 成员名 成员名的命名应符合标识符的规定。 
例如:  
union perdata 

   int class; 
   char office; 
}; 
  定义了一个名为perdata的联合类型,它含有两个成员,一个为整型,成员名为class;另一个为字符数组,数组名为office。联合定义之后,即可进行联合变量说明,被说明为perdata类型的变量,可以存放整型量class或存放字符数组office。 

二、联合变量的说明 

  联合变量的说明和结构变量的说明方式相同, 也有三种形式。即先定义,再说明;定义同时说明和直接说明。以perdata类型为例,说明如下:  
union perdata 

   int class; 
   char officae; 
}; 
union perdata a,b; /*说明a,b为perdata类型*/ 
或者可同时说明为:  
union perdata 
{  
int class; 
char office;  
}a,b; 
或直接说明为:  
union 
{  
int class; 
char office;  
}a,b  
经说明后的a,b变量均为perdata类型。 它们的内存分配示意图如图7—8所示。a,b变量的长度应等于 perdata 的成员中最长的长度, 即等于 
office数组的长度,共10个字节。从图中可见,a,b变量如赋予整型值时,只使用了2个字节,而赋予字符数组时,可用10个字节。 

联合变量的赋值和使用 

  对联合变量的赋值,使用都只能是对变量的成员进行。 联合变量的成员表示为: 联合变量名.成员名 例如,a被说明为perdata类型的变量之后,可使用 a.class a.office 不允许只用联合变量名作赋值或其它操作。 也不允许对联合变量作初始化赋值,赋值只能在程序中进行?挂偾康魉得鞯氖?一个联合变量, 每次只能赋予一个成员值?痪浠八?一个联合变量的值就是联合变员的某一个成员值。 
[例7.15]设有一个教师与学生通用的表格,教师数据有姓名,年龄,职业,教研室四项。学生有姓名,年龄,职业,班级四项。 
编程输入人员数据, 再以表格输出。 
[code:1:8d8ee8c82c] 
main() 

   struct 
   { 
      char name[10]; 
      int age; 
      char job; 
      union 
      { 
         int class; 
         char office[10]; 
      } depa; 
   }body[2]; 
int n,i; 
   for(i=0;i<2;i++) 
   { 
      printf("input name,age,job and department\n"); 
      scanf("%s %d %c",body[i].name,&body[i].age,&body[i].job); 
      if(body[i].job=='s') 
         scanf("%d",&body[i].depa.class); 
      else 
         scanf("%s",body[i].depa.office); 
   } 
   printf("name\tage job class/office\n"); 
   for(i=0;i<2;i++) 
   { 
   if(body[i].job=='s') 
      printf("%s\t%3d %3c %d\n",body[i].name,body[i].age ,body[i].job,body[i].depa.class); 
   else 
      printf("%s\t%3d %3c %s\n",body[i].name,body[i].age, body[i].job,body[i].depa.office); 
   } 

[/code:1:8d8ee8c82c] 
  本例程序用一个结构数组body来存放人员数据, 该结构共有四个成员。其中成员项depa是一个联合类型, 这个联合又由两个成员组成,一个为整型量class,一个为字符数组office。在程序的第一个for语句中,输入人员的各项数据,先输入结构的前三个成员name,age和job,然后判别job成员项,如为"s"则对联合depa·class输入(对学生赋班级编号)否则对depa·office输入(对教师赋教研组名)。 

  在用scanf语句输入时要注意,凡为数组类型的成员,无论是结构成员还是联合成员,在该项前不能再加"&"运算符。如程序第18行中 
body[i].name是一个数组类型,第22行中的body[i].depa.office也是数组类型,因此在这两项之间不能加"&"运算符。程序中的第二个for语句用于输出各成员项的值: 

本章小结 

1. 结构和联合是两种构造类型数据,是用户定义新数据类型的重要手段。结构和联合有很多的相似之处,它们都由成员组成。成员可以具有不同的数据类型。成员的表示方法相同。都可用三种方式作变量说明。 

2. 在结构中,各成员都占有自己的内存空间,它们是同时存在的。一个结构变量的总长度等于所有成员长度之和。在联合中,所有成员不能同时占用它的内存空间,它们不能同时存在。联合变量的长度等于最长的成员的长度。 

3. “.”是成员运算符,可用它表示成员项,成员还可用“->”运算符来表示。 

4. 结构变量可以作为函数参数,函数也可返回指向结构的指针变量。而联合变量不能作为函数参数,函数也不能返回指向联合的指针变量。但可以使用指向联合变量的指针,也可使用联合数组。 

5. 结构定义允许嵌套,结构中也可用联合作为成员,形成结构和联合的嵌套。 

6. 链表是一种重要的数据结构,它便于实现动态的存储分配。本章介绍是单向链表,还可组成双向链表,循环链表等。

系统分类: 软件开发   |    用户分类:    |    来源: 转贴

评论(1) | 阅读(2069)
发表于:2008/8/27 9:12:01
标签:C语言  union  

2

C语言的联合(union)介绍

“联合”与“结构”有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间, 一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间, 一个联合变量的长度等于各成员中最长的长度。应该说明的是, 这里所谓的共享不是指把多个成员同时装入一个联合变量内, 而是指该联合变量可被赋予任一成员值,但每次只能赋一种值, 赋入新值则冲去旧值。如前面介绍的“单位”变量, 如定义为一个可装入“班级”或“教研室”的联合后,就允许赋予整型值(班级)或字符串(教研室)。要么赋予整型值,要么赋予字符串,不能把两者同时赋予它。联合类型的定义和联合变量的说明一个联合类型必须经过定义之后, 才能把变量说明为该联合类型。 

一、联合的定义 

定义一个联合类型的一般形式为:  
union 联合名  
{  
成员表  
}; 
成员表中含有若干成员,成员的一般形式为: 类型说明符 成员名 成员名的命名应符合标识符的规定。 
例如:  
union perdata 

   int class; 
   char office; 
}; 
  定义了一个名为perdata的联合类型,它含有两个成员,一个为整型,成员名为class;另一个为字符数组,数组名为office。联合定义之后,即可进行联合变量说明,被说明为perdata类型的变量,可以存放整型量class或存放字符数组office。 

二、联合变量的说明 

  联合变量的说明和结构变量的说明方式相同, 也有三种形式。即先定义,再说明;定义同时说明和直接说明。以perdata类型为例,说明如下:  
union perdata 

   int class; 
   char officae; 
}; 
union perdata a,b; /*说明a,b为perdata类型*/ 
或者可同时说明为:  
union perdata 
{  
int class; 
char office;  
}a,b; 
或直接说明为:  
union 
{  
int class; 
char office;  
}a,b  
经说明后的a,b变量均为perdata类型。 它们的内存分配示意图如图7—8所示。a,b变量的长度应等于 perdata 的成员中最长的长度, 即等于 
office数组的长度,共10个字节。从图中可见,a,b变量如赋予整型值时,只使用了2个字节,而赋予字符数组时,可用10个字节。 

联合变量的赋值和使用 

  对联合变量的赋值,使用都只能是对变量的成员进行。 联合变量的成员表示为: 联合变量名.成员名 例如,a被说明为perdata类型的变量之后,可使用 a.class a.office 不允许只用联合变量名作赋值或其它操作。 也不允许对联合变量作初始化赋值,赋值只能在程序中进行?挂偾康魉得鞯氖?一个联合变量, 每次只能赋予一个成员值?痪浠八?一个联合变量的值就是联合变员的某一个成员值。 
[例7.15]设有一个教师与学生通用的表格,教师数据有姓名,年龄,职业,教研室四项。学生有姓名,年龄,职业,班级四项。 
编程输入人员数据, 再以表格输出。 
[code:1:8d8ee8c82c] 
main() 

   struct 
   { 
      char name[10]; 
      int age; 
      char job; 
      union 
      { 
         int class; 
         char office[10]; 
      } depa; 
   }body[2]; 
int n,i; 
   for(i=0;i<2;i++) 
   { 
      printf("input name,age,job and department\n"); 
      scanf("%s %d %c",body[i].name,&body[i].age,&body[i].job); 
      if(body[i].job=='s') 
         scanf("%d",&body[i].depa.class); 
      else 
         scanf("%s",body[i].depa.office); 
   } 
   printf("name\tage job class/office\n"); 
   for(i=0;i<2;i++) 
   { 
   if(body[i].job=='s') 
      printf("%s\t%3d %3c %d\n",body[i].name,body[i].age ,body[i].job,body[i].depa.class); 
   else 
      printf("%s\t%3d %3c %s\n",body[i].name,body[i].age, body[i].job,body[i].depa.office); 
   } 

[/code:1:8d8ee8c82c] 
  本例程序用一个结构数组body来存放人员数据, 该结构共有四个成员。其中成员项depa是一个联合类型, 这个联合又由两个成员组成,一个为整型量class,一个为字符数组office。在程序的第一个for语句中,输入人员的各项数据,先输入结构的前三个成员name,age和job,然后判别job成员项,如为"s"则对联合depa·class输入(对学生赋班级编号)否则对depa·office输入(对教师赋教研组名)。 

  在用scanf语句输入时要注意,凡为数组类型的成员,无论是结构成员还是联合成员,在该项前不能再加"&"运算符。如程序第18行中 
body[i].name是一个数组类型,第22行中的body[i].depa.office也是数组类型,因此在这两项之间不能加"&"运算符。程序中的第二个for语句用于输出各成员项的值: 

本章小结 

1. 结构和联合是两种构造类型数据,是用户定义新数据类型的重要手段。结构和联合有很多的相似之处,它们都由成员组成。成员可以具有不同的数据类型。成员的表示方法相同。都可用三种方式作变量说明。 

2. 在结构中,各成员都占有自己的内存空间,它们是同时存在的。一个结构变量的总长度等于所有成员长度之和。在联合中,所有成员不能同时占用它的内存空间,它们不能同时存在。联合变量的长度等于最长的成员的长度。 

3. “.”是成员运算符,可用它表示成员项,成员还可用“->”运算符来表示。 

4. 结构变量可以作为函数参数,函数也可返回指向结构的指针变量。而联合变量不能作为函数参数,函数也不能返回指向联合的指针变量。但可以使用指向联合变量的指针,也可使用联合数组。 

5. 结构定义允许嵌套,结构中也可用联合作为成员,形成结构和联合的嵌套。 

6. 链表是一种重要的数据结构,它便于实现动态的存储分配。本章介绍是单向链表,还可组成双向链表,循环链表等。

系统分类: 软件开发   |    用户分类:    |    来源: 转贴

评论(0) | 阅读(328)
2345678910>>Next >Total , Page /