当前位置:首页 » 编程语言 » 环形缓冲区c语言

环形缓冲区c语言

发布时间: 2022-11-15 04:00:07

A. c语言中,什么是缓冲文件系统和文件缓冲区

目前c语言使用的文件系统分为缓冲文件系统(标准i
/
o)和非缓冲文件系统(系统i
/
o)。缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用,当执
行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”,装满后再从内存“缓冲区”依此读入接收的变量。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。
因此当程序运行时虽然进行了写数据操作,但是如果写入的数据没有装满内存中的缓冲区,就不会将数据写入到磁盘文件中。当程序运行结束后,系统就会将缓冲区中的数据写入到磁盘上的文件中,因此就可以看到文件中的内容。
常用的fopen命令就会使用文件读写缓冲区,fclose命令关闭文件,把缓冲区中的内容写入磁盘上的文件中。详见:
http://study.qqcf.com/web/171/19812.htm

B. 数据结构之环形缓冲器

你有过这样的经历吗?当你打开你的电脑,就因为输入你的密码缺导致出现了一个卡顿。这个卡顿的出现,主要是因为电脑处理器醒来发现它需要开始读取键盘上的迷你处理器。但是,这些信息到底是如何存储在那么小的键盘内存空间里呢?这个就是圆形缓冲器作用的场景。

环形缓冲器 是一种特定的 队列 。所以它也被称作为 圆形队列 。如果你对于队列不太熟悉的话,你从名字上来看你至少能反映出它是一种直线的队伍(如同大家排队去上卫生间)。这个队列里是 先进先出(FIFO) 。所以意味着,第一个进入队伍的人,会第一个出队。那么到底环形缓冲器是如何与众不同的呢?

环形缓冲区通常储存数据在一个定长的数组里(array)。因此只要长度确定以后,它通过两个指针分别指向这个队列的队尾和队首的位置,来追踪队伍的情况。只需要根据队伍的新元素的增加和减少,相应的向前移动指针。而无需去动态地去操作这个数组。举个例子,队首被推出了队列,那么其他队伍里的元素都需要向前移动一位。这种操作是比较低效的。所以环形缓冲区避免了这种操作。因为这个环形队列的实现是数组,所以只能通过添加新元素到队首或者队尾。假如需要添加元素到队列的中间,则可能需要使用链表来实现。

一般来说,这个队列里面没有任何元素的时候和队列已经满的时候,头指针和尾指针是 指向同一个位置 的。

当元素被添加到队列里时,头指针向前移动一此。当元素从队列里删除时,尾指针同理。但是尾指针永远都不应该跳过头指针,因为你生产者永远排在消费者前面,或者当队列为空时两个指针指向同一个元素。当指针移动到数组的末尾位置,它将重新跳转回数组的起始位置。并且 头指针和写指针之间是线程安全的 。假如有多个消费者和生产者公用指针,则需要加锁来保证线程安全。

缓冲区是满、或是空,都有可能出现读指针与写指针指向同一位置。有多种策略用于检测缓冲区是满、或是空。

1. 总是保持一个存储单元为空缓冲区中总是有一个存储单元保持未使用状态。缓冲区最多存入(size-1)。个数据。如果读写指针指向同一位置,则缓冲区为空。如果写指针位于读指针的相邻后一个位置,则缓冲区为满。这种策略的优点是简单、粗暴;缺点是语义上实际可存数据量与缓冲区容量不一致,测试缓冲区是否满需要做取余数计算。

2. 使用数据计数这种策略。不使用显式的写指针,而是保持着缓冲区内存储的数据的计数。因此测试缓冲区是空是满非常简单;对性能影响可以忽略。缺点是读写操作都需要修改这个存储数据计数,对于多线程访问缓冲区需要并发控制。

3. 镜像指示位。缓冲区的长度如果是n,逻辑地址空间则为0至n-1;那么,规定n至2n-1为镜像逻辑地址空间。本策略规定读写指针的地址空间为0至2n-1,其中低半部分对应于常规的逻辑地址空间,高半部分对应于镜像逻辑地址空间。当指针值大于等于2n时,使其折返(wrapped)到ptr-2n。使用一位表示写指针或读指针是否进入了虚拟的镜像存储区:置位表示进入,不置位表示没进入还在基本存储区。

