C语言「生成器」

转载需经作者同意,并注明出处

前言

我的一块板子,好像不支持多线程。于是我想到了python中的生成器。
就是这个MCU:ATMEGA168P
文章看着玩玩而已,仅供学习和娱乐。

正文

首先定义一个测试的函数。(姑且叫它生成器好了)
功能是从1数到a。(类似python的range

int func(void** func_save, char *func_flag, int a){return 0;}

参数中的func_save是这个生成器的参数与局部变量的保存区域,func_flag是它的运行状态。
这里简单地将0作为起始状态,1作为运行状态,2作为结束状态。

这个结构体就是用来保存生成器的东东。

    // The data of the running function.
    typedef struct {int arg_0; int counter;} func_object;

一开始,初始化的时候在内存中开辟一块区域,存放生成器的参数与局部变量。

	if (!*func_save && !(*func_flag)) {
	    *func_save = calloc(1, sizeof(func_object));
	    ((func_object*)*func_save) -> arg_0 = a;
	    *func_flag = 1;
	}

当然有开始就要有结束,做事不能够虎头蛇尾。

    // Exit status.
    if(((func_object*)*func_save) -> counter >= ((func_object*)*func_save) -> arg_0){
        free(*func_save);
        *func_save = NULL;
        *func_flag = 2;
    }

然后在这两片代码中间插入主要的代码。
如果使用类似状态机或断点的设计,则需要在结构体中存放相应的状态
(使用时通常用switch...case...

    // Execute the main function.
    ((func_object*)*func_save) -> counter ++;

然后在函数的末尾将生成结果返回。(把最开始的return 0去掉

    // Yield value.
    return ((func_object*)*func_save) -> counter;

这样一个简易的生成器就造好了

有什么问题呢?
我们先写一段代码测试一下

int main(void){
    void *f1 = 0,*f2 = 0;
    char f1_flag = 0, f2_flag = 0;
    do{
        printf("func 1: %d, ",func(&f1, &f1_flag, 5));
        printf("func 2: %d\n",func(&f2, &f2_flag, 10));
    }while(f1_flag < 2 || f2_flag < 2);
    return 0;
}

然后,熟悉的 Segmentation fault 出现了,wow
Segmentation fault
这是为什么呢?

很简单,因为在生成器生成完之后(即counter >= a时),释放生成器的状态。
释放完了,然后返回的时候又调用了被释放的内存。
于是后果可想而知。

此时生成器是在操作完毕之后才返回的,而非在操作之前就判断,整体结构类似于do...while。(感兴趣的可以调换这两片代码的位置)
而此时最后一个生成结果还没有返回,我们需要创建一个临时变量让它返回。

在定义结构体的代码的下方定义一个临时变量,这里叫它ret好了

	int ret;

修改结束时的代码

    // Exit status.
    if(((func_object*)*func_save) -> counter >= ((func_object*)*func_save) -> arg_0){
    	// 这里,储存最后一个返回值
        ret = ((func_object*)*func_save) -> counter;
        free(*func_save);
        *func_save = NULL;
        *func_flag = 2;
        // 这里,返回
        return ret;
    }

这次应该没有问题了…?
Segmentation fault x2
然鹅,事与愿违。

理由也很简单,虽然生成器已经生成结束(已释放内存),但是再次调用时却没有判断生成器是否已经搞完了。
于是乎,在主要代码的部分又双又叕又又又又又调用了被释放的内存

在执行主要的代码之前,还需要判断生成器的状态。

    // When finished.
    if(*func_flag == 2) return 0;

于是,这一小撮代码就大功告成了。

这回能够正确地执行了
在这里插入图片描述
注意:虽然func1在结束后一直返回0,但是在其他地方可能就会出现“手持两把锟斤拷,口中疾呼烫烫烫”了,所以建议对ret变量进行初始化。

完整代码:

/*
 * Virtual Threading.
 * @author: Qiong-Mengzi(Karyna Sakura/かりなさくら)
*/
#include <stdio.h>
#include <stdlib.h>

int func(void** func_save, char *func_flag, int a){
    // The data of the running function.
    typedef struct {int arg_0; int counter;} func_object;
    int ret;
    if (!*func_save && !(*func_flag)) {
        *func_save = calloc(1, sizeof(func_object));
        ((func_object*)*func_save) -> arg_0 = a;
        *func_flag = 1;
    }
    // When finished.
    if(*func_flag == 2) return 0;
    // Execute the main function.
    ((func_object*)*func_save) -> counter ++;
    // Exit status.
    if(((func_object*)*func_save) -> counter >= ((func_object*)*func_save) -> arg_0){
        ret = ((func_object*)*func_save) -> counter;
        free(*func_save);
        *func_save = NULL;
        *func_flag = 2;
        return ret;
    }
    // Yield value.
    return ((func_object*)*func_save) -> counter;
}

int main(void){
    void *f1 = 0,*f2 = 0;
    char f1_flag = 0, f2_flag = 0;
    do{
        printf("func 1: %d, ",func(&f1, &f1_flag, 5));
        printf("func 2: %d\n",func(&f2, &f2_flag, 10));
    }while(f1_flag < 2 || f2_flag < 2);
    return 0;
}

唔,移植的问题嘛,虽然我又自己的内存池来代替calloc(使用标记-压缩的GC算法),但是懒得调试了。
直接上效果(这里我对输出的部分做了修改,生成器生成结束后不输出)
在这里插入图片描述
代码:

#include <stdlib.h>

int func(void** func_save, char *func_flag, int a){
    // The data of the running function.
    typedef struct {int arg_0; int counter;} func_object;
    int ret;
    if (!*func_save && !(*func_flag)) {
        *func_save = calloc(1, sizeof(func_object));
        ((func_object*)*func_save) -> arg_0 = a;
        *func_flag = 1;
    }
    // When finished.
    if(*func_flag == 2) return 0;
    // Execute the main function.
    ((func_object*)*func_save) -> counter ++;
    // Exit status.
    if(((func_object*)*func_save) -> counter >= ((func_object*)*func_save) -> arg_0){
        ret = ((func_object*)*func_save) -> counter;
        free(*func_save);
        *func_save = NULL;
        *func_flag = 2;
        return ret;
    }
    // Yield value.
    return ((func_object*)*func_save) -> counter;
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println("Start.");
  void *f1 = 0,*f2 = 0;
  char f1_flag = 0,f2_flag = 0;
  do{
    if(f1_flag < 2){
      Serial.print("VThread 1: ");
      Serial.print(func(&f1, &f1_flag, 5));
    }
    if(f2_flag < 2){
      Serial.print(" VThread 2: ");
      Serial.print(func(&f2, &f2_flag, 10));
    }
    Serial.println();
  }while(f1_flag < 2 || f2_flag < 2);
  Serial.println("Finish.");
}

void loop() {
  // put your main code here, to run repeatedly:

}

开发板:Arduino Nano (ATMEGA168P,只有可怜的1KiB内存)

我的Github:https://github.com/Qiong-Mengzi
当然,这个测试代码是不可能放在上面的()

  • 20
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
AutoFlowchart Version 2.4.3 ------------------------------------ what's is AutoFlowchart? -------------------------------------------------------------------- AutoFlowchart, the Professional sourcecode flowcharting tool. AutoFlowchart is a excellent tool to generate flowchart from sourcecode.Its flowchart can expand and shrink. and you can pre-define the the width , height,Horizontal spacing and vertical spacing. Move and zoom is also very easy. It can export the flowchart as a Microsoft Visio/Word file or a bitmap file. It can help programmers understand, document and visualize source code. It supports C,C++,VC++(Visual C++ .NET),Delphi(Object Pascal). In the future,It will support more languages. You can use it on Windows 9X/NT/me/XP. You can trial it for 50 times. Registration fee is $129. http://www.ezprog.com/order.htm what's new? -------------------------------------------------------------------- v2.4.3 [2008-07-12] 1. Add process to "return","continue" and "break"; 2. Revised the color when export to coloried Ms Word file; v2.4.1 [2008-04-21] 1. Add "read only" options; 2. Change multi language captions for "open project" and "save project"; v2.4 [2008-04-02] 1. Add "project" to AutoFlowchart to manage more files; v2.3 [2008-01-14] 1. auto show a detail code ; 2. use GDI+ to draw flowchart and draw text; 3. add search function; 4. change the arrowhead shape; 5. remove a bug when in Windows Server 2003; v2.0.3 [2007-12-06] 1. It's use a SDI window for flowchart instead of MDI, then it would be possible to display the code window on one monitor while displaying the flowchart on a second monitor! Thanks for Mr. Shahine Ghanbarzadeh! v2.0.2 [2007-11-29] 1. Add treatment to exceptional such as unmatched brackets. 2. Add treatment to special function which lines is more than 1000. Ms Visio is supported! Get Started -------------------------------------------------------------------- 1. Open a *.pas/*.c/*.cpp/*.afp file; 2. Double click the begin row of any statement ,then you can see a flowchart; 3. Click a apart of the FlowChart ,you can see the part of sentence of this block; 4. change the value of the first spinedit,you can see the current block. Register Notes: If you are a registered user, please put the license file to the install path. Check out our WWW Home Page: http://www.ezprog.com AutoFlowchart can be ordered for $79 from: CompuServe: ShareIt (#197238) Direct: http://www.shareit.com/product.html?productid=197238&languageid=1 Contact -------------------------------------------------------------------- support : support@ezprog.com sales : sals@ezprog.com Msn : support@ezprog.com _______ ____|__ | (R) --| | |------------------- | ____|__ | Association of | | |_| Shareware |__| o | Professionals -----| | |--------------------- |___|___| MEMBER

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值