循环队列
C语言环境下实现循环队列,我采取了内存分配的方式,使用指针实现数据拷贝。这个问题本身并没有多大的难度,但有两个地方仍需要引起注意:换行读取和最大容量的设置。循环队列通用的两种区别空队列和满队列的方法:一是设置标记位;二是空一元素的位置出来。这次我使用了第二种方法,但是实际上并没有空出一个位置出来,只是将最大值和实际值相比大了1,比如,需要一个能存放8个元素的队列,我们在这里可以设置maxCount为9,这样在判满函数中也不会出现问题。这种情况下,入队出队均由内存分配完成,会省事很多,以下为主要代码和注释:
#include "stdafx.h"
#include<stdlib.h>
#include"malloc.h"
typedef struct queue
{
void *pBase;
size_t elemSize; //元素大小
int front; //指向队列第一个元素
int rear; //指向队列最后一个元素的下一个元素
int occ; //循环队列现有元素个数
int maxCount; //循环队列的最大存储空间
}Queue, *CirQ;
enum TYPE
{
SHORT = 1,
CHAR = 1,
BOOL = 1,
INT = 4,
LONG = 4,
FLOAT = 4,
DOUBLE = 8,
};
//新建环形队列,count为元素个数;elemType为元素类型
CirQ CreateQ(int count, TYPE elemType);
//销毁队列,Q为指定队列
void DestroyQ(CirQ Q);
#pragma region 读写函数的声明
//将队列中cnt个数的元素读到buffer开始的内存中,读取失败返回false,成功返回true
bool ReadShort(CirQ Q, void* buffer, int cnt);
bool ReadChar(CirQ Q, void* buffer, int cnt);
bool ReadBool(CirQ Q, void* buffer, int cnt);
bool ReadInt(CirQ Q, void* buffer, int cnt);
bool ReadLong(CirQ Q, void* buffer, int cnt);
bool ReadFloat(CirQ Q, void* buffer, int cnt);
bool ReadDouble(CirQ Q, void* buffer, int cnt);
//将buffer中cnt个数的元素读到Q队列中
bool WriteBool(CirQ Q, void* buffer, int cnt);
bool WriteChar(CirQ Q, void* buffer, int cnt);
bool WriteBool(CirQ Q, void* buffer, int cnt);
bool WriteInt(CirQ Q, void* buffer, int cnt);
bool WriteLong(CirQ Q, void* buffer, int cnt);
bool WriteFloat(CirQ Q, void* buffer, int cnt);
bool WriteDouble(CirQ Q, void* buffer, int cnt);
#pragma endregion
//获取当前队列中指定类型可用元素个数
int GetAcc(CirQ Q);
//获取当前队列中指定类型已有元素个数
int GetElmCount(CirQ Q);
//判空
bool EmptyQueue(CirQ Q);
//判满
bool FullQueue(CirQ Q);
int main()
{
CirQ p;
#pragma region int
//创建一个可以装8个int的队列
p = CreateQ(8, INT);
int arr[8];
for (int i = 0; i < 8; i++)
{
arr[i] = i;
}
int * p2 = (int *)malloc(sizeof(int) * 8);
bool b = true;
b = WriteInt(p, arr, 8);
WriteInt(p, arr, 5);
ReadInt(p, p2, 3);
#pragma endregion
//销毁分配内存
DestroyQ(p);
free(p2);
system("pause");
return 0;
}
CirQ CreateQ(int maxCount, TYPE elemType)
{
//结构体指针需要初始化
CirQ NQ = (struct queue*)malloc(sizeof(struct queue));
//pBase存放数据
NQ->pBase = (void *)malloc(elemType *maxCount);
if (NULL == NQ->pBase)
{
printf("Memory allocation failure");
return NULL; //退出程序
}
NQ->front = 0; //初始化参数
NQ->rear = 0;
NQ->elemSize = elemType;
NQ->maxCount = maxCount + 1;//预留一个空位置,为了取余并不预留实际位置
NQ->occ = 0;
return NQ;
}
void DestroyQ(CirQ Q)
{
free(Q);
}
#pragma region reads
bool ReadInt(CirQ Q, void* buffer, int cnt)
{
errno_t err;
//对Q判空,若为空,直接返回false
if (EmptyQueue(Q))
{
printf("Error!!!Empty Queue.\n");
return false;
}
//读取元素个数大于现有元素个数,读取溢出
if (cnt > Q->occ)
{
printf("Error!!!Overflow.\n");
return false;
}
//以整数为例
int * p = (int*)(Q->pBase);
//判断是否需要读两次
if ((Q->front + cnt) <= Q->maxCount)
{
//如果不需要换方向,一次性拷贝完全
err = memcpy_s(buffer, (cnt * Q->elemSize), p + Q->front, (cnt * Q->elemSize));
if (err)
{
printf("Error executing memcpy_s.\n");
return false;
}
else
{
Q->occ = Q->occ - cnt;
//向前移动指针
Q->front = (Q->front + cnt) % Q->maxCount;
return true;
}
}
else
{
//换方向了分两次读
int* bufferTmp = (int*)buffer;
err = memcpy_s(buffer, ((Q->maxCount - 1 - Q->front) * Q->elemSize), p + Q->front, ((Q->maxCount-1 - Q->front) * Q->elemSize));
int left = cnt - (Q->maxCount-1 - Q->front);
if (err)
{
printf("Error executing memcpy_s.\n");
return false;
}
else
{
Q->occ = Q->occ - (Q->maxCount - 1 - Q->front);
//buffer也要往前移动,写入到接下来的起始位置
bufferTmp = bufferTmp + (Q->maxCount - 1 - Q->front);
//向前移动指针
Q->front = (Q->front + (Q->maxCount - Q->front)) % Q->maxCount;
//return true;
}
err = memcpy_s(bufferTmp, ((left * Q->elemSize) * Q->elemSize), p + Q->front, (left * Q->elemSize));
if (err)
{
printf("Error executing memcpy_s.\n");
return false;
}
else
{
Q->occ = Q->occ - left;
//向前移动指针
Q->front = (Q->front + left) % Q->maxCount;
return true;
}
}
}
#pragma endregion
#pragma region 不同类型的写
bool WriteInt(CirQ Q, void* buffer, int cnt)
{
errno_t err;
//对Q判满,若为空,直接返回false
if (FullQueue(Q))
{
printf("Error!!!Full Queue.\n");
return false;
}
//存储元素个数大于可用元素个数,存储溢出
if (cnt > (Q->maxCount - Q->occ - 1))
{
printf("Error!!!Overflow.\n");
return false;
}
//以整数为例
int * QTmp = (int*)(Q->pBase);
//判断是否需要写两次
if ((Q->rear + cnt) <= Q->maxCount)
{
//如果不需要换方向,一次性拷贝完全
err = memcpy_s(QTmp + Q->rear, (cnt * Q->elemSize), buffer, (cnt * Q->elemSize));
if (err)
{
printf("Error executing memcpy_s.\n");
return false;
}
else
{
Q->occ = Q->occ + cnt;
//向前移动指针
Q->rear = (Q->rear + cnt) % Q->maxCount;
return true;
}
}
else
{
//换方向了分两次写
int* bufferTmp = (int*)buffer;
err = memcpy_s(QTmp + Q->rear, ((Q->maxCount - 1 - Q->rear) * Q->elemSize), buffer, ((Q->maxCount - 1 - Q->rear) * Q->elemSize));
int left = cnt - (Q->maxCount-1 - Q->rear);
if (err)
{
printf("Error executing memcpy_s.\n");
return false;
}
else
{
Q->occ = Q->occ + (Q->maxCount-1 - Q->rear);
//buffer也要往前移动,写入到接下来的起始位置
bufferTmp = bufferTmp + (Q->maxCount-1 - Q->rear);
//向前移动指针
Q->rear = (Q->rear + (Q->maxCount - Q->rear)) % Q->maxCount;
//return true;
}
err = memcpy_s(QTmp + Q->rear, (left * Q->elemSize), bufferTmp, (left * Q->elemSize));
if (err)
{
printf("Error executing memcpy_s.\n");
return false;
}
else
{
Q->occ = Q->occ + left;
//向前移动指针
Q->rear = (Q->rear + left) % Q->maxCount;
return true;
}
}
}
#pragma endregion
//判断是否为空
bool EmptyQueue(CirQ Q)
{
if (Q->front == Q->rear)
return true;
else
return false;
}
//进队(以整数为例)
bool Enqueue(CirQ Q, int val)
{
if (FullQueue(Q))
return false;
else
{
int * p = (int*)(Q->pBase);
p[Q->rear] = val;
//((int*)Q->pBase)[Q->rear] = val;这样是对的
//Q->pBase[Q->rear] = val;//这样是错误的,原因:void*不能++
Q->rear = (Q->rear + 1) % Q->maxCount;
return true;
}
}
//判满
bool FullQueue(CirQ Q)
{
if (Q->front == (Q->rear + 1) % Q->maxCount) //判断循环链表是否满,留一个预留空间不用
return true;
else
return false;
}
//获取当前队列中指定类型可用元素个数
int GetAcc(CirQ Q)
{
return (Q->maxCount - Q->occ);
}
//获取当前队列中指定类型已有元素个数
int GetElmCount(CirQ Q)
{
return (Q->occ);
}
网络协议之命名管道(Named Pipes)
Named Pipes 是为局域网而开发的协议。 内存的一部分被某个进程用来向另一个进程传递信息,因此一个进程的输出就是另一个进程的输入。 第二个进程可以是本地的(与第一个进程位于同一台计算机上),也可以是远程的(位于联网的计算机上)。
命名管道是管道通信的一种,另外一种是匿名管道。匿名管道只能用在父子进程之间;命名管道可以用在两个进程通信。【进程间通信】
以上链接是MSDN关于C#下使用命名管道通信的示例。可供参考。
以及一篇比较有价值的博文C#命名管道通信
其次,有CodeProject上的源码在project文件夹,亦可参考。如果使用命名管道在进程之间传送文件,可以参考上次socket编程部分。