4. 在读写指针的值相同情况下,如果二者的指示位相同,说明缓冲区为空;如果二者的指示位不同,说明缓冲区为满。这种方法优点是测试缓冲区满/空很简单;不需要做取余数操作;读写线程可以分别设计专用算法策略,能实现精致的并发控制。 缺点是读写指针各需要额外的一位作为指示位。

如果缓冲区长度是2的幂,则本方法可以省略镜像指示位。如果读写指针的值相等,则缓冲区为空;如果读写指针相差n,则缓冲区为满,这可以用条件表达式(写指针 == (读指针异或缓冲区长度))来判断。

5. 读/写计数用。两个有符号整型变量分别保存写入、读出缓冲区的数据数量。其差值就是缓冲区中尚未被处理的有效数据的数量。这种方法的优点是读线程、写线程互不干扰;缺点是需要额外两个变量。

6. 使用一位记录最后一次操作是读还是写。读写指针值相等情况下,如果最后一次操作为写入,那么缓冲区是满的;如果最后一次操作为读出,那么缓冲区是空。这种策略的缺点是读写操作共享一个标志位,多线程时需要并发控制。

Reference:

https://en.wikipedia.org/wiki/Circular_buffer

C. 环形缓冲区

本质上是一个数组,但是它又跟普通的数组不太一样,普通的数组只有一个指针,移动下标是index+1的方式;
而环形缓冲区有两个指针:
head指针:表示读取数据的指针;
tail指针:表示写数据的指针。
它是如何实现环形读写的呢?
通过对读写指针的移动进行取模(求余)运算实现的。
对于读指针,如果当前位置有数据可读,那么读取后指针移动,移动方式为:(head+1)%size。
对于写指针,如果当前位置可写入数据,那么写入数据后指针移动,移动方式为:(tail+1)%size。
通过求余的计算方式,每当指针的值达到size-1时,总能通过求余的方式回到0的位置,而后再进行相应的读写操作,指针再继续按照上述方式进行移动。

环形缓冲区主要用于数据读写,相对应的有两个重要的判断条件:
1、缓冲区为空不能读;
2、缓冲区已满不能写。

D. c语言中多线程读写同一个环形缓冲区的实现

#include <stdio.h>
#include <windows.h>
#include <process.h>

unsigned __stdcall ThreadWrite(void* param);
unsigned __stdcall ThreadRead(void* param);

int WriteSeque = 0;
int ReadSeque = 0;
int RingBuf[4];
void main()
{
HANDLE htw = (HANDLE)_beginthreadex(NULL, 0, ThreadWrite, NULL, 0, 0);
HANDLE htr = (HANDLE)_beginthreadex(NULL, 0, ThreadRead, NULL, 0, 0);
CloseHandle(htw);
CloseHandle(htr);
while (1)
{
if (WriteSeque >= 100)
{
break;
}
}
printf("Quit\n");
}

unsigned __stdcall ThreadWrite(void* param)
{
int i = 0;
while (1)
{
if (WriteSeque >= ReadSeque && WriteSeque- ReadSeque < 4)
{
RingBuf[WriteSeque%4] = i;
printf("Write:%d\n", i);
i++;
WriteSeque++;
Sleep(50);
}
}
}

unsigned __stdcall ThreadRead(void* param)
{
while (1)
{
if (ReadSeque < WriteSeque)
{
printf("Read:%d\n", RingBuf[ReadSeque%4]);
ReadSeque++;
Sleep(100);
}
}
}

为了让你看到效果,读写线程的休眠时间略有不同。

E. C语言里如何设置缓冲区

将数据以链表形式顺序存放,新数据总是放在表尾,待处理的数据总是在头结点下的第一个结点,处理完毕则释放空间。

#define BufferSize 1024 // 合适的大小你知道的
typedef struct node {
char *buffer;
struct node *next;
}*linkList;

LinkList *InitList() {
LinkList *head;
head = (char *)malloc(size(node));
head->next = NULL:
return head;
}

void AddData(LinkList *head, char *data) { // 将新数据添加到表尾
LinkList *p = head;
LinkList *anode = (char *)malloc(size(node));
anode->buffer = (char *)malloc(size(BufferSize));
strncpy(anode->buffer,data,BufferSize);
anode->next = NULL:
while(p->next) p = p->next;
p->next = anode;
}

