C-实现-Atari-经典街机游戏飞天蜈蚣

这是一个用C语言实现的《飞天蜈蚣》的“复刻”,由于Linux操作系统下的图形库不够绘制绚丽的画面,因此在本程序中仅用简单的符号来代表不同的游戏角色.访问本油管链接可以阿达雅游戏的视频: https://www.youtube.com/watch... 基本介绍注意: 本程序需要引入 ncurses 库, 这个库只存在于 Unix 的操作系统. 在开始运行之前,建议您通过以下 Debian/Ubuntu Linux 的命令行安装需要的库: sudo apt−get install libncurses5−dev libncursesw5−dev//libncurses5−dev: Developer’s libraries for ncurses//libncursesw5−dev: Developer’s libraries for ncursesw问题重申基于原始的雅达利游戏, 我重写了部分规则. 为了使我的程序画面更加稳定和流程,我做了以下必要的前提条件. 游戏规则蝎子是游戏里另一个一意孤行的角色. 相比于蜘蛛, 它就完全是个坏蛋, 它虽然只能向左或向右走但是一旦它碰到玩家玩家就会失去一条生命. 蜈蚣蜈蚣是玩家最主要要消灭的敌人. 如果没有任何障碍物, 蜈蚣只会一行接着一行地往下走.每次玩家击中蜈蚣的部位都会变成一个蘑菇. 如果击中的部位既不在头也不在尾, 蜈蚣就会自己撕裂成两部分, 获得新的头部并向上或向下接近玩家. 除此之外, 如果蜈蚣遇到蘑菇或者墙壁时, 它会拐弯下移. 尽量让自己远离蜈蚣,如果被它碰到的话也会失去生命哦! 为了让上述规则更易懂, 我创建了如下关系表格: 撞击关系玩家子弹蘑菇蜈蚣蜘蛛玩家死亡, 失误一次生命蜘蛛死亡, 获得600分蘑菇消失, 蘑菇总数 - 1不考虑蝎子玩家死亡, 失误一次生命蝎子死亡, 获得600分不考虑不考虑蜈蚣玩家死亡, 失误一次生命击中头部给100分,其余情况给10分蜈蚣掉头其中一只蜈蚣掉头蘑菇蘑菇消失, 蘑菇总数 - 14 次成功射击后获得1分不考虑不考虑除此以外, 我在游戏中设置了不同关卡. 每个关卡初始的蜈蚣身节是相等的. 但随着蘑菇数量的增多, 玩家很难能击中蜈蚣. 更不用说蝎子和蜘蛛了. 基本猜想我在运行程序的时候遇到许多非程序性问题. 因此, 我罗列出必要的假设: ...

July 5, 2019 · 20 min · jiezi

PHP源码学习20190403-PHP类与对象

baiyan 全部视频:https://segmentfault.com/a/11... 类的存储谈到PHP中的类,我们知道,类是对象的抽象,是所有通过它new出来对象的模板,它是编译阶段的产物。一个类被抽象出来,它本身有自己的属性、方法等等要素。如果让我们自己去用C语言实现一个类的存储结构,我们如何设计?类的几大要素:类常量、普通属性、静态属性、方法类作用域:所有对象之间共享,如类常量、静态属性、方法对象作用域:所有对象之间独享,如普通属性、动态属性下面我们逐个来看究竟它们是被如何存储的:类常量的存储类常量不能被修改,属于类作用域,以const关键字标识,所有对象共享一份类常量。首先我们举一个PHP类常量的例子:class A{ const PI = 3.14;}这里的PI就是一个类常量。常量名为PI,常量值为3.14。我们可以用两种方式来访问它:类外:A::PI类内:self::PI那么我们看一下常量的存储结构:struct _zend_class_entry { ... HashTable constants_table; //常量哈希表,key为常量名,value为常量值 ...};在PHP7中,类是以一个zend_class_entry结构体来存储的。其中这个constants_table字段,就是用来存储类常量的。我们知道,常量是属于类作用域的,而不是对象作用域,所以它的值被直接放在类结构体中。它是一个hashtable,其中key为常量名,value为常量值。当访问某个常量值的时候,我们可以直接根据常量的名字作为key,到hashtable中查找对应的常量值即可,这里还是很好理解的。普通属性的存储普通属性属于对象作用域,每个对象的属性值可以不同,因为我们现在讲的是类,所以我们在类作用域下讲解一下和普通属性相关的数据在类结构中,究竟在哪里有所体现。举一个PHP普通属性的例子:class A{ public $name = 'jby';}这里name就是属性名,它有一个初始化值为jby,也有两种访问方式:类内部:$this->name类外部:对象->name下面看一下在类结构zend_class_entry中,与普通属性存储相关的字段:struct _zend_class_entry { ... int default_properties_count; //普通属性的数量总和 ... zval *default_properties_table; //存放普通属性的初始化值的数组 ... HashTable properties_info; //存储对象属性的信息哈希表,key为属性名,value为zend_property_info结构体 ... }int default_properties_count字段存储一个类中所有普通属性的数量之和我们知道,由于普通属性是对象作用域,所以每一个对象下的普通属性值是不同的,所以针对不同对象的属性值,需要放在具体不同对象的结构中去存储。但是,由于PHP允许普通属性具有初始化值(如上例的jby),而这个初始化值在所有对象实例中共享,故初始化值可以放在类作用域中进行存储。所以初始化的值(如上例的jby)可以直接存储在类结构体下的zval *default_properties_table这个zval数组中,这个zval就指向一个zend_string,其值为jby。然后我们看具体每个对象中属性的存储。由于普通属性有访问权限(public/protected/private)等额外信息需要存储,所以在类作用域内,存储普通属性的信息需要一个结构体,而且是一个普通属性就要对应一个结构体来存储它的信息。。在类结构zend_class_entry中,我们使用HashTable properties_info这个字段来存储普通属性的信息,而这个字段是一个hashtable,它的key为属性名,value为一个结构体,它就是用来存储每一个普通属性的信息的,叫做zend_property_info。每一个属性,就会对应一个zend_property_info结构:typedef struct _zend_property_info { uint32_t offset; //表示普通属性的内存偏移值或静态属性的数组索引 uint32_t flags; //属性掩码,如public、private、protected及是否为静态属性 zend_string *name; //属性名 zend_string *doc_comment; //文档注释信息 zend_class_entry *ce; //所属类} zend_property_info;//flags标识位#define ZEND_ACC_PUBLIC 0x100#define ZEND_ACC_PROTECTED 0x200#define ZEND_ACC_PRIVATE 0x400#define ZEND_ACC_STATIC 0x01我们看这个存储普通属性信息的结构体。下面的属性名等字段我们很容易理解,那么重点则是这个offset字段。由于类作用域是不能确定每个对象中普通属性的值的(不同对象属性值不同),所以普通属性的值会在对象存储结构zend_object中以数组的形式存储(其实是一个柔性数组,后面会讲到)。它的字面意义是偏移量,那么这个偏移量是相对于谁的偏移量呢?答案就是相对于上述的存储值的数组的偏移量,这个偏移量是以一个zval大小(16)递增的(下面讲到对象结构的时候会具体讲)静态属性的存储静态属性也属于类作用域,以static关键字标识,所有对象共享类中的静态属性。所以在类结构zend_class_entry中,就可以直接将静态属性的值存到这个类结构中,静态属性的使用示例如下:class A{ static $instance = null;}访问静态属性也有两种方式:类内部:self::$instance类外部:A::$instance静态属性在所有对象中共享,所以在类作用域中,可以直接存储它的值:struct _zend_class_entry { ... int default_static_members_count; //静态属性数量总和 ... zval *default_static_members_table; //存放静态属性初始化值的数组 zval *static_members_table; //存放静态属性值的数组 ... HashTable properties_info; //存储对象属性的信息哈希表,key为属性名,value为zend_property_info结构体 ...}int default_static_members_count字段存储一个类中所有静态属性的数量之和default_static_members_table用来存放静态属性的初始化值,这一点和普通属性初始化值的存放是相同思想,不再赘述static_members_table用来直接存放静态属性的值HashTable properties_info同样也是一个key为属性名,value为zend_porperty_info结构体的hashtable,里面同样存放着offset,而这个offset代表每一个静态属性在static_members_table和default_static_members_table这两个存放值的数组中的索引。这样,我们可以快速地根据当前的静态属性名,根据静态属性名这个key,在hashtable中查找到zend_property_info结构体中的offset字段,根据这个偏移量,进而去对应的数组单元中,也就是static_members_table或default_static_members_table数组中,找到当前静态属性名对应的值,这样就快速地完成了一次静态属性的访问。方法的存储由于方法也属于类作用域,所有对象共享相同的方法体。所以在类结构中,就可直接以一个hashtable存储方法。key为方法名称,value为具体的zend_function:struct _zend_class_entry { ... HashTable function_table; //成员方法哈希表 ...}其他一个类,可能它是一个子类,也可能是是一个抽象类或接口、甚至是trait,还有类本身的构造函数、析构函数等等。那么这些信息,我们要如何去表示呢?现在我们看一下这个完整的zend_class_entry类结构:struct _zend_class_entry { char type; //类的类型:内部类ZEND_INTERNAL_CLASS(1)、用户自定义类ZEND_USER_CLASS(2) zend_string *name; //类名 struct _zend_class_entry *parent; //父类指针 int refcount; //引用计数 uint32_t ce_flags; //类掩码,如普通类、抽象类、接口等等 int default_properties_count; //普通属性的数量总和 int default_static_members_count; //静态属性数量总和 zval *default_properties_table; //存放普通属性初始化值的数组 zval *default_static_members_table; //存放静态属性初始化值的数组 zval *static_members_table; //存放静态属性值的数组 HashTable function_table; //成员方法哈希表 HashTable properties_info; //存储对象属性的信息哈希表,key为属性名,value为zend_property_info结构体 HashTable constants_table; //常量哈希表,key为常量名,value为常量值 //构造函数、析构函数以及魔术方法的指针 union _zend_function *constructor; union _zend_function *destructor; union _zend_function *clone; union _zend_function *__get; union _zend_function *__set; union _zend_function *__unset; union _zend_function *__isset; union _zend_function *__call; union _zend_function *__callstatic; union _zend_function *__tostring; union _zend_function *__debugInfo; union _zend_function *serialize_func; union _zend_function *unserialize_func; zend_class_iterator_funcs iterator_funcs; //自定义的钩子函数,通常是定义内部类时使用,可以灵活的进行一些个性化的操作 //用户自定义类不会用到,暂时忽略即可 zend_object* (*create_object)(zend_class_entry *class_type); zend_object_iterator *(*get_iterator)(zend_class_entry *ce, zval *object, int by_ref); int (*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type); /* a class implements this interface */ union _zend_function *(*get_static_method)(zend_class_entry *ce, zend_string* method); /* serializer callbacks */ int (*serialize)(zval *object, unsigned char **buffer, size_t *buf_len, zend_serialize_data *data); int (*unserialize)(zval *object, zend_class_entry *ce, const unsigned char *buf, size_t buf_len, zend_unserialize_data *data); uint32_t num_interfaces; //实现的接口数量总和 uint32_t num_traits; //使用的trait数量总和 zend_class_entry **interfaces; //实现的接口,可以理解为它指向一个一维数组,一维数组里全部存放的都是类结构的指针,指向它所实现的接口类 zend_class_entry **traits; //所使用的trait,理解方法同上 zend_trait_alias **trait_aliases; //trait别名,解决多个trait中方法重名冲突的问题 zend_trait_precedence **trait_precedences; union { struct { zend_string *filename; uint32_t line_start; uint32_t line_end; zend_string *doc_comment; } user; struct { const struct _zend_function_entry *builtin_functions; struct _zend_module_entry *module; //所属扩展 } internal; } info;}对象的存储普通属性的存储现在我们再谈对象。我们知道,对象是类的具体实现,是运行阶段的产物。其普通属性是每个对象独享的,所以,在分析对象中,我们要尤其注重每个对象独特的普通属性值是如何存储的。由于之前在讲类存储的时候已经有了铺垫,还记得之前说的zend_property_info中的offset偏移量吗,我们带着这个知识点,直接看对象的存储结构:struct _zend_object { zend_refcounted_h gc; //内部存有引用计数 uint32_t handle; zend_class_entry *ce; //所属的类 const zend_object_handlers *handlers; HashTable *properties; //存储动态属性值 zval properties_table[1]; //柔性数组,每一个单元都是zval类型,用来存储普通属性值,offset就是相对于当前字段首地址的偏移量};我们知道,一个对象,就对应一个zend_object结构。那么最重要的字段就是zval properties_table[1]字段了。它是一个柔性数组,放到结构体的末尾,可以存储变长大小的数据,且与结构体内存空间紧紧相连(柔性数组请看这一系列的前几篇文章有详细讲解)。在创建一个新对象的时候,在类作用域中存储的普通属性的初始化值,都会拷贝到对象结构中的柔性数组中那么现在,之前讲过的类结构中property_info哈希表中的字段的value值zend_property_info中的offset偏移量字段就要派上用场了。想一下,如果让我们访问某个对象的普通属性的值,应该如何访问: - 通过指针ce找到当前对象对应的类结构zend_class_entry - 取出当前类结构中的Hashtable property_info字段,这个字段是一个哈希表,存有属性的信息。 - 将要查找的属性名作为key,到哈希表中找到对应的value,即zend_property_info结构体,并取出结构体中的offset字段 - 到当前对象zend_object结构体中,通过内存地址计算(柔性数组的起始地址+offset)就可以得到所要访问的当前对象的某个普通属性的值了那么我们看一下其他几个字段的作用:handle:一次request期间对象的编号,每个对象都有一个唯一的编号,与创建先后顺序有关,主要在垃圾回收时使用handlers:保存的对象相关操作的一些函数指针,比如属性的读写、方法的获取、对象的销毁/克隆等等,这些操作接口都有默认的函数,这里存储了这些默认函数的指针:struct _zend_object_handlers { int offset; zend_object_free_obj_t free_obj; //释放对象 zend_object_dtor_obj_t dtor_obj; //销毁对象 zend_object_clone_obj_t clone_obj;//复制对象 zend_object_read_property_t read_property; //读取成员属性 zend_object_write_property_t write_property;//修改成员属性 ...}//处理对象的handlerZEND_API zend_object_handlers std_object_handlers = { 0, zend_object_std_dtor, /* free_obj */ zend_objects_destroy_object, /* dtor_obj */ zend_objects_clone_obj, /* clone_obj */ zend_std_read_property, /* read_property */ zend_std_write_property, /* write_property */ zend_std_read_dimension, /* read_dimension */ zend_std_write_dimension, /* write_dimension */ zend_std_get_property_ptr_ptr, /* get_property_ptr_ptr */ NULL, /* get */ NULL, /* set */ zend_std_has_property, /* has_property */ zend_std_unset_property, /* unset_property */ zend_std_has_dimension, /* has_dimension */ zend_std_unset_dimension, /* unset_dimension */ zend_std_get_properties, /* get_properties */ zend_std_get_method, /* get_method */ NULL, /* call_method */ zend_std_get_constructor, /* get_constructor */ zend_std_object_get_class_name, /* get_class_name */ zend_std_compare_objects, /* compare_objects */ zend_std_cast_object_tostring, /* cast_object */ NULL, /* count_elements */ zend_std_get_debug_info, /* get_debug_info */ zend_std_get_closure, /* get_closure */ zend_std_get_gc, /* get_gc */ NULL, /* do_operation */ NULL, /* compare */}动态属性的存储properties: 普通成员属性哈希表,key为动态属性名,value为动态属性值。对象创建之初这个值为NULL,主要是在动态定义属性时会用到。那么什么是动态属性呢?就是之前在类定义阶段未定义的属性,在运行期间动态添加的属性,如:class A{ public $name = 'jby';}$a = new A();$a->age = 18;这里的age就是动态属性,而name是普通属性。基于之前讲过的查找普通属性的流程,我们由特殊到一般地得出查找所有类型的对象属性的方式:在查找一个对象的属性的时候,会首先按照我们之前讲过的查找普通属性的方式,首先找到偏移量offset,即类结构的zend_class_entry下的properties_info字段中的offset,然后根据这个偏移量offset到对象结构zend_object下的properties_table柔性数组中找。如果按照查找普通属性的方式没有找到,那么我们再去zend_object下的properties字段继续查找动态属性即可,整理如下: - 通过指针ce找到当前对象对应的类结构zend_class_entry - 取出当前类结构中的Hashtable property_info字段,这个字段是一个哈希表,存有属性的信息。 - 将要查找的属性名作为key,到哈希表中找到对应的value,即zend_property_info结构体,并取出结构体中的offset字段 - 到当前对象zend_object结构体中,通过内存地址计算(柔性数组的起始地址+offset)就可以得到所要访问的当前对象的某个普通属性的值 - 如果以上都没有找到,说明它是一个动态属性,那么就去zend_object下的properties哈希表中查找,属性名作为key,到这个哈希表中查找对应的value即可

July 5, 2019 · 3 min · jiezi

交叉链表

无可奈何花落去 似曾相识燕归来 前言两个链表list1和list2如果中间有一个交汇点。怎样在线性时间内求出这个交汇点? 思路分别求出两个链表的长度 把长链做偏移和短链一样长 同时移动两个链表的首指针,判断节点是否相等 相等则找到交叉点 代码实现 // 定义节点 typedef struct Node { int data; struct Node *next; } Node; /// 计算链表的长度 int getListLength(Node *list) { int length = 1; while (list->next != NULL) { length++; list = list->next; } return length; } // 长链表做偏移 Node* relocateNode (Node *list, int offset) { Node *node1 = list; for (NSInteger i = 0; i < offset; i++) { node1 = node1->next; } return node1; } int findCommonNode (Node *list1, Node *list2) { // 计算两个链表的长度 int list1Length = getListLength(list1); int list2Length = getListLength(list2); // 计算两个链表的长度差值 int offset = abs(list1Length-list2Length); // 记录长链短链 Node *longList = list1; Node *shortList = list2; if (list1Length < list2Length) { longList = list2; shortList = list1; } // 长链做偏移 ,偏移后和短链一样长 longList = relocateNode(longList, offset); // 同时移动两个链表的首指针 while (shortList != NULL) { if (longList->data == shortList->data) { // 如果节点值相等 找到交叉点 return longList->data; } longList = longList->next; shortList = shortList->next; } return 0; } int main(int argc, const char * argv[]) { @autoreleasepool { Node *node1 = malloc(sizeof(Node)); node1->data = 1; Node *node2 = malloc(sizeof(Node)); node2->data = 2; Node *node3 = malloc(sizeof(Node)); node3->data = 3; Node *node4 = malloc(sizeof(Node)); node4->data = 4; Node *node5 = malloc(sizeof(Node)); node5->data = 5; Node *node6 = malloc(sizeof(Node)); node6->data = 6; Node *node7 = malloc(sizeof(Node)); node7->data = 7; node1->next = node2; node2->next = node3; node3->next = node4; node4->next = node5; node5->next = node6; node6->next = NULL; node7->next = node4; Node *list1 = node1; // 1 2 3 4 5 6 Node *list2 = node7; // 7 4 5 6 int commonValue = findCommonNode(list1, list2); NSLog(@"commonValue: %d\b", commonValue);// 4 } return 0; }测试结果 ...

July 4, 2019 · 2 min · jiezi

ffmpeg开发知识点回顾

视频花屏/卡顿原因如果GOP分组中的P帧丢失会造成解码端的图像发生错误为了避免花屏问题的发生,一般如果发现P帧或者I帧丢失,就不显示本GOP内的所有帧,直到下一个I帧来后,重新刷新图像。时间基tbr: 帧率tbn:time base of streamtbc:time base of codec时间戳PTS: Presentation timestampDTS: Decoding timestampI(intra)/B(bidirectional)/P(predicted)帧时间戳顺序实际帧顺序:I B B P存放帧顺序:I P B B解码时间戳:1 4 2 3展示时间戳:1 2 3 4从哪儿获得PTSAVPacket中的PTSAVFrame中的PTSav_frame_get_baset_effort_timstamp()计算当前帧的PTSPTS=PTS * av_q2d(video_stream->time_base)av_q2d(AVRotional a){ return a.num/(double)a.den }计算下一帧的PTSvideo_clock: 预测的下一帧视频的PTSframe_delay: 1/tbraudio_clock: 音频当前播放的时间戳多媒体格式转换ffmpeg -i out.mp4 -vcodec copy -acodec copy out.flv -i:输入文件vcodec copy:视频编码处理方式acodec copy:音频编码处理方式该条命令的作用是将视频文件out.mp4格式转换为out.flv,音频编码方式保持不变,视频编码方式保持不变。 录音命令ffmpeg -f avfoundation -i :0 out.wav:0 代表音频设备该条命令表示使用AVfoundation框架录制一段音频数据,数据来源是麦克风,输出文件是out.wav,录制完成之后,使用ffplay out.wav命令进行播放。 录屏命令ffmpeg -f avfoundation -i 1 r 30 out.yuv -f: 指定使用AVfoundation采集数据-i: 指定从哪儿采集数据,它是一个文件索引号-r:指定帧率该条命令表示使用AVfoundation框架,以30帧每秒的帧率录制屏幕,输出文件是out.yuv。使用ffplay可以进行播放,但是播放的时候需要制定屏幕尺寸和录制的数据格式,否则播放不出来。 ffmpeg滤镜命令ffmpeg -i in.mov -vf crop=in_w-200:in_h-200 -c:v libx264 -c:a copy out.mp4 ...

July 4, 2019 · 1 min · jiezi

PHP源码学习20190402-PHP垃圾回收2

baiyan 全部视频:https://segmentfault.com/a/11... 垃圾回收器缓冲区元素的存储在上一篇文章【PHP源码学习】2019-04-01 PHP垃圾回收1中,我们将所有疑似垃圾的元素都放到垃圾回收器缓冲区中,一直存下去,待存满垃圾回收器缓冲区10000个存储单元之后,垃圾回收算法就会启动,对缓冲区中的所有疑似垃圾进行标记与清除。垃圾回收算法需要对这个缓冲区进行扫描遍历,判定每一个存储单元中的内容是否为最终的垃圾。回顾gc_possible_root()函数,我们将疑似垃圾存入了一个大小为10000的缓冲区中:ZEND_API void ZEND_FASTCALL gc_possible_root(zend_refcounted *ref){ ... if (newRoot) { GC_G(unused) = newRoot->prev; } else if (GC_G(first_unused) != GC_G(last_unused)) { newRoot = GC_G(first_unused); GC_G(first_unused)++; } else { //垃圾回收器存满了,才会启动垃圾回收算法 if (!GC_G(gc_enabled)) { return; } GC_REFCOUNT(ref)++; gc_collect_cycles(); //真正启动垃圾回收算法 GC_REFCOUNT(ref)--; ... }}由此可见,垃圾回收算法会在缓冲区存满的时候才会启动,这样会减少垃圾回收算法运行的频率,进而减小性能的损耗且提高效率(详见上一篇文章)垃圾回收算法的运行在此之前,我们已经将垃圾缓冲区内的所有元素均标记成了紫色GC_PURPLE我们在gc_possible_root()函数中调用了gc_collect_cycles()函数,这个gc_collect_cycles是一个函数指针,实际指向的是zend_gc_collect_cycles()函数,最终会调用它:ZEND_API int zend_gc_collect_cycles(void){ ... //遍历垃圾回收器链表,如果垃圾回收器链表中的元素是紫色,对其进行深度优先遍历并将refcount减1,并把它们标记为灰色GC_GREY gc_mark_roots(); //遍历垃圾回收器链表,如果垃圾回收器链表中的元素是灰色,那么判断每个疑似垃圾的元素的refcount是否>0,如果>0就不是垃圾,将其标记为黑色GC_BLACK并且将refcount+1恢复到原来的值;否则为垃圾,将其标记为白色GC_WHITE,它们是真正的垃圾,后面需要释放掉 gc_scan_roots(); ... //把垃圾回收器中为白色GC_WHITE的垃圾单独摘出来放到to_free垃圾链表中,并删除垃圾回收器中不是垃圾的元素 count = gc_collect_roots(&gc_flags); GC_G(gc_active) = 0; if (GC_G(to_free).next == &GC_G(to_free)) { /* 如果垃圾链表to_free为空,那么就没有需要释放的垃圾,直接返回 */ GC_TRACE("Nothing to free"); return 0; } /* 将全局变量中的垃圾链表拷贝到局部变量中 */ to_free.next = GC_G(to_free).next; to_free.prev = GC_G(to_free).prev; to_free.next->prev = &to_free; to_free.prev->next = &to_free; /* 释放全局的垃圾链表 */ GC_G(to_free).next = &GC_G(to_free); GC_G(to_free).prev = &GC_G(to_free); orig_next_to_free = GC_G(next_to_free); ... if (gc_flags & GC_HAS_DESTRUCTORS) { GC_TRACE("Calling destructors"); /* 在析构之前记录引用计数*/ current = to_free.next; while (current != &to_free) { current->refcount = GC_REFCOUNT(current->ref); current = current->next; } /* 析构对象*/ current = to_free.next; while (current != &to_free) { p = current->ref; GC_G(next_to_free) = current->next; if (GC_TYPE(p) == IS_OBJECT) { zend_object *obj = (zend_object*)p; if (!(GC_FLAGS(obj) & IS_OBJ_DESTRUCTOR_CALLED)) { GC_TRACE_REF(obj, "calling destructor"); GC_FLAGS(obj) |= IS_OBJ_DESTRUCTOR_CALLED; if (obj->handlers->dtor_obj && (obj->handlers->dtor_obj != zend_objects_destroy_object || obj->ce->destructor)) { GC_REFCOUNT(obj)++; obj->handlers->dtor_obj(obj); GC_REFCOUNT(obj)--; } } } current = GC_G(next_to_free); } /* 销毁在对象析构过程中出现的值 */ current = to_free.next; while (current != &to_free) { GC_G(next_to_free) = current->next; if (GC_REFCOUNT(current->ref) > current->refcount) { gc_remove_nested_data_from_buffer(current->ref, current); } current = GC_G(next_to_free); } } /* 销毁zval */ GC_TRACE("Destroying zvals"); GC_G(gc_active) = 1; current = to_free.next; while (current != &to_free) { p = current->ref; GC_G(next_to_free) = current->next; GC_TRACE_REF(p, "destroying"); if (GC_TYPE(p) == IS_OBJECT) { //释放对象 zend_object *obj = (zend_object*)p; EG(objects_store).object_buckets[obj->handle] = SET_OBJ_INVALID(obj); GC_TYPE(obj) = IS_NULL; if (!(GC_FLAGS(obj) & IS_OBJ_FREE_CALLED)) { GC_FLAGS(obj) |= IS_OBJ_FREE_CALLED; if (obj->handlers->free_obj) { GC_REFCOUNT(obj)++; obj->handlers->free_obj(obj); GC_REFCOUNT(obj)--; } } SET_OBJ_BUCKET_NUMBER(EG(objects_store).object_buckets[obj->handle], EG(objects_store).free_list_head); EG(objects_store).free_list_head = obj->handle; p = current->ref = (zend_refcounted*)(((char*)obj) - obj->handlers->offset); } else if (GC_TYPE(p) == IS_ARRAY) { //释放数组 zend_array *arr = (zend_array*)p; GC_TYPE(arr) = IS_NULL; /* GC may destroy arrays with rc>1. This is valid and safe. */ HT_ALLOW_COW_VIOLATION(arr); zend_hash_destroy(arr); } current = GC_G(next_to_free); } /* 释放垃圾所占用内存空间 */ current = to_free.next; while (current != &to_free) { next = current->next; p = current->ref; if (EXPECTED(current >= GC_G(buf) && current < GC_G(buf) + GC_ROOT_BUFFER_MAX_ENTRIES)) { current->prev = GC_G(unused); GC_G(unused) = current; } efree(p); current = next; } while (GC_G(additional_buffer) != additional_buffer_snapshot) { gc_additional_buffer *next = GC_G(additional_buffer)->next; efree(GC_G(additional_buffer)); //通过efree()释放内存空间 GC_G(additional_buffer) = next; } /* 收尾 */ GC_TRACE("Collection finished"); GC_G(collected) += count; GC_G(next_to_free) = orig_next_to_free;#if ZEND_GC_DEBUG GC_G(gc_full) = orig_gc_full;#endif GC_G(gc_active) = 0; } return count;}在第一步的gc_mark_roots()中,调用了gc_mark_grey(),标记为灰色:static void gc_mark_roots(void){ gc_root_buffer *current = GC_G(roots).next; while (current != &GC_G(roots)) { if (GC_REF_GET_COLOR(current->ref) == GC_PURPLE) { gc_mark_grey(current->ref); } current = current->next; }}在第二步的gc_scan_roots()中,调用了gc_scan(),进而又调用了gc_mark_black(),标记为黑色,而标记为白色没有调用函数:static void gc_scan_roots(void){ gc_root_buffer *current = GC_G(roots).next; while (current != &GC_G(roots)) { gc_scan(current->ref); //内部还会调用gc_mark_black() current = current->next; }}下面我们总结一下垃圾回收算法的执行流程: - 调用函数gc_mark_roots();首先对垃圾回收器进行深度优先遍历,将refcount-1,并标记为灰色 - 调用函数gc_scan_roots();再次对垃圾回收器进行深度优先遍历,判断当前的refcount,如果大于0,就不是垃圾,标记为黑色,并且要将refcount+1恢复到原来的值;如果等于0,就是垃圾,标记为白色 - 调用函数gc_collect_roots(),将白色的垃圾从垃圾回收器中摘出来,放到垃圾链表中并等待回收;同时将垃圾回收器中不是垃圾的元素移除,以节省垃圾回收器空间 - 释放全局垃圾链表,拷贝到局部垃圾链表,提高效率 - 遍历局部垃圾链表 - 首先析构对象 - 销毁在对象析构过程中出现的值 - 销毁对象与数组的zval - 释放垃圾所占用内存空间 - 收尾工作那么对其进行深度优先遍历并标记的代码都类似,举一个标记灰色的例子:static void gc_mark_grey(zend_refcounted *ref){ HashTable *ht; Bucket *p, *end; zval *zv;tail_call: if (GC_REF_GET_COLOR(ref) != GC_GREY) { ht = NULL; GC_BENCH_INC(zval_marked_grey); GC_REF_SET_COLOR(ref, GC_GREY); if (GC_TYPE(ref) == IS_OBJECT) { zend_object_get_gc_t get_gc; zend_object *obj = (zend_object*)ref; if (EXPECTED(!(GC_FLAGS(ref) & IS_OBJ_FREE_CALLED) && (get_gc = obj->handlers->get_gc) != NULL)) { int n; zval *zv, *end; zval tmp; ZVAL_OBJ(&tmp, obj); ht = get_gc(&tmp, &zv, &n); end = zv + n; if (EXPECTED(!ht)) { if (!n) return; while (!Z_REFCOUNTED_P(--end)) { if (zv == end) return; } } while (zv != end) { if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); GC_REFCOUNT(ref)--; gc_mark_grey(ref); } zv++; } if (EXPECTED(!ht)) { ref = Z_COUNTED_P(zv); GC_REFCOUNT(ref)--; goto tail_call; } } else { return; } } else if (GC_TYPE(ref) == IS_ARRAY) { if (((zend_array*)ref) == &EG(symbol_table)) { GC_REF_SET_BLACK(ref); return; } else { ht = (zend_array*)ref; } } else if (GC_TYPE(ref) == IS_REFERENCE) { if (Z_REFCOUNTED(((zend_reference*)ref)->val)) { ref = Z_COUNTED(((zend_reference*)ref)->val); GC_REFCOUNT(ref)--; goto tail_call; } return; } else { return; } if (!ht->nNumUsed) return; p = ht->arData; end = p + ht->nNumUsed; while (1) { end--; zv = &end->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { break; } if (p == end) return; } while (p != end) { zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } if (Z_REFCOUNTED_P(zv)) { ref = Z_COUNTED_P(zv); GC_REFCOUNT(ref)--; gc_mark_grey(ref); } p++; } zv = &p->val; if (Z_TYPE_P(zv) == IS_INDIRECT) { zv = Z_INDIRECT_P(zv); } ref = Z_COUNTED_P(zv); GC_REFCOUNT(ref)--; goto tail_call; }}我们可以看到,该函数中用到了大量的递归调用。我们知道,垃圾回收就是用来解决循环引用的问题。循环引用的问题是自己引用自己,那么究竟在哪里才引用了自己,就需要进行深度优先遍历。举个例子,数组中的元素可能又是一个数组,里面的数组元素可能还是一个数组,间接引用了好多层,最后一层才引用了外层数组本身。那么,这种情况下,就需要不断地对其进行遍历并标记,直到找到最终最深的引用位置之前,所有中间的间接引用层都是垃圾,我们都需要对其进行标记并且清除。比如之前讲过的循环引用示例: ...

July 3, 2019 · 4 min · jiezi

sonic容器swss启动过程

sonic容器swss启动过程sonic业务进程都是运行在容器中的,那容器启动后是如何启动它的服务呢。 要分析这个问题,首先要搞清楚容器构建过程。我们以docker-orchagent容器为例进行分析。 Dockerfile文件sonic中的Dockerfile由Dockerfile.j2文件生成。 docker-orchagent/Dockerfile.j2 FROM docker-config-engineARG docker_container_nameRUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name\/%syslogtag%/;" /etc/rsyslog.conf## Make apt-get non-interactiveENV DEBIAN_FRONTEND=noninteractiveRUN apt-get updateRUN apt-get install -f -y ifupdown arping libdbus-1-3 libdaemon0 libjansson4## Install redis-tools dependencies## TODO: implicitly install dependenciesRUN apt-get -y install libjemalloc1COPY \{% for deb in docker_orchagent_debs.split(' ') -%}debs/{{ deb }}{{' '}}{%- endfor -%}debs/RUN dpkg -i \{% for deb in docker_orchagent_debs.split(' ') -%}debs/{{ deb }}{{' '}}{%- endfor %}## Clean upRUN apt-get clean -y; apt-get autoclean -y; apt-get autoremove -yRUN rm -rf /debsCOPY ["files/arp_update", "/usr/bin"]COPY ["enable_counters.py", "/usr/bin"]COPY ["start.sh", "orchagent.sh", "swssconfig.sh", "/usr/bin/"]COPY ["supervisord.conf", "/etc/supervisor/conf.d/"]## Copy all Jinja2 template files into the templates folderCOPY ["*.j2", "/usr/share/sonic/templates/"]#程序的入口点ENTRYPOINT ["/usr/bin/supervisord"]从上面的配置来看,容器启动后制定的程序为:/usr/bin/supervisord ...

