首页 > 其他分享 >PA0:关于剩余练习3

PA0:关于剩余练习3

时间:2024-01-22 16:58:06浏览次数:30  
标签:PA0 剩余 RingBuffer int 练习 List char ringbuffer NULL

42、44:  1/19

 栈和队列

栈的特点:先入先出,后入后出 。出口也是入口,另一端封闭。  队列:一头入,另一头出(非传统队列也有一端可出入,另一端出的设计)、

这个练习的要求说实话有点奇怪,不准写.c,只写.h头文件来满足单元测试,看具体的要求,它要求基于之前的list来实现栈和队列,但是又不让用.c。不准用.c,那我要怎么实现?最后没办法,直接在.h里把代码实现也写上算了。

题目要求里给出了提示:继续使用list,但是限制list的元素出入,就可以作为stack和queue来用。考虑到这一点,我打算继续用listnode,list结构体也保留,函数就用之前的list封装一下,限制元素读写方向:

#include <ex32.h>

  #define Stack_count(A) ((A)->count)
  #define Stack_top(A) ((A)->first != NULL ? (A)->first->value : NULL)

List *Stack_create()
{
    return calloc(1, sizeof(List));    
}

void Stack_destory(List *stack)
{
    LIST_FOREACH(stack, first, next, cur) {
     if(cur->prev) {
            free(cur->prev);
        }
    }
    free(stack->last);
    free(stack);
}

void Stack_push(List *stack,char *val)
{
    List_unshift(stack,val); 
}

void Stack_pop(List *stack)
{
    List_shift(stack);
}

队列:

#define Queue_count(A) ((A)->count)

List *Queue_create()
{
    return calloc(1, sizeof(List));    
}

void Queue_destory(List *queue)
{
    LIST_FOREACH(queue, first, next, cur) {
     if(cur->prev) {
            free(cur->prev);
        }
    }
    free(queue->last);
    free(queue);
}

void Queue_send(List *queue,char *val)
{
    List_push(queue,val); 
}

void Queue_recv(List *queue)
{
    List_shift(queue);
}

 

附加题里面提到了DArray 动态数组。如果用动态数组的话,声明需要用malloc(count*sizeof(int))  好处是本身支持随机存取,函数里进行一些调整,可以很方便地实现栈和队列的效果。

 

--------练习44

环形缓冲区

typedef struct {
    char *buffer;
    int length;
    int start;
    int end;
} RingBuffer;

这里用了和之前list不同的定义方式,直接用数字来表示指针,这么做可以更方便地计算缓冲区是否已经被占满。(取余就可以)

 

#define RingBuffer_available_data(B) (((B)->end + 1) % (B)->length - (B)->start - 1) 这个宏比较值得学习,它的作用是简化直接计算当前缓冲区已用空间,和下一个avaliable_space相对应。首先用end指针+1,余length,得到有效的end位置,再减去start,再-1,就能得到当前有效空间的数量。

在.c中具体实现了5个函数,剩下的都直接在头文件中声明;学习代码,可以看到作者想的各方面还是比较细致的。

以get函数为例:

bstring RingBuffer_gets(RingBuffer *buffer, int amount)
{
    check(amount > 0, "Need more than 0 for gets, you gave: %d ", amount);
    check_debug(amount <= RingBuffer_available_data(buffer),
            "Not enough in the buffer.");

    bstring result = blk2bstr(RingBuffer_starts_at(buffer), amount);
    check(result != NULL, "Failed to create gets result.");
    check(blength(result) == amount, "Wrong result length.");

    RingBuffer_commit_read(buffer, amount);
    assert(RingBuffer_available_data(buffer) >= 0 && "Error in read commit.");

    return result;
error:
    return NULL;
    //首先,要获取的数据要大于0,其次,队列可用数据要充足
    //然后,从当前读位置开始,读出amount个字符,转为string输出
    //如果得到的string长度不对也报错
    //然后读指针后移amount个单位 ,超出长度会根据宏自动重头继续
    //移动后,检查可用数据是否大于0
}