void DealData(LinkList *head) {
LinkList *p = head->next;
if(p) {
head->next = p->next;
// p->buffer指向的数据待处理
free(p->buffer); // 处理完毕,释放空间
free(p);
}
}

F. C语言缓冲区在哪里

缓冲区具体在哪里是与操作系统、编译器相关的
以VC++为例。察看getchar的源代码(src\fgetchar.c),有:
int
__cdecl
_fgetchar
(void){
return(getc(stdin));
}
#undef
getchar
int
__cdecl
getchar
(void){
return
_fgetchar();
}
可见getchar()相当于getc(stdin)
继续察看getc(src\fgetc.c),有一段(为便于阅读,有删减):
int
__cdecl
getc
(FILE
*stream){
int
retval;
_ASSERTE(stream
!=
NULL);
_lock_str(stream);
__try
{
retval
=
_getc_lk(stream);
}
__finally
{
_unlock_str(stream);
}
return(retval);
}
这段代码里_lock_str其实是通过Win32
API提供的临街区来锁住文件
接收用户输入发生在_getc_lk,_getc_lk宏调用_filbuf。_filbuf在_filbuf.c中可以查看,这段代码比较长,就不贴出来了
_filbuf主要是调用了_read(_fileno(stream),
stream->_base,
stream->_bufsiz)
而_read最终则是调用了Win32API
ReadFile,以下是用WinDbg输出的getchar的调用栈:
#
ChildEBP
RetAddr
00
0012fe6c
0040a4e7
kernel32!ReadFile
01
0012fea8
0040a3b9
TestStruct!_read_lk+0x107
[read.c
@
146]
02
0012fec0
00403140
TestStruct!_read+0x69
[read.c
@
75]
03
0012fee8
00401290
TestStruct!_filbuf+0xd0
[_filbuf.c
@
127]
04
0012ff08
004012cc
TestStruct!fgetc+0x80
[fgetc.c
@
44]
05
0012ff14
0040103d
TestStruct!getc+0xc
[fgetc.c
@
56]
06
0012ff20
00401058
TestStruct!_fgetchar+0xd
[fgetchar.c
@
37]
07
0012ff28
0040101e
TestStruct!getchar+0x8
[fgetchar.c
@
47]
08
0012ff80
0040115c
TestStruct!main+0xe
[d:\my
programs\teststruct\ts.cpp
@
4]
09
0012ffc0
7c816fe7
TestStruct!mainCRTStartup+0xfc
[crt0.c
@
206]
0a
0012fff0
00000000
kernel32!BaseProcessStart+0x23
可见,getchar最终调用了ReadFile。关于ReadFile的原理以及缓冲区在哪里,请你再提一个问我再回答

G. C语言缓冲区在哪里

缓冲区具体在哪里是与操作系统、编译器相关的
以VC++为例。察看getchar的源代码(src\fgetchar.c),有:
int __cdecl _fgetchar (void){
return(getc(stdin));
}
#undef getchar
int __cdecl getchar (void){
return _fgetchar();
}
可见getchar()相当于getc(stdin)

继续察看getc(src\fgetc.c),有一段(为便于阅读,有删减):
int __cdecl getc (FILE *stream){
int retval;
_ASSERTE(stream != NULL);
_lock_str(stream);
__try {
retval = _getc_lk(stream);
}
__finally {
_unlock_str(stream);
}
return(retval);
}

这段代码里_lock_str其实是通过Win32 API提供的临街区来锁住文件
接收用户输入发生在_getc_lk,_getc_lk宏调用_filbuf。_filbuf在_filbuf.c中可以查看,这段代码比较长,就不贴出来了
_filbuf主要是调用了_read(_fileno(stream), stream->_base, stream->_bufsiz)