July 2, 2019 · 5 min · jiezi

vpp-hqos分析

vpp支持两套qos实现,一套是基于policer实现的qos,另外一套是基于dpdk的qos套件实现的hqos。 基本流程图 如上图所示,worker线程从nic中读取报文进行处理,调用dpdk设备发送函数时,如果配置了hqos,那么设置hqos相关参数,将其送入swq队列(swq队列与worker线程是1:1的关系),worker线程处理结束,hqos线程(根据配置决定个数)轮询从swq中读取报文进行qos处理,qos使用的是dpdk qos套件。 HQOS配置解析dpdk配置dpdk { socket-mem 16384,16384 dev 0000:02:00.0 { num-rx-queues 2 hqos #使能网卡hqos } dev 0000:06:00.0 { num-rx-queues 2 hqos #使能网卡hqos } num-mbufs 1000000}cpu配置cpu { main-core 0 corelist-workers 1, 2, 3, 4 corelist-hqos-threads 5, 6 #启动两个hqos线程,分别使用cpu5和6}通过上面两步配置即可开启hqos配置,会使用默认的hqos配置。 dpdk qos相关参数配置port configurationport { rate 1250000000 /* Assuming 10GbE port */ frame_overhead 24 /* Overhead fields per Ethernet frame: * 7B (Preamble) + * 1B (Start of Frame Delimiter (SFD)) + * 4B (Frame Check Sequence (FCS)) + * 12B (Inter Frame Gap (IFG)) */ mtu 1522 /* Assuming Ethernet/IPv4 pkt (FCS not included) */ n_subports_per_port 1 /* Number of subports per output interface */ n_pipes_per_subport 4096 /* Number of pipes (users/subscribers) */ queue_sizes 64 64 64 64 /* Packet queue size for each traffic class. * All queues within the same pipe traffic class * have the same size. Queues from different * pipes serving the same traffic class have * the same size. */}subport configurationsubport 0 { tb_rate 1250000000 /* Subport level token bucket rate (bytes per second) */ tb_size 1000000 /* Subport level token bucket size (bytes) */ tc0_rate 1250000000 /* Subport level token bucket rate for traffic class 0 (bytes per second) */ tc1_rate 1250000000 /* Subport level token bucket rate for traffic class 1 (bytes per second) */ tc2_rate 1250000000 /* Subport level token bucket rate for traffic class 2 (bytes per second) */ tc3_rate 1250000000 /* Subport level token bucket rate for traffic class 3 (bytes per second) */ tc_period 10 /* Time interval for refilling the token bucket associated with traffic class (Milliseconds) */ pipe 0 4095 profile 0 /* pipe 0到4095使用profile0 pipes (users/subscribers) configured with pipe profile 0 */}pipe configurationpipe_profile 0 { tb_rate 305175 /* Pipe level token bucket rate (bytes per second) */ tb_size 1000000 /* Pipe level token bucket size (bytes) */ tc0_rate 305175 /* Pipe level token bucket rate for traffic class 0 (bytes per second) */ tc1_rate 305175 /* Pipe level token bucket rate for traffic class 1 (bytes per second) */ tc2_rate 305175 /* Pipe level token bucket rate for traffic class 2 (bytes per second) */ tc3_rate 305175 /* Pipe level token bucket rate for traffic class 3 (bytes per second) */ tc_period 40 /* Time interval for refilling the token bucket associated with traffic class at pipe level (Milliseconds) */ tc3_oversubscription_weight 1 /* Weight traffic class 3 oversubscription */ tc0_wrr_weights 1 1 1 1 /* Pipe queues WRR weights for traffic class 0 */ tc1_wrr_weights 1 1 1 1 /* Pipe queues WRR weights for traffic class 1 */ tc2_wrr_weights 1 1 1 1 /* Pipe queues WRR weights for traffic class 2 */ tc3_wrr_weights 1 1 1 1 /* Pipe queues WRR weights for traffic class 3 */}red 拥塞控制red { tc0_wred_min 48 40 32 /* Minimum threshold for traffic class 0 queue (min_th) in number of packets */ tc0_wred_max 64 64 64 /* Maximum threshold for traffic class 0 queue (max_th) in number of packets */ tc0_wred_inv_prob 10 10 10 /* Inverse of packet marking probability for traffic class 0 queue (maxp = 1 / maxp_inv) */ tc0_wred_weight 9 9 9 /* Traffic Class 0 queue weight */ tc1_wred_min 48 40 32 /* Minimum threshold for traffic class 1 queue (min_th) in number of packets */ tc1_wred_max 64 64 64 /* Maximum threshold for traffic class 1 queue (max_th) in number of packets */ tc1_wred_inv_prob 10 10 10 /* Inverse of packet marking probability for traffic class 1 queue (maxp = 1 / maxp_inv) */ tc1_wred_weight 9 9 9 /* Traffic Class 1 queue weight */ tc2_wred_min 48 40 32 /* Minimum threshold for traffic class 2 queue (min_th) in number of packets */ tc2_wred_max 64 64 64 /* Maximum threshold for traffic class 2 queue (max_th) in number of packets */ tc2_wred_inv_prob 10 10 10 /* Inverse of packet marking probability for traffic class 2 queue (maxp = 1 / maxp_inv) */ tc2_wred_weight 9 9 9 /* Traffic Class 2 queue weight */ tc3_wred_min 48 40 32 /* Minimum threshold for traffic class 3 queue (min_th) in number of packets */ tc3_wred_max 64 64 64 /* Maximum threshold for traffic class 3 queue (max_th) in number of packets */ tc3_wred_inv_prob 10 10 10 /* Inverse of packet marking probability for traffic class 3 queue (maxp = 1 / maxp_inv) */ tc3_wred_weight 9 9 9 /* Traffic Class 3 queue weight */}port,subport,pipe,tc,queue这些配置某些参数也可以使用命令行或者api进行配置。 ...

July 2, 2019 · 8 min · jiezi

直播知识结构整理

文章内容来自于逻辑教育公开课。 总结:以上主要是对音视频开发中用到的相关知识进行了一个整理,囊括了流媒体知识、直播技术点、直播架构、音频知识点。

July 2, 2019 · 1 min · jiezi

BST

江南无所谓 聊赠一枝春 前言二叉搜索树插入二叉搜索树遍历二叉搜索树高度二叉搜索树最大值什么是二叉搜索树满足条件: 左节点值 < 根节点值 < 右节点值 定义树节点 typedef struct TreeNode { int data; struct TreeNode *left; struct TreeNode *right; } TreeNode;定义树 typedef struct tree { struct TreeNode *root; } Tree;二叉搜索树的插入插入节点 void insertNode (Tree *tree, int value) { TreeNode *node1 = malloc(sizeof(TreeNode)); node1->data = value; node1->left = NULL; node1->right = NULL; if(tree->root == NULL){ tree->root = node1; } else { TreeNode *temp = tree->root; while (temp != NULL) { if(value < temp->data){ if(temp->left == NULL) { temp->left = node1; return ; } else { temp = temp->left; } } else { if(temp->right == NULL) { temp->right = node1; return; } else { temp = temp->right; } } } } }BST的前中后序遍历 // 前序遍历 根->左->右 void preorderTraverse(TreeNode *tree) { if(tree == NULL) {return;} NSLog(@"%d ", tree->data); preorderTraverse(tree->left); preorderTraverse(tree->right); } //中序遍历 左->根->右 void midTraverse(TreeNode *tree) { if(tree == NULL) {return;} midTraverse(tree->left); NSLog(@"%d ", tree->data); midTraverse(tree->right); } //后序遍历 左->右->根 void postorderTraversal(TreeNode *tree) { if(tree == NULL) {return;} postorderTraversal(tree->left); postorderTraversal(tree->right); NSLog(@"%d ", tree->data); }二叉搜索树高度 int getBSTHeight (TreeNode *node) { if (node == NULL) { return 0; } else { int leftH = getBSTHeight(node->left); int rightH = getBSTHeight(node->right); int max = leftH; if (max < rightH) { max = rightH; } return max+1; } }二叉搜索树最大值 int getMaxNum(TreeNode *node) { if (node == NULL) { return -1; } else { int leftMax = getMaxNum(node->left); int rightMax = getMaxNum(node->right); int current = node->data; int max = leftMax; if (rightMax > max) { max = rightMax; } if (current>max) { max = current; } return max; } }测试功能 int arr[] = {6,3,8,2,5,1,7}; // 创建树 Tree *tree = malloc(sizeof(Tree)); tree->root = NULL; for(int i=0; i<7; i++) { // 树中插入节点 insertNode(tree, arr[i]); } // 计算树的高度 int treeH = getBSTHeight(tree->root); NSLog(@"%d\n", treeH); // 计算树的最大值 int maxNum = getMaxNum(tree->root); NSLog(@"%d\n", maxNum);测试结果 ...

July 1, 2019 · 2 min · jiezi

堆排序heapsort

前言堆排序是排序算法中的一种,算法时间复杂度是O(n log(n))。这里主要介绍堆的构建以及怎样通过heapify操作完成堆排序。代码是用C语言完成的,算法不难,大家可以自己用其他语言实现一下。 什么是堆(Heap)Heap需要满足两个条件: 1.Complete Binary Tree :需要是一颗完全二叉树2.Parent > Children:父节点的值一定要大于子节点的值什么是完全二叉树 生成节点的顺序是从上往下、从左往右依次生成 如下图所示: 父节点的值大于子节点的值 如下图所示: 怎么样用代码表示堆1.假设先有这样一颗完全二叉树,它已经是一个堆了 2.1 我们按照从上往下、从左往右的顺序对每个节点数字进行编号,2.2 我们可以用一个一维数组表示 2.3 使用数组的下标来表示一个完全二叉树的好处就是从任何一个节点出发我都可以通过计算来拿到这个节点的父节点和子节点 构建堆1.假设拿到了一堆数字:10 4 3 5 1 2,这些数字放在了一颗完全二叉树上面,如下图所示: 2.heapify: 把完全二叉树调整成堆,我们把这种操作起个名字叫:heapify 1.第一次heapify操作:把4(父节点)、10、3这三个子节点进行比较,找到最大值和父节点进行交换交换后如下图: 2.第二次heapify操作,把4(父节点)、5、1这三个子节点进行比较,找到最大值和父节点进行交换交换后如下图: 3.这样我们就生成了一个堆:满足完全二叉树、父节点值大于子节点的值 123步的代码实现: void swap(int *tree, int max, int i) { int temp = tree[i]; tree[i] = tree[max]; tree[max] = temp; } /** 对一个二叉树进行heapify操作 @param tree 表示二叉树的数组 @param n 二叉树的节点个数 @param i 表示要对哪个节点进行heapify操作 */ void heapify(int *tree, int n, int i) { if (i >= n) { // 递归函数出口 return; } // 找到i节点的两个子节点 int c1 = i*2 + 1; int c2 = i*2 + 2; // 找个三个节点的最大值 假设i是最大值 int max = i; if(c1 < n && tree[c1] > tree[max]) { // c1 < n 表示节点下面没有子节点 max = c1; } if (c2 < n && tree[c2] > tree[max]) { max = c2; } if (max != i) { // max != i b如果i已经是最大值了就不用交换了 swap(tree, max, i); heapify(tree, n, max);//max节点继续heapify } } int main(int argc, const char * argv[]) { @autoreleasepool { int tree[] = {4,10,3,5,1,2}; int n = 6; heapify(tree, n, 0); for (int i = 0; i < n; i++) { printf("%d\n", tree[i]); } } return 0; }输出结果: ...

July 1, 2019 · 2 min · jiezi

编程语言之问何时该借用何时该创造

本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/Oy... 6 月 22 日,Python 之父 Guido 发了一条推特,说了 Python 的一则历史故事,他说 elif 是从 C 语言中偷过来的: elif 是“else if”的简写,用于条件判断。当只有两个分支时,我们会写成“if...else...”,当出现更多分支时,我们会写成如下格式: if 判断条件1: 做事情1elif 判断条件2: 做事情2else: 做其它事简写而成的 elif 不仅是减少了几个字符,而且由于单一而清晰的用途,它还不会给我们带来理解或使用上的困惑。 但是,简写法并不是主流,完整写法才是主流,C 语言中就是采用完整的写法: if(判断条件1){ 做事情1}else if(判断条件2){ 做事情2}else { 做其它事}没错,C 语言使用的是全拼写法,但是在它的预处理/预编译语句中,还有一个 elif 指令,Guido 所说的“偷”,就是从这来的: #if 常量表达式1// 编译1#elif 常量表达式2// 编译2#else// 编译3#endifPython 没有预编译,所以所谓的偷,跟预编译没有关系,只是在对比两种写法后,借用了更简洁的写法而已。 为什么 C 语言不把两种写法统一起来呢?这我不得而知了,而 Guido 在两种写法中,选择了后一种非主流却更好用的写法。我想对他说,你“偷”得好啊! 实际上,留言区里的人也有同感,纷纷表示:不介意、很 okay、非常喜欢,还有人说“不是偷,而是收获(harvested)”、“不是偷,而是把它提升了一些高度”…… 前不久,我写了一篇《聊聊 print 的前世今生》,print 这个词就是从 C 语言中借用来的。除此之外,如果有人仔细比较这两种语言的关键字和习惯命名,肯定会发现不少相同的内容。 编程语言间有一些共享的元素,这很常见,创造一门语言并不意味着要原创每一个词句,毕竟大部分思想是共通的,作为基础设施的词语更是如此。 那么,我突然好奇了:创造一门编程语言时,什么时候该借用,什么时候该创造呢? 这个问题看起来可能没啥意义,因为终其一生,我们多数人也不大可能会参与创造一门编程语言。 但我觉得它还是极有意义的,首先,提问精神值得肯定,其次,它还提供了一种溯源、甄别、遴选、创造的体系性视角,我认为这是求知的正确思维方式。 带着这个疑惑,我特别想要考察的是 Python 的 for 循环。 ...

June 30, 2019 · 3 min · jiezi

FFmpeg组织结构

学习ffmpeg之前,我们应该对ffmpeg的组织结构有一个大体的了解。ffmpeg安装好之后,使用cd /usr/local/ffmpeg命令进入到ffmpeg目录下,会看到ffmpeg的4个主要目录。 binincludelibshare下面是ffmpeg的组织结构图: 一、bin目录bin目录下主要是编译好的三个工具,ffmpeg、ffplay、ffprobe。ffmpeg主要是提供对音视频进行抽取、滤镜、裁剪等等各种操作的。ffplay主要提供音视频的播放。ffprobe主要是查看音视频的各种信息的。 二、include目录构成和说明下方是ffmpeg的include目录下的组织结构和说明。 目录说明libavcodec提供了一系列编码器的实现libavformat实现在流协议,容器格式及其基本IO访问。libavutil包括了hash器,解码器和各种工具函数。libavfilter提供了各种音视频过滤器。libavdevice提供了访问捕获设备和回放设备的接口。libswresample实现了混音和重采样。libswscale实现了色彩转换和缩放功能。三、lib目录lib目录下的文件,基本上include目录下对应文件的.a & dylib文件。lib目录下的内容如下入所示: 四、share目录share目录下又分为ffmpeg和man两个目录。ffmpeg有一个重要的目录examples,里面有一些示例代码,学习者可以拜读借鉴。man目录下有man1和man3,不知道具体是干什么的。

June 29, 2019 · 1 min · jiezi

VPP-vppmain线程时间轮用法

vpp_main线程通过在文件vppsrcvlibmain.c中包含#include <vppinfra/tw_timer_1t_3w_1024sl_ov.h>指定了使用1t_3w_1024sl_ov类型时间轮构建定时器。 数据结构[ ] vlib_node_main_t/* 线程与定时器相关成员 */typedef struct{ ...... /* Timing wheel for scheduling time-based node dispatch. */ /* 基于时间的调度策略,依赖时间轮构建,使用的是1t_3w_1024sl_ov类型时间轮 */ void *timing_wheel; /* vpp_main线程注册的定时器的私有数据保存向量,索引来自data_from_advancing_timing_wheel */ vlib_signal_timed_event_data_t *signal_timed_event_data_pool; /* Opaque data vector added via timing_wheel_advance. */ /* 已经发生的定时事件的用户句柄保存内存 */ u32 *data_from_advancing_timing_wheel; /* CPU time of next process to be ready on timing wheel. */ f64 time_next_process_ready;/* 该值目前没有使用,根据意思是最近一个可能超时的时间 */ ......} vlib_node_main_t;[ ] vlib_signal_timed_event_data_t/* 定时器私有数据 */typedef struct{ u16 n_data_elts;/* 数据元素个数 */ u16 n_data_elt_bytes;/* 每个元素字节数 */ /* n_data_elts * n_data_elt_bytes 即下面union存放的数据大小*/ u32 n_data_bytes; /* Process node & event type to be used to signal event. */ /* 该事件所属协程 */ u32 process_node_index; u32 event_type_index;/* 事件类型索引,用于通知协程的事件通知机制什么事情发生了 */ /* 私有数据存放位置,数据较少时,放在静态数组inline_event_data中, ** 否则放在动态内存event_data_as_vector中 */ union { u8 inline_event_data[64 - 3 * sizeof (u32) - 2 * sizeof (u16)]; /* Vector of event data used only when data does not fit inline. */ u8 *event_data_as_vector; };}vlib_signal_timed_event_data_t;代码流程初始化时间轮/* Main function. */intvlib_main (vlib_main_t * volatile vm, unformat_input_t * input){ ...... /* 分配时间轮描述控制块 */ nm->timing_wheel = clib_mem_alloc_aligned (sizeof (TWT (tw_timer_wheel)), CLIB_CACHE_LINE_BYTES); /* 确保可以存放10个事件 */ vec_validate (nm->data_from_advancing_timing_wheel, 10); _vec_len (nm->data_from_advancing_timing_wheel) = 0; /* Create the process timing wheel */ /* 创建时间轮 */ TW (tw_timer_wheel_init) ((TWT (tw_timer_wheel) *) nm->timing_wheel, 0 /* no callback */ , 10e-6 /* timer period 10us */ , ~0 /* max expirations per call */ ); ...... return 0;}定时器用户ID构成原则定时器的用户ID由两部分构成,私有数据索引和类型标志位,最低位为类型标志位: ...

June 28, 2019 · 4 min · jiezi

VPP-API机制分析上

VPP除了使用命令行进行配置外,还可以使用API进行配置。VPP不仅支持c语言的API,还支持python,java,lua等高级语言的API,非常适合自动化部署。 API简单使用案例VPP提供了一个VAT客户端程序,用于进行简单的API测试。可执行文件位于: debug版本:vpp源码路径/build-root/build-vpp_debug-native/vpp/bin/vpp_api_testrelease版本:vpp源码路径/build-root/build-vpp-native/vpp/bin/vpp_api_test启动vpp与vat程序sudo systemctl start vppcd vpp源码路径/build-root/build-vpp-native/vpp/bin/sudo vpp_api_testload_one_plugin:68: Loaded plugin: /usr/lib/x86_64-linux-gnu/vpp_api_test_plugins/memif_test_plugin.so......load_one_plugin:68: Loaded plugin: /usr/lib/x86_64-linux-gnu/vpp_api_test_plugins/mactime_test_plugin.sovat# vat# 简单使用[ ] help命令列出所有的命令vat# vat# helpHelp is available for the following:acl_add_replace......[ ] help + 具体命令 列出指定命令的帮助信息vat# help acl_delusage: acl_del <acl-idx>vat# [ ] quit 或者 q 命令退出vatvat# quitadmin@ubuntu:~/vpp/build-root/build-vpp-native/vpp/bin$ [ ] show_version 显示VPP版本vat# show_version program: vpe version: 19.08-rc0~65-g3b62e29c3 build date: Sat Apr 20 13:38:27 CST 2019build directory: /home/admin/vppvat# VPP API交互过程vpp的api是CS模型。VPP是服务器端,监听客户端连接请求,处理客户端发送的api请求,返回应答。如下图所示: 如上图所示,客户端VAT向VPP请求连接,VPP返回新连接。连接建立之后,VAT发送API请求,VPP处理请求返回应答。VPP支持unix套接字或者inet套接字。VPP支持多个客户端同时进行请求。VPP支持使用共享内存进行数据交换,当客户端和服务器端都在同一个节点上的时候,可以选择使用共享内存进行message交换(3,和4交互过程可以选择共享内存交换也可以选择套接字交换),提高通信速率。 VPP API编写说明VPP API命名规则vpp一共支持三种类型的API: ...

June 28, 2019 · 4 min · jiezi

VPPMAIN线程与VPPWORK线程之间的交互机制

VPP是多线程模型,共享地址空间,最快的通信机制就是直接访问彼此之间的数据。VPP自己实现了一套简单的线程安全机制,用于保护临界区。 VPP多线程之间同步采用的是类似于带信号和超时机制的自旋锁,主要有check、sync、release操作。总体上类似于pthread_cond_timedwait中的互斥体改成自旋锁所提供的功能,超过BARRIER_SYNC_TIMEOUT时间的话说明可能发生死锁故直接abort。其中: [ ] vlib_worker_thread_barrier_check类似于pthread_cond_wait操作,等待vlib_worker_threads->wait_at_barrier条件。[ ] vlib_worker_thread_barrier_sync类似于spin_lock操作,置位vlib_worker_threads->workers_at_barrier。只有主线程可以调用该函数,通知其它线程准备同步。[ ] vlib_worker_thread_barrier_release类似于spin_unlock操作,复位vlib_worker_threads->workers_at_barrier。只有主线程可以调用该函数,通知其它线程同步结束。vpp_main线程访问vpp_worker线程的数据的保护机制数据结构vlib_worker_thread_ttypedef struct{ ...... volatile u32 *wait_at_barrier;/* 通知work线程开始等待sync标志,main线程开启sync,设置为1,结束设置为0 */ volatile u32 *workers_at_barrier;/* 统计已经进入sync的worker线程的个数,由worker线程加1 */ i64 recursion_level;/* 当前递归深度 */ u64 barrier_sync_count;/* 当前多少个线程已经同步了,当该值等于work线程数时,开始执行临界区操作 */ u8 barrier_elog_enabled; const char *barrier_caller;/* 开启本次sync的函数名字 */ const char *barrier_context;} vlib_worker_thread_t;vlib_main_ttypedef struct vlib_main_t{ ...... /* debugging */ volatile int parked_at_barrier; /* * Barrier epoch - Set to current time, each time barrier_sync or * barrier_release is called with zero recursion. * 用于计算sync持续时间 */ f64 barrier_epoch; /* Earliest barrier can be closed again */ /* 当前时间小于barrier_no_close_before,不允许启动sync */ f64 barrier_no_close_before; ......} vlib_main_t;相关函数分析[ ] vlib_worker_thread_barrier_syncmain线程调用该函数通知worker线程开始sync,等待所有worker线程进入sync状态后,执行临界操作。 ...

June 28, 2019 · 9 min · jiezi

C语言之通俗易懂的递归算法

摘要记得大学接触的第一门课程就是C语言,里面让我印象深刻之一就是递归,受大学老师讲递归的启发我尝试着用最通俗、最易懂的方式讲解递归。递归其实真的不难。觉得递归很难的朋友,可以试试看一下,相信如果你能认真的看完这篇文章,或许会有很大的收获 递归产生条件递归两个必要条件: **1.递归函数** **2.递归出口**下面的练习会让你清晰的发现,递归其实就是要找 递归函数和递归出口 这两步 练习 假设有个数列 1 3 5 7 9 11 .... 找到第n项的值逻辑分析: 递归函数:f(n) = f(n-1)+2;递归出口:f(1) = 1; 代码实现 /** 假设有个数列 1 3 5 7 9 11 .... 递归函数:f(n) = f(n-1)+2; 递归出口: f(1) = 1; @param n 求n项的值 @return 返回第n项的值 */ int find(int n) { if (n == 1) {// 递归出口 return 1; } else {//递归函数 return find(n-1)+2; } }Fibonacci 斐波那契数列逻辑分析: ...

June 28, 2019 · 2 min · jiezi

GTKGTK介绍

最近在用GTK写一些工具,所以写一个基础教程系列,总结学习成果。 简介GTK是一款开源的、面向多平台的GUI工具箱,其英文全称为GIMP Toolkit。最初是Peter Mattis 和 Spencer Kimball 为GNU Image Manipulation Program (GIMP)编写,用来替代付费的Motif。在后续的发展中,它已经成为通用的GUI库,应用于越来越多的程序,Linux平台的图形应用程序的半壁江山都是使用GTK编写的。 GTK的英文全称GTK的英文全称,让我想到了GCC。GCC最初定位于GNU C Compiler,但随着支持的编译器越来越多,它的定义已经包不住编译器的多样性,所以现在改成了GNU Compiler Collection。这样看来,是不是GTK的名字也得换换了,毕竟现有的名字很局限。 GTK的语言绑定GTK是使用C语言写的,所以其原生API都是面向C的,同时GTK的一大特点是,在C语言层面实现了面向对象的特性。如果你是用C++语言作为开发语言、调用GTK的C接口的话,使用会稍显繁琐,这是语言层面的差异,跟框架关系不大。正是为了避免不同语言调用C的繁琐,GTK提供了多语言的绑定,为不同的语言提供同等抽象级别的语言调用,这样C++程序员就可以直接调用C++的语言绑定,使用方式友好。 GTK的授权GTK是完全免费的,而且基于LGPL协议,这可以保证私有软件通过链接使用GTK可以不把软件源代码开放,对商业应用较友好,这跟GPL协议是不一样的。也正是LGPL协议,使得早些年Gnome(基于GTK编写)风头胜过KDE(基于QT编写)。 GTK的跨平台GTK是跨平台的,支持Unix类的系统、Windows,甚至手机平台。之前我专门有篇文章介绍了在Windows下的环境搭建,C语言的开发环境的搭建还是非常容易的。 GTK vs GTK+关于名字。从网上的资料上,你可以看到GTK+的字眼,这个加号官方是有描述的: The "plus" was added to "GTK" once it was moved out of the GIMP sources tree and the project gained utilities like GLib and the GTK type system, in order to distinguish it from the previous, in-tree version.大意是:GTK从GIMP独立出来之后,加入了一些GLib和GTK类型系统的支持,为了和GIMP代码树中的版本区分,所以带上加号,这一区分就是好多年,给广大的人民群众带来了不小的认知麻烦。在今年,官方终于决定把加号去掉,以后直接叫GTK。 GTK的发布版本关于版本。现在开源的大环境是采用刷版本的方式,像火狐浏览器,谷歌浏览器版本蹭蹭的涨。之前GTK一直采用小步慢跑的版本方式,估计也快要刷版本了,下面引用一篇旧闻: GNOME开发者在多伦多举办的GTK会议上讨论了新的Gtk发布方案,针对Gtk 3.x系列中的问题,开发者提议加快大版本的发布速度:每两年发布一个大版本如 Gtk 4、Gtk 5和Gtk 6,每6个月发布一个与旧版本不兼容的小版本,如Gtk 4.2、Gtk 4.4和Gtk 4.6。这项计划意味着Gtk 4.0不是我们将称之为Gtk 4的最终稳定API。新的大版本能与旧的版本并行安装,如Gtk 4 和Gtk 3能安装在一个系统中,但不兼容的小版本不能,它们使用了相同的pkg-config名字和头文件目录。每一个连续小版本的API将逐渐成熟稳定,也就是说Gtk 4.6发布时API将最终稳定下来,Gtk 4.6可以称之为 Gtk 4了。使用Gtk的开发者可以选择跟随稳定的版本。为什么选择GTK免费这条最实在。大的组织,比如公司,也是很注重成本的;小的个人,财务的承受能力也是有限的,这是GTK的诞生的原因。而且,很多软件授权真的不便宜。 ...

June 27, 2019 · 1 min · jiezi

sonic容器swss启动过程

sonic容器swss启动过程sonic业务进程都是运行在容器中的,那容器启动后是如何启动它的服务呢。 要分析这个问题,首先要搞清楚容器构建过程。我们以docker-orchagent容器为例进行分析。 Dockerfile文件sonic中的Dockerfile由Dockerfile.j2文件生成。 docker-orchagent/Dockerfile.j2 FROM docker-config-engineARG docker_container_nameRUN [ -f /etc/rsyslog.conf ] && sed -ri "s/%syslogtag%/$docker_container_name\/%syslogtag%/;" /etc/rsyslog.conf## Make apt-get non-interactiveENV DEBIAN_FRONTEND=noninteractiveRUN apt-get updateRUN apt-get install -f -y ifupdown arping libdbus-1-3 libdaemon0 libjansson4## Install redis-tools dependencies## TODO: implicitly install dependenciesRUN apt-get -y install libjemalloc1COPY \{% for deb in docker_orchagent_debs.split(' ') -%}debs/{{ deb }}{{' '}}{%- endfor -%}debs/RUN dpkg -i \{% for deb in docker_orchagent_debs.split(' ') -%}debs/{{ deb }}{{' '}}{%- endfor %}## Clean upRUN apt-get clean -y; apt-get autoclean -y; apt-get autoremove -yRUN rm -rf /debsCOPY ["files/arp_update", "/usr/bin"]COPY ["enable_counters.py", "/usr/bin"]COPY ["start.sh", "orchagent.sh", "swssconfig.sh", "/usr/bin/"]COPY ["supervisord.conf", "/etc/supervisor/conf.d/"]## Copy all Jinja2 template files into the templates folderCOPY ["*.j2", "/usr/share/sonic/templates/"]#程序的入口点ENTRYPOINT ["/usr/bin/supervisord"]从上面的配置来看,容器启动后制定的程序为:/usr/bin/supervisord ...

June 27, 2019 · 5 min · jiezi

sonic容器构建

sonic容器构建sonic中大量的组件运行在docker容器中,用于隔离彼此的运行环境,从而解决相互之间的互斥问题。下面我们分析一下sonic中各个容器的构建过程。 Dockerfile文件的生成过程sonic中的容器Dockerfile文件是通过jinjia2模板文件生成的,使用j2命令。 sonic在编译过程中首先会构建一个叫sonic-slave的容易,该容器用来做编译容器,后续所有的编译过程都是在该容器中进行的。 编译容器生成Dockerfile编译容器使用slave.mk作为makefile,其中有这样一段代码将Dockerfile.j2转换成Dockerfile # Targets for building docker images$(addprefix $(TARGET_PATH)/, $(SONIC_DOCKER_IMAGES)) : $(TARGET_PATH)/%.gz : .platform docker-start $$(addprefix $(DEBS_PATH)/,$$($$*.gz_DEPENDS)) $$(addprefix $(FILES_PATH)/,$$($$*.gz_FILES)) $$(addprefix $(PYTHON_WHEELS_PATH)/,$$($$*.gz_PYTHON_WHEELS)) $$(addsuffix -load,$$(addprefix $(TARGET_PATH)/,$$($$*.gz_LOAD_DOCKERS))) $$($$*.gz_PATH)/Dockerfile.j2 $(HEADER) # Apply series of patches if exist if [ -f $($*.gz_PATH).patch/series ]; then pushd $($*.gz_PATH) && QUILT_PATCHES=../$(notdir $($*.gz_PATH)).patch quilt push -a; popd; fi mkdir -p $($*.gz_PATH)/debs $(LOG) mkdir -p $($*.gz_PATH)/files $(LOG) mkdir -p $($*.gz_PATH)/python-wheels $(LOG) sudo mount --bind $(DEBS_PATH) $($*.gz_PATH)/debs $(LOG) sudo mount --bind $(FILES_PATH) $($*.gz_PATH)/files $(LOG) sudo mount --bind $(PYTHON_WHEELS_PATH) $($*.gz_PATH)/python-wheels $(LOG) # Export variables for j2. Use path for unique variable names, e.g. docker_orchagent_debs $(eval export $(subst -,_,$(notdir $($*.gz_PATH)))_debs=$(shell printf "$(subst $(SPACE),\n,$(call expand,$($*.gz_DEPENDS),RDEPENDS))\n" | awk '!a[$$0]++')) $(eval export $(subst -,_,$(notdir $($*.gz_PATH)))_whls=$(shell printf "$(subst $(SPACE),\n,$(call expand,$($*.gz_PYTHON_WHEELS)))\n" | awk '!a[$$0]++')) $(eval export $(subst -,_,$(notdir $($*.gz_PATH)))_dbgs=$(shell printf "$(subst $(SPACE),\n,$(call expand,$($*.gz_DBG_PACKAGES)))\n" | awk '!a[$$0]++')) j2 $($*.gz_PATH)/Dockerfile.j2 > $($*.gz_PATH)/Dockerfile #使用环境变量编译Dockfile.j2 docker build --squash --no-cache \ --build-arg http_proxy=$(HTTP_PROXY) \ --build-arg https_proxy=$(HTTPS_PROXY) \ --build-arg user=$(USER) \ --build-arg uid=$(UID) \ --build-arg guid=$(GUID) \ --build-arg docker_container_name=$($*.gz_CONTAINER_NAME) \ -t $* $($*.gz_PATH) $(LOG) docker save $* | gzip -c > $@ # Clean up if [ -f $($*.gz_PATH).patch/series ]; then pushd $($*.gz_PATH) && quilt pop -a -f; popd; fi $(FOOTER)根容器:docker-base编译的过程首先构建一个docker-base容器。 ...

June 27, 2019 · 4 min · jiezi

SONIC-VLAN配置流程

SONIC VLAN配置流程sonic vlan配置通过订阅config_db的键空间事件完成vlan配置信息从config_db到内核和硬件。config_db.json格式如下: "VLAN": { "Vlan1000": { "vlanid": "1000" } }, "VLAN_MEMBER": { "Vlan1000|Ethernet16": { "tagging_mode": "untagged" } }, "VLAN_INTERFACE": { "Vlan1000|192.168.0.1/27": {} }, 在swss容器中存在一个vlanmgrd进行用于订阅config_db的vlan键空间,响应vlan配置,解析后将其同步到内核和app_db。由portsorch订阅app_db vlan事件,将其同步到asic-db,最终将其同步到硬件。VLAN_INTERFACE接口信息由intfmgr和intforch进行处理。 vlanmgr该进程运行在swss容器中,用于订阅config_db的vlan键空间,响应vlan配置,解析后将其同步到内核和app_db。 该部分涉及文件: vlanmgrd.cpp vlanmgr.cpp vlanmgr.h class VlanMgr : public Orch{public: VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector<string> &tableNames); using Orch::doTask;private: ProducerStateTable m_appVlanTableProducer, m_appVlanMemberTableProducer; Table m_cfgVlanTable, m_cfgVlanMemberTable; Table m_statePortTable, m_stateLagTable; Table m_stateVlanTable; std::set<std::string> m_vlans; void doTask(Consumer &consumer);//主任务 void doVlanTask(Consumer &consumer);//处理vlan事件 void doVlanMemberTask(Consumer &consumer);//处理vlan成员事件 void processUntaggedVlanMembers(string vlan, const string &members);//处理vlan内untag成员,暂时没用 bool addHostVlan(int vlan_id);//将vlan同步到host,即创建vlan bool removeHostVlan(int vlan_id);//删除host中的vlan bool setHostVlanAdminState(int vlan_id, const string &admin_status);//设置hostvlan状态 bool setHostVlanMtu(int vlan_id, uint32_t mtu);//设置host vlan接口的mtu //添加成员 bool addHostVlanMember(int vlan_id, const string &port_alias, const string& tagging_mode); bool removeHostVlanMember(int vlan_id, const string &port_alias);//删除host vlan成员 bool isMemberStateOk(const string &alias);//从state db中获取成员端口是否ok bool isVlanStateOk(const string &alias);//从state db中获取vlan是否ok bool isVlanMacOk();//判断该vlan的mac地址是否ok};实现int main(int argc, char **argv){ Logger::linkToDbNative("vlanmgrd"); SWSS_LOG_ENTER(); SWSS_LOG_NOTICE("--- Starting vlanmgrd ---"); try { vector<string> cfg_vlan_tables = {//订阅了两个config db table CFG_VLAN_TABLE_NAME, CFG_VLAN_MEMBER_TABLE_NAME, }; //连接了三个数据库 DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);//读和订阅config-db数据 DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);//将配置希尔app-db DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0);//获取vlan端口是否准备好 /* * swss service starts after interfaces-config.service which will have * switch_mac set. * Dynamic switch_mac update is not supported for now. * 获取全局的mac地址作为vlan-if的mac地址 */ Table table(&cfgDb, "DEVICE_METADATA"); std::vector<FieldValueTuple> ovalues; table.get("localhost", ovalues); auto it = std::find_if( ovalues.begin(), ovalues.end(), [](const FieldValueTuple& t){ return t.first == "mac";} ); if ( it == ovalues.end() ) { throw runtime_error("couldn't find MAC address of the device from config DB"); } gMacAddress = MacAddress(it->second); //构造vlanmgr VlanMgr vlanmgr(&cfgDb, &appDb, &stateDb, cfg_vlan_tables); std::vector<Orch *> cfgOrchList = {&vlanmgr}; swss::Select s; for (Orch *o : cfgOrchList) { s.addSelectables(o->getSelectables()); } SWSS_LOG_NOTICE("starting main loop"); while (true) { Selectable *sel; int ret; ret = s.select(&sel, SELECT_TIMEOUT); if (ret == Select::ERROR) { SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); continue; } if (ret == Select::TIMEOUT) { vlanmgr.doTask(); continue; } auto *c = (Executor *)sel; c->execute(); } } catch(const std::exception &e) { SWSS_LOG_ERROR("Runtime error: %s", e.what()); } return -1;}VlanMgr::doTask(Consumer &consumer)//vlanmgr主任务,vlan没有sync进程监听内核的netlink事件,内核与硬件的vlan事件都//由vlanmgr负责,vlanmgr负责在linux内核创建vlan和vlan-member,同时往app-db中写入//vlan事件。同时vlanif的IP地址在intfmgr中进行添加,并同步到内核void VlanMgr::doTask(Consumer &consumer){ SWSS_LOG_ENTER(); //获取事件表格名 string table_name = consumer.getTableName(); if (table_name == CFG_VLAN_TABLE_NAME) { doVlanTask(consumer);//vlan添加创建 } else if (table_name == CFG_VLAN_MEMBER_TABLE_NAME) { doVlanMemberTask(consumer);//vlan成员添加创建 } else { SWSS_LOG_ERROR("Unknown config table %s ", table_name.c_str()); throw runtime_error("VlanMgr doTask failure."); }}VlanMgr::doVlanTask(Consumer &consumer)//执行vlan事件void VlanMgr::doVlanTask(Consumer &consumer){ if (!isVlanMacOk())//vlanmac是否准备好了,没有的话等待,这里就是个判断,vlanmac在vlanmgr起来时已经获取了 { SWSS_LOG_DEBUG("VLAN mac not ready, delaying VLAN task"); return; } auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) { auto &t = it->second; string key = kfvKey(t); /* Ensure the key starts with "Vlan" otherwise ignore */ if (strncmp(key.c_str(), VLAN_PREFIX, 4)) { SWSS_LOG_ERROR("Invalid key format. No 'Vlan' prefix: %s", key.c_str()); it = consumer.m_toSync.erase(it); continue; } int vlan_id; vlan_id = stoi(key.substr(4)); string vlan_alias, port_alias; string op = kfvOp(t); if (op == SET_COMMAND) { string admin_status; uint32_t mtu = 0; vector<FieldValueTuple> fvVector; string members; /* Add host VLAN when it has not been created. */ //添加vlan if (m_vlans.find(key) == m_vlans.end()) { addHostVlan(vlan_id); } /* set up host env .... */ //设置host vlan相关信息 for (auto i : kfvFieldsValues(t)) { /* Set vlan admin status 设置管理状态*/ if (fvField(i) == "admin_status") { admin_status = fvValue(i); setHostVlanAdminState(vlan_id, admin_status); fvVector.push_back(i); } /* Set vlan mtu 设置mtu */ else if (fvField(i) == "mtu") { mtu = (uint32_t)stoul(fvValue(i)); /* * TODO: support host VLAN mtu setting. * Host VLAN mtu should be set only after member configured * and VLAN state is not UNKNOWN. */ SWSS_LOG_DEBUG("%s mtu %u: Host VLAN mtu setting to be supported.", key.c_str(), mtu); fvVector.push_back(i); } else if (fvField(i) == "members@") { members = fvValue(i); } } /* fvVector should not be empty */ if (fvVector.empty()) { FieldValueTuple a("admin_status", "up"); fvVector.push_back(a); } //写入app-db m_appVlanTableProducer.set(key, fvVector); m_vlans.insert(key); //写入state-db(6) fvVector.clear(); FieldValueTuple s("state", "ok"); fvVector.push_back(s); m_stateVlanTable.set(key, fvVector); it = consumer.m_toSync.erase(it); /* * Members configured together with VLAN in untagged mode. * This is to be compatible with access VLAN configuration from minigraph. * untag模式配置vlan时,会跟随着vlan一起下发,所以在这里解决vlan成员添加问题。 * tag vlan则在单独的mem表中添加。 */ if (!members.empty()) { processUntaggedVlanMembers(key, members); } } else if (op == DEL_COMMAND)//删除 { if (m_vlans.find(key) != m_vlans.end()) { removeHostVlan(vlan_id);//找到删除 m_vlans.erase(key); m_appVlanTableProducer.del(key);//通知app-db m_stateVlanTable.del(key);//删除状态 } else { SWSS_LOG_ERROR("%s doesn't exist", key.c_str()); } SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); it = consumer.m_toSync.erase(it); } else { SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); it = consumer.m_toSync.erase(it); } }}void VlanMgr::doVlanMemberTask(Consumer &consumer)//处理vlan-member事件void VlanMgr::doVlanMemberTask(Consumer &consumer){ auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) { auto &t = it->second; string key = kfvKey(t); /* Ensure the key starts with "Vlan" otherwise ignore */ if (strncmp(key.c_str(), VLAN_PREFIX, 4)) { SWSS_LOG_ERROR("Invalid key format. No 'Vlan' prefix: %s", key.c_str()); it = consumer.m_toSync.erase(it); continue; } key = key.substr(4); size_t found = key.find(CONFIGDB_KEY_SEPARATOR); int vlan_id; string vlan_alias, port_alias; if (found != string::npos) { vlan_id = stoi(key.substr(0, found)); port_alias = key.substr(found+1); } else { SWSS_LOG_ERROR("Invalid key format. No member port is presented: %s", kfvKey(t).c_str()); it = consumer.m_toSync.erase(it); continue; } //获取vlan名字 vlan_alias = VLAN_PREFIX + to_string(vlan_id); string op = kfvOp(t); // TODO: store port/lag/VLAN data in local data structure and perform more validations. if (op == SET_COMMAND) { /* Don't proceed if member port/lag is not ready yet */ if (!isMemberStateOk(port_alias) || !isVlanStateOk(vlan_alias)) { SWSS_LOG_DEBUG("%s not ready, delaying", kfvKey(t).c_str()); it++; continue; } string tagging_mode = "untagged"; for (auto i : kfvFieldsValues(t)) { if (fvField(i) == "tagging_mode") { tagging_mode = fvValue(i); } } if (tagging_mode != "untagged" && tagging_mode != "tagged" && tagging_mode != "priority_tagged") { SWSS_LOG_ERROR("Wrong tagging_mode '%s' for key: %s", tagging_mode.c_str(), kfvKey(t).c_str()); it = consumer.m_toSync.erase(it); continue; } //添加vlan成员 if (addHostVlanMember(vlan_id, port_alias, tagging_mode)) { key = VLAN_PREFIX + to_string(vlan_id); key += DEFAULT_KEY_SEPARATOR; key += port_alias;//写入app-db数据库 m_appVlanMemberTableProducer.set(key, kfvFieldsValues(t)); } it = consumer.m_toSync.erase(it); } else if (op == DEL_COMMAND) { removeHostVlanMember(vlan_id, port_alias); key = VLAN_PREFIX + to_string(vlan_id); key += DEFAULT_KEY_SEPARATOR; key += port_alias; m_appVlanMemberTableProducer.del(key); SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); it = consumer.m_toSync.erase(it); } else { SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); it = consumer.m_toSync.erase(it); } }}同步app_db数据到asic_dbapp_db的vlan事件由portsorch进行同步,该部分涉及文件: ...

June 27, 2019 · 8 min · jiezi

sonic管理口信息处理流程

sonic管理口信息处理流程管理接口信息配置文件格式管理信息使用MGMT_INTERFACE 表进行配置。对象的key由管理接口名字和IP前缀使用“|”连接而成。属性 gwaddr用于执行默认路由指向管理口,其值为默认网关。属性forced_mgmt_routes 用来强制添加一些路由到default路由表中。 "MGMT_INTERFACE": { "eth0|10.3.100.3/23": { "forced_mgmt_routes": [ "10.0.10.0/29", "10.0.20.5" ], "gwaddr": "10.3.100.1" } }, 管理信息处理流程可以使用config reload 命令重新加载配置文件config_db.json。这样所有配置信息将会被写入config_db(4)。然后会重新启动接口管理服务: Running command: service interfaces-config restart。 通过查看文件interfaces-config.service: admin@switch2:~$ vim /etc/systemd/system/interfaces-config.service[Unit]Description=Update interfaces configurationRequires=database.serviceAfter=database.service [Service]Type=oneshotExecStart=/usr/bin/interfaces-config.sh[Install]WantedBy=multi-user.target可以看出该服务的执行程序是脚本:/usr/bin/interfaces-config.sh 查看脚本/usr/bin/interfaces-config.sh: #!/bin/bashsonic-cfggen -d -t /usr/share/sonic/templates/interfaces.j2 > /etc/network/interfaces[ -f /var/run/dhclient.eth0.pid ] && kill `cat /var/run/dhclient.eth0.pid` && rm -f /var/run/dhclient.eth0.pidsystemctl restart networkingifdown lo && ifup lo从上面可以看出通过sonic-cfggen命令生成/etc/network/interfaces配置文件,然后重新启动networking即可让管理配置生效。 systemctl restart networking命令会重启网卡,让网卡down掉再up。

June 27, 2019 · 1 min · jiezi

sonic配置team与实现机制

sonic配置team与实现机制sonic实现team代码框架图: sonic修改lag模式配置步骤1.修改文件teamd.j2 docker exec -it teamd bash cd /usr/share/sonic/templates/ vim teamd.j2 例如将动态模式改成静态模式: 源文件: { "device": "{{ pc }}", "hwaddr": "{{ hwaddr }}", "runner": { "name": "lacp", "active": true,{% if PORTCHANNEL[pc]['fallback'] and ((PORTCHANNEL[pc]['members'] | length) == 1) %} "fallback": {{ PORTCHANNEL[pc]['fallback'] }},{% else %}{# Use 75% links upperbound as min-links #} "min_ports": {{ (PORTCHANNEL[pc]['members'] | length * 0.75) | round(0, 'ceil') | int }},{% endif %} "tx_hash": ["eth", "ipv4", "ipv6"] }, "link_watch": { "name": "ethtool" }, "ports": for member in PORTCHANNEL[pc]['members'] %} "{{ member }}": {}{% if not loop.last %},{% endif %}{% endfor %} }}修改后的文件: ...

June 25, 2019 · 7 min · jiezi

sonic接口管理处理流程

sonic接口管理处理流程同步config_db.json中接口IP信息到内核该部分通过redis的键空间机制订阅键config_db的键事件,将其同步到内核,本进程存在swss docker中。 相关文件: intfmgrd.cpp intfmgrd.h intfmgr.cpp IntfMgrclass IntfMgr : public Orch{public: IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector<string> &tableNames); using Orch::doTask;private: ProducerStateTable m_appIntfTableProducer;//作为appdb的生产者 Table m_cfgIntfTable, m_cfgVlanIntfTable; Table m_statePortTable, m_stateLagTable, m_stateVlanTable; bool setIntfIp(const string &alias, const string &opCmd, const string &ipPrefixStr); void doTask(Consumer &consumer); bool isIntfStateOk(const string &alias);};}实现//该进程的功能是根据配置文件中的接口地址信息配置linux内核。//使用如下命令进行配置://ip address add 10.254.229.226/27 dev Ethernet20//不支持在加载配置后使用ip address add 命令修改地址,想要修改数据口地址需要更改config_db//后重新reload配置。//本进程的目标是将配置文件的关于接口IP的信息同步到内核。int main(int argc, char **argv){ Logger::linkToDbNative("intfmgrd"); SWSS_LOG_ENTER(); SWSS_LOG_NOTICE("--- Starting intfmgrd ---"); try { //订阅了db4中的三个表格 vector<string> cfg_intf_tables = { CFG_INTF_TABLE_NAME, CFG_LAG_INTF_TABLE_NAME, CFG_VLAN_INTF_TABLE_NAME, }; //连接CONFIG_DB,APPL_DB,STATE_DB,用于读取信息 DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); IntfMgr intfmgr(&cfgDb, &appDb, &stateDb, cfg_intf_tables); // TODO: add tables in stateDB which interface depends on to monitor list std::vector<Orch *> cfgOrchList = {&intfmgr}; swss::Select s; for (Orch *o : cfgOrchList)//运行epoll,监听数据库信息 { s.addSelectables(o->getSelectables()); } SWSS_LOG_NOTICE("starting main loop"); while (true) { Selectable *sel; int ret; //一秒超时,即使订阅的三个数据库没有事件发生也要周期性的处理事件,类似于实现了一个1秒定时器 ret = s.select(&sel, SELECT_TIMEOUT); if (ret == Select::ERROR) { SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); continue; } //超时依然需要运行该函数 if (ret == Select::TIMEOUT) { intfmgr.doTask(); continue; } auto *c = (Executor *)sel; c->execute();//执行do-task } } catch(const std::exception &e) { SWSS_LOG_ERROR("Runtime error: %s", e.what()); } return -1;}#define VLAN_PREFIX "Vlan"#define LAG_PREFIX "PortChannel"IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector<string> &tableNames) : Orch(cfgDb, tableNames), m_cfgIntfTable(cfgDb, CFG_INTF_TABLE_NAME), m_cfgVlanIntfTable(cfgDb, CFG_VLAN_INTF_TABLE_NAME), m_statePortTable(stateDb, STATE_PORT_TABLE_NAME),//读取port状态信息 m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME),//读取lag状态信息 m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME),//读取vlan状态信息 m_appIntfTableProducer(appDb, APP_INTF_TABLE_NAME)//作为appdb的生产者,将配置文件的接口配置信息写入APP_INTF_TABLE_NAME表{}//调用ip address add 10.254.229.226/27 dev Ethernet20命令设置接口IPbool IntfMgr::setIntfIp(const string &alias, const string &opCmd, const string &ipPrefixStr){ stringstream cmd; string res; cmd << IP_CMD << " address " << opCmd << " " << ipPrefixStr << " dev " << alias;; int ret = swss::exec(cmd.str(), res); return (ret == 0);}//从db 6中获取端口状态信息bool IntfMgr::isIntfStateOk(const string &alias){ vector<FieldValueTuple> temp; if (!alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX))//vlan接口 { if (m_stateVlanTable.get(alias, temp)) { SWSS_LOG_DEBUG("Vlan %s is ready", alias.c_str()); return true; } } else if (!alias.compare(0, strlen(LAG_PREFIX), LAG_PREFIX))//lag接口 { if (m_stateLagTable.get(alias, temp)) { SWSS_LOG_DEBUG("Lag %s is ready", alias.c_str()); return true; } } else if (m_statePortTable.get(alias, temp))//port接口 { SWSS_LOG_DEBUG("Port %s is ready", alias.c_str()); return true; } return false;}//"INTERFACE|Ethernet20|10.254.229.226/27"//"PORTCHANNEL_INTERFACE|PortChannel1|10.8.8.200/24"//该程序会处理INTERFACE,PORTCHANNEL_INTERFACE,VLAN_INTERFACE三类接口信息//配置了IP,interface表示一个三层的概念,包括子接口,vlanif,lag-if,phy-ifvoid IntfMgr::doTask(Consumer &consumer){ SWSS_LOG_ENTER(); auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) { KeyOpFieldsValuesTuple t = it->second; string keySeparator = CONFIGDB_KEY_SEPARATOR; vector<string> keys = tokenize(kfvKey(t), keySeparator[0]); string alias(keys[0]);//keys[0]为INTERFACE //vlan接口的ip地址到此结束 if (alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX)) { /* handle IP over vlan Only for now, skip the rest */ it = consumer.m_toSync.erase(it); continue; } size_t pos = kfvKey(t).find(CONFIGDB_KEY_SEPARATOR); if (pos == string::npos) { SWSS_LOG_DEBUG("Invalid key %s", kfvKey(t).c_str()); it = consumer.m_toSync.erase(it); continue; } //提取key中的10.254.229.226/27部分 IpPrefix ip_prefix(kfvKey(t).substr(pos+1)); SWSS_LOG_DEBUG("intfs doTask: %s", (dumpTuple(consumer, t)).c_str()); string op = kfvOp(t); if (op == SET_COMMAND) { /* * Don't proceed if port/lag/VLAN is not ready yet. * The pending task will be checked periodially and retried. * TODO: Subscribe to stateDB for port/lag/VLAN state and retry * pending tasks immediately upon state change. * 由定时器超时1秒触发一次,只有没有up的端口将会由定时器持续检查。 */ if (!isIntfStateOk(alias))//判断端口是否已经ok { SWSS_LOG_DEBUG("Interface is not ready, skipping %s", kfvKey(t).c_str()); it++; continue; } string opCmd("add"); string ipPrefixStr = ip_prefix.to_string(); //设置接口IP地址 setIntfIp(alias, opCmd, ipPrefixStr); } else if (op == DEL_COMMAND) { string opCmd("del"); string ipPrefixStr = ip_prefix.to_string(); setIntfIp(alias, opCmd, ipPrefixStr); } it = consumer.m_toSync.erase(it); continue; }}响应内核接口IP信息变化到APP_DB前面分析过sonic对netlink的相关处理,此处不再赘述。 ...

June 25, 2019 · 10 min · jiezi

sonic处理netlink事件

sonic处理netlink事件 sonic在处理路由,接口up/down,接口地址变化,team等事件上极大的依赖内核。sonic通过监听rtnl事件来响应linux事件。从而感知相关信息变化。 libnlsonic使用libnl库来操作netlink事件,详细内容可以访问http://www.infradead.org/~tgr...。sonic在libnl库基础上封装了类NetLink进行netlink操作。 NetLinkclass NetLink : public Selectable {public: NetLink(int pri = 0); ~NetLink() override; void registerGroup(int rtnlGroup);//注册想要监听的事件,加入组播组 void dumpRequest(int rtmGetCommand); int getFd() override;//判断句柄是否在select的可用事件中 void readData() override;//获取socket中的信息,触发回调函数private: static int onNetlinkMsg(struct nl_msg *msg, void *arg);//回调函数 nl_sock *m_socket;//套接口描述符};NetLink::NetLink(int pri) :NetLink::NetLink(int pri) : Selectable(pri), m_socket(NULL){ m_socket = nl_socket_alloc();//申请描述符 if (!m_socket) { SWSS_LOG_ERROR("Unable to allocated netlink socket"); throw system_error(make_error_code(errc::address_not_available), "Unable to allocated netlink socket"); } nl_socket_disable_seq_check(m_socket);//不进行序列号检查 //注册回调函数,读取信息时,会自动回调该函数onNetlinkMsg nl_socket_modify_cb(m_socket, NL_CB_VALID, NL_CB_CUSTOM, onNetlinkMsg, this); //连接内核netlink int err = nl_connect(m_socket, NETLINK_ROUTE); if (err < 0) { SWSS_LOG_ERROR("Unable to connect netlink socket: %s", nl_geterror(err)); nl_socket_free(m_socket); m_socket = NULL; throw system_error(make_error_code(errc::address_not_available), "Unable to connect netlink socket"); } //非阻塞 nl_socket_set_nonblocking(m_socket); /* Set socket buffer size to 256KB */ nl_socket_set_buffer_size(m_socket, 2097152, 0);}void NetLink::registerGroup(int rtnlGroup)void NetLink::registerGroup(int rtnlGroup){ int err = nl_socket_add_membership(m_socket, rtnlGroup);//加入组播组 if (err < 0) { SWSS_LOG_ERROR("Unable to register to group %d: %s", rtnlGroup, nl_geterror(err)); throw system_error(make_error_code(errc::address_not_available), "Unable to register group"); }}int NetLink::getFd()int NetLink::getFd()//获取套接口句柄{ return nl_socket_get_fd(m_socket);}void NetLink::readData()void NetLink::readData(){ int err; do { err = nl_recvmsgs_default(m_socket);//读取数据,有libnl触发回调函数,处理业务 } while(err == -NLE_INTR); // Retry if the process was interrupted by a signal if (err < 0) { if (err == -NLE_NOMEM) SWSS_LOG_ERROR("netlink reports out of memory on reading a netlink socket. High possiblity of a lost message"); else if (err == -NLE_AGAIN) SWSS_LOG_DEBUG("netlink reports NLE_AGAIN on reading a netlink socket"); else SWSS_LOG_ERROR("netlink reports an error=%d on reading a netlink socket", err); }}int NetLink::onNetlinkMsg(......)//回调函数,读取消息时回调该函数,该函数是一个消息分发器int NetLink::onNetlinkMsg(struct nl_msg *msg, void *arg){ NetDispatcher::getInstance().onNetlinkMessage(msg); return NL_OK;}void NetLink::dumpRequest(.....)void NetLink::dumpRequest(int rtmGetCommand)//用于获取信息,实现get命令,查看内核相关信息{ int err = nl_rtgen_request(m_socket, rtmGetCommand, AF_UNSPEC, NLM_F_DUMP); if (err < 0) { SWSS_LOG_ERROR("Unable to request dump on group %d: %s", rtmGetCommand, nl_geterror(err)); throw system_error(make_error_code(errc::address_not_available), "Unable to request dump"); }}消息分发器class NetDispatcher {public: /**/ static NetDispatcher& getInstance();//获取消息分发器实例,消息分发器全局一个,静态函数 /* * Register callback class according to message-type. * * Throw exception if,注册消息处理函数 */ void registerMessageHandler(int nlmsg_type, NetMsg *callback); /* * Called by NetLink or FpmLink classes as indication of new packet arrival * 给netlink的回调函数 */ void onNetlinkMessage(struct nl_msg *msg);private: NetDispatcher() = default; /* nl_msg_parse callback API */ static void nlCallback(struct nl_object *obj, void *context); std::map<int, NetMsg * > m_handlers;//回调函数存储map};class NetMsg {public: /* Called by NetDispatcher when netmsg matches filters */ virtual void onMsg(int nlmsg_type, struct nl_object *obj) = 0;};}NetDispatcher& NetDispatcher::getInstance()NetDispatcher& NetDispatcher::getInstance()//消息分发器实例获取函数{ static NetDispatcher gInstance;//定义了一个静态分发器,全局一个 return gInstance;}void NetDispatcher::registerMessageHandler()void NetDispatcher::registerMessageHandler(int nlmsg_type, NetMsg *callback)//注册回调函数{ if (m_handlers.find(nlmsg_type) != m_handlers.end()) throw "Trying to registered on already registerd netlink message"; m_handlers[nlmsg_type] = callback;}void NetDispatcher::nlCallback()void NetDispatcher::nlCallback(struct nl_object *obj, void *context){ NetMsg *callback = (NetMsg *)context; callback->onMsg(nl_object_get_msgtype(obj), obj);}void NetDispatcher::onNetlinkMessage()void NetDispatcher::onNetlinkMessage(struct nl_msg *msg)//netlink回调函数的真实实现{ struct nlmsghdr *nlmsghdr = nlmsg_hdr(msg);//获取netlink消息头 auto callback = m_handlers.find(nlmsghdr->nlmsg_type);//获取消息类型对应的NetMsg描述结构 /* Drop not registered messages */ if (callback == m_handlers.end())//没有对应的消息处理函数 return; //解析消息,调用NetDispatcher::nlCallback nl_msg_parse(msg, NetDispatcher::nlCallback, (void *)(callback->second));}使用实例我们以接口管理为例进行说明。 ...

June 25, 2019 · 3 min · jiezi

数据结构双向链表双向循环链表

定义节点 typedef struct Node { int data; struct Node *head; struct Node *next; } DoubleLinkNode;创建双向链表 //创建节点 DoubleLinkNode *rootNode = alloca(sizeof(DoubleLinkNode)); rootNode->data = 10; rootNode->head = NULL; rootNode->next = NULL; DoubleLinkNode *node1 = alloca(sizeof(DoubleLinkNode)); node1->data = 20; node1->head = NULL; node1->next = NULL; DoubleLinkNode *node2 = alloca(sizeof(DoubleLinkNode)); node2->data = 30; node2->head = NULL; node2->next = NULL; DoubleLinkNode *node3 = alloca(sizeof(DoubleLinkNode)); node3->data = 40; node3->head = NULL; node3->next = NULL; //连接节点 rootNode->next = node1; node1->head = rootNode; node1->next = node2; node2->head = node1; node2->next = node3; node3->head = node2; // 反转 DoubleLinkNode *node = reverseDoublelink(rootNode);反转双向链表 ...

June 25, 2019 · 2 min · jiezi

数据结构单向链表

单向列表节点 typedef struct Node { int data; struct Node *next; } LinkNode;单链表创建 反转链表 删除节点 //创建节点 LinkNode *rootNode = malloc(sizeof(LinkNode)); rootNode->data = 1; rootNode->next = NULL; LinkNode *node1 = malloc(sizeof(LinkNode)); node1->data = 2; node1->next = NULL; LinkNode *node2 = malloc(sizeof(LinkNode)); node2->data = 3; node2->next = NULL; //生成链表 rootNode->next = node1; node1->next = node2; //反转链表 LinkNode *node = reverseLink(rootNode); //删除链表中的值为3元素 deleteLinkElement(node, 2);反转链表 LinkNode* reverseLink(LinkNode *node){ if (node == NULL) {return NULL;} LinkNode *pre = NULL; while (node) { LinkNode *nextTemp = node->next; node->next = pre; pre = node; node = nextTemp; } return pre; }删除链表节点 ...

June 25, 2019 · 1 min · jiezi

后续更新将发布在个人博客系统

个人简介本人从事逆向分析多年,自身涉及领域较广(包括爬虫,驱动,图像等),对新奇事物比较感兴趣(比如树莓派),文章类型几乎全是技术相关,干货满满! 由于三方博客网站老是误删我的文章甚至误封我的账号,一时兴起就利用免费的GitPage搭建了一个静态博客网站,从此不再寄人篱下。旧的某些文章已迁移至个人博客,并在排版上做出了优化,后续的更新也将在个人博客上发布。 博客网址如下:https://5ing.github.io 链接如下:1treeS - Blog

June 23, 2019 · 1 min · jiezi

聊聊-print-的前世今生

本文原创并首发于公众号【Python猫】,未经授权,请勿转载。 原文地址:https://mp.weixin.qq.com/s/Nu... (一) 上周,我翻译了一篇文章,解释了为什么 Python 3 把 print 改为函数? 概括有如下几点原因:1、print 不适宜作为应用程序级的语句。2、改为一个函数,可以实现更复杂的功能。3、改为一个函数,能方便地进行替换。 在 Python 2 中,print 是个语句(statement),它的级别就跟 for、if、def 等关键字相同,这是一个古老的设计(毕竟 Python 诞生于 1989 年),改成 print() 函数,意味着它升级了。 在查阅资料的时候,我发现 print 在历代版本中,一直发展变化,到了今天,它自身已足够完善了,可是外部的挑战一直不断。 因此,这篇文章再来聊聊它:介绍 print 的现状,追溯它的历史,说说它的挑战者,挖挖那些更加本质的东西。 (二) 在 3.0 版本中,print() 函数全新登场,开发者可以自定义打印对象的间隔(默认是空格)、终止方式(默认是换行)、以及输出位置(默认是标准输出 sys.stdout)。 而到了 3.3 版本,它还添加了一个新的参数,可以决定是否要刷新数据流。 至此,这个函数的完整格式就变成了 print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False) ,与升级前的 print 语句是天壤之别啦。 优点是显而易见的,可定制的参数带来了使用场景的扩充。 (三) 其实,在这次大版本的改动之前,早期的 print 语句并非是一成不变的,核心开发者们一直在完善它。 例如,在 2000 年的 PEP-214 之前,print 语句只能用于标准输出(sys.stdout),只有当提出这个提案后,print 才可以打印内容到类文件对象(file-like object)中。 (注:PEP 即 Python 改进提案,更多介绍详见旧文《学习Python,怎能不懂点PEP呢?》) ...

June 23, 2019 · 2 min · jiezi

二维数组的旋转赋值递归

描述 给定一个h行h列的整数数组array,要求从array[0][0]元素开始,按回形从外向内顺时针顺序赋值整个数组。如图所示:![图片描述][1]输出结果:4 4 1 2 3 4 12 13 14 5 11 16 15 6 10 9 8 7public static void main(String[] args) { Scanner scanner = new Scanner(System.in); int h = scanner.nextInt();//输入二维数组长度 int[][] arr = new int[h][h];//创建二维数组 setWay(arr,0,0);//赋值 for (int[] p : arr) {//遍历 for (int p1 : p) { System.out.printf("%-4d",p1); } System.out.println(); } System.out.println(t); } static int p = 1;//所赋的值 static int f = 0;//控制转向 0为右 1为下 2为左 3为上 static int t = 0;//纪录函数调用次数 public static boolean setWay(int[][]map,int i,int j){ if(i==map.length || j ==map.length || i == -1 || j == -1){//若超出数组 f = (f+1)%4;//转向 t++; return false; } if (map[i][j] == 0) {// 若当前点没有走过 map[i][j] = p;//赋值 p++; while(p != map.length*map.length+1)//输出到最后一个数跳出循环转向 switch(f){ case 0: if (setWay(map, i, j + 1)) {//向右走 return true; } break; case 1: if (setWay(map, i+1, j )) {// 向下走 return true; } break; case 2: if (setWay(map, i , j-1)) {// 向左走 return true; } break; case 3: if (setWay(map, i-1, j)) {// 向上走 return true; } break; } } f = (f+1)%4;//循环转向 t++; return false; }

June 23, 2019 · 1 min · jiezi

数据结构算法学习队列栈

队列栈与一般线性表区别线性表抽象是存储具有先后顺序元素数据的结构,支持任意位置的插入,删除操作。队列和栈限制插入删除操作,队列只能从尾部插入,首部取出(删除),既先入先出;栈限制插入和取出操作只能在尾部进行,既先入后出。 实现方式队列和栈同一般线性表相同,可用数组和链式结构体实现。由于限制了插入和取出的位置,没有频繁的中间元素操作,数组实现比链式实现效率更高。对应缺点为初始化时要定义数组大小,无法动态分配大小。 代码实现栈struct stack;typedef struct stack sStack;typedef sStack *pStack;#define EmptyTOS -1;struct stack { int capacity; int topOfStack; long long int *data;};pStack elrInitStackInt(int capaticy) { pStack s; s = malloc(sizeof(sStack)); if (s == NULL) { printf("out of space!"); } s->data = malloc(sizeof(long long int) * capaticy); if (s->data == NULL) { printf("out of space!"); } s->capacity = capaticy; elrMakeStackEmpty(s); return s;}void elrFreeStackInt(pStack stack) { if (stack != NULL) { free(stack->data); free(stack); }}int elrIsStackEmpty(pStack stack) { return stack->topOfStack == EmptyTOS;}int elrIsStackFull(pStack stack) { return stack->topOfStack == (stack->capacity - 1);}void elrMakeStackEmpty(pStack stack) { stack->topOfStack = EmptyTOS;}void elrPushStackInt(pStack stack, long long int data) { if (elrIsStackFull(stack)) { printf("full stack"); } else { stack->data[++stack->topOfStack] = data; }}long long int elrPopStackInt(pStack stack) { if (elrIsStackEmpty(stack)) { printf("empty stack"); } else { return stack->data[--stack->topOfStack]; }}队列struct queue;typedef struct queue sQueue;typedef sQueue *pQueue;struct queue { int capacity; int front; int rear; int size; long long int *data;};pQueue elrInitQueueInt(int capaticy) { pQueue s; s = malloc(sizeof(sQueue)); if (s == NULL) { printf("out of space!"); } s->data = malloc(sizeof(long long int) * capaticy); if (s->data == NULL) { printf("out of space!"); } s->capacity = capaticy; elrMakeQueueEmpty(s); return s;}void elrFreeQueueInt(pQueue queue) { if (queue != NULL) { free(queue->data); free(queue); }}int elrIsQueueEmpty(pQueue queue) { return queue->size == 0;}int elrIsQueueFull(pQueue queue) { return queue->size == queue->capacity;}void elrMakeQueueEmpty(pQueue queue) { queue->size = 0; queue->front = 1; queue->rear = 0;}int succ(pQueue queue, int value) { if (++value == queue->capacity) { value = 0; } return value;}void elrEnqueuekInt(pQueue queue, long long int data) { if (elrIsQueueFull(queue)) { printf("full queue"); } else { queue->size++; queue->rear = succ(queue, queue->rear); queue->data[queue->rear] = data; }}long long int elrDequeueInt(pQueue queue) { if (elrIsQueueEmpty(queue)) { printf("empty queue"); } else { queue->size--; int data = queue->data[queue->front]; queue->front = succ(queue, queue->front); }}

June 22, 2019 · 2 min · jiezi

怎么学习C3大方法让你快速入门

序言 C++是一门系统级语言,有些程序员小伙伴学了很久才明白明白栈与堆、内存管理等的含义。 新手程序员学习C++很容易陷入误区,就是不停地啃书本,结果把自己搞的晕头转向的。 一个类的成员函数包含了重载、覆盖、虚函数、纯虚函数等,不得不说复杂。最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。那么,如何学习C++比较靠谱呢?小编接下来会分享3种方法: C++入门学习方法首先要对C++有一个全局的认识,小编建议小伙伴们可以从《C++ Primer》这本书入门。 这本书是很多大牛强推的。可能是是对你影响最大的C++书籍。 怎么学习C++?3大方法让你快速入门在学习的过程中,目的是要看懂书本,这是第一阶段的学习。 第二阶段,要去理解C++对象在内存中是怎么存储,以及成员函数是如何调用。 不妨查阅《深入理解C++对象模型》。 至于第三阶段,就要了解标准库中的容器和算法实现,推荐阅读《STL源码剖析》。 当然,可以一边阅读一边敲代码,照着书本的实例或者习题撸一遍代码。 2、项目实战 任何一门编程语言的学习,都需要做到有的放矢——项目实战。离开了项目实战,阅读编程书籍学习效率将大打折扣。 这就是为什么很多的实习生可以不到两个月,就可以写点小程序。因为他找到了代码的试验田。 怎么学习C++?3大方法让你快速入门3、通过c++编程视频进行学习 一整天闷着看书本,很多程序员做不到吧? 不妨将编程学习过程变得趣味化一些。小伙伴们也可以在网上找些项目视频一边学习一边实践。 学习基础语法,熟悉调用各种库函数,这时你便成为一名初级C++程序员了。 推荐学习路线图: 怎么学习C++?3大方法让你快速入门

June 22, 2019 · 1 min · jiezi

Python数据分析与挖掘实战-对应的代码文件以及目录信息

CSDN下载:哔哩哔哩视频:https://www.bilibili.com/vide...文件夹 PATH 列表卷序列号为 4E8D-6931C:.│ .txt│ Python数据分析与挖掘实战.pdf│ ├─chapter10│ │ code.py│ │ │ ├─data│ │ original_data.xls│ │ test_neural_network_data.xls│ │ train_neural_network_data.xls│ │ water_heater.xls│ │ │ └─tmp│ attribute_extract.xls│ dividsequence.xls│ net.model│ test_output_data.xls│ ├─chapter11│ │ code.py│ │ │ ├─data│ │ discdata.xls│ │ discdata_processed.xls│ │ predictdata.xls│ │ │ └─tmp│ discdata_processed.xls│ ├─chapter12│ .gitattributes│ 7law.zip│ code.py│ README.md│ ├─chapter13│ │ code.py│ │ │ ├─data│ │ data1.csv│ │ data2.csv│ │ data3.csv│ │ data4.csv│ │ data5.csv│ │ enterprise_income.xls│ │ enterprise_incomt.xls│ │ personal_Income.xls│ │ revenue.xls│ │ sales_tax.xls│ │ VAT.xls│ │ │ └─tmp│ 1-net.model│ 2-net.model│ 3-net.model│ 4-net.model│ 5-net.model│ data1_GM11.xls│ data2_GM11.xls│ data3_GM11.xls│ data4_GM11.xls│ data5_GM11.xls│ ├─chapter14│ │ code.py│ │ │ ├─data│ │ business_circle.xls│ │ standardized.xls│ │ │ └─tmp│ standardized.xls│ type_1.png│ type_2.png│ type_3.png│ ├─chapter15│ │ code.py│ │ │ ├─data│ │ huizong.csv│ │ meidi_jd.txt│ │ meidi_jd_neg.txt│ │ meidi_jd_neg_cut.txt│ │ meidi_jd_pos.txt│ │ meidi_jd_pos_cut.txt│ │ meidi_jd_process_1.txt│ │ meidi_jd_process_2.txt│ │ meidi_jd_process_3.txt│ │ meidi_jd_process_end.txt│ │ meidi_jd_process_end_正面情感结果.txt│ │ meidi_jd_process_end_负面情感结果.txt│ │ myDict.txt│ │ stoplist.txt│ │ │ └─tmp│ meidi_jd.txt│ meidi_jd_neg.txt│ meidi_jd_neg_cut.txt│ meidi_jd_pos.txt│ meidi_jd_pos_cut.txt│ meidi_jd_pos_cut_LDA_result.txt│ ├─chapter3│ │ code.py│ │ │ ├─data│ │ catering_dish_profit.xls│ │ catering_sale.xls│ │ catering_sale_all.xls│ │ │ └─img│ programmer_2.png│ programmer_3.png│ ├─chapter4│ │ code.py│ │ │ ├─data│ │ catering_sale.xls│ │ discretization_data.xls│ │ electricity_data.xls│ │ leleccum.mat│ │ normalization_data.xls│ │ principal_component.xls│ │ │ └─img│ 3_1.png│ 3_2.png│ 3_3.png│ ├─chapter5│ │ code.py│ │ │ ├─data│ │ arima_data.xls│ │ bankloan.xls│ │ consumption_data.xls│ │ menu_orders.xls│ │ sales_data.xls│ │ │ ├─img│ │ 3.png│ │ 5.png│ │ 6_1.png│ │ 6_2.png│ │ 6_3.png│ │ 7.png│ │ │ └─tmp│ apriori_rules.xls│ data_type.xls│ pd_0.png│ pd_1.png│ pd_2.png│ ├─chapter6│ │ code.py│ │ │ ├─data│ │ missing_data.xls│ │ model.xls│ │ │ ├─img│ │ 2_1.png│ │ 2_2.png│ │ 3_1.png│ │ 3_2.png│ │ │ └─tmp│ missing_data_processed.xls│ net.model│ tree.pkl│ tree.pkl_01.npy│ tree.pkl_02.npy│ tree.pkl_03.npy│ tree.pkl_04.npy│ ├─chapter7│ │ code.py│ │ │ ├─data│ │ air_data.csv│ │ zscoredata.xls│ │ zscoreddata.xls│ │ ┐═╗╨┼╧╩?╨╘╦╡├?.xls│ │ │ └─tmp│ data_cleaned.csv│ data_cleaned1.csv│ explore.xls│ zscoreddata.xls│ ├─chapter8│ │ code.py│ │ │ ├─data│ │ apriori.txt│ │ data.xls│ │ │ └─tmp│ data_processed.xls│ └─chapter9 ...

June 22, 2019 · 2 min · jiezi

C指针与成员

先看一个例子://统一的接口 include <iostream>using namespace std; struct Point{ int plus(double x, double y){ return x + y;}int minus(double x, double y){ return x - y;}int mul(double x, double y){ return x*y;}int div(double x, double y){ return x/y;}}; int oper(Point & point, int (Point::*pfun)(double x, double y), double x, double y){return (point.*pfun)(x, y);} int main(){ Point pt;int (Point::*pfun)(double, double);pfun = &Point::plus;cout<<oper(pt, pfun, 10, 20);pfun = &Point::minus;cout<<oper(pt, pfun, 10, 20);return 0;} ...

June 22, 2019 · 2 min · jiezi

快速区分指针数组和数组指针

引言指针数组:元素为指针的数组 int*p[5];数组指针:指向数组的指针 int(*p)[5];接下来从名称和C语言定义两方面来说一说。 名称指针数组是一个数组,数组指针是一个指针。也就是以后面的宾语决定。类比: 红苹果 是一个苹果 苹果红 是一种颜色C语言定义int*p[5];[]优先级高于*,所以先是p[5]被定义为一个数组,int*再定义数组p[5](包括数组中所有元素)为指针。此时p仍和[5]以数组p[5]的形式存在,只是这个数组的元素成为了指针。 int(*p)[5];()优先级高于*,(*p)定义为一个指针,我们把*p看成一个整体,用a代替,变为int a[5];可以看出这就是一个普通数组的定义。只不过指向数组地址的a变成了*p,也就是说p是二级指针,它的值*p=a,a是一个地址,指向数组。 死记硬背到底是指针还是数组,p是主角(就他一个数据量,定义来定义去,也就是定义它),看它先和谁定义。在int*p[5];中,p先和[]结合定义,那么它就是数组,什么数组?指针数组。 延伸其实指针和数组,从某种本质看是一样的,他们都是地址。比如定义一个int*a;这是定义了一个整数指针a,也就是定义了一个地址a。而int b[6];是定义了一个长度为6的整数数组,怎么表示这个数组呢?用b表示数组中第一个元素的地址代表整个数组,int b[6];的实质是定义了一个地址b。 无论指针定义还是数组定义,都是定义了一个或几个地址,总归都是地址。

June 20, 2019 · 1 min · jiezi

srs之state-thread库接口分析

1.协程 协程是一种程序组件(以下称为微线程),通常我们把协程理解为自己实现调度,用于提高程序运行效率,降低开发复杂度的微线程。协程在用户态实现代码段的调度,不需要像线程一样切换到内核态进行调度,降低了对系统调度的依赖,减少了大量的中断和换页操作,从而降低了开发复杂度。开发者可以用同步的方式去进行代码开发,不需要考虑多线程模型的线程调度和诸多临界资源竞争问题。 协程在处理异步等待事件时有很大的优势,如IO的读写操作往往比较耗时,CPU在遇到IO操作时需要切换线程,等待IO事件准备好之后再继续执行IO操作,如socket的连接,数据的收发,及文件的读写等都是比较频繁却比较耗时的操作,使用协程可以直接再用户态切换微线程,大大降低了微线程直接的调度过程,提高了代码的运行效率。 2.ST的结构2.1 st_thread 微线程上下文信息结构体,用于创建微线程执行代码段。 2.2 queue RUNQ:队列中存储的是可以被调度运行的微线程,每次调度_st_vp_schedule即从该队列取出一个st_thread去运行。 IOQ:存储处于IO等待状态的threads,当上层调用st_poll时,将该thread放入IOQ中;当底层epoll有IO事件到达时,将该thread从IOQ中移除,并放入RUNQ中。 ZOMBIEQ:当st_thread退出时,放入ZOMBIEQ队列中 SLEEPQ:当st_poll传入大于0的超时参数或者调用st_usleep和st_cond_timewait时,将thread加入到SLEEPQ中。 2.3 st_poll 用于监听各种IO事件,会根据系统能力不同而进行切换(kqueue、epoll、poll、select) 2.4 timer 用于记录各种超时和st_sleep 2.5 vp_schedule 微线程调度切换函数,每次调用都会完成一次微线程切换。 3 接口详解3.1 st_set_eventsys函数原型:int st_set_eventsys(int eventsys)函数描述 设置协程事件通知机制,linux下用epoll,BSD使用kqueue。 参数-eventsys 事件通知机制类型,取值如下:ST_EVENTSYS_DEFAULT(0) :使用库默认事件通知机制ST_EVENTSYS_SELECT(1) :使用select事件通知机制ST_EVENTSYS_POLL(2) :使用poll事件通知机制ST_EVENTSYS_ALT(3) :使用alternate备用的事件通知机制 函数返回值 成功返回0,失败返回-1 3.2 st_get_eventsys函数原型:int st_get_eventsys(void)函数描述 获取协程事件通知机制,linux下用epoll,BSD使用kqueue。 函数返回值 事件通知机制类型,取值如下:ST_EVENTSYS_DEFAULT(0) :使用库默认事件通知机制ST_EVENTSYS_SELECT(1) :使用select事件通知机制ST_EVENTSYS_POLL(2) :使用poll事件通知机制ST_EVENTSYS_ALT(3) :使用alternate备用的事件通知机制 3.3 st_get_eventsys_name函数原型:const char *st_get_eventsys_name(void)函数描述 获取协程事件通知机制的函数名称 函数返回值 返回协程事件通知机制的函数名称,如epoll、kqueue、select、poll等 3.4 st_init函数原型:int st_init(void)函数描述 初始化协程库全局变量、队列、事件通知机制,并创建一个空闲协程 函数返回值 成功返回0,失败返回-1 3.5 _st_netfd_new函数原型:_st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket)函数描述 根据文件描述符创建一个协程描述符结构体 ...

June 20, 2019 · 1 min · jiezi

快速识别-‘指针数组和‘数组指针

引言 指针数组:元素为指针的数组 int* p[5]; 数组指针:指向数组的指针 int(*p)[5]一、汉字名称 指针数组是一个数组,数组指针是一个指针。就是以后面的词(宾语)决定。举例: 红苹果 是一个苹果 苹果红 是一种颜色二、C语言定义 int* p[5] []优先级高于*,所以p[5]先定义一个数组,int*再把p[5](包括数组中所有元素)定义为指针。 此时p仍然和[5]以数组形式存在,只是‘p[5]’这个数组中的元素都被定义成了指针。 int(*p)[5] 括号优先级高,*p定义为一个指针,我们把*p看成一个整体,用a代替,变为int a[5];可以看出这就是一个普通数组的定义。 只不过指向数组地址的a变成了*p,p是一个二级指针,他的值*p=a,而a是数组的指针。 此时*p以指针的形式存在。这个指针指向数组(第一个元素的地址)。 注意:到底是“数组”还是“指针”,这里边的主角是 p 。 链接Raw编辑

June 20, 2019 · 1 min · jiezi

linux学习笔记makefile的写法

前言Makefile介绍 Makefile规则make是如何工作的Makefile使用变量让make自动推导另类风格的Makefilemake中的函数文件搜索绝对顶级的大型工程Makefile<h3 id="1">前言</h3> 一个项目,拥有成百上千的源程序文件,那如何组织这些源码文件的编译和链接呢?此时就需要确定整个工程的编译链接规则,Makefile就是用来指定规则的。而make是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。 <h3 id="1">Makefile介绍</h3> 当我们编译工程时,只要输入make就会去编译了,那么这个make命令执行的时候一定需要一个Makefile文件,通过这个Makefile告诉make命令需要怎么样的去编译和链接程序。 make的工作规则是: 1.如果这个工程没有编译过,那么我们的所有C文件都要编译并被链接。 2.如果这个工程的某几个C文件被修改,那么我们只编译被修改的C文件,并链接目标程序。 3.如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的C文件,并链接目标程序。 只要我们的Makefile写得够好,所有的这一切,我们只用一个make命令就可以完成,make命令会自动智能地根据当前的文件修改的情况来确定哪些文件需要重编译,从而自己编译所需要的文件和链接目标程序。 <h3 id="3">Makefile规则</h3> 在讲述这个Makefile之前,还是让我们先来粗略地看一看Makefile的规则。 target... : prerequisites ... command ... ...target:也就是一个目标文件,可以是Object File,也可以是执行文件,也可以是标签。prerequisites:就是,要生成那个target所需要的文件或是目标。command:也就是make需要执行的命令。(任意的Shell命令) 这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比target文件要新的话,command所定义的命令就会被执行。这就是Makefile的规则。也就是Makefile中最核心的内容。 说到底,Makefile的东西就是这样一点,好像接下来不用再讲任何东西了。呵呵。还不尽然,这是Makefile的主线和核心,但要写好一个Makefile还不够,我会以后面一点一点地结合我的工作经验给你慢慢到来。内容还多着呢。:) 我们来看一个示例: nty_http_server : nty_coroutine.o nty_epoll.o nty_schedule.o nty_socket.o nty_http_server.o gcc -o nty_http_server nty_http_server.o nty_coroutine.o nty_epoll.o nty_schedule.o nty_socket.o -lpthreadnty_coroutine.o:nty_coroutine.c nty_coroutine.h gcc -c nty_coroutine.cnty_epoll.o:nty_epoll.c nty_coroutine.h gcc -c nty_epoll.cnty_schedule.o:nty_schedule.c nty_coroutine.h gcc -c nty_schedule.cnty_socket.o:nty_socket.c nty_coroutine.h gcc -c nty_socket.cnty_http_server.o:nty_http_server.c nty_coroutine.h gcc -c nty_http_server.cclean : rm -rf *.o 在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab键作为开头。记住,make并不管命令是怎么工作的,他只管执行所定义的命令。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。 ...

June 20, 2019 · 3 min · jiezi

FFmpeg常用命令合集

ffmpeg查询命令命令注释-version显示版本号-demuxers显示可用的demuxers-muxers显示可用的muxers-devices显示可用的设备-codecs显示所有编解码器-decoders显示可用的解码器-encoders显示可用的编码器-bsfs显示比特流filter-formats显示可用的格式-protocols显示可用的协议filters显示可用的过滤器-pix_fmts显示可用的像素格式-sample_fmts显示可用的采样格式-layouts显示channel名称-colors显示识别的颜色名称常用命令视频裁剪滤镜(播放器大小裁剪): ffmpeg -i killer.mp4 -vf crop=in_w-200:in_h-200 -c:v libx264 -c:a copy out.mp4视频裁剪(按时间裁剪): ffmpeg -i killer.mp4 -ss 00:02:00 -t 10 ./vedio-result/1.ts视频拼接: ffmpeg -f concat -i input.txt concat_out.mp4(input.txt内部是文件列表,格式是: file 'fileName')视频转图片命令: ffmpeg -i 1.ts -r 1 -f image2 result-image/image-%3d.jpeg图片转视频:ffmpeg -i image-%3d .jpeg -out.mp4ffmpeg录屏命令ffmpeg -f avfoundation -i 1 -r 30 out.yuv -f: 指定使用avfoundation采集数据-i: 指定从哪儿采集数据,它是一个文件索引号-r:指定帧率播放录屏的命令: ffplay -s 2880X1800 -pix_fmt uyvy422 out.yuv -s: 指定分辨率-pix_fmt :录制时的格式查看支持的设备列表:ffmpeg -f avfoundation -list_devices true -i ""录音命令:ffmpeg -f avfoundation -i :0 out.wav多媒体格式转换ffmpeg -i out.mp4 -vcodec copy -acodec copy out.flv ...

June 20, 2019 · 1 min · jiezi

归并排序

/**算法思想:把长度为n的输入序列分成两个长度为n/2的子序列;对这两个子序列分别采用归并排序;将两个排序好的子序列合并成一个最终的排序序列。O(n log n)的时间复杂度。代价是需要额外的内存空间。 */ /** 归并排序--将两段排序好的数组结合成一个排序数组 @param leftArray leftArray @param rightArray rightArray @return NSArray */ NSArray *merger(NSArray *leftArray, NSArray *rightArray) { NSUInteger capacity = leftArray.count+rightArray.count; NSMutableArray *arrayM = [NSMutableArray arrayWithCapacity:capacity]; for (NSInteger index=0, i = 0, j=0; index < capacity; index++) { if (i>=leftArray.count) { arrayM[index] = rightArray[j++]; } else if (j >= rightArray.count) { arrayM[index] = leftArray[i++]; } else if (leftArray[i] > rightArray[j]) { arrayM[index] = rightArray[j++]; } else { arrayM[index] = leftArray[i++]; } } return arrayM.copy;}/** 归并排序 ...

June 19, 2019 · 1 min · jiezi

python中使用ctypes调用so传参设置

问题近日在做一组声纹聚类时,使用了另一团队同学开发的声纹距离算法。该算法对外提供的是一组so包,需要使用方自己去使用。在python中调用纯so包一般使用ctypes类库,用起来看起来简单但也有不少细节容易犯错。本次使用过程中,就遇到传参的问题。 目标so库中对外export的函数是大致如下的三个函数: void* create_handler(); int extract_feature(void* hander); bool destroy(void* handler); 这三个函数使用起来倒也简单,顺序使用就可以了。但发现写成如下形式的python代码后,执行会直接segment fault。 import sys import ctypes so = ctypes.CDLL("./lib/libbase.so") p = so.create_handler() feature = so.extract_feature(p) so.destroy(p)解决这段代码中p是int类型,由void*自动转来,在ctyeps中这种转型本身是没问题的。segment fault发生在extract_feature函数调用中,问题应当出在参数上,回传的handler已经不是原来的pointer了,导致访问指针出错。 查阅ctypes的文档后,发现ctypes可以声明so库中函数的参数,返回类型。试了试,显示声明后问题得到了解决,证明我们的猜想是对的,确实指针发生了变化。修改后代码如下: import sys import ctypes so = ctypes.CDLL("./lib/libbase.so") so.create_handler.restype=ctypes.c_void_p so.extract_feature.argtypes=[ctypes.c_void_p] so.destroy.argtypes=[ctypes.c_void_p] p = so.create_handler() feature = so.extract_feature(p) so.destroy(p)结论:ctypes中传递指针类型参数需要显示声明c函数的参数,返回类型。

June 18, 2019 · 1 min · jiezi

排序

稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;内排序:所有排序操作都在内存中完成;外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;时间复杂度: 一个算法执行所耗费的时间。空间复杂度:运行完一个程序所需内存的大小。 排序 int a[] = {10,9,8,7,6,5,4,3,2,1};int count = sizeof(a)/sizeof(int);选择排序 for (int i = 0; i < count; i++) { int minIndex = i;//从第一个位置开始 for (int j =i+1 ; j <count; j++) { if (a[minIndex] > a[j]) { minIndex = j; // 找到剩余数中最小数的索引 } } // 交换 int temp = a[i]; a[i] = a[minIndex]; a[minIndex] = temp; } 冒泡排序 for (int i = 0; i < count; i++) { for (int j = 0; j < count-1-i; j++) { if (a[j]>a[j+1]) { int temp = a[j]; a[j] = a[j+1]; a[j+1] = temp; } } } 快速排序 ...

June 18, 2019 · 1 min · jiezi

Tidewaysxhprof-和-xhgui-打造-PHP-非侵入式监控平台

推荐阅读Tideways、xhprof 和 xhgui 打造 PHP 非侵入式监控平台超全的设计模式简介(45种)design-patterns-for-humans 中文版MongoDB 资源、库、工具、应用程序精选列表中文版有哪些鲜为人知,但是很有意思的网站?一份攻城狮笔记每天搜集 Github 上优秀的项目一些有趣的民间故事超好用的谷歌浏览器、Sublime Text、Phpstorm、油猴插件合集环境准备安装之前确保已经正确安装了以下软件 PHPNginxMongodb安装 PHP mongodb 扩展$ sudo pecl install mongodbPHP 配置文件中添加 [mongodb]extension=mongodb.so安装 PHP tideaways 扩展常规编译安装 $ git clone https://github.com/tideways/php-xhprof-extension.git$ cd /path/php-xhprof-extension$ phpize$ ./configure$ make$ sudo make installPHP 配置文件中添加 [tideways]extension=tideways_xhprof.so; 不需要自动加载,在程序中控制就行tideways.auto_prepend_library=0; 频率设置为100,在程序调用时可以修改tideways.sample_rate=100安装 xhgui-branch(xhgui 的汉化版)$ git clone https://github.com/laynefyc/xhgui-branch.git$ cd xhgui-branch$ php install.php修改 xhgui-branch 配置文件 <?phpreturn array( ... 'extension' => 'tideways_xhprof', ... 'save.handler' => 'mongodb', 'db.host' => 'mongodb://127.0.0.1:27017', 'db.db' => 'xhprof', ...);启动 mongodb 并设置 xhgui 索引,命令如下:$ mongo> use xhprof> db.results.ensureIndex( { 'meta.SERVER.REQUEST_TIME' : -1 } )> db.results.ensureIndex( { 'profile.main().wt' : -1 } )> db.results.ensureIndex( { 'profile.main().mu' : -1 } )> db.results.ensureIndex( { 'profile.main().cpu' : -1 } )> db.results.ensureIndex( { 'meta.url' : 1 } )xhgui 本地虚拟主机配置参考server { listen 80; server_name xhgui.test; root /Users/yaozm/Documents/wwwroot/xhgui-branch/webroot; # access_log /usr/local/var/log/nginx/access.log; error_log /usr/local/var/log/nginx/error.log; location / { try_files $uri $uri/ /index.php?$query_string; index index.php index.html index.htm; }}针对要分析的站点进行设置,直接在要分析站点的 nginx 配置中增加以下项,然后使配置生效就可以了。$ fastcgi_param PHP_VALUE "auto_prepend_file=/path/xhgui-branch/external/header.php";参考配置 ...

June 18, 2019 · 1 min · jiezi

简单的视频图像防篡改方法

大家都知道视频中图像压缩的重要性,但是图像的安全性也至关重要。特别是在一些需要知识产权保护的视频文件中(比如教育类的视频就是其中的典型),对于视频信息的保护就显得尤为重要。那么今天就给大家介绍一种简单保护视频信息的方法(视频防篡改)。 在视频压缩编码的过程中,其实就是对视频图像的压缩。所有的图像压缩算法基本都遵循以下三点: (1)把数据的重要部分和不重要部分划分出来: (2)过滤掉不重要的部分: (3)保存数据信息。 JPEG算法是图像压缩算法中的经典算。本文中就以JPEG算法压缩图像的过程为例,简单介绍一下图像防篡改的方法。 JPEG算法的第一步:是图像分割。把图像分割成大小为8x8的小块,这些小块在整个压缩的过程中都是单独被处理的。分割示例如下图(1)海岛 图1. 海岛8x8划分 JPEG算法的第二步:颜色空间的转换。如将RGB转换为YCbCr。这一步不在本次视频防篡改算法中,不做过多介绍。 JPEG算法的第三步:离散余弦变换(DCT)。 DCT变换的原理是:世界上任何复杂的事物,都可以分解为足够多的简单事物(类似于积分函数无限逼近)。那么经过DCT变换,可以把一个数组分解成数个数组的和,如果我们把数组视为一个一维矩阵,那么可以把结果看做是一系列矩阵的和。图像在电脑中的表现形式就是数字矩阵,这一特点就完全契合的DCT变换的特性。经过DCT变化的图像数据,第一个数据叫做直流系数(DC),之后的数据叫做交流系数(AC)。DC系数表示的是图像中的主要区域,AC系数表示的是图像中的轮廓的细节部分。转换结果如图(2)。 图(2) DCT转换结果 其中全是100的矩阵代表的是图像中的背景部分,假如像素值都是一样的,那么经过DCT变化后图像的能量将集中在左上角的直流部分。其余细节部分都变成了0。一个8x8的矩阵经过变换后就变成了一个只在左上角拥有一个数字的矩阵。可见DCT变换在图像压缩过程中的威力有多大。 JPEG算法的第四步:数据量化。这一步在本次的视频图像防篡改中起到了至关重要的作用。量化的公式为 : B = G / Q 。B代表的是量化后的结果。G代表的是输如的值。Q代表的是量化系数。经过DCT变换之后的数据需要使用标准的量化表进行量化计算。本次视频修改的部分只在亮度值中进行,所以介绍介绍标准亮度表。见图(3)。 图(3)标准亮度量化表 图(2)的变化结果是左上角只有一个800的矩阵,经过标准量化表的量化计算(800 / 16 = 50) 那么量化后的结果将变成左上角只有一个50的矩阵。其实在真是的图像矩阵中,经过了量化计算后也会呈现出数字集中在左上角,右下角全是0的结果。量化的后的数据会先经过Z字型扫描。扫描过程见图(4) 图(4)Z字型扫描 接下来本文通过在宿主图像的中嵌入一张指纹图片,对视频信息进行防篡改修改。 1.图像的构成 图像点和频率的对应关系: (1)图像的低频分量,图像中主要的信息都保存在低频信息中,他决定了图像的灰度等级,对图像结构的决定作用较小。(2)图像的中频分量,中频信息决定了图像的基本结构,是图像的主要结构。 (3)图像的高频分量,高频信息是图像的边缘和细节,是对图像中频信息的进一步强化。 2.嵌入原理: 若修改低频区域的数据,也就是修改大块的色块区域。容易对原始图像造成损坏,也很容易被看出,隐蔽性较差。 如果对高频信息进行修改,修改的部分就是图像中的边缘轮廓,采用这样的方法对视频的影响较小,但是会被大部分的高频信息处理算法给破坏掉。达不到嵌入的目的。 而图像中的中频部分是比较适合进行防篡改修改的部分。 3.嵌入密钥图像: (1)对图像进行完8x8的划分后,要计算出每个8x8块里面要存放几个像素点。 (2)嵌入像素点的个数 = 嵌入图像像素点总数 / 被嵌入图像划分的8x8块个数。 处理效果见下图: 图(5)原始图像 图(6)嵌入的密钥图像 图(7)恢复 的原始图像 图(8)提取的密钥图像 参考: JPEG算法解密(二)

June 17, 2019 · 1 min · jiezi

C萌新到大牛要看哪些书

初级阶段: C++基础语法:《C++ Primer 第五版》C++语法太过繁杂,很多语法特性一辈子也用不上。对于初学者来说,学完前7章就能写简单的程序。最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 数据结构和算法:《大话数据结构》这实在是一本对新手非常友好的书,暂时先看完前5章就够了。此时应该多做一些练习,可以是简单的小软件或小游戏。 C++萌新到大牛,要看哪些书? C++标准库:《C++ Primer 第五版》没错还是这本书,之前7章学了基础语法。现在继续学习8~12章,涵盖了标准库常用组件的基本用法。想完整学习标准库的同学可以在未来深入阅读《C++标准程序库:自修教程与参考手册》。 进阶学习: 此时可以选择性地把《C++ Primer 第五版》剩下的部分看完,第15章面向对象是重要内容,需要认真研究。 此时也可以把《大话数据结构》看完,或换一本正经的深入讲解算法的书,比如《算法导论》。 C++萌新到大牛,要看哪些书? 《Effective C++》、《C++编程规范》C++给了程序员过高的自由度,这反而不是一件好事,这两本书告诉你使用C++哪些做法是正确的,哪些是错误的,C++进阶必读。还有一本补充读物《More Effective C++》可选读。 深入研究: 《深入探索C++对象模型》深入分析了C++语言本身的设计思路和实现方式 《STL源码剖析》带你阅读标准库源码,提升数据结构、模板技术相关能力。 《C++设计新思维》讲元模板技术使用地出神入化,除非要自己写库,否则很少有机会用到这些技术。但真学会了真的可以出神入化。 掌握这些知识后,就可以算是C++高手了。但学软件开发不仅仅是学一门语言,根据不同的行业还需要学习windows开发、linux开发、界面开发、网络编程,软件工程、面向对象的分析和设计等等非常多的知识。这些不用全都学会,工作中用到什么学什么就可以了。 学习C/C++的伙伴可以私信回复小编“资料”领取全套免费C/C++学习资料、视频

June 17, 2019 · 1 min · jiezi

lua-web快速开发指南5-利用template库构建httpd模板引擎

介绍template模板引擎是为了使用户界面与业务数据(内容)分离而产生的, 其本身并不是一种深奥的技术. template模板引擎首先会将合法的模板编译为lua函数, 然后将模板文件和数据通过模板引擎生成一份HTML代码. cf的admin库整使使用了template来构建服务端渲染页面, 并利用单页面+iframe模式快速完成lua后台开发. 1. template基础语法在真正使用之前, 我们先来学习一下template常见的一些基本语法: {{ lua expression }} - lua expression是一段lua表达式; 作用为输出表达式的结果, 一些特殊符号将会被转义;{* lua expression *} - lua expression是一段lua表达式; 作用为输出表达式的结果, 不会转义任何符号;{% lua code %} - 执行一段lua代码, 如: {% for i = x, y do %} ... {% end %};{# comments #}- comments仅作为注释, 不会包含在输出字符串内. 这段语法的作用类似lua内的--与--[[]];{(template)} - 导入其它模板文件; 同时支持传参: {(file.html, { message = "Hello, World" })};2. 转义字符& 将会转义为 &amp;< 将会转义为 &lt;> 将会转义为 &gt;" 将会转义为 &quot;' 将会转义为 &#39;/ 将会转义为 &#47;3. APItemplate.compile(html)参数html为字符串类型, 可以是:模板文件路径、 ...

June 14, 2019 · 2 min · jiezi

lua-web快速开发指南4-详细了解httpd库的作用

httpd库是基于HTTP 1.1协议实现而来, 内置了高性能的http协议解析器与urldecode解析库. httpd库默认情况下就能工作的很好, 但是在一些需求较为极端的场景还是需要微调一下参数. httpd常用的内置方法介绍1. httpd:timeout(number)设置每个连接到最大空闲(idle)连接等待时间, 超过这个数值httpd将主动断开连接. (默认值为:30秒) 2. httpd:max_path_size(number)设置Path的最大长度, 超过这个值httpd将会返回414. (默认值为: 1024) 3. httpd:max_header_size(number)设置Header最大长度, 超过这个值httpd将会返回431. (默认值为: 65535) 4. httpd:max_body_size(number)设置Body的最大长度, 超过这个值将会返回413. (默认为 1024 * 1024) 5. httpd:before(function)before方法决定API与USE路由回调在触发之前的行为, 默认情况下允许所有路由通过. before方法一般用来设置与修改用户验证路由行为(例如头部验证), 这提供了开发者基于before函数设计中间件的机会. 当开发者设置了function后(即是是一个空函数), 需要利用http库来决定行为. 6. httpd:group(type, prefix, handles)group方法提供了一种批量注册路由的方式, 为一组同一组路由提供简单便方便在注册方法. 第一个参数type为需要批量注册的路由类型; 初始化httpd对象后, 使用app.USE或app.API进行传值; 第二个参数prefix为string类型的头部; 例如:/api、/admin; 第三个参数为一组路由处理函数或处理类数组; 类型为: {route = '/login', class = class}; 注意: 此方法仅支持批量注册API与USE路由, 不可同时注册不同类型路由; 7. httpd:static(folder, ttl)listen方法用于告诉httpd对象监听指定端口. 第一个参数ip暂未被httpd使用(但是必须设置), 默认监听所有网卡的'0.0.0.0'地址与指定的端口号; backlog为用户最大连接等待队列, 合理的设置能减少连接被重置的情况(默认值为128). 8. httpd:run()在httpd库所有参数与路由设置完毕之后, 调用run方法开启监听模式. httpd的请求日志日志格式为: [年/月/日 时:分:秒] - [ip] - [x-real-ip] - [path] - [method] - [http code] - [request handle timeline] ...

June 14, 2019 · 1 min · jiezi

lua-web快速开发指南3-初识httpd库路由

本章假设您已经知道httpd server如何快速搭建, 并且知道cf的启动流程与运行流程, 知晓httpd如何创建与启动. 回顾上一章节-- script/main.lualocal httpd = require "httpd"local app = httpd:new("app")app:static("static", 30)app:listen("0.0.0.0", 8080)app:run()我们利用httpd内置库快速实现了一套httpd静态文件server, 其中包括静态文件目录指定与端口设置. 并且在启动server后可以在看到测试页面. 什么是"路由"与"路由表"?Web路由用于描述资源到处理函数之间的一个映射关系. Web路由表用于描述当前作用域下所有路由的一个集合. 如下所示: /userlogin -> function userlogin(content) ... end/userinfo -> function userinfo(content) ... end对于一个服务端开发者来说! 当接受到客户端的HTTP请求时, 服务端会将请求URL中的PATH进行分割, 然后开始寻找的PATH映射对应的回调处理函数. 当URL映射的回调处理函数被找到时, 将会为其注入整个http上下文并且根据处理函数的行为将返回值展现给资源访问者. 这就是基本的路由雏形. cf中的各种路由cf的httpd库利用这种机制, 为开发者提供了一整套完整的路由注册方法, 其中包括: 静态文件路由、API接口路由、USE页面路由、WebSocket路由. 静态文件路由我们在上一章节已经看到过, 其本质是根据需要读取指定文件而存在的. 这种路由一般有库编写者或者框架编写者实现. 而API接口路由、USE页面路由、Websocket路由则一般由开发自行指定, 这些路由一般都用来处理对应的业务逻辑. 下面我们就开始学习如何在cf中注册路由. 注册API与USE路由1. API路由API接口路由用于快速构建前、后端分离的web开发场景. 它提供了基于http协议提供了基础的前、后端通讯的解决方案, 是目前位置Web领域最为常见的开发模式. 而作为前、后端数据沟通的桥梁自然需要指定指定数据交互类型. 目前为止, API路由的content-type为"application/json", 数据交互格式仅支持: json. httpd库为开发者提供了app:api方法用来注册API路由, 第一个参数是一个字符串类型的资源路径, 第二个参数则是回调处理方法; 现在让我们在main.lua中, 添加我们刚刚学习到的api路由: -- main.lualocal json = require "json"app:api('/userinfo', function(content) return json.encode({ code = 200, user = { name = "CandyMi", age = 29, sex = "男", } })end)然后打开浏览器, 输入http://localhost:8080/userinfo. 我们就可以看到我们输出的接口数据了. ...

June 14, 2019 · 1 min · jiezi

macOS-下ffmpeg源码编译安装

1. 下载ffmpeg源码打开mac 的控制台,切换到您想要保存源码的目录,执行git clone https://git.ffmpeg.org/ffmpeg.git命令下载源码。 2. 进入到ffmpeg目录源码下载完后执行cd /ffmpeg命令,切换到ffmpeg目录下,会看到如下内容。 3.执行如下命令进行编译安装3.1对ffmpeg进行配置在ffmpeg目录下执行如下命令进行ffmpeg的编译前配置。 ./configure --prefix=/usr/local/ffmpeg --enable-gpl --enable-nonfree --enable-libfdk-aac --enable-libx264 --enable-libx265 --enable-filter=delogo --enable-debug --disable-optimizations --enable-libspeex --enable-videotoolbox --enable-shared --enable-pthreads --enable-version3 --enable-hardcoded-tables --cc=clang --host-cflags= --host-ldflags= --disable-x86asm配置的时候会报错,这个时候不要慌,一般都是缺少库,查看错误信息,确实什么库,使用brew install XXX进行安装即可。 如果配置的时候报错,请看这篇文章 3.2编译配置成功以后,就该对ffmpeg进行编译了。在ffmpeg目录下,执行make命令进行编译。温馨提示,这一步需要等待的时间有点长,耐心等待编译完成再继续下一步。 3.3 进行ffmpeg的安装还是在ffmpeg目录下,执行sudo make install命令进行ffmpeg的安装。安装完成后,切换到/usr/local/ffmpeg/bin目录,如果bin目录下有ffmpeg、ffmplay、ffprobe三个目录。恭喜您,你的ffmpeg安装成功了。 敲黑板!!!注意啦!如果目录下缺失ffmplay,那是因为缺少SDL2库,导致编译不出ffmplay,这个时候需要我们先安装sdl2。在ffmpeg目录下执行brew install sdl2。安装完成之后,再重新依次执行3.1、3.2、3.3的命令。 如果在/usr/local/ffmpeg/bin目录下,看到了ffmpeg、ffmplay、ffprobe三个目录,那么这次真的安装成功了,可以开启您的ffmpeg学习之路了。

June 13, 2019 · 1 min · jiezi

教你从零基础小白开始怎么学习C语言

想窥探神秘的代码世界?最好的入口无疑就是C语言。 C语言是计算机体系结构的基础,向下可以操作硬件(包括ARM,DSP,单片机等各种微处理器),还可以写驱动,写OS,写编译器。向上可以进一步的学习C++,JAVA等面向对象语言,再学习一些图形用户界面框架,比如Qt,MFC,就可做出类似于计算器、QQ等Window桌面应用,再比如Android,就可以做出微信等Android应用,再比如Unity3D,就可以做出类似王者荣耀、刺激战场等手游。想想是不是就有点小激动呢! 那我们就脚踏实地,一步一步的走下去吧。先从如何学习C语言开始,后面的详细学习方法之后也会分享的哦。学会了C语言,就进入了计算机领域的大门,对于以后学习C++、Java等面向对象语言都大有益处。 下图是一个网上流行的程序员“鄙视链”。其实我想说的是,还是有一定道理的,哈哈哈,笑而不语。 教你从零基础小白开始怎么学习C语言作为“程序猿大叔”的我总结了学习C语言的几个步骤,其他编程语言学习方法基本类似,希望对大家有所帮助。 1.看书 学习一门编程语言首先学习基本语法。 C语言的基础语法包括数据类型、运算符、表达式、数组、逻辑运算、函数、指针等。学习这些先买一本入门书籍,个人还是推荐经典的《谭浩强C语言》,既然挺多大学选择这本书作为教材,总归有其合理之处吧。这本书对知识点的介绍都比较浅显,但涵盖面比较广。边学语法便敲案例,看着代码在计算机上运行起来是不是也有点小激动。这样便有了继续学习下去的动力。 C语言深入的话推荐《c primer plus》,你会发现有些地方晦涩难懂,不要被疑问绊住脚步,浪费太多时间在细枝末节的地方。C语言只是你进入新世界的第一步而已,而编程的世界远比你想象的更广阔,更有意思。《c primer plus》更适合作为一本字典使用,放在电脑旁,方便随时查阅。 我也推荐通过教学视频入门,老师会讲解重难点知识,并且进行演示,相对于看书会更容易。现在网络这么发达,有很多优质的教学视频可供利用。教你从零基础小白开始怎么学习C语言2.写代码练习 想学好一门编程语言,仅仅是看书而不动手去练习是远远不够的,一定要把书里的代码搬到电脑里。 大多数人是使用Windows系统,那么就先下载一个Visual Studio吧,推荐使用VS2015。 喜欢Linux的朋友,可以下载一个VMWare虚拟机,在再虚拟机中安装Ubuntu等基于Linux内核的操作系统,然后再安装gcc,gdb。 安装好开放环境之后,就可以开始愉快的敲代码了。 试着写一些简单而有趣的代码,比如 Hello World,文件读写,逻辑运算、常用算法等等。 3.总结心得体会 把自己觉得抽象难懂的程序放到VS中跑一跑,调试一番,会有很多发现。很多代码在调试之后就能理解,会有一种恍然大悟的快感。我以前电脑里就经常会有一个Test工程文件夹,哪里有疑惑,就把代码放进去,一Debug,全部疑惑就解开了。 然后把这些总结整理起来,这样知识就变成自己的了。推荐使用印象笔记进行记录,很方便,随时可以查阅。 比如这样,了解 Union 的内存结构 教你从零基础小白开始怎么学习C语言比如这样,了解 C 语言的内存分配教你从零基础小白开始怎么学习C语言教你从零基础小白开始怎么学习C语言4.写一个小作品 基本知识掌握的差不多了,试着写个小程序。比如:计算器、打字游戏、图书管理系统等。 看着人生第一个自己开发的程序,会有很大的成就感。而且在写程序的过程中,你的编程能力也会得到很大的提升。 计算器、打字游戏、图书管理系统控制台程序我都写过,当时是作为课程设计,真的是成就感满满。 教你从零基础小白开始怎么学习C语言很多同学可能不满足于“黑不溜秋”的控制台程序,想做一个有界面的程序,这就需要用到图形用户界面框架了,上面已经列举过了。关于这些的学习方法会在之后介绍。 最后总结: 写代码练习,写代码练习,写代码练习,重要的事情说三遍。! 其实做为一个开发者,有一个学习的氛围跟一个交流圈子特别重要这里请私信我“编程”不管你是小白还是大牛欢迎入住大家一起交流成长。小编会在里面不定期分享干货源码,包括我精心整理的一份c++零基础教程。欢迎各位感兴趣的的小伙伴。 学习思路: 教你从零基础小白开始怎么学习C语言学习资料:教你从零基础小白开始怎么学习C语言

June 12, 2019 · 1 min · jiezi

C-信号处理

免费C语言教程:阿里云大学——开发者课堂信号是由操作系统传给进程的中断,会提早终止一个程序。在 UNIX、LINUX、Mac OS X 或 Windows 系统上,可以通过按 Ctrl+C 产生中断。 有些信号不能被程序捕获,但是下表所列信号可以在程序中捕获,并可以基于信号采取适当的动作。这些信号是定义在 C++ 头文件 <csignal> 中。 ** signal() 函数**C++ 信号处理库提供了 signal 函数,用来捕获突发事件。以下是 signal() 函数的语法: void (*signal (int sig, void (*func)(int)))(int);这个函数接收两个参数:第一个参数是一个整数,代表了信号的编号;第二个参数是一个指向信号处理函数的指针。 让我们编写一个简单的 C++ 程序,使用 signal() 函数捕获 SIGINT 信号。不管您想在程序中捕获什么信号,您都必须使用 signal 函数来注册信号,并将其与信号处理程序相关联。看看下面的实例: #include <iostream>#include <csignal>using namespace std;void signalHandler( int signum ){ cout << "Interrupt signal (" << signum << ") received.\n"; // 清理并关闭 // 终止程序 exit(signum); }int main (){ // 注册信号 SIGINT 和信号处理程序 signal(SIGINT, signalHandler); while(1){ cout << "Going to sleep...." << endl; sleep(1); } return 0;}当上面的代码被编译和执行时,它会产生下列结果: ...

June 12, 2019 · 1 min · jiezi

青春科技24小时无限可能THE-Hack-2019-初心不变使命不改

Hackathon  Hackathon(创客马拉松)是 hacker 和 marathon 的结合,是一项从美国高校引入的科技活动。   参赛选手们组成 2-4人 的团队,在 24小时 的限时内,从头脑风暴开始 创意计划一个科技项目 ,针对日常生活中发生的具体问题制定解决方案,然后利用自己的技术能力和比赛提供的硬件设备,完成项目的开发 。最后,每支队伍将以路演的形式,把产品演示给评委,以评选奖项。  THE Hack 2019 “THE”= Technology Harvests EnlightenmentTHE Hack 2019 将于7月13-14日在上海交通大学闵行校区举办。 2018年的参赛者或许记得,我们将选手分为hack.init()新手组和hackShanghai进阶组。 今年,为减小创客之间的距离,THE Hack 2019将融合hack.init()和hackShanghai两大组别,各年龄、各水平的开发者都可以报名参加。 届时,我们将通过设置各具特色的奖项,强调不同角度,从而在允许新手和经验丰富者同台竞技的同时保证比赛的公平性和趣味性,呼吁全体创客共同加入这场创新盛宴。 Why THE Hack?在THE Hack,你将收获: 与跨领域的各大企业沟通交流 从科技到教育,从文创到家居,THE Hack的赞助商们热心于青年的科技能力培养,也对各位创客的创意和能力展现出了十分的兴趣,为THE Hack提供了资金、技术、奖品、媒体等等支持。在THE Hack现场,将有赞助商演讲与摊位,这将是大家近距离了解这些企业的绝佳机会。加入全国性创客社区,对话各年龄段的创客 THE Hack的参赛选手来自全球各地,身份与背景充满着多样性。但相同的是,他们都对科技怀抱着无尽的向往和热情。他们中隐藏着超高水平的大佬、古灵精怪的点子创造者、潜力无限的萌新,都值得你去发掘、结识。 收获丰厚的奖金、奖品、纪念品 在THE Hack 2019,你有可能获得单支队伍五位数的奖金、总价值六位数的奖品、设计感超强的THE Hack专属定制纪念品(贴纸、笔记本、帆布袋...)...... 作为国内的大型科技赛事,我们的评委是来自各大科技企业的工程师、专业STEM教育者和从业者,因此THE Hack在各领域都有一定知名度。 在工作坊中快速学习,锻炼各项综合能力 THE Hack 2019赛前和现场将组织免费的线上线下工作坊,讲师来自赞助商企业或经验选手,分析各领域的开发前景,速成不同方向产品所需的技能,为现场开发做准备。      同时,一场创客马拉松不仅仅考验开发水平,还考验参赛者的demo演讲、合作沟通、时间安排、商业规划等等能力,因此THE Hack也将锻炼和培养的各项综合能力。 这24个小时里,你和朋友同吃同住,在深夜却依然灯火通明的大楼里交替着休息和工作,眼前的3D打印机和你一起忙碌,身边是此起彼伏的敲代码的声音。你转身和隔壁桌的队伍商量硬件能不能一起用,正好聊起了未来生物科技的前景。夜宵和小蛋糕已经吃过了,桌上的产品也逐渐成型。队友醒了,揉揉眼睛开始准备测试,也摇醒了另一个队友,让她开始做demo用的ppt。 报名参赛访问链接 my.thehack.org.cn,填写申请。我们在收到了你的申请后,会进行全方面的审核,最终已邮件回复的形式,邀请300名创客来到现场。 THE Hack的申请与参赛全程免费,现场免费提供正餐、零食和休息空间;非上海的参赛选手我们将依据交通凭证,提供一定数额的路费报销。  申请入口将会于 7月1日 关闭,请准确填写申请中的所有信息,并认真回答每一个问题,让THE Hack团队感受到你的能力与对科技的热情。    创客马拉松本质上确实是一场比赛,队伍之间竞争为数不多的奖项。但我们不想把它称为一场比赛。没有主办方,没有参赛者,我们都是这场科技狂欢中的一员,在几线流光的代码中,把天马行空的想象转化成实实在在的项目。在科技里,我们都有自己的情怀。准备好了吗? ...

June 12, 2019 · 1 min · jiezi

11-不等于2-来看这道奇怪的C语言题目

对于很多C语言初学者来说,指针是一大难题! 但是指针也是c语言的灵魂,离开指针,可能c语言就只能处理小学数学题了。 最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 但是指针虽然难,但并没有难到大多数人学不会的程度。C语言面向的使用群体是普通人,而不是智商超群的大佬们。只要用心学习,肯定是可以掌握的。 1+1 不等于2? 来看这道奇怪的C语言题目 今天小编又给大家带来一道关于指针的c语言面试题,话不多说上代码: include<stdio.h>int main(){ int vector2 = { {1,2,3,4,5,6,7,8,9,10}, {11,12,13,14,15,16,17,18,19,20} }; int(*a)[10] = vector; printf("%d %d %d %d %d",a, (a + 1), (a + 1), (a[0] + 1), (a[1])); return 0;} 程序首先定义了一个二维数组vector,并使用初始化的方式赋予了1-20的初值。 接着又定义了一个指针a,并令其指向vector。 接下来程序通过指针依次输出5个值。 那么,这个c语言程序的输出是什么呢? 初步分析 显然这题的关键点在于指针a 首先我们要明确一点:在理解指针的时候,要像int char short一样,将它当做一种数据类型。 分析a的定义语句:int(*a)[10] = vector,可以发现a其实是一个 int[10] 类型的数组指针。 那么这个c语言的程序输出结果是什么呢,得到答案最简单粗暴的方式就是直接运行代码: 1+1 不等于2? 来看这道奇怪的C语言题目 c语言中的指针移动 不仅仅是c语言,语言中的数据类型其实就是告诉处理器应该如何访问它,这句话是什么意思呢?请看下图: 1+1 不等于2? 来看这道奇怪的C语言题目 大家都知道数据在内存中的最小粒度是一个字节,上图假设截取内存中的10个字节,在我的电脑上,c语言类型占用了4个字节,因此int类型指针是逐4个字节访问内存的。 同理,short类型的指针是逐2个字节移动的。char类型的指针是逐字节的移动的。 到这里相信大家都发现了,指针的加减法并不像数学概念中的加减一样严格遵循 1+1 =2。 ...

June 11, 2019 · 1 min · jiezi

GTKWindows下Haskell的GTK开发环境搭建

一个在Windows下相对小众的GUI框架-GTK,碰上一个相对小众的编程语言-Haskell,会碰出什么样的火花呢?现实的结果就是:相对于Linux,想在Windows下搭建好一个能工作的开发环境,简直是太难了!本篇文章介绍了一种可用的搭建方法。 安装msys2安装过程比较简单,过程略。假设安装的是64位的版本:msys2_x86_64,使用默认配置安装,默认C盘。 配置环境变量可在当前命令行里配置环境变量,如下: SET PATH=C:\\msys64\\mingw64\\bin;C:\\msys64\\usr\\bin;%PATH%SET PKG_CONFIG_PATH=C:\\msys64\mingw64\\lib\\pkgconfigSET XDG_DATA_DIRS=C:\\msys64\\mingw64\\share上面环境变量只是在当前会话生效;若要永久生效则需: 安装GTK软件包安装完msys2后,启动菜单里会就会有如图所示的三个菜单项: 启动msys2或mingw64任意一个,执行以下命令安装一些相关的函数库和工具: pacman -S mingw-w64-x86_64-gtk3pacman -S mingw-w64-x86_64-gladepacman -S mingw-w64-x86_64-toolchain base-develpacman -S mingw-w64-x86_64-gobject-introspectionpacman -S mingw-w64-x86_64-gtksourceview3上述软件,比如glade,早期编程可能用不到,也可以暂时不安装。如果后续还少其他软件包,使用pacman安装即可。 到这里,C语言的GTK开发环境已经搭建好了,可使用C语言编写GTK程序了。下面是使用C语言编写的一个示例程序example.c: #include <gtk/gtk.h>static voidactivate (GtkApplication* app, gpointer user_data){ GtkWidget *window; window = gtk_application_window_new (app); gtk_window_set_title (GTK_WINDOW (window), "Window"); gtk_window_set_default_size (GTK_WINDOW (window), 200, 200); gtk_widget_show_all (window);}intmain (int argc, char **argv){ GtkApplication *app; int status; app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE); g_signal_connect (app, "activate", G_CALLBACK (activate), NULL); status = g_application_run (G_APPLICATION (app), argc, argv); g_object_unref (app); return status;}使用以下命令编译: ...

June 10, 2019 · 1 min · jiezi

C访问注册表获取已安装软件信息列表示例代码

C++访问注册表获取已安装软件信息列表示例代码 最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 // --------------------------------------------------------------- // FlieNmae: // SofInfo.h // Remark: // 通过读取注册表获得本机已安装软件信息。 // --------------------------------------------------------------- pragma onceinclude <vector>struct SoftInfo { // 软件名 CString m_strSoftName; // 软件版本号 CString m_strSoftVersion; // 软件安装目录 CString m_strInstallLocation; // 软件发布厂商 CString m_strPublisher; // 主程序所在完整路径 CString m_strMainProPath; // 卸载exe所在完整路径 CString m_strUninstallPth; }; class CSoftInfo { private: // 保存已安装常用软件安装信息 std::vector<SoftInfo> m_SoftInfoArr; // 保存系统补丁信息 std::vector<SoftInfo> m_SystemPatchesArr; public: CSoftInfo(); ~CSoftInfo(){} // 获取一个包含常用软件安装信息的Vector std::vector<SoftInfo> GetSoftInfo (void) const; // 获取所有已安装常用软件名 ...

June 10, 2019 · 3 min · jiezi

数据结构算法学习哈希表平方探测

哈希表定义及实现哈希表也叫散列表,是快速执行查找,删除,插入的技术,不支持元素排序信息。原理是通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。关键码值到存储位置的映射被称为哈希函数也叫散列函数,当不同关键key被映射到同一value时,称为冲突。解决冲突主要有:分离链接法:对Hash表中每个Hash值建立一个冲突表,即将冲突的几个记录以表的形式存储在其中。开发定址法:根据冲突算法找到空的单元为止(线性探测法,平方探测法...)。 编程实例算法实现采用平方探测法的实现放在git@code.aliyun.com:c-program/projecteuler.git上projecteulerccommonstruct目录下elr_hash_int.h elr_hash_int.c两个单元内。 解决问题https://www.hackerrank.com/ch...要求用一个map结构存放名字name和电话phone。 定义结构#define MaxStr 20typedef unsigned long int Index;typedef Index Position;typedef char *KeyType;typedef long int *ValueType;struct HashTbl;typedef struct HashTbl *HashTable;enum KindOfEntry { Legitimate, Empty, Deleted };struct HashEntry { KeyType Key; ValueType Value; enum KindOfEntry Info;};typedef struct HashEntry Cell;struct HashTbl { long int TableSize; Cell *TheCells;};初始化及释放HashTable InitializeTable(long int TableSize) { HashTable H; long int i; H = malloc(sizeof(struct HashTbl)); if (H == NULL) { printf("Out of space!\n"); } H->TableSize = TableSize; H->TheCells = malloc(sizeof(Cell) * H->TableSize); if (H->TheCells == NULL) { printf("Out of space!\n"); } for (i = 0; i < H->TableSize; i++) { H->TheCells[i].Info = Empty; H->TheCells[i].Key = NULL; H->TheCells[i].Value = 0; } return H;}void DestroyTable(HashTable H) { int i; if (H == NULL) { return; } for (i = 0; i < H->TableSize; i++) { H->TheCells[i].Info = Empty; if (H->TheCells[i].Key != NULL) { free(H->TheCells[i].Key); } } free(H);}哈希及冲突插入操作Position Hash(KeyType Key, long int TableSize) { long int keyValue = 0; register unsigned char *p; for (p = (unsigned char *)Key; *p; p++) { keyValue = 31 * keyValue + *p; } return keyValue % TableSize;}int KeyEqual(KeyType a, KeyType b) { if ((a == NULL) || (b == NULL)) { return 1; } return strcmp(a, b) == 0 ? 0 : 1;}int Find(KeyType Key, HashTable H, Position *Pos) { Position CurrentPos; long int CollisionNum; int rst = 0; CollisionNum = 0; CurrentPos = Hash(Key, H->TableSize); while (1) { if (H->TheCells[CurrentPos].Info == Empty) break; if (KeyEqual(H->TheCells[CurrentPos].Key, Key) == 0) { rst = 1; break; } CurrentPos += (++CollisionNum << 1) - 1; if (CurrentPos >= H->TableSize) { CurrentPos -= H->TableSize; } } *Pos = CurrentPos; return rst;}void Insert(KeyType Key, ValueType Value, HashTable H) { Position Pos; int exist; exist = Find(Key, H, &Pos); H->TheCells[Pos].Info = Legitimate; if (!exist) { H->TheCells[Pos].Key = malloc(sizeof(char) * MaxStr); } strcpy(H->TheCells[Pos].Key, Key); H->TheCells[Pos].Value = Value;}调用解决问题#include <math.h>#include <stdio.h>#include <stdlib.h>#include <string.h>int main() { long int n = 0; scanf("%ld", &n); HashTable H = InitializeTable(n * 2); for (int i = 0; i < n; i++) { char name[20]; long int phone; scanf("%s %ld", name, &phone); Insert(name, phone, H); } for (int i = 0; i < n; i++) { char name[20]; Position Pos; int exist; scanf("%s", name); // while (() != '\n'); exist = Find(name, H, &Pos); if (exist == 1) { ValueType Value = H->TheCells[Pos].Value; printf("%s=%ld\n", name, Value); } else { printf("Not found\n"); } if (getchar() == EOF) break; } DestroyTable(H); return 0;}

June 9, 2019 · 2 min · jiezi

projecteulerproblem12

problem12地址:https://projecteuler.net/problem=12。 源码:git@code.aliyun.com:c-program/projecteuler.git。问题:1 + 2 + 3 + ...的和中第一个有超过500个因数。 #include <stdio.h>#include <math.h>#include "debug.h"#define NUM 500int main(int argc, char **argv){ int i = 0; int sum = 0; int num = 0; int j; debugTime(); while (num <= NUM){ i++; sum = i * (i + 1) / 2; num = 0; j = 1; while (j <= (sum / j)){ if (0 == (sum % j)){ if (j == (sum / j)) num++; else num += 2; } j++; } } printf("Problem12 Answer: %d\n", sum ); debugTime(); return 0;}

June 9, 2019 · 1 min · jiezi

projecteulerproblem11

problem11地址:https://projecteuler.net/problem=11。 源码:git@code.aliyun.com:c-program/projecteuler.git。问题:找出20X20表格中上下左右乘积最大值。 #include <stdio.h>#include <string.h>#include <stdlib.h>#include "debug.h"#define NUM 20int main(int argc, char **argv){ char *strsrc= "08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08 49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00 81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65 52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91 22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80 24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50 32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70 67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21 24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72 21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95 78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92 16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57 86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58 19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40 04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66 88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69 04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36 20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16 20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54 01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48"; char *sep = " "; char *s = NULL; int list[NUM][NUM]; int i, j; char *tmp; long int iResult = 0; long int tmpResult = 0; debugTime(); s = strdup(strsrc); tmp = strsep(&s, sep); i = 0; while (NULL != tmp){ list[i / NUM][i % NUM] = atoi(tmp); tmp = strsep(&s, sep); i++; } for (i = 0; i < NUM; i++){ for (j = 0; j < NUM; j++){ if (0 == list[i][j]) continue; if (NUM > j + 3){ tmpResult = list[i][j] * list[i][j + 1] * list[i][j + 2] * list[i][j + 3]; iResult = iResult>tmpResult?iResult:tmpResult; } if (NUM > i + 3){ tmpResult = list[i][j] * list[i + 1][j] * list[i + 2][j] * list[i + 3][j]; iResult = iResult>tmpResult?iResult:tmpResult; } if ((NUM > i + 3) && (NUM > j + 3)){ tmpResult = list[i][j] * list[i + 1][j + 1] * list[i + 2][j + 2] * list[i + 3][j + 3]; iResult = iResult>tmpResult?iResult:tmpResult; } if ((NUM > i + 3) && (NUM > j - 3)){ tmpResult = list[i][j] * list[i + 1][j - 1] * list[i + 2][j - 2] * list[i + 3][j - 3]; iResult = iResult>tmpResult?iResult:tmpResult; } } } printf("Problem11 Answer: %ld\n",iResult); debugTime(); return 0;}

June 8, 2019 · 3 min · jiezi

数据结构算法学习表链表

线性表定义及实现线性表是最常用的数据结构,抽象上讲表是存储具有先后顺序元素数据的结构,实现上分为顺序表和链式表。顺序表一般采用C语言中数组实现,数组中存储数据,依靠数据索引确定先后顺序信息,物理上存储连续。根据C中数组的定义,移动,动态申请空间,复制等操作,可以想象顺序表在处理动态变化线性表上的缺点。链式表一般采用C语言中结构体struct实现,每个结构体节点除了存储数据,还存储元素先后顺序信息,物理上存储不连续;根据是否存储前置元素可分为单链表双链表等。插入,删除,移动等动态操作较为方便。 编程实例问题及解决思路projecteuler的Problem10求2000000以内所有素数的和,首先要找到2000000所有素数。判断一个是是否素数,如果采用比之小的数去除,则除法运行次数太多。所以建立一个素数链表,用该链表里的素数去除,判断是否素数;一旦是素数,插入或追加到该链表中,为判断后续数字做嫁衣。详细介绍:https://segmentfault.com/a/11... 代码实现结构定义struct intNode;typedef struct intNode sIntNode;typedef sIntNode *pIntNode;struct intNode{ long long int data; pIntNode next; pIntNode prv;};操作方法//初始化pIntNode elrInitListInt(long long int data){ pIntNode tmp = NULL; tmp = (pIntNode)malloc(sizeof(sIntNode)); tmp->data = data; tmp->prv = tmp->next = tmp; return tmp;}//释放void elrPushListInt(pIntNode pList, long long int data){ pIntNode tmp = NULL; tmp = (pIntNode)malloc(sizeof(sIntNode)); tmp->data = data; tmp->next = pList; tmp->prv = pList->prv; pList->prv->next = tmp; pList->prv = tmp;}//追加元素void elrFreeListInt(pIntNode pList){ pIntNode tmp; while (pList->prv != pList){ tmp = pList->prv; pList->prv = tmp->prv; tmp->prv->next = pList; free(tmp); } free(pList); pList = NULL;}素数全局变量初始化方法pIntNode globalPrime;int globalPrimeCount;long long int globalPrimeNum;void initGlobalPrime(){ globalPrime = elrInitListInt(2); elrPushListInt(globalPrime, 3); elrPushListInt(globalPrime, 5); globalPrimeNum = 0; globalPrimeCount = 3;}void freeGlobalPrime(){ elrFreeListInt(globalPrime); globalPrimeNum = 0; globalPrimeCount = 0; }判断是否素数//优化://1.用链表的素数去除,减少除法运行次数,本质上应该就是Eratosthenes埃拉托斯特尼筛选法//2.采用6n+1,6n+5才可能是素数,减少运行次数 ...

June 8, 2019 · 2 min · jiezi

projecteulerproblem10

problem10地址:https://projecteuler.net/problem=10。 源码:git@code.aliyun.com:c-program/projecteuler.git。问题:找到2000000内质数和。 #include <stdio.h>#include <math.h>#include "debug.h"#include "prime.h"#define NUM 2000000int main(int argc, char **argv){ debugTime(); initGlobalPrime(); printf("Problem10 Answer: %lld\n", findPrimeSumByNum(NUM)); debugTime(); freeGlobalPrime(); debugTime(); return 0;}

June 8, 2019 · 1 min · jiezi

projecteulerproblem8

problem8地址:https://projecteuler.net/problem=8。 源码:git@code.aliyun.com:c-program/projecteuler.git。问题:找到给出一系列数中相邻13位的最大乘积。 #include <stdio.h>#include <string.h>#include "debug.h"#define NUM 13int main(int argc, char **argv){ char *strsrc = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450"; int length = strlen(strsrc); long long int sum = 1; long long int isum; int i, j; debugTime(); for (i=0; i<= NUM - 1; i++){ sum *= (strsrc[i] - '0'); } for (i=0; i <= (length - NUM); i++){ if (strsrc[i] < strsrc[i + NUM]){ isum = 1; for (j = i + 1; j <= i + NUM; j++){ isum *= (strsrc[j] - '0'); } sum = (sum > isum)?(sum):(isum); } } printf("Problem8 Answer: %lld\n", sum); debugTime(); return 0;}

June 8, 2019 · 1 min · jiezi

projecteulerproblem6

problem6地址:https://projecteuler.net/problem=6。 源码:git@code.aliyun.com:c-program/projecteuler.git。问题:找到100和平方与平方和的差值。 #include <stdio.h>#include <math.h>#include "debug.h"#define NUM 100int main(int argc, char **argv){ long int iResult = 0; int i, j; debugTime(); for (i = 1; i < NUM; i++){ for (j = i + 1; j <= NUM; j++){ iResult += i * j; } } iResult *= 2; printf("Problem6 Answer: %ld\n", iResult); debugTime(); return 0;}

June 8, 2019 · 1 min · jiezi

projecteulerproblem7

problem7地址:https://projecteuler.net/problem=7。 源码:git@code.aliyun.com:c-program/projecteuler.git。问题:找到第10001个素数。 #include <stdio.h>#include <math.h>#include "debug.h"#include "prime.h"#define NUM 10001int main(int argc, char **argv){ debugTime(); initGlobalPrime(); printf("Problem7 Answer: %lld\n", findPrimeByCount(NUM)); debugTime(); freeGlobalPrime(); debugTime(); return 0;}

June 8, 2019 · 1 min · jiezi

projecteulerproblem9

problem9地址:https://projecteuler.net/problem=9。 源码:git@code.aliyun.com:c-program/projecteuler.git。问题:和为1000,且满足勾股定理的数最大积。 #include <stdio.h>#include <math.h>#include "elr_debug.h"#define NUM 1000int main(int argc, char **argv){ int a, b, c; long int result = 0; long int lResult; elrDebugTime(EDT_BEGIN); for (a=1; a < NUM / 2; a++){ for (b=a+1; b < NUM; b++){ c = NUM - a - b; if ((c>b) && (c*c==b*b+a*a)) { lResult = a * b *c; result = result>lResult?result:lResult; } } } printf("Problem9 Answer: %ld\n", result); elrDebugTime(EDT_SPEND); elrDebugTime(EDT_END); return 0;}

June 8, 2019 · 1 min · jiezi

projecteulerproblem5

problem5地址:https://projecteuler.net/problem=5。 源码:git@code.aliyun.com:c-program/projecteuler.git。问题:找到能整除1到20最小的数。 #include <stdio.h>#include <math.h>#include "elr_debug.h"#include "elr_list_int.h"#include "elr_prime.h"#define NUM 20int main(int argc, char **argv){ pIntNode tmpPrimeNode; int i; long int lResult = 1; elrDebugTime(EDT_BEGIN); initGlobalPrime(); if (NUM > globalPrimeNum) isPrime(NUM); tmpPrimeNode = globalPrime; do{ if (NUM < tmpPrimeNode->data) break; i = 1; while (pow(tmpPrimeNode->data, i) < NUM) i++; lResult *= pow(tmpPrimeNode->data, i - 1); tmpPrimeNode = tmpPrimeNode->next; }while (globalPrime != tmpPrimeNode); printf("Problem5 Answer: %ld\n", lResult); freeGlobalPrime(); elrDebugTime(EDT_SPEND); elrDebugTime(EDT_END); return 0;}

June 8, 2019 · 1 min · jiezi

projecteulerproblem4

problem4地址:https://projecteuler.net/problem=4。 源码:git@code.aliyun.com:c-program/projecteuler.gitt。问题:找到三位数相乘最大回文数。 #include <stdio.h>#include <math.h>#include "debug.h"#define BIT 3int isPalindrome(const long int number){ char tmp[BIT * 2] = ""; int i = 0; int lth = sprintf(tmp, "%ld", number) - 1; while (i < lth - i){ if (tmp[i] != tmp[lth - i]) return 0; i++; } return 1;}int main(int argc, char **argv){ int iMin = pow(10, BIT - 1); int iMax = pow(10, BIT); long int iResult = 0; int i, j; long int tmpResult = 0; debugTime(); for (i = iMax - 1; i > iMin; i--){ for (j = i; j > iMin; j--){ tmpResult = i * j; if (isPalindrome(tmpResult) && (iResult < tmpResult)){ iResult = tmpResult; break; } } } printf("Problem4 Answer: %ld\n", iResult); debugTime(); return 0;}

June 8, 2019 · 1 min · jiezi

projecteulerproblem3

problem3地址:https://projecteuler.net/problem=3。 源码:git@code.aliyun.com:c-program/projecteuler.git。问题:找到600851475143最大质因数。 #include <stdio.h>#include <math.h>#define MAXNUM 600851475143int main(int argc, char **argv){ long long int tmp = MAXNUM; long long int i; long long int max = sqrt(tmp) + 1; for (i = 2; i <= max; i++){ while (! (tmp % i)){ tmp = tmp / i; } if (1 == tmp) break; } printf("Problem3 Answer: %lld\n", i); return 0;}

June 8, 2019 · 1 min · jiezi

projecteulerproblem2

problem2地址:https://projecteuler.net/problem=2。 源码:git@code.aliyun.com:c-program/projecteuler.git。问题:找到4000000内斐波那契数列中偶数的和。 #include <stdio.h>#define MAXNUM 4000000int main(int argc, char **argv){ long long int sum = 0; long long int a = 1; long long int b = 1; long long int c = 0; while ((c = a + b) <= MAXNUM){ a = b; b = c; if (! (c % 2)) sum = sum + c; } printf("Problem2 Answer: %lld\n", sum);}

June 8, 2019 · 1 min · jiezi

projecteulerproblem1

problem1地址:https://projecteuler.net/problem=1。 问题:找到1000内3或5的倍数的和。 源码:git@code.aliyun.com:c-program/projecteuler.git。 #include <stdio.h>#define MAXNUM 1000int main(int argc, char **argv){ int iResult = 0; int i; for (i = 1; i < MAXNUM; i = i + 2){ if ((i % 3 == 0) || (i % 5 == 0)) iResult += i; } printf("Problem1 Answer: %d\n", iResult); return 0;}

June 8, 2019 · 1 min · jiezi

工作多年精通C该具备哪些技能你会了吗进阶C职业规划

一、C++服务器程序员(流媒体后台,游戏后台,高性能服务器后台) 精通C++,STL,Linux等,熟悉设计模式;熟练掌握一门脚本语言(Lua, Python, Perl等);对多线程环境编程有一定的理解,能独立完成服务器端模块的开发、维护和优化;熟练掌握MySQL数据库的开发维护、性能优化;最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 1.精通C++编程,3年以上服务器开发经验; 2.有手机游戏或者PC网游开发经验,有过完整手游开发经验者优先; 3.精通Socket协议,了解分布式负载和集群类型的服务器框架结构; 4.精通数据库设计; 5.熟悉Python或Lua语言。 1、一年以上服务器开发经验,熟悉C++语言; 2、熟悉TCP/IP协议,熟悉网络编程; 3、熟悉标准SQL语言,熟悉Mysql更佳; 4、熟悉linux,熟练掌握linux基本操作命令; 2年以上C++游戏服务器开发方面的工作经验;对TCP协议相关的开发有全面的认知;对多线程的开发相关的问题有全面的认知;有完整的C++手游服务器开发经验的优先考虑;有Linux平台相关开发经验的优先考虑;3年以上网络游戏后台开发经验; 精通Linux操作系统,具备扎实的Linux环境编程能力; 精通C++编程语言并具备丰富的面向对象编程经验; 精通网络编程并有高并发系统的开发经验; 熟悉MYSQL或其他大型数据库,能够快速根据需求完成高性能数据库设计; 1、本科以上学历,计算机、通信等相关专业; 2、3年以上Unix/Linux下C/C++开发经验,熟悉常用的数据结构、算法,熟练使用STL等标准库; 3、熟悉Unix/Linux下常用架构设计方法,熟悉流媒体网络协议和格式,包括rtmp、rtsp、sip协议,以及mp4、ts流媒体格式封装; 4、熟悉Unix/Linux操作系统原理、常用工具,熟悉Mysql/Oracle等数据库管理、开发,SQL调优; 5、全面的软件知识结构(操作系统、软件工程、设计模式、数据结构、数据库系统、网络安全); 6、有大容量通信系统和大型互联网后台开发经验者优先。 1)计算机相关专业,精通C++语言;熟悉常用数据结构和算法; 2)熟悉Socket、精通TCP/IP协议及编程; 3)具备编写Shell、 Makefile能力,熟练使用Linux基本命令; 4)熟悉Linux操作系统及其环境下的网络通信编程(进程、线程、内存管理、消息机制等); 5)熟悉音视频封装及编解码技术,了解主流的多媒体标准,例如TS、MP4封装、H.264、H.265、FLV、MPEG2、MP3、ACC编码等; 6)熟悉HTTP/RTP/RTSP/RTMP/HLS等流媒体传输协议,音视频解码模块与流媒体协议的对接处理; 7)具备广电行业项目管理经验的,有cdn开发经验者优先; 1、本科及以上学学历,熟悉C/C++语言,3年以上流媒体系统开发经验 2、熟悉常见多媒体数据解码格式,熟悉H264、RTMP、视频切片、音频同步等方面技术; 3、熟悉TCP/IP协议,熟悉多媒体相关协议(HTTP,RTSP,RTMP,RTP/RTCP,P2P,SIP等); 4、深刻理解互联网视频播放原理,对ffmpeg等框架有实际的使用经验; 5、有视频直播、点播、视频会议、监控安防等方面经验优先 6、熟悉Nginx/Squid模块开发优先考虑,有FMS等流媒体服务器搭建经验、P2P系统研发经验、知名优秀的视频产品服务端设计和研发经验优先考虑 二、应用开发工程师windows /linuxc++ (QT和MFC,偏前端) 1、3年以上Visual C++开发经验 2、熟练掌握C、C++、ATL、COM等编程技术。 3、熟练掌握Windows系统编程,熟悉窗口、网络和多线程技术。 两年以上的嵌入式或windows平台QT应用软件开发经验. 熟悉QT跨平台框架,QT图形库和相应的开发工具。至少参加过一个完整的QT项目。 具备数据结构、面向对象、多线程和内存管理的基础和经验 掌握window/wince开发环境 熟悉通用的代码管理工具,例如SVN, Git等 1、2年以上C++开发经验; 2、在Windows开发平台下的客户端开发有丰富的经验,熟知windows操作系统原理; 3、熟悉windows api调用,熟悉MFC类,STL标准库,boost库等等; 4、熟悉windows消息体系,熟悉常用的数据结构与算法,独立研究的能力; 5、熟练使用MFC异常类对windows结构化进行捕捉,能快速定位发布版本程序的异常,帮助组内成员快速定位崩溃,内存泄露,GDI资源泄露,能使用远程调试定位问题; 6、尤其擅长利用各种调试,日志记录,分析工具对windows下客户端开发的代码进行调试,BUG查找,问题原因查找,BUG修复; 7、有以下调试工具使用经验的:spx++,bugtrap,crashreport,log4plus,gflags,bondcheck,有逆向工程ollydbg等调试的优先。 本科或以上学历,计算机、通讯相关专业优先; 1年以上 C++ / Qt开发经验; 有Linux、Windows下的跨平台Qt开发经验; 有 socket 编程经验,能编写TCP、UDP或串口通信程序; ...

June 6, 2019 · 2 min · jiezi

VPP-nodegraph编排过程

VPP node-graph编排过程VPP处理报文时是沿着一个有向图进行处理的,每一个功能单元称之为节点(node)。 数据结构静态数据结构节点全局管理结构vlib_node_main_ttypedef struct{ /* Public nodes. */ /* 节点指针数组,使用下标作为索引 */ vlib_node_t **nodes; /* Node index hashed by node name. */ /* 根据节点名字进行hash,可以根据节点名字进行hash表查找 * 只有main线程才会委会该hash表 */ uword *node_by_name; u32 flags;/* 该标志表示Runtime信息已经被初始化过了 */ #define VLIB_NODE_MAIN_RUNTIME_STARTED (1 << 0) /* Nodes segregated by type for cache locality. Does not apply to nodes of type VLIB_NODE_TYPE_INTERNAL. */ vlib_node_runtime_t *nodes_by_type[VLIB_N_NODE_TYPE]; /* Node runtime indices for input nodes with pending interrupts. */ u32 *pending_interrupt_node_runtime_indices; clib_spinlock_t pending_interrupt_lock; /* Input nodes are switched from/to interrupt to/from polling mode when average vector length goes above/below polling/interrupt thresholds. * 输入节点在中断模式和轮询模式之间进行切换,当向量的平均长度高于轮询长度阈值时 * 将会从中断模式切换到轮询模式(这种情况说明报文非常多),当长度低于中断阈值时,从 * 轮询模式切换到中断模式(压力变小了) */ u32 polling_threshold_vector_length; u32 interrupt_threshold_vector_length; /* Vector of next frames. */ /* 帧数组,由内部节点组成,其中n1是节点的下一跳个节点的个数,元素是节点运行索引 * node_runtime_index与帧数据索引构成的帧。 */ /* |----node 1的n1个元素|----node 2的n2个元素|......| ----node n的n个元素| */ /* 只针对内部节点 */ vlib_next_frame_t *next_frames; /* Vector of internal node's frames waiting to be called. * 等待被调用的内部节点,通常是上一个节点的报文处理后指向的下一个节点 */ vlib_pending_frame_t *pending_frames; /* Timing wheel for scheduling time-based node dispatch. */ void *timing_wheel; vlib_signal_timed_event_data_t *signal_timed_event_data_pool; /* Opaque data vector added via timing_wheel_advance. */ u32 *data_from_advancing_timing_wheel; /* CPU time of next process to be ready on timing wheel. */ f64 time_next_process_ready; /* Vector of process nodes. One for each node of type VLIB_NODE_TYPE_PROCESS. */ vlib_process_t **processes; /* Current running process or ~0 if no process running. */ u32 current_process_index; /* Pool of pending process frames. */ vlib_pending_frame_t *suspended_process_frames; /* Vector of event data vectors pending recycle. */ void **recycled_event_data_vectors; /* Current counts of nodes in each state. */ u32 input_node_counts_by_state[VLIB_N_NODE_STATE]; /* Hash of (scalar_size,vector_size) to frame_sizes index. */ uword *frame_size_hash; /* Per-size frame allocation information. */ /* 不同大小的帧的分配信息,是一个数组,与上面的hash表是两种索引方式 */ vlib_frame_size_t *frame_sizes; /* Time of last node runtime stats clear. */ f64 time_last_runtime_stats_clear; /* Node registrations added by constructors */ vlib_node_registration_t *node_registrations;} vlib_node_main_t;节点类型typedef enum{ /* An internal node on the call graph (could be output). */ VLIB_NODE_TYPE_INTERNAL, /* Nodes which input data into the processing graph. Input nodes are called for each iteration of main loop. 输入节点,报文流转入口 */ VLIB_NODE_TYPE_INPUT, /* Nodes to be called before all input nodes. Used, for example, to clean out driver TX rings before processing input. 输入节点之前处理的节点,用于处理一些在处理输入报文之前的任务。 比如清除发送缓冲区(好像没有注册该功能的节点)。目前只注册了两个该 类型的节点:epoll和session */ VLIB_NODE_TYPE_PRE_INPUT, /* "Process" nodes which can be suspended and later resumed. */ /* vpp的协程节点,用于处理可以挂起的任务,比如命令行,api等业务 */ VLIB_NODE_TYPE_PROCESS, VLIB_N_NODE_TYPE,} vlib_node_type_t;节点功能函数描述结构typedef struct _vlib_node_fn_registration{ vlib_node_function_t *function; /* 功能函数 */ int priority; /* 优先级,同一节点可以注册多个处理函数,选择优先级最高的,值越大优先级越高 */ struct _vlib_node_fn_registration *next_registration;/* 形成链表 */ char *name;/* 名字,必须要和其所属的节点一致,否则注册会失败 */} vlib_node_fn_registration_t;注册节点描述结构,用于表示一个注册节点typedef struct _vlib_node_registration{ /* Vector processing function for this node. 节点的功能函数,从下面注册的功能函数链表中选择一个优先级最高的最为该成员的值 */ vlib_node_function_t *function; /* Node function candidate registration with priority 节点功能函数链表 */ vlib_node_fn_registration_t *node_fn_registrations; /* Node name. 节点名字 */ char *name; /* Name of sibling (if applicable). */ /* 兄弟节点名字 */ char *sibling_of; /* Node index filled in by registration. 节点索引 */ u32 index; /* Type of this node. 节点类型 */ vlib_node_type_t type; /* Error strings indexed by error code for this node. 节点错误码映射表 */ char **error_strings; /* Buffer format/unformat for this node. */ format_function_t *format_buffer; unformat_function_t *unformat_buffer; /* Trace format/unformat for this node. */ format_function_t *format_trace; unformat_function_t *unformat_trace; /* Function to validate incoming frames. */ u8 *(*validate_frame) (struct vlib_main_t * vm, struct vlib_node_runtime_t *, struct vlib_frame_t * f); /* Per-node runtime data. 节点运行时数据,私有数据存储位置 */ void *runtime_data; /* Process stack size. */ u16 process_log2_n_stack_bytes; /* Number of bytes of per-node run time data. */ u8 runtime_data_bytes; /* State for input nodes. */ u8 state; /* Node flags. */ u16 flags; /* protocol at b->data[b->current_data] upon entry to the dispatch fn */ u8 protocol_hint; /* Size of scalar and vector arguments in bytes. */ u16 scalar_size, vector_size; /* Number of error codes used by this node. */ u16 n_errors; /* Number of next node names that follow. 该节点指向的下一个节点个数 */ u16 n_next_nodes; /* Constructor link-list, don't ask... 所有节点通过该成员形成链表 */ struct _vlib_node_registration *next_registration; /* Names of next nodes which this node feeds into. 下一个节点数组,存储的是名字、 */ char *next_nodes[];} vlib_node_registration_t;节点注册相关的宏#ifndef CLIB_MARCH_VARIANT#define VLIB_REGISTER_NODE(x,...) \ __VA_ARGS__ vlib_node_registration_t x; \ //声明一个需要注册的节点static void __vlib_add_node_registration_##x (void) \ //声明一个静态的添加一个节点的函数,有constructor属性,在main函数之前执行 __attribute__((__constructor__)) ; \static void __vlib_add_node_registration_##x (void) \{ \ //定义添加节点函数,即将节点x链接到vm->node_main.node_registrations链表中 vlib_main_t * vm = vlib_get_main(); \ x.next_registration = vm->node_main.node_registrations; \ vm->node_main.node_registrations = &x; \} \static void __vlib_rm_node_registration_##x (void) \ //从链表中移除节点 __attribute__((__destructor__)) ; \static void __vlib_rm_node_registration_##x (void) \{ \ vlib_main_t * vm = vlib_get_main(); \ VLIB_REMOVE_FROM_LINKED_LIST (vm->node_main.node_registrations, \ &x, next_registration); \} \__VA_ARGS__ vlib_node_registration_t x // 定义一个需要注册的节点,这里没有分号,是因为使用这个宏的时候有分号,并且初始化该变量。#else#define VLIB_REGISTER_NODE(x,...) \static __clib_unused vlib_node_registration_t __clib_unused_##x#endifVPP定义的节点样例我们以DPDK类型的输入节点来进行分析。 ...

June 5, 2019 · 21 min · jiezi

VPP接口层分析

VPP接口层分析 接口层是硬件驱动和上层软件之间一层抽象代码,屏蔽硬件的差异,为上层软件提供一些统一的操作接口。上层软件调用接口层的操作进行报文的读入与发出,同时可以进行硬件设备的设置以及相关信息(比如统计数据)的读取。 vpp支持多种驱动类型的网络设备,比如dpdk,netmap,af_packet等等。同一种驱动的物理设备可以有多个,比如一个设备可以有多个dpdk接口,因此抽象了链路层接口hw_interface。在网络中有很多的虚拟设备,它们依附于物理设备,例如vlan设备对于同一个hw_interface可以有4096个虚拟的子接口。为了描述这些虚拟接口,vpp在hw_interface的基础上又封装了一层sw_interface来表示接口。sw_interface是整个接口层对上层软件的一个抽象,上层软件使用sw_interface索引用来表示具体操作的设备。 VPP支持的物理网络设备类型一类设备表示使用相同硬件驱动的设备,比如dpdk类型的以太网设备,af_packet类型的虚拟以太网设备,netmap以太网设备等,属于物理层的描述。 设备类描述结构/* A class of hardware interface devices. *//* 一类硬件接口的操作函数集合,这些函数是在硬件设备的驱动上封装的一层 */typedef struct _vnet_device_class{ /* Index into main vector.类索引 */ u32 index; /* Device name (e.g. "FOOBAR 1234a").设备类名字 */ char *name; /* Function to call when hardware interface is added/deleted. */ /* 添加/删除一个该类设备的实例函数 */ vnet_interface_function_t *interface_add_del_function; /* Function to bring device administratively up/down. */ /* 设备UP/DOWN操作函数 */ vnet_interface_function_t *admin_up_down_function; /* Function to call when sub-interface is added/deleted */ /* 以该类型设备为主设备,添加/删除一个子接口的函数 */ vnet_subif_add_del_function_t *subif_add_del_function; /* Function to call interface rx mode is changed */ /* 接收模式变化函数 */ vnet_interface_set_rx_mode_function_t *rx_mode_change_function; /* Function to call interface l2 mode is changed */ /* 接口的二层模式变化操作函数 */ vnet_interface_set_l2_mode_function_t *set_l2_mode_function; /* Redistribute flag changes/existence of this interface class. */ u32 redistribute; /* Transmit function. */ /* 发送函数 */ vlib_node_function_t *tx_function; /* Transmit function candidate registration with priority */ /* 注册的多个候选的发送函数,最终根据优先级选择一个最高的赋值给tx_function */ vlib_node_fn_registration_t *tx_fn_registrations; /* Error strings indexed by error code for this node. */ /* 发送函数错误原因字符数组 */ char **tx_function_error_strings; /* Number of error codes used by this node. */ /* tx_function_error_strings数组大小 */ u32 tx_function_n_errors; /* Renumber device name [only!] support, a control-plane kludge */ int (*name_renumber) (struct vnet_hw_interface_t * hi, u32 new_dev_instance); /* Interface flow offload operations */ /* 流量卸载功能操作函数集合 */ vnet_flow_dev_ops_function_t *flow_ops_function; /* Format device instance as name. */ format_function_t *format_device_name; /* Parse function for device name. */ unformat_function_t *unformat_device_name; /* Format device verbosely for this class. */ format_function_t *format_device; /* Trace buffer format for TX function. */ format_function_t *format_tx_trace; /* Format flow offload entry */ format_function_t *format_flow; /* Function to clear hardware counters for device. */ /* 清除统计函数 */ void (*clear_counters) (u32 dev_class_instance); uword (*is_valid_class_for_interface) (struct vnet_main_t * vnm, u32 hw_if_index, u32 hw_class_index); /* Called when hardware class of an interface changes. */ void (*hw_class_change) (struct vnet_main_t * vnm, u32 hw_if_index, u32 new_hw_class_index); /* Called to redirect traffic from a specific interface instance */ /* 强制重定向一个接口的流量到指定的node */ void (*rx_redirect_to_node) (struct vnet_main_t * vnm, u32 hw_if_index, u32 node_index); /* Link-list of all device classes set up by constructors created below */ /* 形成链表 */ struct _vnet_device_class *next_class_registration; /* Function to set mac address. */ /* 链路层地址变化函数 */ vnet_interface_set_mac_address_function_t *mac_addr_change_function;} vnet_device_class_t;设备类输出函数注册结构该结构与node的功能函数注册结构是一样的 ...

June 5, 2019 · 19 min · jiezi

PHP源码学习20190401-PHP垃圾回收1

baiyan 全部视频:https://segmentfault.com/a/11... 垃圾回收触发条件我们知道,在PHP中,如果一个变量的引用计数减少到0(没有任何地方在使用这个变量),它所占用的内存就会被PHP虚拟机自动回收,并不会被当做垃圾。垃圾回收的触发条件是当一个变量的引用计数的值减少1之后,仍不为0(还有某个地方在使用这个变量),才有可能是垃圾。需要让我们人工去对其进行进一步的检验,看它是否真的是垃圾,然后再做后续的操作。一个典型的例子就是在我们使用数组与对象的过程中可能存在的循环引用问题。它会让某个变量自己引用自己。看下面一个例子:<?php$a = ['time' => time()];$a[] = &$a; //循环引用unset($a);我们可以知道,unset($a)之后,$a的type类型变成了0(IS_UNDEF),同时其指向的zend_reference结构体的refcount变为了1(因为$a数组中的元素仍然在引用它),我们画图来表示一下现在的内存情况: 那么问题出现了,$a是unset掉了,但是由于原始的zend_array中的元素仍然在指向仍然在指向zend_reference结构体,所以zend_reference的refcount是1,而并非是预期的0。这样一来,这两个zend_reference与zend_array结构在unset($a)之后,仍然存在于内存之中,如果对此不作任何处理,就会造成内存泄漏。以上详细的讲解请看:【PHP源码学习】2019-03-19 PHP引用那么如何解决循环引用带来的内存泄漏问题呢?我们的垃圾回收就要派上用场了。在PHP7中,垃圾回收分为垃圾回收器和垃圾回收算法两大部分在这篇笔记中只讲解第一部分:垃圾回收器垃圾回收器在PHP7中,如果检测到refcount减1后仍大于0的变量,会首先把它放入一个双向链表中,它就是我们的垃圾回收器。这个垃圾回收器相当于一个缓冲区的作用,待缓冲区满了之后,等待垃圾回收算法进行后续的标记与清除操作。垃圾回收算法的启动时机并不是简单的有一个疑似垃圾到来,就要运行一次,而是待缓冲区存满了之后(规定10001个存储单元),然后垃圾回收算法才会启动,对缓冲区中的疑似垃圾进行最终的标记和清除。这个垃圾回收器缓冲区的作用就是减少垃圾回收算法运行的频率,减少对操作系统资源的占用以及对正在运行的服务端代码的影响,下面我们通过代码来详细讲解。垃圾回收器存储结构垃圾回收器的结构如下:typedef struct _gc_root_buffer { zend_refcounted *ref; struct _gc_root_buffer *next; //双向链表,指向下一个缓冲区单元 struct _gc_root_buffer *prev; //双向链表,指向上一个缓冲区单元 uint32_t refcount;} gc_root_buffer;垃圾回收器是一个双向链表,那么如何维护这个双向链表首尾指针的信息,还有缓冲区的使用情况等额外信息呢,现在就需要使用我们的全局变量zend_gc_globals了:typedef struct _zend_gc_globals { zend_bool gc_enabled; //是否启用gc zend_bool gc_active; //当前是否正在运行gc zend_bool gc_full; //缓冲区是否满了 gc_root_buffer *buf; /*指向缓冲区头部 */ gc_root_buffer roots; /*当前处理的垃圾缓冲区单元,注意这里不是指针*/ gc_root_buffer *unused; /*指向未使用的缓冲区单元链表开头(用于串联缓冲区碎片)*/ gc_root_buffer *first_unused; /*指向第一个未使用的缓冲区单元*/ gc_root_buffer *last_unused; /*指向最后一个未使用的缓冲区单元 */ gc_root_buffer to_free; gc_root_buffer *next_to_free; ... } zend_gc_globals;垃圾回收器初始化那么现在,我们需要为垃圾回收器分配内存空间,以存储接下来可能到来的可疑垃圾,我们通过gc_init()函数实现空间的分配:ZEND_API void gc_init(void){ if (GC_G(buf) == NULL && GC_G(gc_enabled)) { GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES); GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES]; gc_reset(); }}GC_G这个宏是取得以上zend_gc_globals结构体中的变量。我们现在还没有生成缓冲区,所以进入这个if分支。通过系统调用malloc分配一块内存,这个内存的大小是单个缓冲区结构体的大小 * 10001:#define GC_ROOT_BUFFER_MAX_ENTRIES 10001那么现在我们得到了大小为10001的缓冲区(第1个单元不用),并把它的步长置为gc_root_buffer类型,随后将它的last_unused指针指向缓冲区的末尾,然后通过gc_reset()做一些初始化操作:ZEND_API void gc_reset(void){ GC_G(gc_runs) = 0; GC_G(collected) = 0; GC_G(gc_full) = 0; ... GC_G(roots).next = &GC_G(roots); GC_G(roots).prev = &GC_G(roots); GC_G(to_free).next = &GC_G(to_free); GC_G(to_free).prev = &GC_G(to_free); if (GC_G(buf)) { //由于我们之前分配了缓冲区,进这里 GC_G(unused) = NULL; //没有缓冲区碎片,置指针为NULL GC_G(first_unused) = GC_G(buf) + 1; //将指向第一个未使用空间的指针往后挪1个单元的长度 } else { GC_G(unused) = NULL; GC_G(first_unused) = NULL; GC_G(last_unused) = NULL; } GC_G(additional_buffer) = NULL;}根据这个函数中的内容,我们可以画出当前的内存结构图: ...

June 5, 2019 · 2 min · jiezi

青春科技24小时无限可能

鹰科技官方微信公众号协办方Techomedia官方公众号

June 5, 2019 · 1 min · jiezi

不知道C这七大特性绝对枉为圈中人

作为一种计算机语言,C++经历了许多发展变化。 当然,这些改变并不是一蹴而就的。C++曾经缺乏活力与创新,因此很不受欢迎。 但是在C++标准委员会决定加速发展这个语言之后,形势发生了改变。 2011年起,C++一跃成为了具有活力、不断演进、广受喜爱的计算机语言。最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 C++蜕变后也并没有简单多少,仍是最难的编程语言之一。但是,C++确实比之前更加人性化了。 本文要讲的是的C++的一些新特性(以有8年历史的C++11为例),相信每个程序员都会对这个话题感兴趣。 注:本文略过了一些高级特性。 关键字auto当C++11第一次引入auto时,程序员们纷纷喜极而泣! auto的意义是使C++编译器可以在编译时推导数据类型,这样就不用每次都要声明数据类型了。当数据类型为map<string,vector<pair<int,int>>>时尤为便捷。 不知道C++这七大特性,绝对枉为圈中人 没有initializer,就无法声明数据类型(见第五行)。这是说得通的。第五行指令并没有让编译器推导数据类型。 起初,auto的功能比较有限。在之后新版本的C++中,auto的功能越来越强大。 不知道C++这七大特性,绝对枉为圈中人 第七行和第八行中使用了括号初始化 (bracketedinitialization),这也是C++11的新特性之一。 请注意使用auto时,编译器必须能够推导数据类型。 一个有趣的问题是:如果写下autoa = {1, 2, 3}会发生什么?这是个编译错误吗?是一个矢量吗? 不知道C++这七大特性,绝对枉为圈中人 实际上,C++11引入了std::initializer_list<type>.如果声明auto,花括号初始化列表会被当做轻量级容器。 最终,正如前文所言,当数据结构复杂时,编译器类型推导很有帮助: 不知道C++这七大特性,绝对枉为圈中人 别忘了检查第25行!auto [v1,v2] = itr.second纯粹是C++17的新特性。这个特性叫做结构化绑定。在旧版本C++中,程序员需要单独获取每个变量。但是结构化绑定给这一过程带来了便利。此外,如果想获得数据使用引用(reference),只需要加上一个symbol--auto&[v1,v2] = itr.second. Lambda表达式C++11引入了lambda表达式,这类似于JavaScript里的匿名函数。它们都是函数对象,没有名字,且基于简洁的语法在不同作用域上捕获变量。它们也可以被分配给变量。 如果需要在代码中进行一些小而快的操作,又不愿意为此单独写一个函数,那么Lambdas很有用。另一种常见用法是将lambdas作为比较函数。 不知道C++这七大特性,绝对枉为圈中人 以上例子可以说明很多问题。 首先,请注意花括号初始化是如何提升权重的。然后是通用的begin(),end() (这也是C++11的新增部分)。接着是作为数据比较器的lambda函数。lambda函数的参数被声明为auto(这是C++14的新增部分)。在C++14之前是不能对于函数参数使用auto 的。 正如现代C++的awesome库中定义的那样: · []—不捕获任何对象。所以不能在lambda表达式内使用全局作用域的局部变量,只能使用参数。 · [=]— 按值捕获作用域中的局部对象(局部变量,参数)。只可使用不可修改。 · [&]—按引用捕获作用域中的局部对象(局部变量,参数)。可以被修改。例子如下。 · [this]—按值捕获this 指针。 · [a, &b]—按值捕获对象a ,按引用捕获对象b。 所以,如果想在lambda函数内部将数据转换为其他格式,可以利用作用域的优势来运用lambda.比如: 不知道C++这七大特性,绝对枉为圈中人 在上面这个例子中,如果在lambda表达式中按值捕获([factor])局部变量,则不能改变第五行的factor.原因很简单——没有权限。 最终,请注意示例中使用了val 作为引用 (reference). 这确保了lambda函数内部的任何变化都会改变vector. 不知道C++这七大特性,绝对枉为圈中人 学完现代C++后,她们乐开了花!(摄影:Ian Schneider 图源:Unsplash) if/switch内的初始化语句C++17的这个特性十分讨喜: 不知道C++这七大特性,绝对枉为圈中人 ...

June 4, 2019 · 1 min · jiezi

PHP源码学习20190328-Zend虚拟机

baiyan 全部视频:https://segmentfault.com/a/11... 原视频地址:http://replay.xesv5.com/ll/24... 复习基本概念首先复习几个基本概念: opline:在zend虚拟机中,每条指令都是一个opline,每个opline由操作数、指令操作、返回值组成opcode:每个指令操作都对应一个opcode(如ZEND_ASSIGN/ZEND_ADD等等),在PHP7中,有100多种指令操作,所有的指令集被称作opcodeshandler:每个opcode指令操作都对应一个handler指令处理函数,处理函数中有具体的指令操作执行逻辑我们知道,在经过编译阶段(zend_compile函数)中,我们生成AST并对其遍历,生成一条条指令,每一条指令都是一个opline。之后通过pass_two函数生成了这些指令所对应的handler,这些信息均存在op_array中。既然指令和handler已经生成完毕,接下来的任务就是要交给zend虚拟机,加载这些指令,并最终执行对应的handler逻辑。指令在PHP7中,由以下元素构成: struct _zend_op { const void *handler; //操作执行的函数 znode_op op1; //操作数1 znode_op op2; //操作数2 znode_op result; //返回值 uint32_t extended_value; //扩展值 uint32_t lineno; //行号 zend_uchar opcode; //opcode值 zend_uchar op1_type; //操作数1的类型 zend_uchar op2_type; //操作数2的类型 zend_uchar result_type; //返回值的类型};在PHP7中,每个操作数有5种类型可选,如下:#define IS_CONST (1<<0)#define IS_TMP_VAR (1<<1)#define IS_VAR (1<<2)#define IS_UNUSED (1<<3) /* Unused variable */#define IS_CV (1<<4) /* Compiled variable */IS_CONST类型:值为1,表示常量,如$a = 1中的1或者$a = "hello world"中的hello worldIS_TMP_VAR类型:值为2,表示临时变量,如$a=”123”.time(); 这里拼接的临时变量”123”.time()的类型就是IS_TMP_VAR,一般用于操作的中间结果IS_VAR类型:值为4,表示变量,但是这个变量并不是PHP中常见的声明变量,而是返回的临时变量,如$a = time()中的time()IS_UNUSED:值为8,表示没有使用的操作数IS_CV:值为16,表示形如$a这样的变量对AST进行遍历之后,最终存放所有指令集(oplines)的地方为op_array: struct _zend_op_array { uint32_t last; //下面oplines数组大小 zend_op *opcodes; //oplines数组,存放所有指令 int last_var;//操作数类型为IS_CV的个数 uint32_t T;//操作数类型为IS_VAR和IS_TMP_VAR的个数之和 zend_string **vars;//存放IS_CV类型操作数的数组 ... int last_literal;//下面常量数组大小 zval *literals;//存放IS_CONST类型操作数的数组};op_array的存储情况为了复习op_array的存储情况,我们具体gdb一下,使用下面的测试用例:<?php$a = 2;根据以上测试用例,在zend_execute处打一个断点,这里完成了对AST的遍历并生成了最终的op_array,已经进入到虚拟机执行指令的入口。首先我们先观察传入的参数op_array,它是经过AST遍历之后生成的最终的op_array: ...

June 4, 2019 · 3 min · jiezi

小白该如何学好C

小白该如何学好C++?那么作为一个从C语言小白摸爬滚打、入坑无数到成长为如今的高级C++游戏开发工程师、高级C++服务端工程师、项目经理、技术总监、我想跟大家分享下我大牛的学习心得与体会! Linus曾说过:“C++是一门很恐怖的语言,而比它更恐怖的是很多不合格的程序员在使用着它”,这个世界上最难的编程语言可能非C++莫属了,呵呵,虽然有点夸张...... 但是, 大家记住,难度越高意味着含金量与竞争力越高,越能把你和别人区分开来,所以,你在一开始就需要有很小心谨慎的态度,并把C++当成一种难以训服的猛兽来看待。最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 那么如果你只是一时兴起,并没有真正沉下心来想学好一门语言,那么我不建议你学,因为学习C++是痛苦的,没有兴趣,那么这个学习过程将是一种折磨与煎熬! 门槛与含金量并存,一旦你学会了C++,可以说,很多语言对你而言也就不是难事了。 一般呢,学习C++的新手学员有以下几种情况: 小白该如何学好C++?1.纯小白(非计算机专业、没学过任何编程语言) 2.接触过C语言、但没学好 针对第1、2种情况,我建议一定要把C语言基础打牢,如果C没学好,学C++会很受挫,打击自信心,可以循序渐进,不知道怎么学的可以关注我们,我们有一站式的学习方案! 3.C语言已经学的很好了,但没接触过C++ 有了扎实的C语言基础,恭喜,你可以开始学习C++了 4.C++基础语法都学会了,然后想学点高级的? 以我多年的C++游戏服务器开发经验,企业中真实对开发者的考察要求无非就那么几点: C++数据结构,重点,最基础的链表、队列,栈,排序算法,各种算法,此乃程序的核心。 C++泛型编程,模板,各种项目中存在大量模板设计,特别是框架, C++多线程,线程同步,线程池。 99%的项目一定用到,列为重点。 Socket网络编程,各种并发网络模型(Select WSASelect IOCP) C++静态库、动态链接库dll ,开发项目中90%、SDK中一定用到,无需精通,但必须掌握。 Win32 API 高级编程 , 重点,真实企业项目中大量用到win32 API ,一定有相关的经验。 5.C++进阶知识也会了,你需要学习基础框架。 MFC框架 。 可选,虽说MFC在windows桌面运用中因为其复杂性越来越显得过时了。但是其OO思想还是发挥的淋漓尽致,可以了解熟悉下,各种控件、对话框、单文档程序、多文档走一遍,不仅可以掌握桌面运用开发,还可以再次巩固面向对象的理念,总之MFC是个大杂烩,总会学点东西的。 QT框架. 跨平台的应用程序和用户界面框架,linux与windows桌面应用开发的优秀框架。 GTK+(GIMP Toolkit) GIMP 一个功能强大跨平台的图形库,是GNU/Linux下开发图形界面的应用程序的主流开发工具之一。 STL库,优秀的跨平台模板库STL 包含大量的STL容器,算法和函数等。 Boost库 一个可移植、提供源代码的C++库,作为STL标准库的后备,是C++标准化进程的开发引擎之一,优秀而高效的模板与算法 6.相关开发工具、无需深入、会使用即可。 磨刀不误砍柴工,打仗还得有枪,下面最常用的工具你必须掌握: 集成开发环境: VS2010 ~ VS2017 ,越来越强大的IDE。 代码配置管理工具: 最简单易用的SVN 或 最流行的Git 7.有了以上6个方面的基础,说明你已经具备企业开发的能力了。 但是每一行业都有其专注的技术,所以,你需要开始细分方向了,仔细思考你究竟想从事哪个行业,然后对号入座, 此刻就是术业有专攻了!一位崭新的C++大牛即将诞生...... 小白该如何学好C++?以下附上真实企业中相应岗位需要掌握的技能: 【流媒体开发工程师】 熟悉视音频编解码算法(如mpeg-4、H.264、H.265、G711、AAC等); 熟悉流媒体协议 RTP/RTCP , RTMP, RTSP, SIP ,HLS, HDS, TS; ...

June 3, 2019 · 1 min · jiezi

用-iouring-替代-epoll-实现高速-polling

前面的文章说到 io_uring 是 Linux 中最新的原生异步 I/O 实现,实际上 io_uring 也支持 polling,是良好的 epoll 替代品。 API使用 io_uring 来 poll 一个 fd 很简单。首先初始化 io_uring 对象(io_uring_queue_init),拿到 sqe(io_uring_get_sqe)是所有 io_uring 操作都必要的,前文已经介绍这里不做过多说明。拿到 sqe 之后,使用 io_uring_prep_poll_add 初始化 sqe 指针。 static inline void io_uring_prep_poll_add(struct io_uring_sqe *sqe, int fd, short poll_mask);第一个参数就是前面获得的 sqe 指针;第二个参数是你要 poll 的文件描述符;第三个是标志位,这里 io_uring 没有引入新的标志(宏),而是沿用了 poll(2) 定义的标志,如 POLLIN、POLLOUT 等。 如其他 I/O 请求一样,每个 sqe 都可以设置一个用户自己的值在里面,使用 io_uring_sqe_set_data 可以看到一次只能添加一个 poll 请求。如果有多个 fd,那么重复调用 io_uring_get_sqe 获取多个 sqe 指针分别 io_uring_prep_poll_add 即可。io_uring_get_sqe 不是系统调用不会进入内核,io_uring_prep_poll_add 则是简单的结构体参数赋值,所以没有速度问题。 ...

June 2, 2019 · 1 min · jiezi

linux-socket编程socket接口

常用socket函数Windows 和 Linux 上常用的 socket API 函数并不多,除了特定操作系统提供的一些基于自身系统特性的 API, 大多数 Socket API 都源于BSD Socket (即伯克利套接字(Berkeley Sockets)),因此这些 socket 函数在不同的平台有着相似的签名和参数。 经常有想学习网络编程的新人询问要掌握哪些基础的socket API,我这里给一个简单的函数列表,列表中给出的都是应该熟练掌握的 socket 函数。 常用 Berkeley Sockets API 一览表 函数名称函数简单描述附加说明socket创造某种类型的套接字 bind将一个 socket绑定一个ip与端口的二元组上listen将一个 socket 变为侦听状态 connect试图建立一个 TCP 连接一般用于客户端accept尝试接收一个连接一般用于服务端send通过一个socket发送数据 recv通过一个socket收取数据 select判断一组socket上的读事件 gethostbyname通过域名获取机器地址 close关闭一个套接字,回收该 socket 对应的资源Windows 系统中对应的是 closesocketshutdown关闭 socket 收或发通道 setsockopt设置一个套接字选项 getsockopt获取一个套接字选项 socketsocket()函数的原型如下,这个函数建立一个协议族为domain、协议类型为type、协议编号为protocol的套接字文件描述符。如果函数调用成功,会返回一个标识这个套接字的文件描述符,失败的时候返回-1。 #include <sys/types.h> /* See NOTES */#include <sys/socket.h>int socket(int domain, int type, int protocol);domain函数socket()的参数domain用于设置网络通信的域,函数socket()根据这个参数选择通信协议的族。通信协议族在文件sys/socket.h中定义。 名称含义名称含义PF_UNIX PF_LOCAL本地通信PF_X25ITU-T X25 / ISO-8208协议AF_INET,PF_INETIPv4 Internet协议PF_AX25Amateur radio AX.25PF_INET6IPv6 Internet协议PF_ATMPVC原始ATM PVC访问PF_IPXIPX-Novell协议PF_APPLETALKAppletalkPF_NETLINK内核用户界面设备PF_PACKET底层包访问typetype 函数socket()的参数type用于设置套接字通信的类型,主要有SOCKET_STREAM(流式套接字)、SOCK——DGRAM(数据包套接字)等。 ...

June 1, 2019 · 2 min · jiezi

C初入门

C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象,方法、即时变量。 对象 - 对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 - 摇动、叫唤、吃。对象是类的实例。 类 - 类可以定义为描述对象行为/状态的模板/蓝图。最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。方法 - 从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。 即时变量 - 每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。 C++ 程序结构 让我们看一段简单的代码,可以输出单词 Hello World。 实例: include <iostream>using namespace std; // main() 是程序开始执行的地方 int main() { cout << "Hello World"; // 输出 Hello World return 0; } 接下来我们讲解一下上面这段程序: C++ 语言定义了一些头文件,这些头文件包含了程序中必需的或有用的信息。上面这段程序中,包含了头文件 <iostream>。下一行 using namespace std; 告诉编译器使用 std 命名空间。命名空间是 C++ 中一个相对新的概念。 C++初入门下一行 // main() 是程序开始执行的地方 是一个单行注释。单行注释以 // 开头,在行末结束。 下一行 int main() 是主函数,程序从这里开始执行。 ...

June 1, 2019 · 1 min · jiezi

如今的C渗透了哪些应用领域

1、从C到C++ 计算机诞生初期,用机器语言或汇编语言编写程序; 第一种高级语言FORTRAN诞生于1954年; BASIC语言(1964)是由FORTRAN语言的简化而成的是为初学者设计的小型高级语言; C语言是1972年由美国贝尔实验室的 D.M.Ritchie 研制成功的。它是为计算机专业人员设计的; 大多数系统软件和许多应用软件都是用C语言编写的。随着软件规模的增大,用C语言编写程序渐渐吃力了,于是便创造出了C++语言。最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 2、C++的特点 C++是由 AT&T Bell实验室 于20世纪80年代初在C语言的基础上成功开发出来的。C++保留了C语言原有的所有优点,并增加了面向对象的机制。 C++是由C语言发展而来的,与C兼容。用C语言写的程序基本上可以不加修改地用于C++。从C++的名字可以看出它是C的超集。C++既可用于面向过程的程序设计,又可用于面向对象的程序设计,是一种功能强大的混合型程序设计语言。 备注:C++不是纯面向对象语言。 3、C++是对C语言的“增强” (1) 在原来面向过程机制的基础上,对C语言的功能做了很多扩充。 (2) 增加了面向对象的机制。 面向对象程序设计,是针对开发较大规模的程序而提出来的,目的是提高软件开发的效率。不要把面向对象和面向过程对立起来,面向对象和面向过程不是矛盾的,而是各有用途互为补充的。小编推荐一个学C/C++的学习裙【六二七,零一二,四六四】,无论你是大牛还是小白,是想转行还是想入行都可以来了解一起进步一起学习!裙内有很多干货和技术分享! 所以,通俗来讲,大家在学习的过程中不要把C语言和C++做为两门独立或者对立的语言来学习,本身他们就可以看成是一门语言,任何一款支持C++的编译工具都支持C语言。C++就是C语言的超集,包括了所有C语言的特性,所以你把C++学会了,C语言自然也就会了。如果你之前学过C语言,那么再学习下C++的面向对象思想,那么两者你也就都会了。 给大家举个例子: 学习编程的人针对谭浩强这个名字应该几乎都听过。谭浩强老师的C语言/C++语言的编程书籍可以说是国内很多大学的标杆教材了。我读大学时候的C语言教材就是谭浩强老师的。 他有一本《C语言程序设计》专门讲解C语言的,还有一本《C++程序设计》专门讲解C++的,书挺厚的,包括了C语言的部分。还有一本是《C++面向对象程序设计》是专门讲解C++的,所以可以这么说:《C语言程序设计》+《C++面向对象程序设计》=《C++程序设计》,这也充分体现了C语言和C++的关系。 说到谭浩强老师我这里要说一说了。国内很多读者对谭浩强老师褒贬不一。但我个人确实是比较推崇谭浩强老师的书,大学时候就是用他的书籍入门的。有很多人说他的书籍有错误,讲解的不好。我要说这个错误不怪他,《C语言程序设计》第一版刚出来的时候,系统还是Win98,16位的,所以相关一些类型占用的字节数等等,跟我们现在使用的x86或者x64根本不一样,所以才出现了网友口中的错误。不过《C语言程序设计》、《C++程序设计》 等谭浩强老师的书籍后续都有新版本,所以大家也不要总拿第一版或者老版本来说事儿。 4、用途:C语言和C++都能做什么呢? C语言:操作系统底层、系统驱动、单片机、嵌入式方面 等等; 如今的C++渗透了哪些应用领域?C++:网络游戏开发、音视频技术、Socket网络通信,另外,苹果/谷歌/微软 等大型软硬件公司的系统或者软件上面都支持C/C++语言的集成开发。 你常用的软件大多数都是C++写的,例如:Office软件:MS Office,WPS Office,OpenOffice/LibreOffice,你所用的Windows也用了大量的C++,你说你在用C#和SQL,那我告诉你你用的 VS IDE 核心部分是C++写的,你用的C#,C# 的.Net执行框架也是C++写的,你用的Sql数据库,是Sql Server吧?很不幸,Sql Server也是cpp写的、你上网页在用浏览器吗?很不幸,浏览器内核都是C++写的,界面大多数浏览器界面也是C++写的,你聊天用的 QQ、YY、Skype 等也是C++写的。这些都是你绝对有在用的,至于其他,还有很多,杀毒软件、PhotoShop、Maya,N多行业软件,几乎所有的端游 等等,都是C++写的。你问我C++能做什么实际的东西,我告诉你什么也做不了,你信吗?哈哈 C++ 的几个常见的发展方向:客户端,游戏,服务端,嵌入式,移动端(移动只要是跨平台的移动端用C++写通用部分,GUI可以用平台特性,也可以用C++的跨平台框架) 针对界面UI方面C++的选择也是很多的,其实C++还是擅长“内功”的方面。如果你想带个GUI界面的话,也有很多类似Qt之类的框架和界面库可以使用,网上有很多,而且很多都跨平台,还开源,不管是 Windows,还是Mac,还是Linux,还是移动端平台,通吃的。本身C/C++就是跨平台的。 另外,说点大家感兴趣的,什么远程控制软件,什么木马,什么外挂等等,可以说95%以上都是用C/C++来写的。 难道这么多的用途还不值得大家来好好学学C/C++吗? 另外,闻道有先后、术业有专攻,C++虽然功能很强大,几乎什么都能做,但有的地方也是不适合,不是不能做而是不适合。比如网页开发,C++也能做,但是还是建议使用Java-Web或者PHP之类的语言来做,毕竟他们就是为了Web开发而生的。

May 30, 2019 · 1 min · jiezi

CC语言0|C和C的联系与区别

1 C语言 C语言之所以命名为C,是因为 C语言源自Ken Thompson发明的B语言,而 B语言则源自BCPL语言。 1967年,剑桥大学的Martin Richards对CPL语言进行了简化,于是产生了BCPL(Basic Combined Programming Language)语言。并且他用B语言写了第一个UNIX操作系统。 1972年,美国贝尔实验室的 D.M.Ritchie 在B语言的基础上最终设计出了一种新的语言,他取了BCPL的第二个字母作为这种语言的名字,这就是C语言。最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 C语言非常简洁,只有32个关键字,9种控制语句,34种运算符。 具体来说,C语言是一个结构化语言,重点在于数据结构和算法的实现。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事物)控制)。 C语言可以做任何用处,但最大的用处还是写写操作系统和编译器之类的。 C 语言是非常有效率的,很多时候你都需要考虑内存的管理等底层的东西。很可惜这些都需要你去花很多的时间去做。作为一种底层编程语言,可以通过指针进行很直接的内存管理,另外,很多语言都是用 C 来设计的, 比如 perl , java, python。 C语言是一种通用性的编程语言,它既具有高级语言的特点,又具有汇编语言的特点。,1978年后,C语言已先后被移植到大、中、小及微型机上,它可以作为工作系统设计语言,编写系统应用程序,也可以作为应用程序设计语言,编写不依赖计算机硬件的应用程序。它的应用范围广泛,具备很强的数据处理能力,不仅仅是在软件开发上,而且各类科研都需要用到C语言,适于编写系统软件,三维,二维图形和动画,具体应用比如单片机以及嵌入式系统开发。尽管C语言是为实现操作系统软件而设计的,但它也广泛的应用于开发便携式应用软件。 1.1 C语言是一个有结构化程序设计、具有变量作用域(variable scope)以及递归功能的过程式语言。 1.2 C语言传递参数有值传递(pass by value)和指针传递(a pointer passed by value)两种方式。 1.3 不同的变量类型可以用结构体(struct)组合在一起。 1.4 只有32个保留字(reserved keywords),使变量、函数命名有更多弹性。 1.5 部份的变量类型可以转换,例如整型和字符型变量。 1.6 通过指针(pointer),C语言可以容易的对存储器进行低级控制。 1.7 预编译处理(preprocessor)让C语言的编译更具有弹性。 示例代码: include <stdio.h>int main(void){ printf("hello, world!n"); return 0; }2 C++语言 20世纪70年代中期,Bjarne Stroustrup在剑桥大学计算机中心工作。他使用过Simula和ALGOL,接触过C。他对Simula的类体系感受颇深,对ALGOL的结构也很有研究,深知运行效率的意义。既要编程简单、正确可靠,又要运行高效、可移植,是Bjarne Stroustrup的初衷。以C为背景,以Simula思想为基础,正好符合他的设想。1979年,Bjame Sgoustrup到了Bell实验室,开始从事将C改良为带类的C(C with classes)的工作。1983年该语言被正式命名为C++。 ...

May 29, 2019 · 2 min · jiezi

C基础教程比较全面适合小白

C ++是一种通用编程语言。 C ++可以创建计算机程序。 从应用程序,音乐播放器,甚至视频游戏它都可以胜任。 C ++主要源自于C语言。 你的第一个C++程序 一个C ++程序是一个命令或语句的集合。最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 下面是一个简单的代码,我们将输出"Hello world!" : include <iostream>using namespace std;int main(){ cout << "Hello world!"; return 0;}C++提供了许多实用的头文件,这些头文件里包含了程序运行时需要用到的一些方法,比如我们在上面的程序中就引入了iostream。 以#为开头是告诉编译器,该行代码需要预处理。include 是告诉编译器,需要引入iostream这个头文件。iostream文件定义了输入流/输出流对象。 include <iostream>C ++编译器是忽略空行的,空白行可以改善代码的可读性和结构。 空格(如空格,制表符和换行符)也被忽略,这些符号一样也是有助于提高代码的可读性。 include <iostream>using namespace std;//使用名称为std的命名空间int main(){ cout << "Hello world!"; return 0;}std命名空间包含C ++标准库的功能。Main函数 Main函数是程序的入口,程序从int main()开始执行。 include <iostream>using namespace std;int main(){ cout << "Hello world!"; return 0;}大括号{}表示函数的开始和结束,也可以称为函数的主体。 每个C ++程序的入口点都是main()。访问流对象 include <iostream>using namespace std;int main(){ cout << "Hello world!"; return 0;} 上述代码中,cout << "Hello World!"用来将"Hello World!"输出到屏幕上。 ...

May 28, 2019 · 2 min · jiezi

CC-统计词频

分别用C和C++实现词频统计,要求用fopen打开文本文件,最后将统计结果输出至文件。 1、首先用纯C实现,定义一个存放单词和出现频次的结点,用链表实现存储,代码如下: #include <stdio.h>#include <stdlib.h>#include <string.h>// 定义一个存放单词和频次的节点,用链表实现存储typedef struct WordNode { char word[100]; // 存放单词 int word_cnt; // 单词出现次数 struct WordNode* p_next; // 结构体指针} WordNode;// 函数声明void ProcessWord(char* word); // 处理当前单词并统计void CountWord(char* word); // 统计当前单词WordNode* SearchWord(char* word); // 查找当前单词void SortResult(void); // 对统计结果按字典顺序排序void PrintResult(void); // 输出统计结果void Release(void); // 释放内存// 全局变量WordNode* g_p_head = NULL; // 存放单词链表的头指针int main(int argc, char* argv[]) { if (argc != 2) { printf("usage: %s filepath", argv[0]); exit(1); } char temp_word[100]; // 用于临时存放当前单词 FILE* fp_read = NULL; if ((fp_read = fopen(argv[1], "r")) == NULL) { printf("open %s error!\n", argv[1]); exit(1); } // 循环读取文本中的单词并进行处理 while (fscanf(fp_read, "%s", temp_word) != EOF) { ProcessWord(temp_word); } fclose(fp_read); // 关闭文件 SortResult(); // 按字典顺序排序所统计的单词 PrintResult(); // 输出统计结果至文本 Release(); // 释放内存 return 0;}// 处理当前单词并统计void ProcessWord(char* word) { char* special_char = ",;.:?\"\'\\><-+=|*^&%$#@!()"; // 可能存在的特殊字符,标点符号 int spec_length = strlen(special_char); int word_length = strlen(word); int int_res = atoi(word); // 如果当前单词不是数字而是英文单词,才进行单词统计 if (int_res == 0 && strcmp("0", word) != 0) { int i, j; for (i = 0; i < word_length; i++) { // 将单词中的大写字母均转化为小写字母 if (word[i] >= 'A' && word[i] <= 'Z') { word[i] = word[i] + 32; } // 将单词中可能存在的特殊字符均以空格符代替 for (j = 0; j < spec_length; j++) { if (word[i] == special_char[j]) { word[i] = ' '; } } } // 用strtok分割经上述步骤处理的单词(可能存在多个单词),并对每个单词进行统计 char* temp_word = NULL; for (temp_word = strtok(word, " "); temp_word != NULL; temp_word = strtok(NULL, " ")) { CountWord(temp_word); } } else { // 说明传入的字符串是数字,直接返回不做处理 return; }}// 统计当前单词void CountWord(char* word) { WordNode* p_find = NULL; p_find = SearchWord(word); if (p_find == NULL) { return; } else { p_find->word_cnt++; // 对应单词次数加一 }}// 查找当前单词(链表查找效率较低下)WordNode* SearchWord(char* word) { // 链表为空时,统计第一个单词 if (g_p_head == NULL) { g_p_head = new WordNode; strcpy(g_p_head->word, word); g_p_head->word_cnt = 0; g_p_head->p_next = NULL; return g_p_head; } // 检查现有链表,确定当前查找单词的位置 WordNode* p_cur = g_p_head; WordNode* p_pre = NULL; while ((p_cur != NULL) && (strcmp(p_cur->word, word) != 0)) { p_pre = p_cur; p_cur = p_cur->p_next; } // 该单词不存在时,直接加入链表尾部 if (p_cur == NULL) { p_cur = new WordNode; strcpy(p_cur->word, word); p_cur->word_cnt = 0; p_cur->p_next = NULL; p_pre->p_next = p_cur; } return p_cur;}// 对统计结果按字典顺序排序void SortResult(void) { WordNode* p_pre = NULL; WordNode* p_cur = NULL; WordNode p_tmp; for (p_pre = g_p_head; p_pre->p_next != NULL; p_pre = p_pre->p_next) { for (p_cur = p_pre->p_next; p_cur != NULL; p_cur = p_cur->p_next) { if (strcmp(p_pre->word, p_cur->word) > 0) { // 交换结点的数据域和指针域 p_tmp = *p_pre; *p_pre = *p_cur; *p_cur = p_tmp; p_tmp.p_next = p_pre->p_next; p_pre->p_next = p_cur->p_next; p_cur->p_next = p_tmp.p_next; } } }}// 输出统计结果void PrintResult(void) { FILE* fp_write = fopen("result_liang.txt", "w"); if (g_p_head == NULL) { printf("No word in this file!\n"); return; } WordNode* p_cur = g_p_head; while (p_cur != NULL) { fprintf(fp_write, "%-20s\t%d\n", p_cur->word, p_cur->word_cnt); p_cur = p_cur->p_next; } fclose(fp_write);}// 释放内存void Release(void) { if (g_p_head == NULL) { return; } WordNode* p_cur = g_p_head; WordNode* p_p_tmp = NULL; while (p_cur != NULL) { p_p_tmp = p_cur->p_next; delete p_cur; p_cur = p_p_tmp; }}经编译运行结果可以发现两个问题:一是整个过程用链表实现,在统计单词时需要查找整个链表,效率较低,且后期还需要对链表进行排序,总之,利用这种方式实现整体效率较为低下;二是在代码的ProcessWord函数中,以下过程是在处理文本中可能存在的数值的情况,利用atoi函数判断,atoi函数返回值为0则代表当前word不是数值而是英文单词,否则返回转换的数值。但有一种情况,即当word为“0”或是“00”或是“000”等时,返回的结果也为0,只利用strcmp("0", word)判断是不行的,若文本中存在类似“00”或者“000”等多0的情况,统计结果中还是会出现“0”和它的频次。 ...

May 28, 2019 · 5 min · jiezi

流程图绘制工具有哪些国产的流程图设计软件

亿图流程图制作软件是一款用于绘制各种流程图,同时兼具跨平台,云储存,分享功能的专业流程图制作软件。操作简单,功能强大,非常容易实现可视化、分析和交流复杂信息。软件内置海量精美的流程图模板与图库,帮助你轻松绘制项目管理流程图,程序流程图,工作流程图,过程流程图等。 如何用来绘制一个流程图呢 第一步 选择从模板创建或者创建一个新页面 方法一:创建一个新的页面 点击文件-新建-流程图。 双击模板下的流程图选择需要绘制的种类,进入编辑状态。 方法二:使用模板创建程图 点击文件-新建-流程图。 当找到需要的模板时,双击模板或者点击右上角预览窗口下的创建导按钮,即可成功创建一个含有预设内容的流程图。 第二步 添加图形 方法一:用图形的浮动按钮添加 从左侧模板库中拖出一个流程形状。 点击四周的浮动按钮。 方法二: 从库里拖放添加 从界面左边的符号库里拖动一个图形。 把拖动的图形移动到要吸附的标题旁,松开鼠标会自动链接。 第三步 排版和连接线样式 排版十分灵活,可以智能的调整大小和对齐,还可以根据已经存在图形的位置标出对齐线,其自动性为我们带来便捷。 第四步 添加文本和其他内容 添加文本 双击流程图图形。 输入文本。 点击绘图页面的任意空白区域或者按 ESC 键完成输入文字。 另外流程图制作软件不仅可以添加文件,还可以添加超链接,附件、图释和其他内容以提供上下文信息。 第五步 美化功能 ...

May 27, 2019 · 1 min · jiezi

原生的-Linux-异步文件操作iouring-尝鲜体验

Linux异步IO的历史异步IO一直是 Linux 系统的痛。Linux 很早就有 POSIX AIO 这套异步IO实现,但它是在用户空间自己开用户线程模拟的,效率极其低下。后来在 Linux 2.6 引入了真正的内核级别支持的异步IO实现(Linux aio),但是它只支持 Direct IO,只支持磁盘文件读写,而且对文件大小还有限制,总之各种麻烦。到目前为止(2019年5月),libuv 还是在用pthread+preadv的形式实现异步IO。 随着 Linux 5.1 的发布,Linux 终于有了自己好用的异步IO实现,并且支持大多数文件类型(磁盘文件、socket,管道等),这个就是本文的主角:io_uring IOCP于IO多路复用模型 epoll 不同,io_uring 的思想更类似于 Windows 上的 IOCP。用快递来举例:同步模型就是你从在电商平台下单前,就在你家楼下一直等,直到快递公司把货送到楼下,你再把东西带上楼。epoll 类似于你下单,快递公司送到楼下,通知你可以去楼下取货了,这时你下楼把东西带上来。虽然还是需要用户下楼取货(有一段同步读写的时间),但是由于不需要等快递在路上的时间,效率已经有非常大的提升。但是,epoll不适用于磁盘IO,因为磁盘文件总是可读的。 而 IOCP 就是一步到位,直接送货上门,连下楼取的动作都不需要。整个过程完全是非阻塞的。 io_uring 的简单使用io_uring 是一套系统调用接口,虽然总共就3个系统调用,但实际使用却非常复杂。这里直接介绍封装过便于用户使用的 liburing。 在尝试前请首先确认自己的 Linux 内核版本在 5.1 以上(uname -r)。liburing 需要自己编译(之后可能会被各大Linux发行版以软件包的形式收录),git clone 后直接 ./configure && sudo make install 就好了。 io_uring 结构初始化liburing 提供了自己的核心结构 io_uring,它内部封装了 io_uring 自己的文件描述符(fd)以及其他与内核通信所需变量。 struct io_uring { struct io_uring_sq sq; struct io_uring_cq cq; int ring_fd;};使用之前需要先初始化,使用 io_uring_queue_init 初始化此结构。 ...

May 27, 2019 · 2 min · jiezi

QT新手笔记01

QT自定义控件方法: 第一步 创建一个QTProject,基类为QWidget(勾选ui); 第二步 在.ui界面中创建所需控件; 第三步 在这个Project中点击Project名添加新文件, 添加一个C++ class,继承QWidget; 第四步 在这个的.ui中拖一个Widget控件,然后选择Widget右键提升,输入第一个的类名; 第五步 关联第一个类中的控件之间的联系.例子:horizintalSlider and spinbox api(片段): setRange(0,100) connect(ui->horizontalSlider,&QSlider::valueChanged,[=](int value){ ui->spinBox->setValue(value); connect(ui->spinBox, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), ui->horizontalSlider,&QSlider::setValue);拓展: set two PushButtons and one can get the value of the horizontalSlider, the another one can set the value of the horizontalSlider.Mind: 在第一个创建的自定义Widget中给出2个函数, int get_value()----the first PushButton和void set_value(int value)---the second PushButton, 最后通过按钮的槽函数就可以实现了.常用事件处理方法: 在所有组件的父类 QWidget 中,定义了很多事件处理的函数,如◼ keyPressEvent():键盘按键按下事件◼ keyReleaseEvent():键盘按键松开事件◼ mouseDoubleClickEvent():鼠标双击事件◼ mouseMoveEvent():鼠标移动事件◼ mousePressEvent():鼠标按键按下事件◼ mouseReleaseEvent() : 鼠标按键松开事件◼ 等等这些函数都是 protected virtual 的,也就是说,我们可以在子类中重新实现这些函数。 ...

May 26, 2019 · 1 min · jiezi

C语言中链表的基本操作

C语言中的链表C语言中链表的基本操作#include <stdio.h>#include <malloc.h>//malloc函数的实现#include <stdlib.h>//exit函数//定义了一个数据类型,该数据类型含有三个变量struct Arr{ int * pBase;//存取的是数组第一个元素的地址 int len;//数组所能容纳的最大元素的个数 int cnt;//当前数组有效的个数};void init_arr(struct Arr *pArr,int length);bool append_arr(struct Arr *pArr,int val);//追加bool insert_arr(struct Arr *pArr,int pos,int val);//pos的值从1开始bool delete_arr(struct Arr *pArr,int pos,int *pVal);bool is_empty();bool is_full(struct Arr *pArr);void sort_arr(struct Arr *pArr);void show_arr(struct Arr *pArr);void inversion_arr(struct Arr *pArr);int main(void){ struct Arr arr; int val; init_arr(&arr,6); show_arr(&arr); append_arr(&arr,1); append_arr(&arr,-4); append_arr(&arr,40); append_arr(&arr,2); append_arr(&arr,76); append_arr(&arr,3); append_arr(&arr,4); if( delete_arr(&arr,4,&val)) { printf("删除成功\n"); printf("您删除的元素是:%d\n",val); }else { printf("删除失败!\n"); } /* append_arr(&arr,1); append_arr(&arr,2); append_arr(&arr,3); append_arr(&arr,4); append_arr(&arr,5); insert_arr(&arr,1,99); */ show_arr(&arr); inversion_arr(&arr); printf("倒置之后的数组的内容是:\n"); show_arr(&arr); sort_arr(&arr); printf("排序之后的数组的内容是:\n"); show_arr(&arr); //printf("%d\n",arr.len); return 0;}//初始化void init_arr(struct Arr *pArr ,int length){ pArr->pBase=(int *)malloc(sizeof(int)*length); if(NULL == pArr->pBase) { printf("动态内存分配失败!\n"); exit(-1); }else { pArr->len =length; pArr->cnt = 0; } return;}//判断数组为空返回truebool is_empty(struct Arr *pArr){ if(0 == pArr->cnt) return true; else return false;}//判断是否满返回truebool is_full(struct Arr *pArr){ if(pArr->cnt == pArr->len) return true; else return false;}//数组输出void show_arr(struct Arr *pArr){ if(is_empty(pArr)) { printf("数组为空!\n"); } else { for(int i =0;i<pArr->cnt;++i) printf("%d ",pArr->pBase[i]); printf("\n"); }}//追加数据bool append_arr(struct Arr *pArr,int val){ //满时返回false if(is_full(pArr)) return false; //不满时追加 else pArr->pBase[pArr->cnt] = val; (pArr->cnt)++; return true;}//插入元素bool insert_arr(struct Arr *pArr,int pos,int val){ int i; if(is_full(pArr)) return false; if(pos<1 || pos>pArr->cnt+1) return false; for(i = pArr->cnt-1;i>=pos-1;--i) { pArr->pBase[i+1]=pArr->pBase[i]; } pArr->pBase[pos-1] = val; (pArr->cnt)++; return true;}//删除元素bool delete_arr(struct Arr *pArr,int pos,int *pVal){ int i; if(is_empty(pArr)) return false; if(pos<1 || pos>pArr->cnt) return false; *pVal =pArr->pBase[pos-1]; for(i = pos;i<pArr->cnt;++i) { pArr->pBase[i-1] = pArr->pBase[i]; } (pArr->cnt)--; return true;}//元素倒置void inversion_arr(struct Arr *pArr){ int i =0; int j = pArr->cnt-1; int t; while(i<j) { t = pArr->pBase[i]; pArr->pBase[i]=pArr->pBase[j]; pArr->pBase[j] = t; ++i; --j; }}//元素排序void sort_arr(struct Arr *pArr){ int i,j; int temp; for(i = 0;i<pArr->cnt;++i) { for(j = i+1;j<pArr->cnt;++j) { if(pArr->pBase[i] > pArr->pBase[j]) { temp = pArr->pBase[i]; pArr->pBase[i]=pArr->pBase[j]; pArr->pBase[j] = temp; } } }}

May 23, 2019 · 2 min · jiezi

敲代码无聊那是你没见过这些骚气高端的代码

很多人说程序员是码农,是字母的搬运工,一天天敲着重复的代码,十分的无趣。其实程序员的世界,从来是一个充满想象力的天堂。今天小易给大家看看几款程序员小伙伴们那些清奇的奇思妙想吧。 首先是“忧国忧民”的佛系代码最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 敲代码无聊?那是你没见过这些骚气高端的代码!这个代码很巧妙地组成了一个佛的形象,作者还在其下题了一首颇为忧心的诗,要小编说,你有这写佛祖代码的功夫,还不如直接写个好看的代码哄女孩啊喂! 比起上面这位佛系的作者来说,下面的这个爱心的代码作者就很会来事了,何愁找不到女票是不是,用代码来表白,别出心裁呀是不是,屏幕前的程序员是不是有点心动咧。 敲代码无聊?那是你没见过这些骚气高端的代码!接下来,就为大家分享几款比较正经的代码了。通过计算代码本身的面积来计算圆周率的近似值这个代码来自世界混乱C代码大赛International Obfuscated C Code ContestIOCCC这个大赛的宗旨是在C语言本身可以执行的条件下看谁能写出最有创意的最让人难以理解的C语言代码代码的长度要求限制在4kb以内大赛的官方链接为The International Obfuscated C Code Contest 第23届IOCCC竞赛已经在2014年10月27日结束但是我似乎找不到源代码我们来看看第22届IOCCC竞赛中的代码中又没有什么好玩的东西吧 Best Painting 敲代码无聊?那是你没见过这些骚气高端的代码!Most Lazy SKLer Most lazy SKIer 敲代码无聊?那是你没见过这些骚气高端的代码!Best Use of 1 Infinite Loop Best use of 1 Infinite Loop 敲代码无聊?那是你没见过这些骚气高端的代码!下面是以前一些比较有趣的代码 2012Most Elementray Use of C Most elementary use of C 敲代码无聊?那是你没见过这些骚气高端的代码!2012Most Functional Most functional 敲代码无聊?那是你没见过这些骚气高端的代码!1998Best of Show Carl Banks' Blog: IOCCC Flight Simulator 敲代码无聊?那是你没见过这些骚气高端的代码!接下来,再为大家分享几款视觉上的盛宴: (原文出自:http://www.matrix67.com/blog/...) Kyle McCormick 在 StackExchange 上发起了一个叫做 Tweetable Mathematical Art 的比赛,参赛者需要用三条推这么长的代码来生成一张图片。具体地说,参赛者需要用 C++ 语言编写 RD 、 GR 、 BL 三个函数,每个函数都不能超过 140 个字符。每个函数都会接到 i 和 j 两个整型参数(0 ≤ i, j ≤ 1023),然后需要返回一个 0 到 255 之间的整数,表示位于 (i, j) 的像素点的颜色值。举个例子,如果 RD(0, 0) 和 GR(0, 0) 返回的都是 0 ,但 BL(0, 0) 返回的是 255 ,那么图像的最左上角那个像素就是蓝色。 ...

May 23, 2019 · 2 min · jiezi

业务流程图总图wps流程图怎么做

大多数人在工作中都需要做各种流程设计,日程排期表,头脑风暴,竞争分析,还有很多专业领域的图形图表来有效梳理自己的思路,提高工作效率。流程图的专业程度也直接影响着整个团队的工作效率。好的流程设计可以将复杂的数据进行清晰的展示,让团队分析或观看起来更加清楚明了。一般在企业、公司、科研、生产线、教学、医疗上等都会派上非常大的作用,一个生产流程,一幅工作流程、一个公司的运营模式都只需要用一张流程图就可以简单的概括出来,所以制作流程图是办公人员必备的武器之一。 绘制专业的流程图的必要性 当你对那些简洁美观的流程图感到羡慕不已,是否好奇它们是怎样做出来的,是否想知道需要什么样的专业技能。今天,这一切将变得非常简单,你只需要点击几下鼠标就能制作出属于自己的可视化流程图。而且一切操作都异常简洁。 流程图的基本符号 首先,设计流程图的难点在于对业务逻辑的清晰把握。熟悉整个流程的方方面面。这要求设计者自己对任何活动、事件的流程设计,都要事先对该活动、事件本身进行深入分析,研究内在的属性和规律,在此基础上把握流程设计的环节和时序,做出流程的科学设计。研究内在属性与规律,这是流程设计应该考虑的基本因素。 也是设计一个好的流程图的前提条件。 然后再根据事物内在属性和规律进行具体分析,将流程的全过程,按每个阶段的作用、功能的不同,分解为若干小环节,每一个环节都可以用一个进程来表示。在流程图中进程使用方框符号来表达。 既然是流程,每个环节就会有先后顺序,按照每个环节应该经历的时间顺序,将各环节依次排开,并用箭头线连接起来。 箭头线在流程图中表示各环节、步骤在顺序中的进展。 对某环节,按需要可在方框中或方框外,作简要注释,也可不作注释。 经常判断是非常重要的,用来表示过程中的一项判定或一个分岔点,判定或分岔的说明写在菱形内,常以问题的形式出现。对该问题的回答决定了判定符号之外引出的路线,每条路线标上相应的回答。 选择好的流程图制作工具 亿图发布第一款支持快捷操作的流程图制作工具从而极大的降低了专业流程设计的门槛,让大多数人可以在很短的时间里绘制出专业的流程图。 现在我来介绍一下亿图软件在流程图制作上的优势。常言道:工欲善其事,必先利其器。面对每天纷繁复杂的工作,好的工具是必不可少的。流程图虽然简单,但是在各行各业中也演变出很多细分领域。每个领域都使用自己特有的符号来表达。如果没有特别的需求,常用的流程图,可以直接选择基本流程图模板即可。如果流程处理设计多个部门,职能,则可以选择跨职能流程图。如果设计数据交互,可以绘制数据流程图。需要用来描述各个部门的职能和业务流程的可以选择工作流程图,顾名思义,工作流程图就是指企业内部发生的某项业务从起始到完成,由多个部门、多个岗位、经多个环节协调共同完成的完整过程。 在软件中,双击打开模板后,就可以使用预设的标准流程图符号。这些符号都是符合行业标准,并被行业熟知认可的。所以大家设计时,尽量不要随意弄一个没有含义的符号。 符号拖曳到画布中,是可以任何放置在任何位置的,很多人说,用Word就可以画流程图,那不是专业人士应该信的。Word更多是文字处理,而不是图文混排。 流程图制作技巧 这里我介绍一些用软件绘制流程图中经常遇到的技巧。 自由布局在拖曳形状的时候会出现辅助线,这些线条是供设计者用来参考被拖曳形状和周边形状的间距和对齐的。一旦出现等间距或者对齐线,就可以松开鼠标,间距合理不仅让流程图看起来更加专业,也让形状之间的线条不至于扭曲。 在设计过程中后期,每个符号都线条连接住,如果需要变更符号类型,往往需要先删除符号,再拖曳新符号,调整适当大小,再重新连接周边符号,最后编辑文字。这么复杂?其实不用,只需要选中符号,然后使用快捷按钮替换即可。 那如何让流程图从外观上瞬间专业呢?答案是使用强大的主题变换功能,通过变更主题,修改主题颜色,可以让设计者在流程设计中无需关心配色和整体色调。之需要最后更改主题方案和主题颜色即可。 说到最后,提一下亿图图示是支持在一个文档中创建多个页面的,每个形状都可以插入超链接、注释,定义符号数据,也支持图层操作。打印是所见即所得,可以缩放打印,也可以分页打印。可以导出成矢量级别的PDF格式,可编辑的Word文档,PPT原生文档。支持高DPI图片格式输出。新版本还支持云分享和跨平台。 只有流程图软件做的专业,才会让每个人都能轻松制作专业的流程图。

May 22, 2019 · 1 min · jiezi

用C的源码一键获取密码超完整的hack教学

早期SMB协议在网络上传输明文口令。后来出现"LAN Manager Challenge/Response"验证机制,简称LM,它是如此简单以至很容易被破解。微软提出了WindowsNT挑战/响应验证机制,称之为NTLM。现在已经有了更新的NTLMv2以及Kerberos验证体系。Windows加密过的密码口令,我们称之为hash(中文:哈希),Windows的系统密码hash默认情况下一般由两部分组成:第一部分是LM-hash,第二部分是NTLM-hash。最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 用C++的源码一键获取密码,超完整的hack教学!只为解释我不是飘零专业户更不是网络验证专业户,这些是大牛级别的人玩的,小菜玩不动,更不想玩,这个工具只是群内的好友需要,不是为了炫耀甚么,某人心态请摆正,正好之前也有一定的研究.. 1、获取标题或密码 2、获取窗口控件的句柄(十六进制显示) 3、获取窗口或控件的类名 代码展示: 用C++的源码一键获取密码,超完整的hack教学!实现效果: 用C++的源码一键获取密码,超完整的hack教学!1、利用十六进行加密文本 2、实用软件解密出明文 3、可以设定文本加密密码 4、支持用户生成随机密码更安全 5、界面简洁,使用简单 用C++的源码一键获取密码,超完整的hack教学!我在被渗透主机上进行尝试过,发现也是可行的,不过chopper的虚拟终端下会显示错误,实际上已经成功执行Powershell代码。不过总体感觉还是Prodump用起来更加方便。对了,Metasploit中也有集成mimkatz的。 用C++的源码一键获取密码,超完整的hack教学!最后,如果你想学C++可以私信小编“学习”获取素材资料以及开发工具和听课权限! 用C++的源码一键获取密码,超完整的hack教学!

May 22, 2019 · 1 min · jiezi

Xcode中配置FFmpeg环境

一、安装 FFmpeg安装 FFmpeg 可以通过源码安装的方式进行安装。为了方便,这里使用 brew 的方式进行安装。brew install ffmpeg二、创建Xcode项目这里创建的是 Command Line Tool 项目。在下一步中的开发语言我选择了 C++ 三、配置FFmpeg依赖如下图所示,找到 Header Search Path 和 Library Search Path 如果你是用 brew 安装的话,可以通过 brew info ffmpeg 命令,可以查看FFmpeg的安装位置。 brew info ffmpeg# 输出的路径是:/usr/local/Cellar/ffmpeg/4.1.3_1Header Search Path输入:/usr/local/Cellar/ffmpeg/4.1.3_1/includeLibrary Search Path输入:/usr/local/Cellar/ffmpeg/4.1.3_1/libLinked Framework and LibrariesGeneral -> Linked Framework and Libraries Add Others -> Command + Shift + G 赶快胜利,接下来,就是两个人的时间了...

May 22, 2019 · 1 min · jiezi

整数溢出

什么是整数溢出由于整数在内存里保存在一个固定长度的空间内,它能存储的最大值和最小值是固定的,如果我们尝试去存储一个数,而这个数又大于这个固定的最大值时,就会导致整数溢出 整数溢出的危害如果一个整数用来计算一些敏感数值,如缓冲区大小或数值索引,就会产生潜在的危险。通常情况下,整数溢出并没有改写额外的内存,不会直接导致任意代码执行,但是它会导致栈溢出和堆溢出,而后两者都会导致任意代码执行。由于整数溢出出现之后,很难被立即察觉,比较难用一个有效的方法去判断是否出现或者可能出现整数溢出。 整数溢出关于整数的异常情况主要有三种: 溢出 只有有符号数才会发生溢出。有符号数最高位表示符号,在两正或两负相加时,有可能改变符号位的值,产生溢出溢出标志OF可检测有符号数的溢出回绕 无符号数0-1时会变成最大的数,如1字节的无符号数会变为255,而255+1会变成最小数0.f进位标志CF可检测无符号数的回绕截断 将一个较大宽度的数存入一个宽度小的操作数中,高位发生截断常见题型整数转换回绕和溢出截断整数转换 -- newbugku一道整数溢出题目f4n_pwnmain函数int __cdecl main(int argc, const char **argv, const char **envp){ int v4; // [esp+Ch] [ebp-1Ch] unsigned int buf; // [esp+10h] [ebp-18h] int v6; // [esp+14h] [ebp-14h] int fd; // [esp+18h] [ebp-10h] int i; // [esp+1Ch] [ebp-Ch] setvbuf(stdout, 0, 2, 0); puts("###### Welecome to ctf game ######\ninput your name length : "); read_name(); puts("let's begin guess num game "); fd = open("/dev/urandom", 0); if ( fd < 0 || read(fd, &buf, 4u) < 0 ) { puts("error"); exit(0); } close(fd); srand(buf); for ( i = 0; i <= 9; ++i ) { v6 = rand() % 9 + 3; printf("Round %d , please guess the num : \n", i); fflush(stdout); fflush(stdin); __isoc99_scanf("%d", &v4); if ( v4 != v6 ) { printf("you fail"); exit(0); } } printf("u are great! this is your flag"); getflag(); return 0;}猜测随机数可得flag,hhh ...

May 21, 2019 · 2 min · jiezi

PHP源码学习20190327-passtwo函数详解笔记

grape 全部视频:https://segmentfault.com/a/11... 原视频地址:http://replay.xesv5.com/ll/24... 流程回顾上节课我们把$a=1这个过程编译梳理了一遍,我们了解到op1,op2,result,opcode的生成过程,下面我们把整个过程来回顾一下。 static zend_op_array *zend_compile(int type){ zend_op_array *op_array = NULL; zend_bool original_in_compilation = CG(in_compilation); CG(in_compilation) = 1; CG(ast) = NULL; CG(ast_arena) = zend_arena_create(1024 * 32); //首先会分配内存 if (!zendparse()) { //zendparse(就是yyparse)(zend_language_parse.y) ==> 通过parser调用lexer,生成抽象语法树ast_list,存到CG(ast);yyparse是通过bison编译zend_language_parser.y生成 int last_lineno = CG(zend_lineno); zend_file_context original_file_context; zend_oparray_context original_oparray_context; zend_op_array *original_active_op_array = CG(active_op_array); op_array = emalloc(sizeof(zend_op_array)); init_op_array(op_array, type, INITIAL_OP_ARRAY_SIZE); //初始化oparray CG(active_op_array) = op_array; if (zend_ast_process) { zend_ast_process(CG(ast)); } zend_file_context_begin(&original_file_context); zend_oparray_context_begin(&original_oparray_context); zend_compile_top_stmt(CG(ast)); //编译ast生成oparray CG(zend_lineno) = last_lineno; zend_emit_final_return(type == ZEND_USER_FUNCTION); //PHP中会加return 1,在此进行处理 op_array->line_start = 1; op_array->line_end = last_lineno; pass_two(op_array); //对于handler的处理 zend_oparray_context_end(&original_oparray_context); zend_file_context_end(&original_file_context); CG(active_op_array) = original_active_op_array; } zend_ast_destroy(CG(ast)); zend_arena_destroy(CG(ast_arena)); CG(in_compilation) = original_in_compilation; return op_array;}大体流程为:词法分析->语法分析->编译ast生成op_array->处理return 1->对于handler做处理以上处理return 1 环节之前的文章中我们都已经提到过,如果有不太理解的请翻阅之前的文章。接下来我们gdb程序到环节return 1。代码: ...

May 21, 2019 · 2 min · jiezi

[资源]C-程序员必收藏

C++ 资源大全中文版 标准库 C++标准库,包括了STL容器,算法和函数等。 C++ Standard Library:是一系列类和函数的集合,使用核心语言编写,也是C++ISO自身标准的一部分。Standard Template Library:标准模板库C POSIX library : POSIX系统的C标准库规范ISO C++ Standards Committee :C++标准委员会最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 框架 C++通用框架和库 Apache C++ Standard Library:是一系列算法,容器,迭代器和其他基本组件的集合ASL :Adobe源代码库提供了同行的评审和可移植的C++源代码库。Boost :大量通用C++库的集合。BDE :来自于彭博资讯实验室的开发环境。Cinder:提供专业品质创造性编码的开源开发社区。Cxxomfort:轻量级的,只包含头文件的库,将C++ 11的一些新特性移植到C++03中。Dlib:使用契约式编程和现代C++科技设计的通用的跨平台的C++库。EASTL :EA-STL公共部分ffead-cpp :企业应用程序开发框架Folly:由Facebook开发和使用的开源C++库JUCE :包罗万象的C++类库,用于开发跨平台软件libPhenom:用于构建高性能和高度可扩展性系统的事件框架。LibSourcey :用于实时的视频流和高性能网络应用程序的C++11 evented IOLibU : C语言写的多平台工具库Loki :C++库的设计,包括常见的设计模式和习语的实现。MiLi :只含头文件的小型C++库openFrameworks :开发C++工具包,用于创意性编码。Qt :跨平台的应用程序和用户界面框架Reason :跨平台的框架,使开发者能够更容易地使用Java,.Net和Python,同时也满足了他们对C++性能和优势的需求。ROOT :具备所有功能的一系列面向对象的框架,能够非常高效地处理和分析大量的数据,为欧洲原子能研究机构所用。STLport:是STL具有代表性的版本STXXL:用于额外的大型数据集的标准模板库。Ultimate++ :C++跨平台快速应用程序开发框架Windows Template Library:用于开发Windows应用程序和UI组件的C++库Yomm11 :C++11的开放multi-methods. 人工智能 btsk :游戏行为树启动器工具Evolving Objects:基于模板的,ANSI C++演化计算库,能够帮助你非常快速地编写出自己的随机优化算法。Neu:C++11框架,编程语言集,用于创建人工智能应用程序的多用途软件系统。 异步事件循环 Boost.Asio:用于网络和底层I/O编程的跨平台的C++库。libev :功能齐全,高性能的时间循环,轻微地仿效libevent,但是不再像libevent一样有局限性,也修复了它的一些bug。libevent :事件通知库libuv :跨平台异步I/O。 音频 音频,声音,音乐,数字化音乐库 FMOD :易于使用的跨平台的音频引擎和音频内容的游戏创作工具。Maximilian :C++音频和音乐数字信号处理库OpenAL :开源音频库—跨平台的音频APIOpus:一个完全开放的,免版税的,高度通用的音频编解码器Speex:免费编解码器,为Opus所废弃Tonic: C++易用和高效的音频合成Vorbis: Ogg Vorbis是一种完全开放的,非专有的,免版税的通用压缩音频格式。 生态学 ...

May 15, 2019 · 3 min · jiezi

网络编程CS模型总结

什么是socket将底层复杂的协议体系,执行流程,进行了封装,封装完的结果,就是一个SOCKET,也就是说,SOCKET是我们调用协议进行通信的操作接口 数据类型:SOCKET 转定义:unsigned int 在系统里每一个socket对应着==唯一的一个整数==,比如23,对应着socket的协议等信息,在通信中,就使用这些整数进行通信,系统会自动去找这些整数所对应的协议应用每个客户端有一个socket,服务器有一个socket,通信时就是通过socket,来表示和谁传递信息 创建socket/* 函数原型 */SOCKET socket( int af, /*地址的类型*/ int type, /*套接字类型*/ int protocol /*协议类型*/);参数1:地址类型地址类型形式==AF_INET==192.168.1.103(IPV4,4字节,32位地址)AF_INET62001:0:3238:DFE1:63::FEFB(IPV6,16字节,128位地址)AF_BTH6B:2D:BC:A9:8C:12(蓝牙)AF_IRDA红外通信地址不止只有IP地址参数2:套接字类型类型用处==SOCK_STREAM==提供带有OOB数据传输机制的顺序,可靠,双向,基于连接的字节流。 使用传输控制协议(TCP)作为Internet地址系列(AF_INET或AF_INET6)SOCK_DGRAM支持数据报的套接字类型,它是固定(通常很小)最大长度的无连接,不可靠的缓冲区。使用用户数据报协议(UDP)作为Internet地址系列(AF_INET或AF_INET6)SOCK_RAW提供允许应用程序操作下一个上层协议头的原始套接字。 要操作IPv4标头,必须在套接字上设置IP_HDRINCL套接字选项。 要操作IPv6标头,必须在套接字上设置IPV6_HDRINCL套接字选项SOCK_RDW提供可靠的消息数据报。 这种类型的一个示例是Windows中的实用通用多播(PGM)多播协议实现,通常称为可靠多播节目SOCK_SEQPACKET提供基于数据报的伪流数据包参数3:协议类型协议类型用处IPPROTO_TCP传输控制协议(TCP)IPPROTO_UDP用户数据报协议(UDP)IPPROTO_ICMPInternet控制消息协议(ICMP)IPPROTO_IGMPInternet组管理协议(IGMP)IPPROTO_RM用于可靠多播的PGM协议==填写0==代表系统自动帮我们选择协议类型 ==参数1、2、3是要相互配套使用的==,不能随便填,使用不同的协议就要添加不同的参数返回值成功返回可用的socket变量失败返回INVALID_SOCKET,可以使用WSAGetlasterror()返回错误码if (INVALID_SOCKET == socketServer){ int a = WSAGetLastError( ); WSACleanup(); return 0;}创建socket代码SOCKET socketListen = socket(AF_INET,SOCK_STREAM,0);if(INVALID_SOCKET == socketListen){ int a = WSAGetLastError( ); WSACleanup(); return 0;}bind()函数作用:给socket绑定端口号与地址 地址:IP地址端口号 同一个软件可能占用多个端口号(不同功能)每一种通信的端口号是唯一的int bind( SOCKET s, /*服务器创建的socket*/ const sockaddr *addr, /*绑定的端口和具体地址*/ int namelen /*sizeof(sockaddr)*/);参数1:==被绑定socket变量==参数2:绑定端口号和地址定义一个==SOCKADDR_IN==数据类型,是一个结构体: typedef struct sockaddr_in {#if(_WIN32_WINNT < 0x0600) short sin_family; /* 地址类型 */#else //(_WIN32_WINNT < 0x0600) ADDRESS_FAMILY sin_family;#endif //(_WIN32_WINNT < 0x0600) USHORT sin_port; /* 端口号 */ IN_ADDR sin_addr; /* IP地址 */ CHAR sin_zero[8];} SOCKADDR_IN, *PSOCKADDR_IN;其中IN_ADDR sin_addr; 又是一个结构体typedef struct in_addr { union { struct { UCHAR s_b1,s_b2,s_b3,s_b4; } S_un_b; struct { USHORT s_w1,s_w2; } S_un_w; ULONG S_addr; } S_un;#define s_addr S_un.S_addr /* can be used for most tcp & ip code */#define s_host S_un.S_un_b.s_b2 // host on imp#define s_net S_un.S_un_b.s_b1 // network#define s_imp S_un.S_un_w.s_w2 // imp#define s_impno S_un.S_un_b.s_b4 // imp ##define s_lh S_un.S_un_b.s_b3 // logical host} IN_ADDR, *PIN_ADDR, FAR *LPIN_ADDR;127.0.0.1 本地回环地址,用于本地网络测试,数据不出计算机端口号:0~65535 ...

May 15, 2019 · 3 min · jiezi

C语言程序设计复习指导

1)程序结构是三种:顺序结构、循环结构、选择结构(if和switch) 2)读程序都要从main()入口,然后从最上面顺序往下读(碰到循环做循环,碰到选择做选择) 3)计算机的数据在电脑中保存是以二进制的形式,数据存放的位置就是他的地址。 4)bit 是位 是指为0或者1。byte是指字节,一个字节=八个位。 1、编译预处理不是C语言的一部分,不再运行时间。C语言编译的程序称为源程序,它以ASCII数值存放在文本文件中。 2、每个C语言程序中main函数是有且只有一个。 3、在函数中不可以再定义函数。 4、算法的是一定要有输出的,他可以没有输入。 5、break可用于循环结构和switch语句。 6、逗号运算符的级别最低。 第一章 1)合法的用户标识符 合法的要求是由字母,数字,下划线组成。有其它元素就错了。 并且第一个必须为字母或则是下划线。第一个为数字就错了。 关键字不可以作为用户标识符号。main define scanf printf都不是关键字。迷惑的地方If是可以做为用户标识符。因为If中的第一个字母大写了,所以不是关键字。 2)实型数据的合法形式: 2.333e-1就是合法的,且数据是2.333*10-1。 e前e后必有数,e后必为整数。 3)字符数据的合法形式: ‘1’是字符占一个字节,“1”是字符串占两个字节(含有一个结束符合)。 ‘0’的ASCII数值表示为48,‘a’的ASCII数值的97,‘A’的ASCII数值是65。 4)整型一般是两个字节,字符型是一个字节,双精度一般是4个字节: 5)转义字符 在程序中 int a=0X6d,是把一个十六进制的数给变量a注意这里的0x必须存在。 在程序中 int a=06d,是一个八进制的形式。 6)算术运算符号的优先级别: 同级别的有的是从左到右,有的是从右往左。 7)强制类型转化: 一定是(int)a不是int(a),注意类型上一定有括号。 注意(int)(a+b)和(int)a+b的区别。前是把a+b转型,后是把a转型再加b. 8)表达式 是表达式就一定有数值。 赋值表达式:表达式数值是最左边的数值,a=b=5;该表达式为5,常量不可以赋值。 自加、自减表达式:假设a=5,++a(是为6),a++(为5); 运行的机理:++a是先把变量的数值加上1,然后把得到的数值放到变量a中,然后再用这个++a表达式的数值为6,而a++是先用该表达式的数值为5,然后再把a的数值加上1为6, 再放到变量a中。进行了++a和a++后在下面的程序中再用到a的话都是变量a中的6了。 ++在前先加后用,++在后先用后加。 逗号表达式:优先级别最低;表达式的数值逗号最右边的那个表达式的数值。 (2,3,4)的表达式的数值就是4 9)位运算 例1:char a=6,b; b=a<<2; 这种题目的计算是先要把a的十进制6化成二进制,再做位运算。 在没有舍去数据的时候,<<左移一位表示乘以2;>>右移一位表示除以2。 10)018的数值是非法的,八进制是没有8的,逢8进1。 11)%符号两边要求是整数。不是整数就错了。 12)取整丢小数的情况: 1、int a=1.6; 2、(int)a;