考虑到了各种可能导致失败的情况,包括目标字节数小于0、环形队列内数据不足、字符串转换失败、字符串结果与目标长度不符和读指针移动后已用空间小于0.

在作者的环形队列设计里,start表示数据起始位置,它和end指针一样,都可能不在0的位置,也就是说数据有可能不从0开始。

单元测试的代码:

static RingBuffer *ringbuffer=NULL;  //把关键指针声明在外面,这样不用重复 注意用了static,但不用应该也可以?
//一开始准备的指针是NULL,具体的创建留在后面create函数

char *test1="test";
char *res1=NULL;
char *test2="add";
char *res2=NULL;
char *test3="op";
char *res3=NULL;
char *test4="a";
char *res4=NULL;
int t1=0,t2=0,t3=0,t4=0;


char * test_create(int length)
{
    ringbuffer=RingBuffer_create(length);
    mu_assert(ringbuffer != NULL, "Failed to create ringbuffer.");
    return NULL;
}

char *test_destroy()
{
    RingBuffer_destroy(ringbuffer);
    mu_assert(ringbuffer ==NULL,"Failed to destroy.");
    return NULL;
}


char *test_read_and_write()
{
    RingBuffer_write(ringbuffer,test1,5);
    mu_assert(RingBuffer_gets(ringbuffer,5)=="test" , "write data error");
    t1 = RingBuffer_read(ringbuffer,res1,5);
    mu_assert(res1=="test" || t1 != -1, "read data error");

    RingBuffer_write(ringbuffer,test2,4);
    mu_assert(RingBuffer_gets(ringbuffer,4)=="add","write data error");
    t2 = RingBuffer_read(ringbuffer,res2,4);
    mu_assert(res2=="add" || t2 != -1, "read data error");

    RingBuffer_write(ringbuffer,test3,3);
    mu_assert(RingBuffer_gets(ringbuffer,3)=="op","write data error");
    t3 = RingBuffer_read(ringbuffer,res3,3);
    mu_assert(res3=="op" || t3 != -1, "read data error");

    RingBuffer_write(ringbuffer,test4,2);
    mu_assert(RingBuffer_gets(ringbuffer,2)=="a","write data error");
    t4 = RingBuffer_read(ringbuffer,res4,2);
    mu_assert(res4=="a" || t4 != -1, "read data error");

//write 不需要返回值   read会把数据读出存在target里,并返回amount,出问题返回-1
    return NULL;
}

char *all_tests()
{
    test_create();
    test_read_and_write();
    test_destroy();

    printf("all test passed\n");

    return NULL;
}

RUN_TESTS(all_tests);

 

 

维基百科里的实现:

#include <stdio.h>

enum { N = 10 };  // N elements of the circular buffer

int buffer [N];   // Note that N-1 is the actual capacity, see put function
int writeIndx = 0;
int readIndx  = 0;

int put (int item) 
{
  if ((writeIndx + 1) % N == readIndx)
  {
     // buffer is full, avoid overflow
     return 0;
  }
  buffer[writeIndx] = item;
  writeIndx = (writeIndx + 1) % N;
  return 1;
}

int get (int * value) 
{
  if (readIndx == writeIndx)
  {
     // buffer is empty
     return 0;
  }

  *value = buffer[readIndx];
  readIndx = (readIndx + 1) % N;
  return 1;
}

int main ()
{
  // test circular buffer
  int value = 1001;
  while (put (value ++));
  while (get (& value))
     printf ("read %d\n", value);
  return 0;
}

代码写的很简洁。效果是直接运行N=10次,将缓冲区填至N-1个空间被占(设计问题,留了一个位置没有用),然后再逐个打印出每一项缓冲区内容。对于这个缓冲区,在缓冲区填满以后不会再试图循环填充数据,而是直接提示已经满额。不过提到的通过POSIX调用实现无限区域的代码似乎没有出现。如果允许用c++的话,那可以利用C++的STL来实现一个自动扩展的无限大的循环队列。

 