而_read最终则是调用了Win32API ReadFile,以下是用WinDbg输出的getchar的调用栈:
# ChildEBP RetAddr
00 0012fe6c 0040a4e7 kernel32!ReadFile
01 0012fea8 0040a3b9 TestStruct!_read_lk+0x107 [read.c @ 146]
02 0012fec0 00403140 TestStruct!_read+0x69 [read.c @ 75]
03 0012fee8 00401290 TestStruct!_filbuf+0xd0 [_filbuf.c @ 127]
04 0012ff08 004012cc TestStruct!fgetc+0x80 [fgetc.c @ 44]
05 0012ff14 0040103d TestStruct!getc+0xc [fgetc.c @ 56]
06 0012ff20 00401058 TestStruct!_fgetchar+0xd [fgetchar.c @ 37]
07 0012ff28 0040101e TestStruct!getchar+0x8 [fgetchar.c @ 47]
08 0012ff80 0040115c TestStruct!main+0xe [d:\my programs\teststruct\ts.cpp @ 4]
09 0012ffc0 7c816fe7 TestStruct!mainCRTStartup+0xfc [crt0.c @ 206]
0a 0012fff0 00000000 kernel32!BaseProcessStart+0x23

可见,getchar最终调用了ReadFile。关于ReadFile的原理以及缓冲区在哪里,请你再提一个问我再回答

H. C语言中,什么是缓冲文件系统和文件缓冲区

文件缓冲区即系统在读写程序时在内存中开辟的数据源与数据目标中间的一个用于保存完整数据内容的缓冲区域。
目前c语言使用的文件系统分为缓冲文件系统(标准i
/
o)和非缓冲文件系统(系统i
/
o)。缓冲文件系统的特点是:在内存开辟一个“缓冲区”,为程序中的每一个文件使用,当执
行读文件的操作时,从磁盘文件将数据先读入内存“缓冲区”,装满后再从内存“缓冲区”依此读入接收的变量。执行写文件的操作时,先将数据写入内存“缓冲区”,待内存“缓冲区”装满后再写入文件。
因此当程序运行时虽然进行了写数据操作,但是如果写入的数据没有装满内存中的缓冲区,就不会将数据写入到磁盘文件中。当程序运行结束后,系统就会将缓冲区中的数据写入到磁盘上的文件中,因此就可以看到文件中的内容。

I. C语言里如何设置缓冲区,

很简单的定义一个数组,用两个变量下标来指向头和尾,新数据来尾++ 老数据处理完头++ 两个下标超过界限时从头开始循环利用 中间要考虑缓存数据空和数据满的情况

或者你动态分配空间,来一个信号分配空间,插入队列链表,处理完一个出队列,释放空间

J. 怎么计算环形缓冲区长度

两个函数未经调试

#defineMAXLEN200
intBuff[MAXLEN];
int*Ptr_W=Buff;//写指针初始化为Buff[0]
int*Ptr_R=NULL;//读指针初始化为NULL

//读数据。成功读出时,返回1,x存放读出的结果。不成功返回0,x的内容不可用
intReadData(intBuff[],int*x){
if(Ptr_R==NULL||Ptr_R>=Ptr_W)return0;
*x=*Ptr_R;
Ptr_R+=Buff+(Ptr_R-Buff+1)%MAXLEN;
return1;
}

//写数据。成功写入时,返回。不成功返回0
intWriteData(intBuff[],intx){
if(Ptr_R==NULL){//初次写入
Ptr_R=Buff;//初次写入时,附带完善读指针的初始化操作
*Ptr_W++=x;
return1;
}
if(Ptr_W==Ptr_R)return0;//缓冲区满
*Ptr_W=x;
Ptr_W+=Buff+(Ptr_W-Buff+1)%MAXLEN;
return1;
}

热点内容
ecstore数据库 发布:2025-01-13 07:29:43 浏览:295
手机设置密码忘记了怎么解开 发布:2025-01-13 07:28:29 浏览:19
存储卡交流 发布:2025-01-13 07:16:06 浏览:982
php字符串浮点数 发布:2025-01-13 07:15:28 浏览:997
python排序cmp 发布:2025-01-13 07:09:04 浏览:71
云脚本精灵 发布:2025-01-13 07:03:27 浏览:617
高维访问 发布:2025-01-13 07:03:23 浏览:974
保卫萝卜有脚本吗 发布:2025-01-13 06:30:29 浏览:741
天猫上传 发布:2025-01-13 06:06:35 浏览:156
php处理并发 发布:2025-01-13 06:03:44 浏览:283