May 14, 2019 · 1 min · jiezi

C|高质量代码的规范要求与自我审查

1 文件结构 1.1 头文件和定义文件的名称是否合理? 1.2 头文件和定义文件的目录结构是否合理? 1.3 版权和版本声明是否完整? 1.4 头文件是否使用了 ifndef/define/endif 预处理块? 1.5 头文件中是否只存放“声明”而不存放“定义”?最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 2 程序的版式 2.1 空行是否得体? 2.2 代码行内的空格是否得体? 2.3 长行拆分是否得体? 2.4 “{” 和 “}” 是否各占一行并且对齐于同一列? 2.5 一行代码是否只做一件事?如只定义一个变量,只写一条语句。 2.6 If、for、while、do 等语句自占一行,不论执行语句多少都要加“{}”。 2.7 在定义变量(或参数)时,是否将修饰符 * 和 & 紧靠变量名? 2.8 注释是否清晰并且必要? 2.9 注释是否有错误或者可能导致误解? 2.10 类结构的 public, protected, private 顺序是否在所有的程序中保持一致? 3 命名规则 3.1 命名规则是否与所采用的操作系统或开发工具的风格保持一致? 3.2 标识符是否直观且可以拼读? 3.3 标识符的长度应当符合“min-length && max-information”原则? 3.4 程序中是否出现相同的局部变量和全部变量? 3.5 类名、函数名、变量和参数、常量的书写格式是否遵循一定的规则? 3.6 静态变量、全局变量、类的成员变量是否加前缀? 4 表达式与基本语句 ...