一点遗留的尾巴:练习44:环形缓冲区-笨办法学C(Learn C The Hard Way) (cntofu.com)

标签:PA0,剩余,RingBuffer,int,练习,List,char,ringbuffer,NULL
From: https://www.cnblogs.com/namezhyp/p/17974859

相关文章

  • G2303、G2318期末复习习题册第五章解答(剩余部分)
    ......
  • CVE-2018-19518复现练习
    概述漏洞概述:imap_open函数在传递邮箱名给ssh之前没有正确过滤接收的参数,导致攻击者可以利用-oProxyCommand参数向IMAP服务器发起命令执行恶意代码影响版本:Ubuntu、Debian、RedHat、SUSE环境搭建cd/CVE-2018-19518docker-composeup-d查看网站的组件直接看页面也没有什么......
  • 第一章练习1-9
    1.解释为什么程序1-7的交换函数没有把形参x和y所对应的实参的值交换。如何修改代码,使实参的值得到交换?程序1-7交换两个整数的不正确的代码voidswap(intx,inty){//交换整数x和yinttemp=x;x=y;y=temp;}调用swap函数是将实参的值复制一份给x和y,本质是交换......
  • 蓝桥杯准备---练习日志
    2024-01-17利用Cubemx配置usart中Asyn...和Syn....的意思是什么? 使用串口时报错如下------解决办法:添加st官方提供的串口驱动文件 修改底层printf内部的fputs代码---实现printf函数通过串口输出,需要利用LIB什么的......我猜测这个函数是向某个文件流中写......
  • 前端学习-简单项目练习01-小兔鲜
    一些笔记使用flex-wrap换行(一行一个元素)ul{display:flex;flex-wrap:wrap;}ulli{flex:100%;}html中让img撑满整个divdiv要设置宽高,img也要有宽高且均为100%,最重要的是img要给display:block。<divid="mainDiv"style="width:100%;height:100%">......
  • 贪心算法练习
    问题描述:设有n个正整数,将他们连接成一排,组成一个最大的多位整数。例如:n=4时,4个整数21,8,901,6连成的最大整数为:9018621。贪心选择策略:(1)将所有数字转化为字符串形式。(2)将所有字符串按照长度从大到小排序。如果长度相同,则按照字典序从大到小排序。(3)将排序后的字符......
  • 图论练习笔记
    P1606[USACO07FEB]LilypadPondG首先跳的过程肯定不会经过相同位置,所以之前经过的位置可以视为原状态,所以可以把添加的莲花数量视为当前路径长度,问题也就转化成了最短路计数。因为求的是添加莲花的方案数而不是经过路径的方案数,所以可以把已有的莲花连通块缩起来,以水格子为状......
  • 二次剩余
    考虑若有非\(0\)解,那么两个解在模意义下互为相反数。判定\(n\)在模\(p\)意义下是否有二次剩余,只要看\(n^{\frac{p-1}{2}}\)为\(1\)还是\(-1\)即可。Cipolla算法流程是,任意随一个\(a\)使得\(a^2-n\)不是二次剩余。设\(i^2\equiva^2-n\pmodp\),注意此......
  • $20240119$ 练习题解
    \(20240119\)练习题解CF472D通过尝试我们容易发现,与点\(1\)最近的点一定是直接儿子。我们要是把它作为突破点,那就成功了一半了。假设点\(2\)与点\(1\)最近,又假设我们可以用函数\(F(x)\)来确定\(x\)点的子树形态。那我们会发现如果我们还有剩余的节点,那么剩余的节点......
  • CVE-2012-1823复现练习
    环境搭建:DockerDesktop开启cd/CVE-2012-1823docker-composeup-d本地访问80端口分析PHP-CGI直接将用户的请求作为了PHP-CGI的参数执行,导致远程代码的执行。先上执行结果PHP的运行模式:CGI通用网关接口,接收网页浏览器的数据发送给web服务器,再把执行结果返回给浏览器参......