May 13, 2019 · 2 min · jiezi

makefcountextjumpfcontext

我们先弄清如何进行协程的切换,程序可以在某个地方挂起,跳转到另外的流程中执行,并且可以重新在挂起处继续运行。那如何实现呢? 我们先来看一个例子,有下面2个函数,如果在一个单线程中让输出结果依次是 funcA1 funcB1 funcA2 funcB2 ... ,你会怎么做呢? void funcA(){ int i = 0; while(true){ //to do something printf("funcA%d ",i); i++; }}void funcB(){ int i = 0; while(true){ //to do something printf("funcB%d ",i); i++; }}如果从c代码的角度来看,如果单线程运行到func1 的 while循环中,如何能调用到func2的while循环呢?必须使用跳转。 首先想到是goto。goto是可以实现跳转,但是goto不能实现函数间的跳转。无法满足这个要求。即使可以实现函数间跳转,难道就可行吗?那这里不得不说下C函数调用过程 具体相见这篇文章https://blog.csdn.net/jelly_9/article/details/53239718子程序或者称为函数,在所有语言中都是层级调用,比如A调用B,B在执行过程中又调用了C,C执行完毕返回,B执行完毕返回,最后是A执行完毕。所以子程序调用是通过栈实现的,子程序调用总是一个入口,一次返回,调用顺序是明确的。程序运行有2个部分,指令,数据。栈保存的数据,指令通过寄存器(rip)控制。 2个函数内部的跳转必须保证栈是正确,所以跳转之前需要保存好当前的栈信息,然后跳转。另外我们可以得到另一个信息,在一个栈上实现多个流程直接的跳转是不能实现的。所以需要多个栈来维护。那我们来看jump_fcontext是怎么实现跳转 c语言函数声明int jump_fcontext(fcontext_t *ofc, fcontext_t nfc, void* vp, bool preserve_fpu);汇编代码如下.text.globl jump_fcontext.type jump_fcontext,@function.align 16jump_fcontext: pushq %rbp /* save RBP */ pushq %rbx /* save RBX */ pushq %r15 /* save R15 */ pushq %r14 /* save R14 */ pushq %r13 /* save R13 */ pushq %r12 /* save R12 */ /* prepare stack for FPU */ leaq -0x8(%rsp), %rsp /* test for flag preserve_fpu */ cmp $0, %rcx je 1f /* save MMX control- and status-word */ stmxcsr (%rsp) /* save x87 control-word */ fnstcw 0x4(%rsp)1: /* store RSP (pointing to context-data) in RDI */ movq %rsp, (%rdi) /* restore RSP (pointing to context-data) from RSI */ movq %rsi, %rsp /* test for flag preserve_fpu */ cmp $0, %rcx je 2f /* restore MMX control- and status-word */ ldmxcsr (%rsp) /* restore x87 control-word */ fldcw 0x4(%rsp)2: /* prepare stack for FPU */ leaq 0x8(%rsp), %rsp popq %r12 /* restrore R12 */ popq %r13 /* restrore R13 */ popq %r14 /* restrore R14 */ popq %r15 /* restrore R15 */ popq %rbx /* restrore RBX */ popq %rbp /* restrore RBP */ /* restore return-address */ popq %r8 /* use third arg as return-value after jump */ movq %rdx, %rax /* use third arg as first arg in context function */ movq %rdx, %rdi /* indirect jump to context */ jmp *%r8.size jump_fcontext,.-jump_fcontext/* Mark that we don't need executable stack. */.section .note.GNU-stack,"",%progbits寄存器的用途可以先了解下https://www.jianshu.com/p/571... ...

May 12, 2019 · 4 min · jiezi

现代编程语言的值传递与引用传递

现代编程语言对于值传递与引用传递的支持程度是比较不同的 首先介绍值传递与引用传递的概念 值传递将变量a传递到其他的函数并对其更改,不能影响a的值 引用传递在其他的作用域对传入的变量a的更改可以影响a的值 Note: 在这里的值的概念,对于原始类型,指的就是字面的值,如1,2,'a'; 而对于动态内存分配/类,则指的是指向这个分配内存/类的引用,而非解引用后指向的内存/类所保存的值 C语言C语言本身只支持值传递,但是通过指针这一概念,通过解引用可以达到引用传递的效果 C++作为C语言的超集发展起来的语言,C++支持C语言的值传递与指针传递,同时C++还添加了引用传递(某种意义上是指针的语法糖),所以C++实际上通过两种语法支持引用传递 下面演示C/C++的值传递与引用传递 #include <iostream>#include <vector>#include <algorithm>using namespace std;/// 基础类型热引用void swap_ref(int &a, int &b) { int t = a; a = b; b = t;}/// 类的引用void swap_ref(string &a, string &b) { string t = a; a = b; b = t;}/// 值传递void swap_val(int a, int b) { int t = a; a = b; b = t;}/// 类的值传递void swap_val(string a, string b) { string t = a; a = b; b = t;}/// 基于指针进行引用传递void swap_ptr(int *a, int *b) { int t = *a; *a = *b; *b = t;}void swap_ptr(string *a, string *b) { string t = *a; *a = *b; *b = t;}int main() { int a = 1; int b = 2; swap_ref(a, b);//引用传递 printf("%d %d\n", a, b); swap_val(a, b);//值传递 printf("%d %d\n", a, b); string x = "x", y = "y"; swap_ref(x, y);//引用传递 cout << x << " " << y << endl; swap_val(x, y);//值传递 无效果 cout << x << " " << y << endl; /// \brief 使用指针本身进行值传递 通过解引用达到了解引用的效果 swap_ptr(&a, &b);//通过指针引用传递 printf("%d %d\n", a, b); swap_ptr(&x, &y);//指针的引用传递 cout << x << " " << y << endl; return 0;}$ ./main.exe2 12 1y xy x1 2x yNotes: ...

May 11, 2019 · 2 min · jiezi

C动态内存管理好难怎么办零基础图文讲解小白轻松理解原理

首先我们先了解一下内存:C语言使用malloc/free动态管理内存空间,C++引入了new/delete,new[]/delete[]来动态管理内存。 介绍new/delete,new[]/delete[]之前我们先了解一下operator new,operator delete,operator new[],operator delete[]函数。 注:这些函数并没有重载new/delete表达式。最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。 函数声明如下: void* operator new(size_t size); void operator delete(size_t size); void* operator new[](size_t size); void operator delete[](size_t size); 析:operator new/operator delete,operator new[]/operator delete[]是标准库函数,用法和malloc/free的用法一样,只负责分配/释放空间,但实际上operator new/operator delete只是malloc/free的一层封装。 new/delete:动态管理对象; new[]/delete[]动态管理对象数组。 int* ptr1=new int;//动态分配4个字节的空间 delete ptr; int* ptr2=new int(4);//动态内存分配4个字节空间并初始化 delete ptr2; int* ptr3=new int[4];//动态内存分配16个字节空间 delete[]; 1>,new/delete实际上做了什么事呢?? new:先调用operator new分配空间,再调用构造函数初始化空间。 delete:先调用析构函数清理对象,再调用operator delete释放空间。2>,new[]/delete[]实际上做了什么事呢?? new[n]:调用operator new分配空间,再调用n次构造函数初始化对象。 delete[n]:调用n次析构函数清理对象,再调用operator delete释放空间。 为什么编译器会知道调用多少次构造函数,析构函数呢? 原来在new[ ]分配空间的时候会在头部多分配4个字节来存n,这样在调用new[]/delete[]时就知道调用几次构造函数和析构函数了。 new/delete,new[]/delete[]为什么要成对出现? 当new在开辟内置类型的空间时,不成对出现是可以的;但是当开辟非内置类型空间时,就要多开辟4个字节,这时如果不成对使用就会造成内存泄漏或者程序崩溃。 用宏模拟实现new[]/delete[]申请和释放数组 //DELETE_ARRAY参数中传n define NEW_ARRAY(ptr,type,n)do{ ptr=(type)operatornew(sizeof(type)n); ...

May 11, 2019 · 1 min · jiezi