callstack的恢复

一.问题的现象程序崩溃的时候有时候会产生dump,或者弹出错误框“×××已停止工作”,这时候用windbg打开dump文件或者attach到进程,查看异常的线程,有时候线程的调用栈已经看不到,如何最大限度的手动恢复栈呢?我也曾经为了解决这个问题查了不少资料,现在也没找到完美的方法,限于自己的知识,只能尽力去恢复。 环境和工具主备操作系统是windows server 2012(64)64,异常的程序是64位,使用的调试工具windbg也是64位。 三.问题追踪首先,调用k命令,输出如下: 0:013> kChild-SP RetAddr Call Site00000000`3c98db18 00007ffc`749213ed ntdll!ZwWaitForMultipleObjects+0xa00000000`3c98db20 00007ffc`772c8241 KERNELBASE!WaitForMultipleObjectsEx+0xe100000000`3c98de00 00000000`00000000 KERNEL32!WerpReportFaultInternal+0x581通过这个callstack,我们很难知道异常在哪。接着,执行!teb指令,查看线程栈的起始位置,输出如下: 0:013> !tebTEB at 00007ff6df4ae000 ExceptionList: 0000000000000000 StackBase: 000000003c990000 StackLimit: 000000003c98d000 SubSystemTib: 0000000000000000 FiberData: 0000000000001e00 ArbitraryUserPointer: 0000000000000000 Self: 00007ff6df4ae000 EnvironmentPointer: 0000000000000000 ClientId: 000000000000148c . 0000000000001c08 RpcHandle: 0000000000000000 Tls Storage: 0000000000e9acd0 PEB Address: 00007ff6df5f6000 LastErrorValue: 0 LastStatusValue: c0000017 Count Owned Locks: 0 HardErrorMode: 0找到线程栈的起始地址和结束地址后,执行命令: dps 000000003c98d000 000000003c990000输出的内容如下: 0:013> dps 000000003c98d000 000000003c99000000000000`3c98d000 00000000`0000000000000000`3c98d008 00000000`0000000000000000`3c98d010 00000000`0000000000000000`3c98d018 00007ffc`776bd702 ntdll!EtwEventWriteNoRegistration+0x92...00000000`3c98d0b8 00007ffc`7770be5a ntdll!WaitForWerSvc+0x8a00000000`3c98d0c0 00000000`0000000100000000`3c98d0c8 00007ffc`7770bf20 ntdll!WerpAllocateAndInitializeSid+0xac00000000`3c98d0d0 00000000`0000000000000000`3c98d0d8 00000000`0000000000000000`3c98d0e0 00000000`0048004600000000`3c98d0e8 00007ffc`776d8380 ntdll! ?? ::FNODOBFM::`string'00000000`3c98d0f0 00000000`0000100000000000`3c98d0f8 00007ffc`7770bfa9 ntdll!WerpFreeSid+0x41 ...00000000`3c98ec38 00007ffc`776d262a ntdll!KiUserExceptionDispatcher+0x3a ... 上面的内容输出很长,内容也很丰富,只列了一部分,执行下面的命令: ...

June 26, 2019 · 1 min · jiezi

有哪些重点大学适合低分考生捡漏看这篇文章就够了

随着各地高考分数地陆续公布,很多高考生们正紧张的研究高考报志愿。高考是一场智慧与毅力的较量搏斗,无数案例证明,科学的志愿填报方案可以为孩子高考“加分”不少,甚至可以实现“低分高就”的奇迹。 以下是适合低分考生“捡漏”的重点大学,供大家参考。 我国共有39所 985院校,77所211院校,这116所重点大学一般来讲在本省都属于第一批次招生,考生填报的时候总是望而却步。 但是,以上12所重点大学大多因为地理位置的原因,偶尔会有报考人数不足的情况,而部分专业在本省或者外省就会在第二批次招生,这就是成绩不算很高,又想进重点大学的学生最好的选择。 接下来就给大家详细介绍一下这12所重点院校及低分录取的原因。 985大学 湖南大学自从湖南大学被定位于“双一流大学B类高校”之后,关于湖南大学的各种说法,不绝于耳。有人说:湖南大学因为错过湘雅医学院,而失去了与中南大学比肩而立的优势。也有人说:湖南大学依然是综合类大学的强校,人文底蕴深厚,理工科与人文社科均衡发展,不要因为一个B类双一流大学的定位而低估湖南大学。 纵览湖南大学的优势专业,其发展实力依然深厚功底,具体的优势专业包括:化学、机械工程、车辆工程、土木工程、通信工程、材料科学与工程等专业。另外,湖南大学的金融学、会计学也具备较强的发展优势。 东北大学东北大学坐落在东北中心城市辽宁省沈阳市,在河北省秦皇岛市设有东北大学秦皇岛分校,是一所具有爱国主义光荣传统的大学,在技术创新、转移和产学研合作方面有自己的办学特色。 可不少的人把东北大学视作为我国985大学当中的一所排名靠后的大学,许多人更忽视了这所985大学的存在,自从东北地区失去了我国经济发展重心的优势后,东北大学的发展也大不如从前,缺少相关的资金扶持,而且加上在地缘上都占据优势,东北大学逐年所招收的生源质量也有所下降,高考录取分数线在国内39所985大学当中其实也并不高,适合低分考生捡漏。 山东大学谈到山东大学,有人说:山东大学怎么可能被低估?山东大学可是百年学府,国内的前20名大学,不应该被低估。也有人说:山东大学正在走下坡路,昔日的齐鲁第一学府,不再像以前一样辉煌了。事实究竟是什么,同学们可以看一看山东大学的双一流学科,以及山东大学在教育部第四轮学科评估当中的成绩。 不论怎么说山东大学现在的发展实力,它的王牌专业依然具备深厚的发展功底,其优势学科具体包括:数学、化学、材料科学与工程、医学、机械设计制造及其自动化、通信工程、软件工程等专业。 兰州大学同样是百年学府,同样是曾在国际上享有盛誉的高等学府,兰州大学的发展历程,见证了我国西部顶尖学府的沧桑变迁。现在的兰州大学依然是中央直属的副部级大学,也是双一流大学的A类高校,在最新的ESI综合实力排行榜当中,兰州大学位居27名,超过了北京理工大学以及华东师范大学这些985高校。 但是对优质生源的吸引力,已经不如从前。这些年来,兰州大学因地理位置偏远而面临着人才流失的问题,所招的生源质量下降了许多,但是即使在地缘上不占据先天的优势,兰州大学仍然是国内一所实力强悍的985大学,兰州大学王牌专业,依然没有辜负王牌的称号,其生态学、大气科学、草学、化学、核科学等专业,均具备强劲的发展实力,而前四个专业也是兰大的双一流学科。 哈尔滨工业大学哈尔滨工业大学被誉为“国内工程师的摇篮”,在我国工科大学当中,位居前列,在2018年公布的全球最佳大学排行榜当中,哈尔滨工业大学位居国内第10名,从学科建设实力来看,哈尔滨工业大学有17门学科被选为国家A类学科. 但这些年来,哈尔滨工业大学的科研创新能力发展有些滞后,高考录取分数线也有所下降,对于那些想就读985大学,但高考成绩不太理想的考生,可优先考虑报读哈尔滨工业大学。 西安交通大学西安交通大学由于在地域上不占优势,而且发展也相对滞后,甚至被上海交通大学所赶超,但是从整体角度上来看,西安交通大学,办学历史悠久,学科发展实力雄厚,共有14个学科进入世界学术机构前1%,人们喜欢西安交通大学跟上海交通大学相互对比,而这两所大学最大的区别就在于地缘,上海交通大学的地理位置得天厚,更受考生的青睐,而西安交通大学位居我国西北内陆地区,跟上海交通大学的地理位置相比,确实要差得多。 211大学 太原理工大学太原理工大学可能很少有人了解它是一所211,就是因为这所学校太低调了!但它的实力真的挺厉害的,因为无论是它的土木还是建筑、机械或者电气都很厉害的。 但是作为重理的大学最大的缺点在于它的文科专业太弱,所以在全国各省录取时,录取分数比其它专业要低一些。 河海大学(常州校区)河海大学在国内也算是一所重点211大学了,被称作小985,实力也是非常雄厚的,尤其是它的水利专业,在全国更是数一数二的。不过低分想上河海大学是可以的,不过是常州校区,大部分重点大学的分校都要比校本部录取分数低,因此稍微努力一下,中等生是可以冲刺一下这所学校的。 这所学校的主要专业有能源与动力工程、工业设计、计算机科学与技术、国际经济与贸易、工商管理等。 9.东北农业大学东北农业大学位于黑龙江省哈尔滨市,是一所以农科为优势,以生命科学和食品科学为特色,农、工、理、经、管等多学科发展的国家世界一流学科建设高校,国家首批“211工程”重点建设高校。 但因为农学相关专业考生的报考热情不高,导致学生在综合考虑兴趣爱好和就业方向之后,报考该类专业的考生并不多,因此农学类的大学高考录取分数普遍较低。甚至有时候学校的录取分数低于很多普通一本大学,这对中等生来说,就是个福音了。 10.青海大学青海大学是我国西北地区重点建设的一所211院校,它在整个西北部高等院校中是十分不错的,青海大学兼顾了本硕博教育,同时学校学科十分齐全,该校如今开设了众多的硕士及博士点。 但因为地理位置的原因,学校的整体实力在211高校中仅处于中下游水平。再加上,青海省经济相对于东部地区较落后,所以使得青海大学录取线在211院校中并不是很高,在部分省份有第二批次招生名额,可以随时关注。 11.西藏大学西藏大学是西藏自治区所属的综合性大学,同样是“211工程”重点建设大学,国家“双一流”世界一流学科建设高校,西藏大学2018年在云南、山西、甘肃、湖北、四川、河南、河北、山东、山西、陕西等多个省份都在第二批次进行招生。 12.石河子大学石河子大学很多人都不知道,这所大学位于戈壁明珠-新疆石河子市,学校有着63年的办学历史,是国家世界一流学科建设高校,也是“211工程”重点建设大学。 这所学校名气比较小,每年石河子大学的录取分数线都非常低,该校农学类学科实力比较强在北京、河北、吉林、黑龙江、江苏、安徽、河南等地有本科第二批次的招录。 以上这12所重点大学,和同类的重点大学相比,分数确实低很多,但它们的综合实力真的挺厉害的,尤其是部分学校的重点专业,基本毕业都不愁工作而且都是王牌。 以下是适合低分考生“捡漏”的重点专业,供大家参考。 图片描述 下面两张图教你看懂如何选专业。 图片描述 图片描述 嗅嗅就是大数据采集与管理专业,收入高、后面还有一群小迷妹跟着呢~ 图片描述 惊喜的是,还有一个小迷妹居然是学酒店管理的学妹,不仅长得美,还有钱! 图片描述 不过,嗅嗅要提醒大家,并不是以上所有的专业都十全十美! 学完计算机数学及应用软件专业后,(嗅嗅的同事)出来工作是这样的... 图片描述 学完材料科学的兄弟,出来后能常年霸占“单身贵族”的高贵身份。 图片描述 高考关系着学生的未来和前途,在报考上一定要慎重,希望各位家长和学生在真正报考前能充分了解高考志愿的填报事宜。 预祝各位高考学子都能考上心仪的院校。 以下为公众号内容 ·end· 往期精选: ·不敢高攀,不愿将就,你的高考志愿将何去何从? ·考生警惕!野鸡大学泛滥地区排行,看看有没有你的家乡!

June 26, 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

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

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

June 23, 2019 · 1 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

vector的性能利器reserve

转载请注明文章出处:https://tlanyan.me/reserve-of... vector是C++编程时的常用容器,其帮助用户自动管理存储空间,简单易用,且能避免资源泄露的问题。需要动态分配存储空间的场景,完全可替代原生数组。 vector被人诟病的地方在于性能。C++ 11引入array容器,有原生数组的性能,编译期能确定大小的情况可取代vector。但对于运行期才能确定大小的情况,array不适用,还是得上vector。 实践中提高vector性能的要点是尽量使用reserve(仅次于换编译器和STL实现)。运行期依然不能确定数组的个数,明智的选择是什么也不做,push_back/emplace_back就足够;运行期能确定个数,则应该用reserve,不建议用传递大小的数组构造函数或者调用resize。 reserve vs resizereserve和resize函数都能分配足够容纳下指定个数对象的空间。不同之处是resize(或构造函数传递数组个数)会改变数组的size(即当前元素的指针),并且极大的可能性会调用存储对象的(复制)构造函数。reserve做的事情就比较纯粹:仅分配所需的空间。 一段代码说明三者的区别: // file: test.cpp#include <iostream>#include <vector>class Foo { public: Foo() { std::cout << "Foo constructor" << std::endl; }};int main(int argc, char* argv[]) { std::cout << "initialize vector with element number..." << std::endl; std::vector<Foo> vec1(5); std::cout << "-------------" << std::endl; std::cout << "vec1 size:" << vec1.size() << std::endl << std::endl; std::cout << "vector resize..." << std::endl; std::vector<Foo> vec2; vec2.resize(5); std::cout << "-------------" << std::endl; std::cout << "vec2 size:" << vec2.size() << std::endl << std::endl; std::cout << "vector reserve..." << std::endl; std::vector<Foo> vec3; vec3.reserve(5); std::cout << "-------------" << std::endl; std::cout << "vec3 size:" << vec3.size() << std::endl << std::endl; return 0;}用gcc编译程序:g++ -std=c++0x -o test -O2 test.cpp,然后./test执行程序,结果如下: ...

June 23, 2019 · 1 min · jiezi

C在指定内存构造对象

转载请注明文章出处:https://tlanyan.me/construct-... 问题为了提高程序的性能,一个做法是一次性分配足够多的内存,从而避免多次申请以及数据拷贝。对于c++,有一个问题:如何在已分配好的内存上构造对象? 前文“vector的性能利器:reserve”提到使用reserve预先分配内存,再push_back或emplace_back,存储过万个大对象时可极大提升效率。探究其实现原理,会发现分配内存简单,调用标准库或者nedmalloc、tcmalloc等库中的函数即可;有了内存,问题同样变成如何在已分配的内存上构造对象? 方案有两种解决方案解决这个问题。 placement new第一种方案是使用placement new。其用法过程为:首先分配足够大的内存;然后用placement new语法生成对象:new(ptr) xxx(),其中ptr是足够容纳所指对象的指针。 一个使用例子: class Person {private: int age; std::string name;public: // methods};int main(int argc, char** argv) { char mem[sizeof(Person)]; // 或者 auto mem = malloc(sizeof(Person)); auto p = new(mem) Person(); assert((void*)p == (void*)mem); // 两个指针指向同一块内存 return 0;}使用placement new有三个注意点:一是要有足够的内存放置对象,这是必须的;二是指针应该是“对齐”的,例如对于4字节对齐的系统,指针地址应该是4的整数倍;三是你(可能)需要显式调用析构函数完成对象的销毁。 operator new使用new生成对象实际上执行了三个操作: 调用operator new分配内存调用类的构造函数返回指针其中operator new是可重载的,无论全局还是特定类。其函数原型为: void* operator new(size_t sz);回到把对象在指定内存上构造的问题上,我们可以通过重载operator new,返回已分配内存的指针。然而由于operator new函数只接受一个参数,地址指针需要是“全局”变量才能生效。这样想来,这种方案实用性并不高。 其他如果你希望像vector中的reserve先分配内存,然后在其上装载对象,可以使用allocator。allocator定义在<memory>头文件中,能对指定类型分配合适的内存,并可手动调用对象的构造函数和析构函数。</memory> 用法示例: int main(int argc, char** argv) { std::allocator<Person> alloc; auto p = alloc.allocate(1); // 分配一个Person对象的内存 alloc.construct(p); // 调用Person的构造函数,如果构造函数有参数,参数写在p之后 // p 现在是一个指向Person的指针,且其指向对象被初始化过 // 对p进行一些操作 // 销毁对象,但不释放内存,等同于调用p->~Person() alloc.destroy(p); // 释放内存 alloc.deallocate(p, 1); return 0;}对于可以内部管理的情形,建议使用allocator而非placement new。 ...

June 23, 2019 · 1 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

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

C友元初步

本例对C++友元的使用从代码上作了一个简单的归纳,但不仅限于本问讲到的用法,友元还可以重载等。** /全局函数作为友元/ ** include <iostream>include <cmath>using namespace std; class Point{public: Point(double x, double y){ _x = x; _y = y;}void getFormatxy();friend double distance(Point &a, Point &b);private: double _x, _y;}; void Point::getFormatxy(){ cout<<"("<<_x<<","<<_y<<")"<<endl;}double distance(Point &a, Point &b){ double dx = a._x - b._x;double dy = a._y - b._y;return sqrt(dx*dx + dy*dy);} int main(){ Point p1(3.0, 4.0), p2(6.0, 8.0);p1.getFormatxy();p2.getFormatxy();double d = distance(p1, p2);cout<<"distance is "<<d<<endl;return 0;} ** /一个类的成员函数作友元/ ...

June 22, 2019 · 2 min · jiezi

leetcode

排列找下一个 https://leetcode.com/problems/next-permutationvoid nextPermutation(vector<int>& nums) { if(nums.size()<2){ return; } int i,j; for(i=nums.size()-2;i>=0;i--){ if(nums[i]<nums[i+1]){ //1.找最后一个左边比右边小的begin sort(nums.begin()+i+1,nums.end()); //2.后面的升序排 for(j=i+1;j<nums.size();j++){ if(nums[j]>nums[i]){ //3.找比begin大的第一个数交换 swap(nums[i],nums[j]); return; } } break; } } if(i<0){ sort(nums.begin(),nums.end()); return; }}Anmhttps://leetcode.com/problems/permutations-ii/vector<vector<int>> permuteUnique(vector<int>& nums) { vector<vector<int>> ret; if(nums.empty()){ return ret; } sort(nums.begin(),nums.end()); //1.排序 每次第一个都会入,如果不排序 跳过重复会用不了 permuteInner(nums,0,ret); return ret;}void permuteInner(vector<int> nums,int stable,vector<vector<int>>& ret) { if(stable==nums.size()-1){ ret.push_back(nums); return; } for(int j=stable;j<nums.size();j++){ //2.跳过重复 if(stable!=j&&nums[j]==nums[stable]){ continue; } //3.相当于前面j位已经排好,求j后面的排序组合,但前面j位可以是后面的任何一个。交换 swap(nums[stable],nums[j]); permuteInner(nums,stable+1,ret); } }Cmn 非重复 ...

June 21, 2019 · 10 min · jiezi

致敬图灵数说人工智能的前世今生

童话故事里,吃了毒苹果的白雪公主还能被王子救活,现实生活中,毒苹果带给我们的痛却无法抹去,65年前,我们伟大的“人工智能之父”艾伦·麦席森·图灵因为一个毒苹果永远的离开了这个世界,离开了他所热爱的人工智能事业。 6月23日是图灵先生的诞辰,所以嗅嗅今天想在这里和大家一起缅怀伟人,聊聊人工智能那些事儿~ 1.人工智能之父图灵,你真的了解吗? 今天前嗅带您从多角度走近图灵,认识不一样的“人工智能之父”。 图片来源:百度百科 如迷的解谜者——图灵 《科学美国人》曾这样评价图灵矛盾的一生:“一个核心的悖论是,他认为电脑能够跟人脑并驾齐驱,但是他本人的个性却是率性而为、我行我素、无法预见,一点也不像机器输出来的东西。” 改变历史的一见钟情 1927年,15岁的图灵在罗斯公学遇到了克里斯朵夫·默卡,因为默卡梦想的学校是剑桥,因此图灵也把剑桥定为目标,并提前一年参加高考,这个一见钟情式的会面,改变了图灵的一生,甚至人类的历史。 布雷契莱园的抢眼教授 图灵花粉过敏,却又拒绝使用脱敏药物,所以他每次骑车去布雷契莱园上下班时都会戴着防毒面具,十分抢眼;由于战争时期瓷器奇缺,为了防止茶杯丢失,他用铁链把茶杯锁定在暖气管子上,成为布雷契莱园的笑谈。 图灵先生传奇的一生还有很多值得我们去怀念,但是篇幅有限,嗅嗅就不一一讲述啦~一组图灵生平纪事盘点送给大家~ 说到图灵先生就不得不提一下人工智能了~ 2.人工智能的前世今生,你真的知道吗? 今天大家就和前嗅一起来到时光隧道,回溯人工智能的发展历程吧~ (1)1950年,图灵发表《计算机器与智能》,最先开始讨论计算机与智能的关系,并提出认定机器智能的“图灵测试”。 (2)1951 年,西洋跳棋程序和国际象棋程序相继诞生,1962年,这个程序击败了美国的西洋跳棋州冠军。 (3)1956 年,达特茅斯会议标志着人工智能正式诞生,之后十几年成为人工智能的黄金时代,人工智能迎来了第一次大发展。(4)1957年,罗森布拉特提出了模拟人脑神经网络的感知机模型。 (5)1968年,美国斯坦福国际咨询研究所研发成功世界上第一台智能机器人Shakey。它具备一定程度的人工智能,能够自主进行感知、环境建模、行为规划并执行任务 。 (6)1981年,日本开展第五代计算机项目,旨在制造出能够与人对话、翻译语言、解释图像,并且能像人一样推理的机器。随后,英国、美国也纷纷响应,人工智能迎来又一次大发展时期。 (7)1997年,IBM制造的深蓝超级计算机战胜了国际象棋世界冠军卡斯帕罗夫。 (8)2006年,“大数据+深度学习”成为人工智能领域最受重视和最成功的方法,带动整个人工智能领域快速发展。 (9)2016年3月,谷歌旗下DeepMind公司开发的阿尔法围棋(AlphaGo)以总比分4:1战胜围棋世界冠军李世石。 (10)2017年5月,升级版的AlphaGo以3:0战胜世界排名第一的柯洁。 (11)2017年10月18日,DeepMind团队公布AlphaGo Zero,从空白状态学起只需3天学习就以100:0的战绩击败曾战胜柯洁的AlphaGo系统。 其实从1956年人工智能的概念在美国达特茅斯得以确立,再到ALPHAGO和柯洁的世纪围棋大战,从支付宝的刷脸支付,再到无人驾驶车辆的正式上路,人工智能一直在不断发展,并且已经开始应用到我们的生活中,人工智能正在经历其发展历程上的第三次浪潮,那么我们又该如何乘风破浪呢? 3.已站在巨人之肩,前路又将如何? 早在2017年,人工智能已被列入我国政府工作报告,成为国家战略性新兴产业发展规划的重点之一。 图片来源:新华社 从计划中我们可以看出,国家领导对人工智能的发展十分重视,做了一个“新一代”的人工智能发展规划。不仅把人工智能当做经济增长点,而且想要利用人工智能改善民生,甚至打算建设一个“智能社会”,国家的重视往往代表着我国人工智在未来发展的道路上能够获得更多的助力。 就目前的情况来看,遍地开花的人工智能核心产业链每一个层面的每一个板块,都具有蓬勃发展的前景。 基础层:基础层的智能芯片作为人工智能的核心,已经成为各大科技巨头布局的重点领域,但由于智能芯片的技术和标准仍处于探索阶段,所以说中国企业还是有弯道超车的机会的,加上此时中美贸易战的打响,更有可能全方位的加快我国智能芯片的自主创新和研发。 技术层:语音识别技术被广泛应用于各行各业,如医疗、教育、互联网、电子信息、办公等行业,尽管语音识别有较高的技术壁垒,但科大讯飞也已经拥有世界领先的中文语音识别技术了。 应用层:智能安防随着图片识别技术和云计算的发展,安防系统也将从被动式防御转型为预警式智能防御。根据中国电子学会发布的信息,预计2020年,智能安防将占比我国人工智能应用层产业规模的16%,人工智能领域已然成为股权投资的一大“金矿”。 图片来源:中国电子学会 根据Gartner2019年发布的技术成熟度曲线预测,人工智能在未来的10年内将成为最具颠覆性的技术,无处不在的“AI+”将会成为主流。 图片来源:Gartner官网 前嗅认为人工智能未来发展之路虽然荆棘遍地,但发展空间还是非常巨大的,至于十年后AI究竟会不会改变世界,相信四个字足以代表:未来可期~

June 21, 2019 · 1 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

C-assign-function-overload-Demos

include <iostream>include <string.h>using namespace std; class mystring{public: mystring(){ _str = new char[1]; *_str = '\0';}mystring(char *s){ int len = strlen(s); _str = new char[len+1]; strcpy(_str, s);}mystring(const char* s){ if(s == nullptr){ _str = new char[1]; }else{ _str = new char[strlen(s) + 1]; strcpy(_str, s); }}//copy constructormystring(const mystring &another){ _str = new char[strlen(another._str) + 1]; strcpy(_str, another._str);}mystring &operator=(const mystring &another){ if(this == &another){ return *this; } delete []this->_str; int len = strlen(another._str); _str = new char[len+1]; strcpy(_str, another._str); return *this;}mystring &operator+(const mystring &another){ int catLen = strlen(this->_str); int srcLen = strlen(another._str); int len = catLen + srcLen; this->_str = static_cast<char *>(realloc(this->_str, len+1)); memset(this->_str+catLen,0,srcLen+1); strcat(this->_str, another._str); return *this;}bool operator == (const mystring &another){ return strcmp(this->_str, another._str) == 0;}bool operator > (const mystring &another){ return strcmp(this->_str, another._str) > 0;}bool operator < (const mystring &another){ return strcmp(this->_str, another._str) < 0;}char &operator[](int idx){ return _str[idx];}~mystring(){ delete [] _str;}private: ...

June 20, 2019 · 1 min · jiezi

地震地图告诉你地震带离你家有多远危险指数最高的城市竟然是……

6月17日晚,10点55分。位于川、滇、黔三省交界的四川宜宾发生6.0级地震,震源深度16千米。截至6月18日,地震已造成12人死亡,125人受伤。让我们为四川同胞祈福,愿灾区人民一切安好。 1. 四川境内多次发生地震,为何这个区域地震如此频繁?从2013年甘孜藏族自治州白玉县地震到这次宜宾市长宁县地震,7年间,四川境内发生12次地震,为何这个区域地震如此频繁? 中国地震台网中心专家认为,目前,四川已有2次7.0级强震,但接下来仍有发生6级左右地震的风险。此外,四川近年来发生地震的原因均为巴颜喀拉地块运动的结果。地震活动都是由地壳运动引起的,因此地震集中发生的地带就叫地震带。 中国地震局地质研究所研究员通过大数据发现,中国共隐藏着25条地震带,并以占世界7%的陆地面积,承载着全球33%的大陆地震,是世界上大陆地震最多的国家,和日本的情形接近。以下为最近年的地震地图,看看地震带离你家有多远? 由于印度板块不断北移带来的挤压,总的来说,地震更多地发生在西部。中国东西部发生地震的频率比大概为1:5。以下为中国部分城市地震危险度排名,看看你所在城市的地震危险度。 (以上数据来源:国家地震局、北京地震局、中国地震信息网、中国国家地理杂志。)2. 地震来了怎么办?前嗅大数据分析总结:地震发生时,至关重要的是要有清醒的头脑,镇静自若的态度。只有镇静,才有可能运用平时学到的地震知识判断地震的大小和远近。由此可见,地震,虽然目前人类还不能完全避免和控制,但是只要能掌握自救互救技能,就能使灾害降到最低限度。|如果你在室内切记不要慌张地往室外跑!按照趴下、掩护、稳住的安全原则保护自己。1、保持镇定并迅速关闭电源、煤气(天然气)、自来水开关。2、打开出入的门,确保出口,这是由于地震的晃动会造成门窗错位,打不开门。3、随手抓个垫子保护头部,用湿毛巾住口、鼻,切勿靠近窗户,以防玻璃震破。4、尽快躲在地震活命三角区,蹲下或坐下,脸朝下,额头枕在两臂上。|如果你在室外1、站立于空旷处,同时注意头顶物体掉落。2、远离各类建筑物,远离崖边、河边、海边、港口,若在桥上或地下道,也应镇静迅速地离开。3、行驶中的车辆,驶离高速公路或高架桥,找个安全的地方再尽可能快和安全地停车。4、听从国家防灾机构发布的信息,不要听信谣言,不要轻举妄动。 5、呆在屋里,切记不要使用电梯,不要慌张地往室外跑。我们永远不知道,明天和意外哪个先来临,唯有好好珍惜当下!在灾难的袭击和自然的威力面前,人类的力量显得如此渺小。

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

valgrind之callgrind性能优化

1.callgrind概述 它主要用来检查程序中函数调用过程中各个函数的CPU消耗,以便开发者分析程序中各个函数的CPU使用情况,方便优化提供程序的运行效率。 2 安装valgrind$sudo apt install valgrind3 编译 编译程序时使用–g –O0,即编译调试版,不优化程序性能。如:gcc -g -O0 sample.c –o sample 4 调试运行$valgrind --tool=callgrind ./svpushlnx 程序运行结束后,会在当前目录下生成callgrind.out.($pid)分析日志 5 分析CPU消耗1.4.1 安装kcachegrind$sudo apt install kcachegrind1.4.2 分析callgrind.out.($pid)日志文件$kcachegrind callgrind.out.24034 如上图所示,lr为执行指令占程序运行过程中总指令的百分率。lr越高的函数,则说明其消耗的CPU资源越高。优化的空间就越大。当用户选中左边方框的函数后右下角会出现该函数的子调用过程(调用栈)已及各个子过程的ir,用户可以双击子过程进入子过程的调用栈。而右上角则显示,有哪些函数可能会调用被选中的函数。

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

不敢高攀不愿将就你的高考志愿将何去何从

一年一度的高考已经结束,考生们期盼已久的解放看似已经来临,实则想要跨进高等学府还差那临门一脚——填报志愿。最近网上更是流传着一句话,说是填报志愿就像找对象,选了好专业,怕自己上不了线,选了稳妥专业,又怕浪费自己的分数,既不敢高攀,又不愿将就。今天前大嗅就用大数据来和大家聊聊报考那些事~1.一流院校三流专业VS二流院校王牌专业选哪个?其实这个问题是需要分情况讨论的~如果你的分数属于中等偏下,勉强可以上一个本科,那么请在本科和专科之间选择本科,否则你后面还要再花时间和经历去专升本。如果你的分数足够高,足以上全国前十的名校,首先需要考虑的就是学校,这个时候专业就没有那么重要了,各大企业还是认毕业证书的。如果你的分数属于中等偏上,但不足以上前十的名校,那么就请在一个差不多的大学里选择王牌专业,这个社会也是认可能力的。小编高考的时候就听说过一句话,叫做宁当鸡头不当凤尾,就是说如果分数超批次线十几分、二十几分,去一本不稳妥,有可能被调剂专业,那就不如去一个能够随便选专业的二本,学校不是特别差,专业又喜欢,还怕自己学不好吗~2.钱途与兴趣哪一个更重要?说起钱途这个话题,嗅嗅就要在这里说一个不怎么招人待见但是很实际的问题,就是穷人家的孩子和富人家孩子报志愿有些差别的。对于富人家的孩子来说,无论报什么志愿他们都有退路,可以不靠专业吃饭,自己兴趣爱好占第一位。但是对于穷人家的孩子来说,你选的专业将来是要养家糊口的,所以填报志愿的时候就更要清醒理智。从实际情况出发,首选实用性,也就是说读完大学你至少要有一技在手,要看得到,摸得着的。比如说你能敲代码、发明个小机器什么的,此时如何找到好工作、如何挣到钱是第一位,兴趣是第二位。如果你偏偏就是想从兴趣入手呢?嗅嗅也可以说一点小小的建议,首先你要明确你的兴趣是什么?现在说白了就算是工作一两年的人,可能也未必知道自己的兴趣是什么。你可能要用“大学四年加毕业前两年”的时间去寻找,那么有一个问题就很关键了:这一次选完专业之后,当你发现自己真正感兴趣的领域时,能否进行二次选择呢?二次选择目前有三次机会:大二换专业、考研、毕业找工作,特别是找工作,有一半的概率会重新来过。接下来前大嗅就来跟你说说哪些专业好转行吧,算是填志愿的后悔药~3.未来选择余地大的专业有哪些?有一句话叫做“专业不等于职业,更不等于行业”,根据这一点我们就可以选择那些对口行业和职业比较宽的专业:第一个,会计类,所有行业都需要、专业性又强的职业,而且就业率也很高;第二个,有一些需要考证的职业,会计师、金融分析师、精算师、心理咨询师等等,自己不是很确定就可以不选,如果将来喜欢上这些职业,可以通过考证来转行,虽然毕业以后再去学习考试有些难,但是也要记住一句话,心之所向,素履以往,主要就是看自己的毅力了第三个,嗅嗅说几个通过大数据预测的热门岗位:大数据、人工智能、AI、VR、AR,这几个不出意外的话不仅好找工作,而且还是高薪。说了选择余地大的专业,接下来就要进入排雷阶段了~ 4.有哪些专业坑千万别跳?说了这么多,相信大家对于填报志愿这件事应该是有一点小小的眉目了,前大嗅最大的心愿就是希望能够通过数据的力量去帮助大家解决问题,所以今天的最后,福利来啦~盘点大学十大巨坑专业送给迷茫的你(悄咪咪的告诉你这可是前大嗅用大数据分析出来的呢)接下来我们就来说说这些专业哪里坑吧~ 新闻类专业:编辑、摄影、剪辑无所不学,最后却只能踏上公考这座独木桥,这就是目前新闻类专业毕业生的现状。 化学工程:长期搞化学伤身体不说,最致命的就是毕业以后工资低啊,毕竟都是要养家糊口的人呢。 市场营销:这个专业说白了,绝对的价值导向型,企业最看重的是你的实力,学历学位并不是第一要素,双商才是,傻白甜的小可爱要注意啦,不要入坑~ 法学:法学是一个能让你做梦都在背法条的专业,来自于一个法学学长的“忏悔”,可以说是一个不疯魔,不成活的专业。 历史专业:一个曾被授予持之以恒冷门奖的专业,学习内容枯燥乏味,毕业以后“不知去向”。 电子商务:随着各大电商平台的崛起,看似很热门的专业,其实这个专业你大学究竟学了什么你毕业以后也不会清楚的,因为基本用不到,电子商务实操才是王道。 公共管理专业:国内毕业的这个专业就业前景一直不是太好,大多需要国外留学镀金回来才值钱~ 环境工程:工业发展水平不高,环保工作无论怎么做都很难达到国家的要求和规模,所以这个工作真的很难着手,而且又是一个低工资的专业。 数学与应用数学:如果您是一个数学十级痴迷爱好者,嗅嗅就不拦着您了,如果不是,要慎重啊,真的是令人头秃的专业。 生物工程:由于目前国内没有比较好的生物公司,未来就也就有很大问题,如果是海外镀金归来,高校教教书也还是可以的~ 今天就先聊这些吧,嗅嗅已经口干舌燥了,想了解更多报考资讯,请继续关注我们呦~嗅嗅用数据陪您聊生活~

June 14, 2019 · 1 min · jiezi

KMP再次总结

为何连算法都会总忘记=。=反省,脑袋有包关键点:target串(长的),partten串,如果二者在j上不等,将partten可以向前移动next[j]而取代只前移1如何确定next[j]? T与P在j-1前都相等,所以若移动后想要相等,移动后的前面部分也要与这部分T相等,三者相等:T[j-k~j]=P[j-k~j]=P[1~K],否则移动都是冗余的 即转为短串P的重复问题。先看看如何找到子串的冗余f(i)的长度。因为都是与1比,很简单:若i之前的都相等,第i个也相等,则加1,否则是1,再继续 a b c a b c a c a b 1 1 1 2 3 4 5 1 2 3因为我们要求的是不匹配的,每次计算都是前一个,向右移一下 0 1 1 1 2 3 4 5 1 2所以若要匹配,可以至少移动到k与不匹配的值比较(k-fi步)。进一步 若T与P移动后的p[k]相等,我们知道,k前面与Tj前面是相等的,但p[k]!=t[j]则肯定不匹配,与刚移动前情况一样,递归移动到两个值不等或开头即可,否则也都不匹配就没有意义常规: a b c a b c a d a b a b c a b c a c a b a b c a b c a c a b更进一步,最后前的两部可以省略: ...

June 14, 2019 · 1 min · jiezi

算法之动态规划

动态规划总结最优解 依赖重复计算的 独立的 子过程1.发现最优解结构2.推到递归式3.自低向上存储子过程结果过(备忘录,非最优子过程,只是存储,类似迷宫)4.最优解的存储和计算 这里1:1)子问题发现/定义、假设已经有了最优解,怎么通过子问题解决。比如工厂最快流水线,已经选了一条路径,要知道如何选择的,需要知道除固定节点后前面如何选择的。矩阵乘法,知道划分后,需要知道最外层两个括号分别如何计算。LCS知道最终解后,需要知道除最后一个节点外前面解如何选择的。2)选择 需要依赖哪些子问题(越少越好,矩阵乘法无法做到只依赖一个,依赖两个。工厂流水和LCS可以选择多个,但是一个很简单直接求取,选择依赖一个);需要选择依赖(矩阵循环所有一对可能选择其1,LCS和工厂选择满足更优的)3)注意满足重复计算和独立(最长非重复路径这种问题因为一个子问题用的点其他子问题不能用,所以子问题的最优不是独立的,无法用动态规划),需要简单的证明最优子解的选择会导致最优解4)局部计算(i-k或者0-2) LCS:两个串最长非连续公共子串 假设最优解已有去推子问题(固有思维就是对两个串加节点,才是n与n-1的关系,不太一样的思维) 矩阵矩阵最小乘法次数的组合同样的,考虑矩阵多一个n,n-1的关系,改思维习惯 生产线 这个虽然可以用加一个节点,但是从最终最优解来推也可以的。 climits的INT_MAXvector<vector<int> > ret(row,vector<int>(col)); dfs不熟悉:https://leetcode.com/problems...https://leetcode.com/problems...位运算:https://leetcode.com/problems...

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

simdjsonphp-高速解析json

介绍simdjson_php(https://github.com/crazyxman/...,它绑定simdjson来实现快速解析,simdjson是一个高速的json解析器,它使用了大多数SIMD单一指令。simdjson介绍:https://github.com/lemire/sim... 环境依赖php7+带有AVX2的处理器(即,2013年发布的Haswell微体系结构的Intel处理器和2017年发布的Zen微体系结构的AMD处理器),大多数cpu都是支持的最近的C ++编译器(例如,GNU GCC或LLVM CLANG或Visual Studio 2017),我们假设C ++ 17。GNU GCC 7或更高版本或LLVM的clang 6或更高版本检查操作系统/处理器是否支持它: OS X: sysctl -a | grep machdep.cpu.leaf7_featuresLinux: grep avx2 /proc/cpuinfo使用简介当需要获取一个较大json串中的某个key时 使用simdjson_key_value() 是比较合适的,不像json_decode() 把整个json串解析成数组,开辟不必要的内存,当然在性能上是略逊于hash查找的。当验证一个字符串是否为json时simdjson_isvaild() 是比较合适的,并且是非常快的,同样不需要通过json_decode()来验证。//检查字符串是否为一个有效的json:$isValid = simdjson_isvalid($jsonString); //return bool//解析一个json字符串,返回数组,对象,null,类似json_decode(),第三个参数为解析的深度$parsedJSON = simdjson_decode($jsonString, true, 512); //return array|object|null. "null" string is not a standard json/*{ "Image": { "Width": 800, "Height": 600, "Title": "View from 15th Floor", "Thumbnail": { "Url": "http://www.example.com/image/481989943", "Height": 125, "Width": 100 }, "Animated" : false, "IDs": [116, 943, 234, 38793, {"p": "30"}] }}*///注意. "\t" 是一个分割符. 它必须是一个控制字符. 它用来分割对象的key或数组的下标//例如. "Image\tThumbnail\tUrl" 是正确. 'Image\tThumbnail\tUrl' 是错误的//根据json串获取指定key的值$value = simdjson_key_value($jsonString, "Image\tThumbnail\tUrl");var_dump($value); // string(38) "http://www.example.com/image/481989943"$value = simdjson_key_value($jsonString, "Image\tIDs\t4", true);var_dump($value); /*array(1) { ["p"]=> string(2) "30"}*///获取json解析后的资源,只解析一次,后续使用不再解析$resource = simdjson_resource($jsonString);//根据json资源获取指定key的值$value = simdjson_key_value($resource, "Image\tThumbnail\tUrl");var_dump($value); // string(38) "http://www.example.com/image/481989943"$value = simdjson_key_value($resource, "Image\tIDs\t4", true);var_dump($value); /*array(1) { ["p"]=> string(2) "30"}*///检查key是否存在,参数可以是一个json串也可以是一个json资源,返回true,false,null。当第一个参数是字符串时返回null代表解析失败$res = simdjson_key_exists($jsonString, "Image\tIDs\t1");var_dump($res) //bool(true)$res = simdjson_key_exists($resource, "Image\tIDs\t1");var_dump($res) //bool(true)性能测试(秒)filenamejson_decodesimdjson_decodesimdjson_isvalidapache_builds.json0.003073000.002252000.00018100canada.json0.139550000.027739000.00358300citm_catalog.json0.030309000.013340000.00117000github_events.json0.002941000.000904000.00008500gsoc-2018.json0.042925000.011120000.00186700instruments.json0.005097000.002318000.00017500marine_ik.json0.098336000.044175000.00463400mesh.json0.018692000.007226000.00114800mesh.pretty.json0.035762000.007381000.00163400numbers.json0.002636000.000699000.00018200random.json0.017135000.009739000.00063000twitter.json0.012586000.006184000.00057400twitterescaped.json0.014359000.006504000.00074300update-center.json0.015060000.008691000.00047800You may run the benchmarks by running the commands: ...

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

设计模式之Singleton单件模式

一、动机1、在软件系统中,经常有这样一个特殊的类,必须保证他们在系统中只存在一个实例,才能保证他们的逻辑正确性,以及良好的效率。2、如何绕过常规的构造器,提供一种机制来保证一个类只有一个实例;3、这应该是设计者的责任,而不是使用者的责任;4、保证一个类仅有一个实例,并提供一个该实例的全局访问点(GOF) 二、实现分析1、非线程安全的单件模式; #ifndef SINGLETON_H#define SINGLETON_H#include<iostream>class singleton{private: singleton(int data=0):data(data) { std::cout << "创建构造函数" << std::endl; }; singleton(const singleton&) {}; ~singleton() {}; static singleton* point; int data;public: static singleton* get_singleton(int); void set_singleton(int); void get_data();};singleton* singleton::point= nullptr;singleton* singleton::get_singleton(int data = 0){ if (point == nullptr) { point = new singleton(data); } return point;};void singleton::set_singleton(int data) { this->data = data;}void singleton::get_data() { std::cout << "the data is : " << this->data<<std::endl;}#endif // !SINGLETON_H我们实现了最简单的单例模式,为了满足仅仅生成一个实例,因此我们应该为客户重新提供一个实例化接口并且屏蔽类的默认构造函数与默认复制构造函数,随后实现这个实例化接口,我们可以看到以下代码句为核心: ...

June 11, 2019 · 2 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

课程记录二侯捷C高级编程

一、主要内容1、如果所创建的类存在指针成员,一般都需要定义拷贝构造函数、拷贝赋值构造函数与析构函数;2、new与delete的底层过程; 二、示例代码代码内容如下: #ifndef CLASS_H#define CLASS_H#include<iostream>using std::cout;using std::endl;class data_class{public: data_class(double,double); ~data_class(); double Get_data1() { return data1; } void Set_data1(double data1) { data1 = data1; }; void Set_data2(double data2) { data2 = data2; } double Get_data2() { return data2; }private: double data1; double data2;};data_class::data_class(double data1 = 0,double data2=0):data1(data1),data2(data2){}data_class::~data_class(){ cout << "调用data_class析构函数" << endl;}class class_for_copy_test{public: class_for_copy_test(data_class*);//参数初始化; class_for_copy_test(const class_for_copy_test&); class_for_copy_test(const class_for_copy_test*); class_for_copy_test& operator =(const class_for_copy_test&); ~class_for_copy_test();private: data_class* data_point;};class_for_copy_test::class_for_copy_test(data_class* point_new_out =NULL){ data_class* point_new; if(point_new_out ==NULL) point_new = new data_class();//传入为空指针 else point_new = new data_class(point_new_out->Get_data1(),point_new_out->Get_data2());//传入为空指针 data_point = point_new;//传入指针非空}class_for_copy_test::class_for_copy_test(const class_for_copy_test& same_class) { data_class* new_data = new data_class(same_class.data_point->Get_data1(),same_class.data_point->Get_data2()); this->data_point = new_data;}class_for_copy_test::class_for_copy_test(const class_for_copy_test* same_class) { data_class* new_data; if (same_class == NULL) { cout << "传入为空指针" << endl; new_data = new data_class(0, 0); } else new_data = new data_class(same_class->data_point->Get_data1(),same_class->data_point->Get_data2()); this->data_point = new_data;};class_for_copy_test& class_for_copy_test::operator =(const class_for_copy_test& other_object) { if (other_object.data_point == this->data_point) return *this; delete this->data_point; data_class* new_data = new data_class(other_object.data_point->Get_data1(), other_object.data_point->Get_data2()); this->data_point = new_data; return *this;};class_for_copy_test::~class_for_copy_test() { cout << "调用class_for_copy_test析构函数" << endl; delete this->data_point;//析构内部指针}#endif // !CLASS_H1、包含指针的类的设计在类class_for_copy_test中定义一个成员变量data_class的指针,data_class类包含两个double类型的成员,其中该函数的拷贝构造函数为: ...

June 10, 2019 · 2 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

工作多年精通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

重磅重构开源-让H5标签代替C实时解码播放speex压缩协议的音频文件-IM的福音

这么牛逼的轮子,肯定要美图镇楼Speex是一套主要针对语音的开源免费,无专利保护的音频压缩格式。本轮子,适用超大型项目,因为库本身很大,当然本身IM项目就没有小项目吧Speex(音标[spi:ks])是一套开源免费的、无专利保护的、针对语音设计的音频压缩格式。Speex项目通过以提供昂贵的专用语音编解码器的免费替代方案为目标,来降低语音应用程序的进入门槛。此外,Speex非常适用于互联网应用程序,并提供了其他大多数编解码器中不存在的有用特性。最后,Speex是GNU项目的一部分,可以在修订后的BSD许可证下使用。编码流程使用Speex的API函数对音频数据进行压缩编码要经过如下步骤:定义一个SpeexBits类型变量bits和一个Speex编码器的内存指针变量enc。调用speex_bits_init(&bits)函数初始化bits。调用enc = speex_encoder_init(&speex_nb_mode)函数初始化enc。其中speex_nb_mode是SpeexMode类型的变量,表示的是窄带模式。还有speex_wb_mode表示宽带模式、speex_uwb_mode表示超宽带模式。调用函数 int speex_encoder_ctl(void * state, int request, void * ptr)来设定编码器的参数,其中参数state表示编码器的内存指针;参数request表示要定义的参数类型,如SPEEX_GET_FRAME_SIZE表示设置帧大小,SPEEX_SET_QUALITY表示编码的质量等级;参数ptr表示要设定的值。初始化完毕后,对每一帧声音作如下处理:调用函数speex_bits_reset(&bits)重置bits,然后调用函数speex_encode(enc_state,input_frame, &bits)进行编码,参数bits中保存编码后的Speex格式数据帧。编码结束后,调用函数speex_bits_destroy(&bits),speex_encoder_destroy(enc_state)来销毁SpeexBits和编码器。 解码流程对已经编码过的Speex格式音频数据帧进行解码要经过以下步骤:定义一个SpeexBits类型变量bits和一个Speex解码器的内存指针变量dec。调用speex_bits_init(&bits) 函数初始化bits。调用dec = speex_decoder_init(&speex_nb_mode) 函数初始化dec。调用函数speex_decoder_ctl(void * state, int request, void * ptr)来设定解码器的参数。调用函数 speex_decode(void * state, SpeexBits * bits, float * out)对参数bits中的Speex格式音频数据帧进行解码,参数out中存放解码后的音频数据帧。调用函数speex_bits_destroy(&bits), speex_decoder_destroy(void * state)来销毁SpeexBits和解码器说重点当做即时通信产品,像微信这种的手机端,它们接受到很有可能就是speex协议压缩后的音频文件。当然,文件后缀是wav或者ogg都无关紧要H5的audio标签可以播放 音频格式及浏览器支持目前, <audio>元素支持三种音频格式文件: MP3, Wav, 和 Ogg:浏览器 MP3 Wav Ogg Internet Explorer 9+ YES NO NOChrome 6+ YES YES YESFirefox 3.6+ NO YES YESSafari 5+ YES YES NOOpera 10+ NO YES YES音频格式的MIME类型Format MIME-typeMP3 audio/mpegOgg audio/oggWav audio/wav本开源库基于speex封装,抽取了必须要的文件后进一步封装,修改了在复杂环境下的兼容本源码支持环境 ...

June 5, 2019 · 1 min · jiezi

网络云盘项目fastdfs环境搭建

准备预备工具包:1.libfastcommon-1.0.38.tar.gz2.fastdfs-5.11.tar.gz3.fastdfs-nginx-module-1.20.tar.gz4.libevent-2.1.8-stable.tar.gz libfastcommon-1.0.38.tar.gz安装$ tar xzvf libfastcommon-1.0.38.tar.gz$ cd libfastcommon-1.0.38$ ./make.sh$ sudo ./make.sh installfastdfs-5.11.tar.gz 安装$ tar xzvf fastdfs-5.11.tar.gz$ cd fastdfs-5.11$ ./make.sh$ sudo ./make.sh install安装完成测试:root@iZbp1anc6yju2dks3nw5j0Z:~/yunpan/lib/fastdfs/fastdfs-5.11# fdfs_testThis is FastDFS client test program v5.11Copyright (C) 2008, Happy Fish / YuQingFastDFS may be copied only under the terms of the GNU GeneralPublic License V3, which may be found in the FastDFS source kit.Please visit the FastDFS Home Page http://www.csource.org/for more detail.Usage: fdfs_test <config_file> <operation> operation: upload, download, getmeta, setmeta, delete and query_servers配置tracker:先建立fastdfs存储目录如下:修改etc/fdfs 配置文件: ...

June 5, 2019 · 2 min · jiezi

青春科技24小时无限可能

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

June 5, 2019 · 1 min · jiezi

STL漂亮的写法

mapunordered_map(等价于hash_map)和map用法类似。unordered_map不会根据key的大小进行排序,map 内部数据的组织,基于红黑树实现,红黑树具有自动排序的功能,因此map内部所有的数据,在任何时候,都是有序的。unordered_map(hash_map) 基于哈希表,数据插入和查找的时间复杂度很低,几乎是常数时间,而代价是消耗比较多的内存。底层实现上,使用一个下标范围比较大的数组来存储元素,形成很多的桶,利用hash函数对key进行映射到不同区域进行保存。 map<string, vector<string>> aMap;//或替换为hash_map vector<vector<string>> aResult; for (auto s : strs) { string ss = s; sort(ss.begin(), ss.end()); aMap[ss].push_back(s); } for (auto s : aMap) { aResult.push_back(s.second); }

June 4, 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

一个数组如何过滤另一个数组中的值如下图和代码

问题一个数组如何过滤另一个数组中的值,如下图和代码一个数组拥有全部数据另一个数组只拥有部分数据拥有全部数组数组要根据拥有部分数据的数组,过滤得到新的数组 代码拥有全部数据的数组 let allRoutes = [ { path: '/', name: 'home', component: () => import('@/views/Home.vue'), children: [ { path: '/role', name: 'role', meta: { tile: '角色' }, component: () => import('@/views/role/index.vue'), redirect: '/role/list', children: [ { path: 'create', name: 'roleCreate', meta: { tile: '创建角色' }, component: () => import('@/views/role/create.vue') }, { path: 'list', name: 'roleList', meta: { tile: '角色列表' }, component: () => import('@/views/role/list.vue') }, { path: 'update', name: 'roleUpdate', meta: { tile: '修改角色' }, component: () => import('@/views/role/update.vue') } ] }, { path: '/adminUser', name: 'adminUser', meta: { tile: '管理员' }, component: () => import('@/views/adminUser/index.vue'), children: [ { path: 'create', name: 'adminUserCreate', meta: { tile: '创建管理员' }, component: () => import('@/views/adminUser/create.vue') }, { path: 'list', name: 'adminUserList', meta: { tile: '管理员列表' }, component: () => import('@/views/adminUser/list.vue') }, { path: 'update', name: 'adminUserUpdate', meta: { tile: '修改管理员' }, component: () => import('@/views/adminUser/update.vue') } ] } ] }, { path: '*', name: '404', component: () => import('@/views/404.vue') } ]拥有部分数据的数组相比全部数据的数组,没有了name: 'roleCreate', ...

June 4, 2019 · 1 min · jiezi

201906月技术笔记

Week23201906031、手上有个活,之前不确定问了一下,答一下是“编译错误”,正好之前也遇到过这个文件的makefile编译错误,这个确实不太熟悉,又折腾一上午后,准备跟leader求救了,没想到只是把文件的告警给消除了。14:20前就干完了。2、消除告警的报错,帮靠编译器的提示也不一定全能解决问题,还是得琢磨一下问题点在哪。 根据提示并没有解决好问题,va_start里类型的问题。3、待进一步要了解的事 (1)svn的switch怎么用?SVN switch 用法详解 项目上换了svn,我试了reroad不行,就另行下载了。(2)把陈皓的“跟我一起起Makefile”给看一遍呢。(3)FMT_CHECK(3,4) 是啥意思,我没太懂。(4)vsprintf()用得也不太多。

June 3, 2019 · 1 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

设计模式之Strategy策略模式

1、动机与意图动机:在软件构建的过程中,某些对象使用的算法可能多种多样,经常改变,如果将这些算法都编码到对象中,将会使对象变得异常复杂,增加与改变现有算法可能带来严重的逻辑问题;而且有时候支持不使用的算法也是一个性能上的负担(具体上体现在代码太长会使得其在保存的位置出现问题),因此将对象与算法解耦,避免上述问题才是本算法动机。意图:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。本模式使得算法可独立于使用它的客户而改变。 2、分析示例代码: #ifndef STRATEGY#define STRATEGY#include<iostream>using std::cout;using std::ends;using std::endl;class Tax_cal{public: Tax_cal(); ~Tax_cal(); virtual double get_tax(double source_money)const = 0;private:};Tax_cal::Tax_cal(){}Tax_cal::~Tax_cal(){}class chinese_tax:public Tax_cal{public: chinese_tax(); ~chinese_tax(); double get_tax(double source_money) const { return source_money * 5.6/100 ; }};chinese_tax::chinese_tax(){}chinese_tax::~chinese_tax(){}class Japanese:public Tax_cal{public: Japanese(); ~Japanese(); double get_tax(double source_money)const { return source_money * 17 / 100; }private:};Japanese::Japanese(){}Japanese::~Japanese(){}class bank_calcu {public: bank_calcu(double); ~bank_calcu() {}; void Calculate(const Tax_cal*);private: double money;};bank_calcu::bank_calcu(double money) :money(money) {};void bank_calcu::Calculate(const Tax_cal* tax_demo) { cout << tax_demo->get_tax(money) << endl;}#endif // !STRATEGY我们定义了一个客户端的类bank_calcu用于计算不同地方的税率,在成员函数Calculate()中计算税率,但是不同的地方有不同的税率,譬如说代码中的中国与日本(当然数据是瞎编的),传统的方法是在Calculate()内部写上一个if-else或者switch语句来进行区别,但是依旧存在问题:1、用不到的代码块就应该被去除,否则影响执行效率;2、违反了开闭原则(对增加开放,对修改封闭);3、容易出错,现在的逻辑还没有那么复杂,判断逻辑十分复杂的话,修改会造成严重的逻辑问题。税率计算的过程我们定义一个抽象类Tax_cal,使用其进行税率的具体计算,不同的计算规则在其子类中进行具体实现,通过这种方式能够有效地实现税率计算的功能。运行代码为: ...

June 3, 2019 · 1 min · jiezi

tcp协议-拥塞控制

补充tcp协议与滑动窗口我们在向对端发送数据时,并不是一股脑子任意发送,因为TCP建立连接后,就是建立了一根管道,这跟管道上,实际上有很多的工作设备,比如路由器和交换机等等,他们都会对接收到的TCP包进行缓存,以便实现排序,然后发送,但是这些设备并不是只为一个TCP连接中转数据包,大量的网络包也许会耗尽存储空间,从而导致TCP连接的吞吐量急剧下降。为了避免这种情况的发送,TCP的设计必须是一种无私的协议,它必须去探测这种网络拥塞的问题,否则我们想想,一旦出现拥塞(判断是否丢包或者是否发生重传),如果TCP只能做重传,那么重传数据包会使得网络上的包更多,网络的负担更重,于是导致更大的延迟以及丢更多的包,于是会进入一个恶性循环,如果网络上的所有TCP连接都是如此行事的话,那么马上就会形成“网络风暴”,会拖垮整个网络,这也是一个灾难。那么TCP就应该能够检测出来这种状况,当拥塞出现时,要做自我牺牲,就像交通阻塞一样,每一辆车都应该把路给让出来,而不是再去抢路了。这说的就是拥塞控制。那是如何控制的呢? 首先,我们得看TCP是如何充分利用网络的,TCP实际上就是逐步探测这个通道的传输的最大能力,这个逐步探索就是我们要讲的慢启动算法,这个慢启动算法就是:新建立的连接不能一开始就大量发送数据包,而是应该根据网络状况,逐步地增加每次发送数据包的量。 具体的工作步骤就是: 慢启动算法: 发送方维护一个拥塞窗口,刚开始时,这个拥塞窗口(cwnd,congestion window)设置为1,这个1代表是一个MSS个字节。如果每收到一个ACK,那么就指数增长这个cwnd(2,4,8,16,32,64)等,实际上不会这么一直指数级增长下去,TCP会设置一个慢启动的阈值(ssthresh,slow start threshold,65535个字节) ,当cwnd >= ssthresh时,进入拥塞避免阶段。拥塞避免阶段 每收到一个ACK时,cwnd = cwnd + 1/cwnd;每当每过一个RTT时,cwnd = cwnd + 1;这样放缓了拥塞窗口的增长速率,避免增长过快导致网络拥塞,慢慢的增加调整到网络的最佳值。在这个过程中如果出现了拥塞,则进入拥塞状态。拥塞状态 那是如何判断出现拥塞状态呢?只要出现丢包就认为进入了拥塞状态。进入拥塞状态也分两种情况:1) 等到RTO超时(重传超时),重传数据包。TCP认为这种情况太糟糕,反应也很强烈: sshthresh = cwnd /2cwnd 重置为 1进入慢启动过程快速重传 2)连续收到3个duplicate ACK时,重传数据包,无须等待RTO。此情况即为下面的快速重传。 【问题】什么情况下会出现3个duplicate ACK? TCP在收到一个乱序的报文段时,会立即发送一个重复的ACK,并且此ACK不可被延迟。 如果连续收到3个或3个以上重复的ACK,TCP会判定此报文段丢失,需要重新传递,而无需等待RTO。这就叫做快速重传。 TCP Tahoe的实现和RTO超时一样。 TCP Reno的实现是: sshthresh = cwndcwnd = cwnd /2进入快速恢复算法——Fast Recovery上面我们可以看到RTO超时后,sshthresh会变成cwnd的一半,这意味着,如果cwnd<=sshthresh时出现的丢包,那么TCP的sshthresh就会减了一半,然后等cwnd又很快地以指数级增涨爬到这个地方时,就会成慢慢的线性增涨。我们可以看到,TCP是怎么通过这种强烈地震荡快速而小心得找到网站流量的平衡点的。 快速恢复算法 TCP Reno这个算法定义在RFC5681。快速重传和快速恢复算法一般同时使用。快速恢复算法是认为,你还有3个Duplicated Acks说明网络也不那么糟糕,所以没有必要像RTO超时那么强烈。 注意,正如前面所说,进入Fast Recovery之前,cwnd 和 sshthresh已被更新: sshthresh = cwndcwnd = cwnd /2然后,真正的Fast Recovery算法如下: cwnd = sshthresh + 3 * MSS (3的意思是确认有3个数据包被收到了)重传Duplicated ACKs指定的数据包如果再收到 duplicated Acks,那么cwnd = cwnd +1如果收到了新的Ack,那么,cwnd = sshthresh ,代表恢复过程结束,然后就进入了拥塞避免的算法了。如果我们仔细思考一下上面的这个算法,你就会知道,上面这个算法也有问题,那就是——它依赖于3个重复的Acks。注意,3个重复的Acks并不代表只丢了一个数据包,很有可能是丢了好多包。但这个算法只会重传一个,而剩下的那些包只能等到RTO超时,于是,进入了恶梦模式——超时一个窗口就减半一下,多个超时会超成TCP的传输速度呈级数下降,而且也不会触发Fast Recovery算法了。 ...

June 3, 2019 · 1 min · jiezi

课程记录一侯捷C高级编程

在本章主要简述定义一个C++类的时候需要注意的细节,包括初始化列、操作符重载等。实例讲解代码为: #ifndef COMPLEX_H#define COMPLEX_H#include<iostream>using std::ostream;using std::endl;using std::cin;using std::ends;using std::cout;class Complex{public: Complex(double, double); Complex(const Complex&); ~Complex(); double Get_re()const; double Get_img()const; Complex& operator +=(const Complex &);private: double re; double img; friend Complex& _add(Complex*, const Complex&);//由于需要去除private元素,所以这设置为友元,在调用一个函数的友元的时候,其也具备访问另一个对象private的能力};inline Complex::Complex(double re = 0, double img = 0) :re(re), img(img){};inline Complex::Complex(const Complex & comp) { this->re = comp.re; this->img = comp.img; cout << "调用了复制构造函数" << ends;}inline Complex::~Complex(){ cout << "析构函数被调用" << endl;};inline double Complex::Get_re() const{ return re;};inline double Complex::Get_img() const{ return img;};inline Complex& _add(Complex* ths, const Complex& plus_ele){ ths->re += plus_ele.re; ths->img += plus_ele.img; return(*ths);}//下面是包含this的重载运算符inline Complex& Complex::operator +=(const Complex& plus_ele){ return _add(this, plus_ele);}//下面的都是不包含this的重载运算符inline Complex operator +(const Complex & comp1, const Complex & comp2){ return Complex(comp1.Get_re() + comp2.Get_re(), comp1.Get_img() + comp2.Get_img());}inline ostream& operator <<(ostream& os, const Complex& comp)//主要是为了应对连续使用<<的情况{ return os << comp.Get_re() << "+" << comp.Get_img()<<"i";}inline void test_reference(Complex com){ cout << com << ends;}#endif // !COMPLEX_H1、构造一个类的相关建议:1、成员数据一定放在private;主要是为了防止通过对象直接更改内部的值,当然可以通过成员函数来更改其中的值,目的是增加类的封装性。 ...

June 2, 2019 · 1 min · jiezi

linux-网络编程-socket选项

socket选项函数功能:用来读取和设置socket文件描述符属性的方法#include <sys/scoket.h>int getsockopt ( int sockfd, int level, int option_name, void* option_value, socklen_t* restrict option_len );int setsockopt ( int sockfd, int level, int option_name, const void* option_value, socklen_t option_len);socket选项表如下: getsockopt和setsockopt 这两个函数成功时返回0,失败时返回-1并设置errno。对于服务器而言,有部分socket选项只能在调用listen系统调用前针对监听socket设置才有效。这是因为连接socket只能由accept调用返回,而accept从listen监听队列接受的连接至少已经完成了TCP三次握手的前两个步骤(因为listen监听队列中的连接至少已进入SYN_RCVD状态),这说明服务器已经往被接收连接上发送出了TCP同步报文段。但有的socket选项却应该在TCP同步报文段中设置,比如TCP最大报文段选项。对这种情况,linux给开发人员提供的解决方案是:对监听socket设置这些socket选项,那么accept返回的连接socket将自动继承这些选项。这些选项包括:SO_DEBUG、SO_DONTROUTE、SO_KEEPALIVE、SO_LINGER、SO_OOBINLINE、SO_RCVBUF、SO_RCVLOWAT、SO_SNDBUF、SO_SNDLOWAT、TCP_MAXSEG和TCP_NODELAY。对于客户端而言,这些socket选项则应该在调用connect函数之前设置,因为connect调用成功返回之后,TCP三次握手已完成。SO_REUSEADDR选项前面讨论过TCP连接的TIME_WAIT状态,并提到服务器程序可以通过设置socket选项SO_REUSEADDR来强制使用被处于TIME_WAIT状态的连接占用的socket地址。#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <assert.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <errno.h>#include <string.h> int main( int argc, char* argv[] ){ if( argc <= 2 ) { printf( "usage: %s ip_address port_number\n", basename( argv[0] ) ); return 1; } const char* ip = argv[1]; int port = atoi( argv[2] ); int sock = socket( PF_INET, SOCK_STREAM, 0 ); assert( sock >= 0 ); int reuse = 1; setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof( reuse ) ); struct sockaddr_in address; bzero( &address, sizeof( address ) ); address.sin_family = AF_INET; inet_pton( AF_INET, ip, &address.sin_addr ); address.sin_port = htons( port ); int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) ); assert( ret != -1 ); ret = listen( sock, 5 ); assert( ret != -1 ); struct sockaddr_in client; socklen_t client_addrlength = sizeof( client ); int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength ); if ( connfd < 0 ) { printf( "errno is: %d\n", errno ); } else { char remote[INET_ADDRSTRLEN ]; printf( "connected with ip: %s and port: %d\n", inet_ntop( AF_INET, &client.sin_addr, remote, INET_ADDRSTRLEN ), ntohs( client.sin_port ) ); close( connfd ); } close( sock ); return 0;}经过setsocketopt的设置之后,即使sock处于TIME_WAIT状态,与之绑定的socket地址也可以立即被重用。此外,我们也可以通过修改内核参数/proc/sys/net/ipv4/tcp_tw_recycle 来快速回收被关闭的socket,从而使得TCP连接根本就不进入TIME_WAIT状态,进而允许应用程序立即重用本地的socket地址。SO_RCVBUF和SO_SNDBUF选项SO_RCVBUF和SO_SNDBUF选项分别表示TCP接收缓冲区和发送缓冲区的大小。不过,当我们用setsockopt来设置TCP的接收缓冲区和发送缓冲区的大小时,系统都会将其值加倍,并且不得小于其个最小值。TCP接收缓冲区的最小值是256字节,而发送缓冲区的最小值是2048字节(不过,不同的系统可能有不同的默认最小值)。此外,我们可以直接修改内核参数/proc/sys/net/ipv4/tcp_rmem和/proc/sys/net/ipv4/tcp_wmem来强制TCP接收缓冲区和发送缓冲区的大小没有最小值限制。修改TCP发送缓冲区的客户端程序: ...

June 1, 2019 · 4 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

Effective-C-条款02尽量以constenuminline替换define

这个条款也可以改为“可以用编译器替换预处理器”,因为#define有很多的问题和不方便之处。 #define ASPEC 1.6454当你使用了这个常量但获得一个编译错误信息时,可能会带来困惑,因为错误信息会提到1.6454 而不会提到名称本身,尤其是这个量又别人或在另外一个头文件中定义就更麻烦了。而改为const double ASPEC = 1.6454就不会出现这种情况。 当const、指针结合在一起时应该注意‘*’与const的相对位置; 另一个要注意的问题是如果你想在class中设立这样一个作用域为这个类的变量,则让它成为一个static成员: class G{static const int num = 5;int sc[num];};然而,num是常量的声明式而非定义式,通常C++要求你对你所使用的任何东西提供一个定义式。但如果它是一个class专属常量又是static,且为整数类型(integral type,例如int,char,bool),则需特殊处理。只要不取它们的地址,你可以声明并使用它们而无须提供定义式。但如果你取某个class专属常量的地址,或纵使你不取其地址但是编译器却(不正确的)坚持要看到一个定义式,你就必须另外提供定义式如下: const int G::num;·#define是不提供任何封装的,也就是说没有private #define之类的东西,但是const可以。 另一个好用的东西是enum hack.像在类中加入下面的代码: enum{Nun = 5};用Nun作为5的一个代称,而且可以避免用指针来获取你的整数常量的地址,enum可以完成这个约束。而且它很实用,经常会用到。

June 1, 2019 · 1 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

设计模式之Template-Method

1、设计模式的使用场景模板模式的主要应用场景为:1、在软件的构造过程种,它常常有稳定的整体操作,但各个子步骤却有着很多改变的需求,或者由于固有的原因二无法与任务的整体框架同时实现;其定义一个操作中的算法骨架,而将一些步骤延迟(变化的部分)到子类中,模板的方法使得子类可以不改变(复用)一个算法的结果即可重定义该算法的某些特定步骤。2、更为通俗的说法是,在父类中实现某个通用的流程,具体小的实现步骤在各个特定的子类中实现,这样加强了代码的复用,但是使用本设计模式的前提是存在一成不变的通用流程。 2、具体分析从图中可与看出来存在两个抽象类Document、Application,其中Document用于对文档进行各类操作,当然起也是抽象类,因为对于不同的文档需要细化操作细节,我们仅看用于实现抽象操作一个文件的Application类,其具体的操作流程如下所示: 首先判断这个文件是否可以被打开,随后使用DoCreateDocument()来产生处理文档的特定类MyDocument,以便实现对不同文件的不同操作,最后通过一系列流程来完成打开过程。值得注意的是OpenDocument()中逻辑过程是稳定的不变的,其中每一步的具体实现是变化的,因此将OpenDocument设置为非虚的成员函数,而将组成OpenDocument的子步骤,也就是CanOpenDocument()、AddDocument()、AboutToOpenDocument()与DoCreateDocument()设置为虚的成员函数。 3、适用性1、一次性实现一个算法的不变部分,并将可变的行为留给子类来实现;2、各子类中公共的行为应被提取出来并集中到一个公共父类中以避免代码重复;3、控制子类扩展。 4、注意点1、使用C++访问控制 在C++中,一个模板方法调用的可变化的那些操作可以被定义为保护成员。这保证他们只在模板方法中被调用(封装原则,反正具体的实现步骤都需要封装在一个特定的对外方法中,如前面的OpenDocument()方法中)。必须被重定义的可变化的成员函数必须被定义为纯虚函数,模板自身不需被重定义;因此将这个模板方法定义为一个非虚成员函数。2、尽量减少需要重定义的部分 本方法本来就是将主要的工作交给库的设计者(父类),而子类作为直接的方法使用者无需掌握繁杂的使用流程,若需要重定义大量方法不就违反了这个规则。 5、实验代码#ifndef TEMPLATE_H#define TEMPLATE_H#include<iostream>#include<string>using std::string;class Abstract_class{public: Abstract_class(double re = 0, double img = 0) :re(re), img(img) {}; virtual ~Abstract_class(); void run() { step1(); step2(); step3(); step4(); }private: double re; double img; virtual void step1(string s=" ") = 0; virtual void step2(string s = " ") = 0; virtual void step3(string s = " ") = 0; virtual void step4(string s = " ") = 0;};Abstract_class::~Abstract_class(){ std::cout << "触发Abstract_class析构函数" << std::endl;}class Concretclass1:public Abstract_class{private: double re; double img; void step1(string s) ; void step2(string s) ; void step3(string s) ; void step4(string s) ;public: Concretclass1(double re = 0, double img = 0) :re(re), img(img) {}; virtual ~Concretclass1() { std::cout << "触发Concretclass1析构函数" << std::endl; };};void Concretclass1::step1(string s) { std::cout << "this is step1:+:" <<(re+img)<< std::endl;}void Concretclass1::step2(string s){ std::cout << "this is step2:-:" <<(re - img) << std::endl;}void Concretclass1::step3(string s){ std::cout << "this is step3:*:" << (re * img) << std::endl;}void Concretclass1::step4(string s){ std::cout << "this is step4:/:" << (re /img) << std::endl;}#endif // !TEMPLATE_H在代码中我们定义了一个抽象类Abstract_class,这个类中包含一个该子类的通用处理流程run,但是run里面的子实施流程是由各个子类实现的,因此根据之前的说法run方法应该设为public且非虚的,他的子实施流程应该设为private且是虚的(因为外界对象不需要看到这个方法,只需要看到run就行了,增加了封装性)。在子类Concretclass1中我们实现了这些子实施流程,随后直接使用run()方法就达到目的了。.c文件的内容为: ...

May 31, 2019 · 1 min · jiezi

前嗅做你的专属技术合作人

May 31, 2019 · 0 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

基于C可变参数模板格式化字符串

遨游于C++世界时,最讨厌的当属于对c-style的兼容????。 在格式化字符串时,通常使用的是snprintf这个c函数。snprintf是sprintf的安全版,能够避免缓冲区溢出。 char buf[1024] = {0};std::string s = "Hello world";snprintf(buf, sizeof(buf), "format str: %s", s.c_str());snprintf接受的参数跟printf差不多,都是c-style的数据类型,如%s接受的是const char*类型的数据,这就需要我们将std::string做一个转换。这一个小小的转换让我觉得非常不爽,有没有可能让std::string在做某个函数的参数时能自动做转换?甚至让一个将一个普通的对象自动转换成const char*类型? 接下来我们将利用c++11的可变参数模板(variadic templates)、参数包(parameters pack)、完美转发(perfect forwarding)等特性来实现这一想法。 参数列表完美转发写C++代码时,我满脑子都是怎么最大限度地提高性能。我们这次的目标也一样,在提供方便的同时,不要对性能有太大影响,甚至不影响。 首先是要将传入fmt函数的参数完美转发至snprintf。 template<typename... Args>string fmt(const char *format, Args&&... args) { char buf[128] = {0}; snprintf(buf, sizeof(buf), format, convert(std::forward<Args>(args))...); return buf;}这是一个可变参数模板,args表示传入的参数们。我们的思路是将传入的参数做一个转换之后传给snprintf。 为了原封不动的保持左右值引用,首先是用Args&&代替Args的参数类型,此处模板函数的Args需要编译器推导,所以是一个通用引用(Universal reference),可以指代左值或右值。用std::forward<Args>能保持参数的左右值性质,做到参数的完美转发。 自动参数转换在convert这个函数中,我们要将特定的类型转换成const char*类型,而那些能被snprintf接受的类型如int, double, char*,则原封不动的返回。 convert函数针对不同的参数类型需要返回不同的类型。这里也将返回值作为一个模板类型即可。 template<typename T>struct item_return { using type = T&&;};convert函数的定义为: template<typename T>inline typename item_return<T>::type convert(T&& arg) { return static_cast<T&&>(arg);}convert函数默认将传入的参数原封不动的返回。接下来我们要做模板的偏特化,对于指定的对象,将其转换为const char *类型 ...

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

linux-学习笔记

进程学习笔记转自 https://www.cnblogs.com/jackl... 进程控制块(PCB)============ 在Linux中task_struct结构体即是PCB。PCB是进程的唯一标识,PCB由链表实现(为了动态插入和删除)。进程创建时,为该进程生成一个PCB;进程终止时,回收PCB。PCB包含信息:1、进程状态(state);2、进程标识信息(uid、gid);3、定时器(time);4、用户可见寄存器、控制状态寄存器、栈指针等(tss)每个进程都有一个非负的唯一进程ID(PID)。虽然是唯一的,但是PID可以重用,当一个进程终止后,其他进程就可以使用它的PID了。PID为0的进程为调度进程,该进程是内核的一部分,也称为系统进程;PID为1的进程为init进程,它是一个普通的用户进程,但是以超级用户特权运行;PID为2的进程是页守护进程,负责支持虚拟存储系统的分页操作。除了PID,每个进程还有一些其他的标识符: #if defined __USE_XOPEN_EXTENDED || defined __USE_XOPEN2K8/* Return the session ID of the given process. */extern __pid_t getsid (__pid_t __pid) __THROW;#endif/* Get the real user ID of the calling process. */extern __uid_t getuid (void) __THROW;/* Get the effective user ID of the calling process. */extern __uid_t geteuid (void) __THROW;/* Get the real group ID of the calling process. */extern __gid_t getgid (void) __THROW;/* Get the effective group ID of the calling process. */extern __gid_t getegid (void) __THROW;五种进程之间转换关系如图: ...

May 28, 2019 · 1 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

代码基础

返回数组的形式直接return array只会返回第一个元素哦!下面介绍四种返回数组方法 #include <iostream>#include <string.h>#include <math.h>#define CITY_NUM 11000class LbsIndexCity{public: LbsIndexCity(int city_id):m_city_id(city_id){} int get_city_id(){ return m_city_id; }private: int m_city_id;};LbsIndexCity* m_city[CITY_NUM];void add_m_city(int city_id){ m_city[101]=new LbsIndexCity(city_id);}/*1.LbsIndexCity* (* get_m_city())[CITY_NUM]{ return &m_city;}*//*2.typedef LbsIndexCity* arrT[CITY_NUM];arrT * get_m_city(){ return &m_city;}*//*3.auto get_m_city() -> LbsIndexCity*(*)[CITY_NUM]{ return &m_city;}*/LbsIndexCity* arrT[CITY_NUM]={nullptr};decltype(arrT) *get_m_city(){ return &m_city;}int main() { add_m_city(1001); LbsIndexCity* (*m_city)[CITY_NUM] = get_m_city(); for(int city_id =0; city_id < CITY_NUM; city_id++) { if((*m_city)[city_id] == NULL){ continue ; }else{ std::cout <<(*m_city)[city_id]->get_city_id()<<std::endl; } } std::cout << "cyy,Hello, World!" <<std::endl; return 0;}单例1.饿汉实现 ...

May 28, 2019 · 4 min · jiezi

C中的STL详解

1、string类 头文件:string 如果s有三个字符,传统C的字符串的s[3]是’0’字符,但是C++的string则是只到s[2]这个字符而已。 (1)常见构造函数 string():构造空的string类对象,既空字符串string(const char* s):使用C语言的字符串构造string类对象string(size_t n,char c):构造后的string类对象包括n个字符cstring(const string& s):用string类对象s拷贝构造另一个对象string(const string& s, size_t n):使用对象s中的第n个字符开始构造新的string类对象(2)常用的函数 size_t size() const:返回字符串有效字符长度size_t length() const:返回字符串有效的长度size_t capacity() const:返回当前容量(即string中不必增加内存即可存放的元素个数)bool empty() const:判断字符串是否为空串,是返回true,不是返回falsevoid clear():清空有效的字符void reserve(size_t res_arg = 0):修改string类的容量,为字符串预留空间int max_size()const; 返回string对象中可存放的最大字符串的长度void resize(int len,char c);把字符串当前大小置为len,多去少补,多出的字符c填充不足的部分string &insert(int p,const string &s); //在p位置插入字符串sstring &replace(int p, int n,const char *s); //删除从p开始的n个字符,然后在p处插入串sstring &erase(int p, int n); //删除p开始的n个字符,返回修改后的字符串string substr(int pos = 0,int n = npos) const; //返回pos开始的n个字符组成的字符串void swap(string &s2); //交换当前字符串与s2的值string &append(const char *s); //把字符串s连接到当前字符串结尾void push_back(char c) //当前字符串尾部加一个字符cconst char *data()const; //返回一个非null终止的c字符数组,data():与c_str()类似,用于string转const char*其中它返回的数组是不以空字符终止,const char *c_str()const; //返回一个以null终止的c字符串,即c_str()函数返回一个指向正规C字符串的指针, 内容与本string串相同,用于string转const char*int find(char c,size_t pos=0) const;//从字符串pos位置开始向后找字符c,找到返回该字符在字符串中的位置,找不到返回-1int rfind(char c, size_t pos = npos);//从字符串pos位置开始向前查找字符c,返回该字符在字符串中的位置size_type find( const basic_string &str, size_type index ); //返回str在字符串中第一次出现的位置(从index开始查找),如果没找到则返回string::npossize_type find( const char *str, size_type index ); // 同上size_type find( const char *str, size_type index, size_type length ); //返回str在字符串中第一次出现的位置(从index开始查找,长度为length),如果没找到就返回string::npossize_type find( char ch, size_type index ); // 返回字符ch在字符串中第一次出现的位置(从index开始查找),如果没找到就返回string::npos注意:查找字符串a是否包含子串b,不是用 strA.find(strB) > 0 而是 strA.find(strB) != string:npos。(npos可以表示string的结束位子,是string::type_size 类型的,也就是find()返回的类型。find函数在找不到指定值得情况下会返回string::npos。) ...

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

C-Primer-Plus-引用

认识引用Declares a named variable as a reference, that is, an alias to an already-existing object or function. 来自: C++参考手册先从一个简单的例子开始: 交换函数 swap() 。 // 代码片段01void swap01(int a, int b) { int temp = a; a = b; b = temp;}// 代码片段02void swap02(int *a, int *b) { int temp = *a; *a = *b; *b = temp;}int main(int argc, const char * argv[]) { int a= 10, b = 20; swap01(a, b); // 不会交换 swap02(&a, &b); // 会交换 return 0;}上述代码似乎不用解释都知道它所要说明的问题。似乎与今天的主题无关,但是不着急,看下 代码片段02 。内部充斥着 *a 、*b 这样的操作,代码似乎不够简洁。有没有什么方式可以使其更简洁一些呢?有! 引用!!。 ...

May 26, 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代码大赛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

读书笔记Effective-C09杂项

作者:LogM 本文原载于 https://segmentfault.com/u/logm/articles,不允许转载~ 9. 杂项9.1 条款53:不要轻易忽视编译器的警告也不要依赖编译器给你指出错误,因为不同的编译器对错误的敏感度是不同的。9.2 条款54:让自己首席包括TR1在内的标准程序库C++的一些扩展特性会在TR1,虽然这些特性随着C++标准版本的更新逐渐合并到标准中.9.3 条款55:让自己熟悉BoostBoost是一个C++开发者社群,由C++委员会创建,它是C++新特性的试验场,TR1的许多扩展特性是从Boost提交的。

May 22, 2019 · 1 min · jiezi

常用的公式编辑器软件

互联网时代,我们越来越依赖机器。用机器学习的好处就是方便、快捷。作为理科学生,在课堂上见证老师通过编辑器熟练输入复杂公式。想到我们是否可以通过计算机软件,将数学笔记做整理,方便在笔记本上浏览和复习呢?下面小编介绍一款专业公式编辑器软件,让我们高效学习、复习数学这一门重要学科。亿图公式编辑器(EdrawMath)是一款功能强大的数学公式编辑器。相比较WPS自带的公式编辑器,它可以帮我们实现复杂公式和符号的编写,无缝兼容Office办公文档。相信这款颜值较高的软件和贴心的设计,在数学学习的道理上能够帮助到你。 EdrawMath,好用的数学笔记公式编辑器结合做题出现的错题,可以将易错题通过EdrawMath做整理,还有常考知识重点做分类,将重点标记出来。将写好的公式可以通过云收藏在线保存公式,方便二次编辑。通过整理笔记的过程也加深了知识的记忆,云端保存的笔记方便我们随时浏览进行多次复习。数学学习的重点就是基础的掌握,具体到公式的应用后期的解题思路才会愈加清晰。 如何用EdrawMath做数学笔记1.汉字部分可支持键盘直接输入,方便我们重点标注说明和做分类。重要内容可以加粗、斜体标注,方便查找关键词。 2.输入公式按着从左到右顺序,注意切换到英文输入法。功能栏找到“上下标模板”,找到对应的数学符号,填补上数字即可。 3.编辑好的公式记得在线保存,点击右边新增分类,自定义名称《数学笔记》。点击加号,输入公式名称即可。 以上就是给大家介绍的用亿图公式编辑器做数学笔记的基本操作,无需下载亿图软件在线即可轻松编辑公式,同时方便同学们将重点知识进行分类整理和有针对性的做复习。现在,如果你也想要用一款线上工具来整理数学笔记,也可以用EdrawMath来提升自己的学习效率!

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

业务流程图例题word流程图怎么画

跨职能流程图是一种常见的商务流程图,用于显示业务进程或职能单位(如部门)之间的关系,并由这些职能单位负责实施该进程步骤。不仅被广泛用于大中小企业的内部管理,还涉及到门店销售和仓储信息管理等诸多领域。跨职能流程图可分为水平布局和垂直布局,垂直布局偏向于职能单位,而水平布局则强调的是进程本身。 绝大多数商务人士日常会接触到一些主流的软件产品,例如微软的办公软件。不论是Office word、Excel,还是PowerPoint,都给用户带来不错的操作体验,有效提升工作效率。Visio是微软公司的一款矢量绘图的软件,主要用于绘制图表、流程图等,不少用户希望借助该软件以解决工作中遇到的一些问题。实际上,Visio的功能蛮丰富,但是因为收费昂贵和模板较少的问题,让不少用户却而止步。寻求Visio相类似产品,成为越来越多商务人士的选择。亿图图示专家(EdrawMax),是同类型中合适的选择。 类似Visio的专业流程图软件——亿图图示专家 亿图图示是一款多类型的图形图表绘制软件,不仅可以用来绘制跨职能流程图,也是绘制思维导图、信息图、甘特图、网页设计图、方向地图、科学插画图等等。除此以外,软件内置了12000多种模板和实例,以及实时在线免费模板供用户选择。当用户完成绘制之后,可见作品导出文件为Visio、SVG、HTML、PS、JPG、PNG、PDF等多种格式。 为此,我们将Visio的功能与亿图图示的功能作对比,得出以下分析表。 为什么亿图图示可以一定程度上类似Visio 性价比高,无需花费上千元,也能买到强大的软件。 内置丰富的流程图模板。即使用户零基础的绘图经验,只要选择一个自己喜欢的模板,然后修改上面的文本及图片内容即可。 产品界面类似于Office软件中的Word,非常容易上手。 支持实时团队协作,支持在各终端显示屏查看浏览。 能打印或共享您所绘制的跨职能流程图。支持把文件导出为PDF,Html,Word,Visio,PNG,JPG,PPT,EPS等格式进行保存。 支持海量图表类型。亿图自带200个图表。 亿图支持跨平台使用,可以在Windows、Mac以及Linux上同时使用,而Visio只能在Windows。 丰富的跨职能流程图符号 垂直跨职能流程图形状:可用于创建泳道图。 基本的流程图形状:满足90%以上的跨职能图绘制需求。 箭头形状:用于表达流程图的连接关系。 点击亿图图示下载,立即免费体验! 或者点击此处,查看更多亿图流程图软件使用功能和技巧!

May 22, 2019 · 1 min · jiezi

业务流程图基本元素类似visio的软件

跨职能流程图是一种常见的商务流程图,用于显示业务进程或职能单位(如部门)之间的关系,并由这些职能单位负责实施该进程步骤。不仅被广泛用于大中小企业的内部管理,还涉及到门店销售和仓储信息管理等诸多领域。跨职能流程图可分为水平布局和垂直布局,垂直布局偏向于职能单位,而水平布局则强调的是进程本身。 绝大多数商务人士日常会接触到一些主流的软件产品,例如微软的办公软件。不论是Office word、Excel,还是PowerPoint,都给用户带来不错的操作体验,有效提升工作效率。Visio是微软公司的一款矢量绘图的软件,主要用于绘制图表、流程图等,不少用户希望借助该软件以解决工作中遇到的一些问题。实际上,Visio的功能蛮丰富,但是因为收费昂贵和模板较少的问题,让不少用户却而止步。寻求Visio相类似产品,成为越来越多商务人士的选择。亿图图示专家(EdrawMax),是同类型中合适的选择。 类似Visio的专业流程图软件——亿图图示专家 亿图图示是一款多类型的图形图表绘制软件,不仅可以用来绘制跨职能流程图,也是绘制思维导图、信息图、甘特图、网页设计图、方向地图、科学插画图等等。除此以外,软件内置了12000多种模板和实例,以及实时在线免费模板供用户选择。当用户完成绘制之后,可见作品导出文件为Visio、SVG、HTML、PS、JPG、PNG、PDF等多种格式。 为此,我们将Visio的功能与亿图图示的功能作对比,得出以下分析表。 为什么亿图图示可以一定程度上类似Visio 性价比高,无需花费上千元,也能买到强大的软件。 内置丰富的流程图模板。即使用户零基础的绘图经验,只要选择一个自己喜欢的模板,然后修改上面的文本及图片内容即可。 产品界面类似于Office软件中的Word,非常容易上手。 支持实时团队协作,支持在各终端显示屏查看浏览。 能打印或共享您所绘制的跨职能流程图。支持把文件导出为PDF,Html,Word,Visio,PNG,JPG,PPT,EPS等格式进行保存。 支持海量图表类型。亿图自带200个图表。 亿图支持跨平台使用,可以在Windows、Mac以及Linux上同时使用,而Visio只能在Windows。 丰富的跨职能流程图符号 垂直跨职能流程图形状:可用于创建泳道图。 基本的流程图形状:满足90%以上的跨职能图绘制需求。 箭头形状:用于表达流程图的连接关系。 点击亿图图示下载,立即免费体验! 或者点击此处,查看更多亿图流程图软件使用功能和技巧!

May 22, 2019 · 1 min · jiezi

读书笔记Effective-C08定制new和delete

作者:LogM 本文原载于 https://segmentfault.com/u/logm/articles,不允许转载~ 8. 定制new和delete8.1 条款49:了解new-handler的行为 new-handler相当于new的异常处理函数。new申请内存发生异常,调用new-handler处理,处理完了之后继续尝试new,如果还是出错,继续调用new-handler,以此反复。//标准库对于new-handler是这么写的://new_handler被定义成一个函数指针//set_new_handler函数的参数是个指针,指向new无法分配足够内存时应该调用的函数;//set_new_handler函数的返回值是个指针,指向set_new_handler之前设置的new_handler函数namespace std { typedef void (*new_handler)(); new_handler set_new_handler(new_handler p) throw();}//可以这样使用set_new_handlervoid oufOfMem(){ std::cerr << "Unalbe to new\n"; std::abort();}int main(){ std::set_new_handler(outOfMem); int * pBigArray = new int[10000000000L];}一般来说,new-handler函数会做以下事情的某几件: a. 让更多内存可被使用。使得再次尝试new能成功。b. 安装另一个new-handler。使用更强力的new-handler处理。c. 卸载new-handler。实在不行,卸载new-handler使new抛出异常。d. 抛出bad_alloc的异常。e. 退出程序。abort()或exit()。8.2 条款50:了解new和delete的合理替换时机这部分讲的是:如果你觉得编译器自带的new和delete不好用,应该怎么写一个自定义的函数替换。作者自己也提到了,要自定义new和delete不是简单写个函数就好了,内存的申请和释放会涉及到计算机体系架构中比较底层的东西,比如"内存对齐"。所以重头开始写new和delete是比较复杂的,可以买现成的商业产品,或者在开源代码上修改。8.3 条款51:编写new和delete时需要固守常规条款50已经说明了,自定义new和delete比较复杂,非必要不建议重写。这部分讲了自定义new和delete时需要注意的事项: operator new应该内含一个无穷循环,并在其中尝试分配内存,分配不了,调用new-handler。operator new对0 bytes的内存申请,也需要正常返回指针。operator delete应该在收到null指针时不做任何事。8.4 条款52:写了placement new也要写placement deleteWidget* pw = new Widget//这句话中总共有2个函数被调用:1.operator new分配内存;2.Widget的构造函数//当调用Widget构造函数发生异常时,需要delete掉第一步new出来的内存//如果使用C++自带的new和delete,这个情况不需要用户考虑;如果是自定义new和delete,需要用户考虑

May 22, 2019 · 1 min · jiezi

读书笔记Effective-C07模板与泛型

作者:LogM 本文原载于 https://segmentfault.com/u/logm/articles,不允许转载~ 7. 模板与泛型7.1 条款41:了解隐式接口和编译期多态//不用模板的写法class Widget { Widget(); virtual ~Widget(); virtual std::size_t size() const; virtual void normalize(); void swap(Widget& other); ...};void doProcess(Widget& w) { if (w.size() > 10; && w!= ...) { Widget temp(w); temp.normalize(); temp.swap(w); }}//w支持的接口是类型Widget决定的,这称为"显式接口"。//Widget类里面的virtual函数是在运行期确定具体调用哪个函数,这称为"运行期多态"。//使用模板的写法template<typename T>void doProcessing(T& w) { if (w.size() > 10 && w != ...) { T temp(w); temp.normalize(); temp.swap(w); }}//w支持的接口,是由w所参与执行的操作所决定的,比如例子中的w需要支持size()、normalize()、swap()、拷贝构造、不等比较。这称为"隐式接口"。//w所参与执行的操作,都有可能导致template的具现化,使函数调用得以成功,具现化发生在编译期。这称为"编译期多态"。7.2 条款42:了解typename的双重意义//第一重意义template<class T> class Widget;template<typename T> class Widget;//上面两句话效果完全一样//第二重意义//考虑一个例子template<typename C>void print2nd(const C& container) { C::const_iterator* x; //bad,不加typename被假设为非类型,理由见下面注释 ...}//一般,我们认为C::const_iterator指的是某种类型,但是存在一种逗比情况://C是一个类,const_iterator是这个类的int型的成员变量,x是一个int型的变量,那么上面一句话就变成了两个int的相乘。//正因为有这种歧义情况的存在,C++假设不加typename的"嵌套从属名称"是非类型。//应该这么写template<typename C>void print2nd(const C& container) { typename C::const_iterator* x; //ok,告诉编译器,C::const_iterator是类型 ...}7.3 条款43:学习处理模板化基类内的名称//基类template<typename T>class MsgSender {public: ... void sendClear(const MsgInfo& info); ...};//派生类template<typename T>class LoggingMsgSender : public MsgSender<T> {public: void sendClearMsg(const MsgInfo& info) { sendClear(info); //bad,理由见下方注释 }}//编译器遇到LoggingMsgSender类时,不知道要继承哪种MsgSender类,所以编译器不知道sendClear这个函数是MsgSender类里继承下来的成员方法,还是类外面的全局的函数。//为什么说不同的MsgSender类不一定有sendClear成员方法呢?因为C++允许template的特化,比如我在下面写了一个特化的类,这个特化的类为空类,就没有sendClear成员方法。template<>class MsgSender<CompanyZ> { };//解决这个问题的方法,本质就是告诉编译器,sendClear函数的来源。具体来说,有三种方法://方法1template<typename T>class LoggingMsgSender : public MsgSender<T> {public: void sendClearMsg(const MsgInfo& info) { this->sendClear(info); //ok,告诉编译器,sendClear函数是类内的成员方法 }}//方法2template<typename T>class LoggingMsgSender : public MsgSender<T> {using MsgSender<T>::sendClear; //先声明,告诉编译器,如果遇到sendClear函数,则视为类内的成员方法进行编译public: void sendClearMsg(const MsgInfo& info) { sendClear(info); //ok }}//方法3template<typename T>class LoggingMsgSender : public MsgSender<T> {public: void sendClearMsg(const MsgInfo& info) { MsgSender<T>::sendClear(info); //ok,告诉编译器,sendClear函数是类MsgSender<T>内的成员方法 }}//方法3不太好的地方是,假如sendClear()是virtual函数,这种写法会把它的多态性破坏;方法1和方法2则不会破坏。7.4 条款44:将与参数无关的代码抽离templates编译器对template的处理,实际上是对所有可能的template具现出具体代码//模板类template<typename T, std::size_t n>class SquareMatrix {public: ... void invert(); //该函数与template无关}//使用SquareMatrix<double, 5> sm1;SquareMatrix<double, 10> sm2;sm1.invert();sm2.invert();//这个例子中,invert()函数与template无关,但它被编译器生成了两份,造成重复。作者认为将与参数无关的代码抽离templates,可以避免编译器产生这类的重复代码;但我觉得有时候要达到这个目的,会造成代码可读性和编写效率的下降,实际使用时还是要权衡。7.5 条款45:运用成员函数模板接受所有兼容类型假设派生类D继承于基类B,由B具现化的模板类和由D具现化的模板类,并不能相互转换。以代码表述:class B {...};class D : public B {...};template<typename T>class SmartPtr {public: SmartPtr(T* realPtr); ...}//使用SmartPtr<B> pt1 = SmartPtr<D>(new D); //bad,SmartPtr<B>与SmartPtr<D>没有继承关系来使得他们相互转换//解决方法template<typename T>class SmartPtr {public: SmartPtr(T* realPtr); template<typename U> SmartPtr(const SmartPtr<U>& other); //建立一个泛化拷贝构造函数,来解决上面的问题 ...}//当然,对于赋值函数也可以这么操作7.6 条款46:需要类型转换时,请为模板定义非成员函数这条把条款24扩充到模板类上。template<typename T>class Rational {public: ... Rational(const T& numerator, const T& denominator); Rational(sonst T& num); const T numerator() const; const T denominator() const;}const Rational<T> operator* (const Rational<T>& lhs, const Rational<T>& rhs) { ...}//使用Rational<int> lhs(1, 9);Rational<int> result;result = lhs * 2; //bad,template的推导不考虑隐式类型转换,编译器猜不出T是什么result = 2 * lhs; //bad,template的推导不考虑隐式类型转换,编译器猜不出T是什么//解决方法template<typename T>class Rational {public: ... Rational(const T& numerator, const T& denominator); Rational(sonst T& num); const T numerator() const; const T denominator() const; friend const Rational operator*(const Rational& lhs, const Rational& rhs) { //这里要把类外面operator*实现的代码拷贝一份到这里 ... } //在类内声明friend函数,使编译器在类初始化时可以先具现出: //"const Rational<int> operator* (const Rational<int>& lhs, const Rational<int>& rhs)"};const Rational<T> operator* (const Rational<T>& lhs, const Rational<T>& rhs) { ...}//使用Rational<int> lhs(1, 9);Rational<int> result;result = lhs * 2; //ok,由于friend函数带来的具现化,编译器执行到这里时,具现化好的函数中,已经有满足需要的了,不需要推导Tresult = 2 * lhs; //ok,由于friend函数带来的具现化,编译器执行到这里时,具现化好的函数中,已经有满足需要的了,不需要推导T7.7 条款47:使用traits classes表现类型信息STL中广泛使用traits classes来标记容器属于哪一类容器(比如"可随机访问容器":vector、deque等)7.8 条款48:认识template元编程(TMP)所谓元编程,是执行于编译器内的程序,C++以template实现元编程。优点:a. 完成一些以前不可能完成的任务;b. 将工作从运行期转移到编译期(比如之前在运行期才找到的错误可以在编译期找到)。缺点:编译时间变长。TMP不同于"正常化的"C++,还没有完全被C++标准支持,普通用户可以暂时不用了解。

May 22, 2019 · 2 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

读书笔记Effective-C06继承与面向对象

6. 继承与面向对象6.1 条款32:确定你的public继承是is-a关系public继承的子类对象需要保证可以被视作父类对象来调用函数。class Person {...};class Student : public Person {...};void eat(const Person& p);void study(const Student& s);eat(p); // okeat(s); // ok,Student可以视作Person调用函数study(s); //okstudy(p); //error,Person不能视作Student6.2 条款33:避免遮掩继承而来的名称int x; //global变量void someFunc() { double x; //local变量 std::cin >> x; //local变量的x遮掩了global变量的x,实际起作用的是local变量的x}//定义基类class Base {private: int x;public: virtual void mf1() = 0; virtual void mf1(int); virtual void mf2(); void mf3(); void mf3(double); ...};//定义派生类class Derived : public Base {public: virtual void mf1(); void mf3(); void mf4(); ...};//使用Derived d;int x;...d.mf1(); //ok,调用Derived::mf1d.mf1(x); //bad,因为Derived::mf1遮掩了Base::mf1d.mf2(); //ok,调用Base::mf2d.mf3(); //ok,调用Derived::mf3d.mf3(x); //bad,因为Derived::mf3遮掩了Base::mf3//解决方法1//定义派生类class Derived : public Base {public: using Base::mf1; //让基类中名为mf1和mf3的所有东西在此作用域内可见 using Base::mf3; virtual void mf1(); void mf3(); void mf4(); ...};//使用Derived d;int x;...d.mf1(); //ok,调用Derived::mf1d.mf1(x); //ok,调用Base::mf1d.mf2(); //ok,调用Base::mf2d.mf3(); //ok,调用Derived::mf3d.mf3(x); //ok,调用Base::mf3//解决方法2//定义基类class Base {public: virtual void mf1() = 0; virtual void mf1(int); ...};//定义派生类class Derived : public Base {public: virtual void mf1() { Base::mf1(); } // 转交函数 ...};//使用Derived d;int x;...d.mf1(); //ok,调用Derived::mf1d.mf1(x); //bad,因为Base::mf1被遮掩,且Base::mf1(int)没有被转交6.3 条款34:区分接口继承和实现继承如下代码所示,有3类继承关系: ...

May 21, 2019 · 2 min · jiezi

C介绍与入门学习

C++是C语言的继承,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。C++擅长面向对象程序设计的同时,还可以进行基于过程的程序设计,因而C++就适应的问题规模而论,大小由之。 C++不仅拥有计算机高效运行的实用性特征,同时还致力于提高大规模程序的编程质量与程序设计语言的问题描述能力。 免费教程链接:C++ 入门教程(开发文档) 语言特点: 支持数据封装和数据隐藏 在C++中,类是支持数据封装的工具,对象则是数据封装的实现。C++通过建立用户定义类支持数据封装和数据隐藏。 在面向对象的程序设计中,将数据和对该数据进行合法操作的函数封装在一起作为一个类的定义。对象被说明为具有一个给定类的变量。每个给定类的对象包含这个类所规定的若干私有成员、公有成员及保护成员。完好定义的类一旦建立,就可看成完全封装的实体,可以作为一个整体单元使用。类的实际内部工作隐藏起来,使用完好定义的类的用户不需要知道类是如何工作的,只要知道如何使用它即可。 支持继承和重用 在C++现有类的基础上可以声明新类型,这就是继承和重用的思想。通过继承和重用可以更有效地组织程序结构,明确类间关系,并且充分利用已有的类来完成更复杂、深入的开发。新定义的类为子类,成为派生类。它可以从父类那里继承所有非私有的属性和方法,作为自己的成员。 支持多态性 采用多态性为每个类指定表现行为。多态性形成由父类和它们的子类组成的一个树型结构。在这个树中的每个子类可以接收一个或多个具有相同名字的消息。当一个消息被这个树中一个类的一个对象接收时,这个对象动态地决定给予子类对象的消息的某种用法。多态性的这一特性允许使用高级抽象。 继承性和多态性的组合,可以轻易地生成一系列虽然类似但独一无二的对象。由于继承性,这些对象共享许多相似的特征。由于多态性,一个对象可有独特的表现方式,而另一个对象有另一种表现方式。 工作原理编辑 C++语言的程序因为要体现高性能,所以都是编译型的。但其开发环境,为了方便测试,将调试环境做成解释型的。即开发过程中,以解释型的逐条语句执行方式来进行调试,以编译型的脱离开发环境而启动运行的方式来生成程序最终的执行代码。[9] 生成程序是指将源码(C++语句)转换成一个可以运行的应用程序的过程。如果程序的编写是正确的,那么通常只需按一个功能键,即可搞定这个过程。该过程实际上分成两个步骤。 第一步是对程序进行编译,这需要用到编译器(compiler)。编译器将C++语句转换成机器码(也称为目标码);如果这个步骤成功,下一步就是对程序进行链接,这需要用到链接器(linker)。链接器将编译获得机器码与C++库中的代码进行合并。C++库包含了执行某些常见任务的函数(“函数”是子程序的另一种称呼)。例如,一个C++库中包含标准的平方根函数sqrt,所以不必亲自计算平方根。C++库中还包含一些子程序,它们把数据发送到显示器,并知道如何读写硬盘上的数据文件。 语言评价: C++是在C语言的基础上开发的一种面向对象编程语言,应用非常广泛。常用于系统开发,引擎开发等应用领域,支持类、封装、继承、多态等特性。C++语言灵活,运算符的数据结构丰富、具有结构化控制语句、程序执行效率高,而且同时具有高级语言与汇编语言的优点。 C++语言是对C语言的扩充,从Simula中吸取了类,从ALGOL语言中吸取了运算符的一名多用、引用和在分程序中任何位置均可说明变量,综合了Ada语言的类属和Clu语言的模块特点,形成了抽象类,从Ada Clu和ML等语言吸取了异常处理,从BCPL语言中吸取了用//表示注释C++语言保持了C语言的紧凑灵活、高效以及易于移植性强等优点,它对数据抽象的支持主要在于类概念和机制,对面向对象风范的支持主要通过虚拟机制函数因C++语言既有数据抽象和面向对象能力,运行性能高,加上C语言的普及,而从C语言到C++语言的过渡较为平滑,以及C++语言与C语言的兼容程度可使数量巨大的C语言程序能方便地在C++语言环境中复用,使C++语言在短短几年内能流行。 免费C++ 入门教程:阿里云大学—开发者课堂

May 21, 2019 · 1 min · jiezi

Rainbond-514发布复杂微服务架构整体升级和回滚

Rainbond 5.1.4发布, 复杂微服务架构整体升级和回滚 今天为大家带来Rainbond 5.1系列第四个更新版本,本次版本更新的主要内容是复杂微服务架构应用整体升级和回滚,能实现复杂微服务架构的持续交付,和复杂架构企业级应用快速交付和升级,另外还有一些小的优化和BUG的修复。 Rainbond是开源的企业应用云操作系统,支撑企业应用的开发、架构、交付和运维的全流程,通过无侵入架构,无缝衔接各类企业应用,底层资源可以对接和管理IaaS、虚拟机和物理服务器。 复杂微服务架构应用整体升级和回滚 面对复杂的微服务架构,微服务组件可能几十个,服务之间存在业务依赖;微服务的版本管理复杂;开发测试流程低效,针对以上问题,单个微服务管理的模式已经不适用,需要考虑微服务架构整体管理。这次的更新能实现复杂微服务架构的整体版本,微服务独立开发,测试环境和生产环境整体升级和回滚,升级的过程只更新变化的服务和配置,过程滚动更新,实现业务不间断升级。 升级和回滚的过程通过Rainbond应用市场实现,Rainbond应用市场定义了一种对应用的存储、共享、交付、管理途径. Rainbond应用市场与传统意义上的镜像仓库不同之处在于,它基于镜像仓库、包仓库和对象存储等存储系统支持,定义了支持大型、分布式数字化业务系统的标准云原生应用模型,并针对应用模型提供创建、发布、存储、交付、安装、升级等一系列业务支持,对内可作为以便捷灵活的方式共享企业创造的业务系统、中间件的业务性管理平台,对外可作为根据行业特性构建行业话交付标准、交付流程和交付路径的基础,应用市场的最大优点在于它涵盖的不仅是服务组件和应用(业务系统),甚至于解决方案都可以支持一键分享、一键安装使用,极大的便利用户,只需安装使用,使用者不需要懂技术。 在5.1.4之前, rainbond仅仅支持对云市应用中单个服务的升级, 如果想要升级整个云市应用, 则需要单独地对每个服务进行升级, 且无法升级新添加的服务. 这给各位用户的使用带来了极大的不便. 为了让用户有的操作更加的简单, 提高使用体验, 我们在5.1.4版本中, 对应用市场进行了改造升级. 功能特性 灵活的升级方式: 可以自由地选择需要升级的服务, 可以全部升级也可以部份升级.创建新添加服务: 除了可以升级已有的服务外, 还可以创建旧版本没有, 但是新版本有的服务.详细的变更信息: 在升级界面中, 可以查看当前版本与新版本服务之间属性的变更.详细的升级记录: 对每次升级操作, rainbond都进行了详细的记录, 包括: 升级操作的时间, 版本号的变更和各服务属性的变更信息等.自动回滚: 在应用升级的过程中, 如果程序发生了异常, 会回滚到升级前的状态, 避免只升级部分属性或服务.手动回滚: 升级成功后, 如果新版本有缺陷导致各个服务无法正常工作, 或者你更倾向升级前的版本, 那么可以选择手动回滚, 回到之前的版本.简单的演示 更详细的说明, 请参考: 服务升级文档 其他改进 第三方服务新添加实例地址时, 允许地址中带有端口镜像服务支持修改镜像仓库帐号, 密码等信息grctl命令行工具增加身份属性gateway将自定义网关策略的域名以环境变量的方式注入到服务中(相关文档)将环境变量,配置文件等配置信息综合为环境配置分享应用时支持定义不分享的服务支持服务链接信息和环境变量的相互转移关闭或重启服务时, 增加二次确认, 防止误操作安装方面: 优化安装时初始化数据中心流程优化调整安装任务结构,调整离线镜像文件路径支持调整网络类型优化部分组件配置参数优化安装过程中宿主机IP段与容器ip段冲突问题BUG修复 【重要】修复了关闭服务时, pod无法被删除或删除需要花费比较多时间的问题【重要】修复了多管理节点中, 某个节点rbd-hub服务异常了,但gateway没有将其下线导致goodrain.me服务异常的问题修复了第三方服务的网关访问策略控制错误修复了删除端口报系统异常的错误修复了编辑HTTPs网关策略, 无法勾选 HTTP rewriet HTTPs 的问题修复了更改构建源后无法重新检测语言的错误修复了无法修改健康检测参数的错误修复了云市应用版本号显示不全的问题修复了添加镜像服务时, 没有高级选项按钮的问题修复了构建源中镜像Tag显示不全的问题修复了创建应用时勾选的是有状态应用,创建成功后却是无状态应用的问题修复了无法将无状态应用修改为有状态应用的问题修复了禁止调度计算节点后, 导致可用资源统计错误的问题修复了第三方服务TCP访问策略状态错误且无法操作的问题修复了网关策略参数配置中Websocket不生效的问题修复了云市应用导出的docker-compose.yaml中的镜像有误的问题修复了环境变量名格式验证有误的问题, 支持带"."的环境变量名安装和升级 ...

May 21, 2019 · 1 min · jiezi

Effective-C

条款01:视C++为一个语言联邦一开始,C++只是加上一些面向对象特性的C,但是随着语言的成熟,它变得更加灵活多变,如Template、STL、异常。 今天C++已经是一个多重范型编程语言,同时支持过程形式、面向对象形式、函数形式、泛型形式、元编程形式。这些能力和弹性使C++变得强大。但我们该如何理解呢? 最简单的方法是将C++视为一个由相关语言组成的联邦而非单一语言。在某一个次语言中,守则变得简单。在C++中,主要的次语言有4个: C 说到底C++是以C为基础的,数组、内置数据类型、指针都是来自于C的。Object-Oriented C++ 这一部分也就是C with Classes所诉求的,classes、封装、继承、多态······体现了面向对象的特性。Template C++ 这是C++的泛型编程部分。良好的编程守则中“唯template适用”的条款并不罕见,实际上template威力强大,他们带来崭新的编程范型,也就是所谓的templatemetaprogramming(TMP,模板元编程)。STL 它对容器、迭代器、算法以及函数对象的规约有极佳的紧密配合与协调。C++并不是一个带有一组守则的一体语言:它是由四个次语言组成的联邦政府。C++高效编程守则视状况而变化,取决于你使用那一部分。由于有这4种不同的风格,所以当你对内置数据类型操作时,通过值传递比通过引用传递更高效;但当你使用的是自定义的对象时,由于构造函数和析构函数的存在,传递const引用更好。但当你使用STL库时,由于STL是通过指针实现的,所以传递值一般更好。

May 21, 2019 · 1 min · jiezi

读书笔记Effective-C03资源管理

作者:LogM 本文原载于 https://segmentfault.com/u/logm/articles,不允许转载~ 3. 资源管理3.1 条款13:使用"资源管理类"管理资源对于 new 出来的对象,某些意外情况下 delete 没有被执行(代码遗漏、鲁棒性低或者出现异常),导致资源泄漏。作者建议使用智能指针管理 new 出来的对象。auto_ptr在拷贝或者赋值时,新指针获得资源拥有权,旧指针变为null。std::auto_ptr<Investment> pInv1(createInvestment()); //pInv1获得资源拥有权std::auto_ptr<Investment> pInv2(pInv1); //pInv2获得资源拥有权,pInv1变nullpInv1 = pInv2; //pInv1获得资源拥有权,pInv2变nullshared_ptr使用计数的方式来确定有哪些指针在使用该资源,计数为0释放资源。它的拷贝和赋值则没有auto_ptr的那个问题。3.2 条款14:在资源管理类中小心拷贝行为接3.1,在某些不适用"智能指针"的场景,需要自定义"资源管理类"来管理资源,作者建议需要考虑下这个类的拷贝和赋值。3.3 条款15:在"资源管理类"中提供对原始资源的访问在3.1,我们使用智能指针管理对象,此时指针类型是std::auto_ptr<Investment>,但如果有个函数需要类型为Investment*的参数怎么办呢?智能指针有.get()函数可以显式转换成原始指针;它们也重载了operator->和operator*,可以隐式转换。类似的,自定义的"资源管理类"也需要考虑显示转换和隐式转换,提供对原始资源的访问。3.4 条款16:成对使用new和delete时要采取相同形式简单来说,就是new出来的对象要用delete释放,new []出来的对象要用delete []释放。new出来的对象如果使用delete []释放,将导致未定义的行为;new []出来的对象如果用delete释放,很可能只释放了数组的第一个元素。3.5 条款17:以独立语句将对象置入智能指针//假设有下面这个函数,它有两个参数,调用该函数时,会做下面三件事://a. 调用new Widget//b. 执行shared_ptr的构造//c. 执行priority()////执行顺序是未定义的,可能为a->b->c,也可能a->c->b等等。注意,当顺序为a->c->b,且priority()抛出异常时,资源泄漏。processWidget(std::shared_ptr<Widget>(new Widget), priority());//解决方法:别偷懒,写成两句话std::shared_ptr<Widget> pW(new Widget);processWidget(pW, priority());

May 19, 2019 · 1 min · jiezi

读书笔记Effective-C02构造析构赋值

作者:LogM 本文原载于 https://segmentfault.com/u/logm/articles,不允许转载~ 2. 构造/析构/赋值2.1 条款05:C++会自动编写default构造、拷贝构造、析构、赋值函数//你以为你写了个没有代码的空类class Empty{};// 实际上,C++自动生成了很多函数class Empty{public: Empty() {...} //默认构造函数 Empty(const Empty& rhs) {...} //拷贝构造函数 ~Empty() {...} //析构函数 Empty& operator=(const Empty& rhs) {...} //赋值函数};2.2 条款06:声明为private防止自动生成的函数被使用//把拷贝构造函数和赋值函数声明为private类型,防止被使用class Empty {public: ...private: ... Empty(const Empty&); Empty& operator=(const Empty& rhs);};2.3 条款07:使用多态特性时,基类的析构函数必须为virtual //一个多态的场景 class TimeKeeper { //计时器(基类) public: TimeKeeper(); virtual ~TimeKeeper(); ... }; class AtomicClock: public TimeKeeper {...} //原子钟 class WristWatch: public TimeKeeper {...} //腕表 //往往这么使用多态特性 TimeKeeper* ptk = getTimeKeeper(); ... delete ptk;上面是使用多态的一个场景,当delete ptk时,因为ptk是TimeKeeper类型,如果基类析构函数不是virtual,那么ptk的析构时只调用基类析构函数,析构不正确;使用virtual ~TimeKeeper();保证ptk析构时调用正确的子类析构函数 ...

May 19, 2019 · 1 min · jiezi

海量人脸特征检索解决方案演进之路

1. 概述 人脸识别技术在最近几年得到了长足进步,目前在人脸识别领域业界领先的厂家识别准确率均达到了99%以上,因此大量人脸相关的应用场景开始逐步落地,例如人脸支付、人员布控、寻找失踪人口等,此外,结合人脸的追踪技术,也开始出现了分析人流走向、分析景点旅客行走规律、人员行为偏好分析等。这些应用虽然表现形式多样,但最终都是基于人脸特征检索这一技术实现的。 首先介绍一下人脸特征是什么。目前图像识别算法能够在一张照片中发现人脸,并能够对人脸中的轮廓进行识别和标记,算法使用这些标记点构造出表示该张人脸的特征的矩阵,这个过程称为人脸特征提取,得到的矩阵称为人脸特征矩阵,在工程上,特征矩阵一般以一维矩阵表示,以二进制数组的方式进行存储。 当需要确认两张人脸照片是否同一个人时,可以通过上述公式计算这两张人脸的特征矩阵之间的相似度,以此作为两个人脸的相似度,当相似度超过一定阈值时,就认为是同一个人,该阈值是经验值,不同厂家的特征提取算法不同,得到的经验值也会不同。提高阈值,会提高准确率(认为是同一个人的情况下判断正确的占比),但会降低查全率(能匹配到的人脸在人脸库中全部匹配人脸的占比)。因此在不同的应用场景下,由于准确率和查全率的权重不同,导致了阈值也会不同。如在寻找失踪人口的场景,是宁可找错也不应放过的,阈值就会相应调低,让更多相似的人脸能被看到;在人脸支付场景,准确率是最重要的,那么阈值相应就会较高,当然也会导致匹配失败的次数增多。 把大量人脸特征集中存储可形成特征库,若要判断一个人在不在这个特征库中,只需要拿这个人的人脸照片对应的人脸特征,跟特征库里每个特征计算相似度,把相似度超过阈值的特征对应的照片找出即可。通过一个特征来比对一个特征库的场景,往往称为人脸1:N比对,与之对应的两个特征库之间的比对,往往称为人脸M:N比对。 算法场景通常会提供如下的M:N比对接口: 接口实现两个特征库之间的比对,如上图中4个特征的库和3个特征的库的比对,可得到12个相似度。当要实现1:N时,只需让其中一个特征库只包含一个特征即可。此前基于E5-2640V3 CPU(16物理核)实测1:N,每秒可实现1.5亿对特征比对。 2. 性能指标要求 在今年某地市的项目中,系统从一万多人脸摄像机中采集人脸抓拍图片进行特征提取后形成特征库,业务层需要实现人脸检索功能。其中数据规模为:每天约2000万张人脸抓拍图,图片平均约30KB,人脸特征约600字节,即每月6亿个特征,每月特征库单副本约占用350GB空间,数据需要存储1年,人脸检索要求1:1亿在3秒内响应,需要支持10个并发,需要支持根据时间、摄像机编号、相似度阈值来过滤人脸。 当然这是在最近才提出的性能指标要求,两年前,在人脸相关项目还没有大面积落地时,对人脸检索的性能指标要求还在千万级以下的人脸比对,而且只需支持1-3个并发,但也由于当时没有可参考的案例,业务场景处于摸索阶段,因此在人脸检索上需要支持更丰富的检索条件,如除了时间和摄像机编号外,还需要支持根据性别、年龄段、是否戴帽子、是否戴眼镜等条件过滤数据。 下面将从最简单的方案讲起,逐步推进到支持千万级、亿级人脸比对的方案,让大家对方案的演进有个整体的了解。 3. 解决方案演进3.1 人脸动态库方案 在内部验证阶段,使用单机存储固定特征个数(可能是一千万个)的特征库,每个特征对应记录ID、时间戳、摄像机编号等信息。每天新增的特征形成一个单独的小特征库,每天定时把小特征库合并到大特征库,并把大特征库中最旧的同量特征删除,保持特征库的大小。在检索时先对全库进行1:N,根据阈值过滤出部分记录后,再抽取对应记录的额外信息,与页面检索条件进行匹配,返回结果。 优点:由于是单机系统,方案实现和维护都比较简单缺点:单机支持的特征数量有限,无法横向扩展,检索并发度低,过滤条件无法灵活变化3.2 ES分布式人脸检索方案3.3 基于RocksDB的分布式特征索引方案3.4 基于小特征加速比对的检索方案3.5 基于GPU优化的检索方案0 0 0 0 0 0 0 0 0

May 18, 2019 · 1 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框架

thrift在网络一节中简单介绍了thrift的协议部分,在工程中会用得到thrift的线程并发,process,server库。定义idl后生成代码和业务编写代码的关系如下: 运行过程: 1.开启threaft线程池 主线程创建n个,开启,数量不够workerMonitor_.wait。到100个就死了(加锁,结束释放) 工作线程开启后,加锁,增加数量,workerMonitor_.notify,任务空monitor_.wait(),否则取任务,判断等待队列长度不到阈值则manager_->maxMonitor_.notify(),释放锁。执行任务。结束后继续抢锁循环2.开启nonblockingserver,io线程就绪 非0号其他线程先start,设置eventbase(iothread),createpipe,注册notify;event_base_loop(eventBase_, 0);【无监听,每个io线程自己的event_base】 0号线程注册事件,设置eventbase(iothread);注册监听;createpipe,注册notify。0号io线程run,开始监听。其他io线程join3.0号监听到handleEvent accept 加锁create connection分配连接给io线程(轮询)释放锁,通知分配的线程notifyhandler4.分配到连接的IO线程notifyhandler(read notifyfd,transition) 本次transition: 读取,调用addtask=>setidle,不需要监听cfd5.addtask thrift,加锁,如果tasks_.size() >= pendingTaskCountMax_,maxMonitor_.wait(timeout);加入task队列,有空闲线程monitor_.notify()。任何一种monitor都公用一个锁。 这里的task就是process然后notifyIOThread(read notifyfd,transition)。6.处理后通知IO线程 transition将cfd改为监听写事件,加入到本线程的事件监听中,调用connenction的回调发送。7.connenction的回调发送之后继续notifyIOThread 本次transition重置缓存区结束。总结:多reactor多线程模式,一个accept,多个读写,单独任务处理。正常只需要一个reactor。单reactor多线程形式。 http_server 关于优雅重启nginx这种多进程的比价好做,因为子进程可以独立于父进程。主进程fork,继承监听fd,锁等,exec()执行完整代码。此时旧的子进程和新的子进程都可以抢锁监听fd处理连接,关闭旧主进程,给旧的子进程发送关闭信号,子进程在处理后才会监听到信号,做到了优雅。线程没办法独立监听信号。 连接池add的就是任意连接对象。实现connect,reconnect.比如 for (int i = 0; i < connectionCount; ++i) { RedisClient* redis = new RedisClient(host, port, conn_timeout_ms, rw_timeout_ms); redis->init();//CONNECT redisPool_.add(redis); }改造的redis_pool连接池+线程池+hiredis分别负责连接管理和并发请求处理。封装目的:一般并发到分片获取数据的代理都有以下缺点:一个失败全部失败,要等所有返回才返回,而mget的失败会被放大。因此自己在业务层控制整个mget的超时时间和返回,到代理层已经拆分为当个get,用线程池实现。 spdlog业务调用 spdlog::set_async_mode(8192*4, spdlog::async_overflow_policy::block_retry,nullptr, std::chrono::seconds(3));std::string info_file = FLAGS_log_path + "/" + FLAGS_info_fileauto debug_logger = spdlog::basic_logger_mt("debug_logger", info_file.c_str());debug_logger->set_pattern("INFO: %Y-%m-%d %H:%M:%S %v");inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, Args... args){ sink_ptr sink = std::make_shared<Sink>(args...); return details::registry::instance().create(logger_name, { sink }); /*锁控制 new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb); //这里启线程 _loggers[logger_name] = new_logger;*/} auto logger = spdlog::get("warn_logger");\ if (logger != NULL) { \ logger->info("{}:{} {}", cplusutils::servbase_basename(__FILE__), __LINE__, log_info.str()); \ }info()=>log()->push_msg()spdlog的push_msg就是enqueue ...

May 15, 2019 · 2 min · jiezi

读书笔记Effective-C01让自己习惯C

1. 让自己习惯C++1.1 条款01:视C++为语言联邦1.1.1 C++的发展: 阶段1:C with Classes;阶段2:加入异常(exceptions)、模板(templates)、STL库;阶段3:过程形式 + 面向对象形式 + 函数形式 + 泛型形式 + 元编程形式1.1.2 如何使用:视为四个次语言的组合 C。最基础的C。面向对象的C。C with Classes。Template C++。泛型编程,更深奥的还有模板元编程(TMP),但一般人用不到。STL。作者认为STL有自己的一套规约,可以单独拎出来。1.2 条款02:尽量以const, enum, inline替换 #define1.2.1 #define用于常量的场景 缺点: a. #define定义的常量导致编译出错时,出错提示不友好;b. #define无视作用域(scope),无封装性。解决: a. 大多数情况可以用const替换;b. 旧编译器对类内static const语法较苛刻,此时可用enum替换#define。1.2.2 $define用于宏(函数)的场景 缺点: a. 需要注意括号的使用,给自己添麻烦;b. #define无视作用域(scope),无封装性。解决:使用inline函数。1.3 条款03:尽可能使用const原因:让编译器辅助对常量/常量函数的错误使用。1.3.1 const常量 const char* p = ...; // p可改变,*p不可改变char const * p = ...; // 同上,有些人会这么写char* const p = ...; // p不可改变,*p可改变const char* const p = ...; // p不可改变,*p不可改变const std::vector<int>::iterator iter = ...; // iter不可改变,*iter可改变std::vector<int>::const_iterator iter = ...; // iter可改变,*iter不可改变const Rational operator* (const Rational& lhs, const Rational& rhs); // 用于避免出现 (a * b) = c 这样的代码1.3.2 const函数 ...

May 15, 2019 · 1 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

简单易用的图像解码库介绍-stbimage

说到图像解码库,最容易想起的就是 libpng 和 libjpeg 这两个老牌图像解码库了。 libpng 和 libjpeg 分别各自对应 png 和 jpeg 两种图像格式。这两种格式的区别如下: png 支持透明度,无损压缩的图片格式,能在保证不失真的情况下尽可能压缩图像文件的大小,因此图像质量高,在一些贴纸应用中也大部分用的是 png 图片。 jpg 不支持透明度,有损压缩的图片格式,有损压缩会使得原始图片数据质量下载,也因此它占用的内存小,在网页应用中加速速度快。 要想在工程中同时解码 png 和 jpeg 格式图片,就必须同时引用这两种库,而且还得经过一系列编译步骤才行。 在这里,介绍一个简单易用的图像库:stb_image 。Github 地址为:https://github.com/nothings/stb ,目前已经有了 9600+ Star 。它的使用非常简单,看看 README 可能你就会了。 <!--more--> 看看它的源码,你会发现全是 .h 头文件。这就是它的强大之处了,仅需在工程中加入头文件就可以解析图像了(实际上是函数实现等内容都放在头文件了而已)。 重点关注如下三个头文件: stb_image.h 用于图像加载stb_image_write.h 用于写入图像文件stb_image_resize.h 用于改变图像尺寸下面就开始实践吧,先给出一个完整的例子: #include <iostream>#define STB_IMAGE_IMPLEMENTATION#include "stb_image.h"#define STB_IMAGE_WRITE_IMPLEMENTATION#include "stb_image_write.h"#define STB_IMAGE_RESIZE_IMPLEMENTATION#include "stb_image_resize.h"#include <string>#include <stdio.h>#include <stdlib.h>#include <vector>using namespace std;int main() { std::cout << "Hello, STB_Image" << std::endl; string inputPath = "/Users/glumes/Pictures/input.png"; int iw, ih, n; // 加载图片获取宽、高、颜色通道信息 unsigned char *idata = stbi_load(inputPath.c_str(), &iw, &ih, &n, 0); int ow = iw / 2; int oh = ih / 2; auto *odata = (unsigned char *) malloc(ow * oh * n); // 改变图片尺寸 stbir_resize(idata, iw, ih, 0, odata, ow, oh, 0, STBIR_TYPE_UINT8, n, STBIR_ALPHA_CHANNEL_NONE, 0, STBIR_EDGE_CLAMP, STBIR_EDGE_CLAMP, STBIR_FILTER_BOX, STBIR_FILTER_BOX, STBIR_COLORSPACE_SRGB, nullptr ); string outputPath = "/Users/glumes/Pictures/output.png"; // 写入图片 stbi_write_png(outputPath.c_str(), ow, oh, n, odata, 0); stbi_image_free(idata); stbi_image_free(odata); return 0;}这个例子很简单也很全面,主要就是加载了一张图片,并将它的宽高都缩小一倍,并保存缩小后图片。 ...

May 13, 2019 · 2 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

编译原理学习一去除代码中的注释

前言开始学习编译原理了耶~关于编译原理的所有练习,按照老规矩,还是用我最喜欢的C#语言来实现,运行在.NetCore平台上~关于这个系列的所有代码已经上传到github了,项目主页: https://github.com/Deali-Axy/CompilerConstructionLearning本次题目对C或C++等高级程序设计语言编写的源程序中的//注释和/…/注释进行删除,保留删除后的源程序。要求以文件形式进行保存。思路分析程序主要功能就是消除已经编写好的源程序中的注释。在源程序中注释有两种形式,一种是单行注释,用“//”表示,另一种是多行注释,用“/…/”表示。针对这两种形式,程序中用了if..else..语句加以判断,并做出相应的处理。在这里还有可能出现另一种情况,上述两种注释符号可能出现在引号中,出现在引号中的注释符号并没有注释功能,因此在引号中出现的注释符号不应该被消除。所以,这次编写的程序将要分三种情况分析。第一种情况,单行注释:if (ch != temp){ // 这里就是单行注释 ofile.put(ch); ch = ifile.get();}或者 if (ch != temp){ /* 这里就是单行注释 */ ofile.put(ch); ch = ifile.get();}第二种情况,块注释:if (ifile.fail() || ofile.fail()){ cerr << "open file fail\n"; return EXIT_FAILURE; /*返回值EXIT_FAILURE(在cstdlib库中定义),用于向操作系统报* 告打开文件失败*/}第三种情况,行后注释:ifile.close(); // 关闭文件ofile.close();cout << "/////*////ret/rtr////";system("pause");return 0;还有一个关键的注意点可以看到这一行 cout << "/////*////ret/rtr////";这个字符串用双引号包起来的代码中有很多斜杠,所以要避免将这些斜杠识别为注释。这里我用的方法是在处理注释前先把包含注释符号的字符串替换掉,等注释删除之后,再添加回去。 实现代码注释写得很详细啦,配合上面的思路分析,我就不再继续分析代码了~ var sReader = new StreamReader(filePath);var newSource = "";var inBlock = false;var replaceFlag = false;var tempLine = ""; // 用于保存被替换的特殊行代码while (!sReader.EndOfStream){ var line = sReader.ReadLine(); if (line.Length == 0) continue; // 去除空行 var quotationPattern = "^(.*?)\".*//.*\""; var quotationResult = Regex.Match(line, quotationPattern); if (quotationResult.Success) { System.Console.WriteLine("替换特殊代码,双引号中包裹注释斜杠"); tempLine = quotationResult.Groups[0].Value; replaceFlag = true; line = Regex.Replace(line, quotationPattern, REPLACEMENT); } // 单行注释 if (line.Trim().StartsWith(@"//")) continue; if (line.Trim().StartsWith(@"/*") && line.EndsWith(@"*/")) continue; // 注释块 if (Regex.Match(line.Trim(), @"^/\*").Success) inBlock = true; if (Regex.Match(line.Trim(), @"\*/$").Success) { inBlock = false; continue; } // 行后注释 // 使用非贪婪模式(.+?)匹配第一个// var pattern = @"^(.*?)//(.*)"; // var pattern = @"[^(.*?)//(.*)]|[^(.*?)/\*(.*)\*/]"; var result = Regex.Match(line, pattern); if (result.Success) { System.Console.WriteLine("发现行后注释:{0}", result.Groups[2]); line = result.Groups[1].Value; } // 还原被替换的代码 if (replaceFlag) { System.Console.WriteLine("还原特殊代码"); line = line.Replace(REPLACEMENT, tempLine); replaceFlag = false; } if (inBlock) continue; newSource += line + Environment.NewLine;}var outputPath = "output/exp1.src";System.Console.WriteLine("去除注释完成,创建新文件。");using (var sWriter = new StreamWriter(outputPath)){ sWriter.Write(newSource);}System.Console.WriteLine("操作完成!文件路径:{0}", outputPath);结果测试源文件#include <iostream>#include <fstream>#include <iomanip>#include <cstdlib>using namespace std;int main(){ cout << '/'; ifstream ifile; //建立文件流对象 ofstream ofile; ifile.open("f:\\上机实验题\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.cpp"); //打开F盘根目录下的fileIn.txt文件 ofile.open("f:\\上机实验题\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.obj"); if (ifile.fail() || ofile.fail()) { //测试打开操作是否成功 cerr << "open file fail\n"; return EXIT_FAILURE; /*返回值EXIT_FAILURE(在cstdlib库中定义),用于向操作系统报* 告打开文件失败*/ } char ch; ch = ifile.get(); //进行读写操作 while (!ifile.eof()) { if (ch == 34) { //双引号中若出现“//”,双引号中的字符不消除 char temp = ch; //第一个双引号 ofile.put(ch); ch = ifile.get(); while (!ifile.eof()) { if (ch != temp) { //寻找下一个双引号 ofile.put(ch); ch = ifile.get(); } else { ofile.put(ch); break; } } ch = ifile.get(); continue; //双引号情况结束,重新新一轮判断 } if (ch == 47) { //出现第一个斜杠 char temp2 = ch; ch = ifile.get(); if (ch == 47) { //单行注释情况 ch = ifile.get(); while (!(ch == '\n')) ch = ifile.get(); } else if (ch == '*') { //多行注释情况 while (1) { ch = ifile.get(); while (!(ch == '*')) ch = ifile.get(); ch = ifile.get(); if (ch == 47) break; } ch = ifile.get(); } else { ofile.put(temp2); //temp2保存第一个斜杠,当上述两种情况都没有时,将此斜杠输出 } //ch = ifile.get(); } //cout << ch << endl; ofile.put(ch); //将字符写入文件流对象中 ch = ifile.get(); //从输入文件对象流中读取一个字符 } ifile.close(); //关闭文件 ofile.close(); cout << "/////*////ret/rtr////"; system("pause"); return 0;}处理后的结果#include <iostream>#include <fstream>#include <iomanip>#include <cstdlib>using namespace std;int main(){ cout << '/'; ifstream ifile; ofstream ofile; ifile.open("f:\\上机实验题\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.cpp"); ofile.open("f:\\上机实验题\\C++\\ConsoleApplication2\\ConsoleApplication2\\源.obj"); if (ifile.fail() || ofile.fail()) { cerr << "open file fail\n"; return EXIT_FAILURE; } char ch; ch = ifile.get(); while (!ifile.eof()) { if (ch == 34) { char temp = ch; ofile.put(ch); ch = ifile.get(); while (!ifile.eof()) { if (ch != temp) { ofile.put(ch); ch = ifile.get(); } else { ofile.put(ch); break; } } ch = ifile.get(); continue; } if (ch == 47) { char temp2 = ch; ch = ifile.get(); if (ch == 47) { ch = ifile.get(); while (!(ch == '\n')) ch = ifile.get(); } else if (ch == '*') { while (1) { ch = ifile.get(); while (!(ch == '*')) ch = ifile.get(); ch = ifile.get(); if (ch == 47) break; } ch = ifile.get(); } else { ofile.put(temp2); } } ofile.put(ch); ch = ifile.get(); } ifile.close(); ofile.close(); cout << "/////*////ret/rtr////"; system("pause"); return 0;}完整代码https://github.com/Deali-Axy/CompilerConstructionLearning/blob/master/code/exp/exp1/Exp1.cs ...

May 11, 2019 · 3 min · jiezi

继甲骨文裁员Java服软Python后国产原创IT技术已经成熟让中国科技不再受制于人

“甲骨文创始人拉里•埃里森(Larry Ellison):不能让中国培养比美国还多的工程师!”世界第二大软件公司甲骨文,突然在中国区进行大裁员。甲骨文仍在中国盈利,中国区一下裁员六成,引起中国研究中心集体对公司进行声讨和抗议。据甲骨文员工透漏,前一天晚上还在加班改bug,第二天就通知被裁了,甚至要求被裁员工在30分钟内交出公司资产,离开大楼。透过中国区被甲骨文裁员、中兴被美国商务部制裁风波,面对国外技术垄断,如何让中国科技不再受制于人?以下的国际一流企业:Oracle发布了java 11,宣布了Java开始收费;微软的ASP付费空间,收取操作系统的版权费;知识库开源内容付费PHP问答系统持续进行,技术不断被国外垄断,无一例外。 还记得当时最受欢迎的编程语言C++吗?在20世纪80年代初期,美国AT&T贝尔实验室的本贾尼•斯特劳斯特卢普博士发明了C++程序设计语言。那么,windows(美国微软)是最支持C++的,还提供IIS(互联网信息服务),1988 年推出了SQL Server (关系型数据库管理系统)第一个OS/2版本,其Access(数据库应用的开发工具软件)成为了一个专门的数据库应用开发工具。1995年8月24日,微软公司发行了内核版本号为4.0的一个混合了16位/32位的Windows系统——Windows 95,并成为当时最成功的操作系统。紧随其后,微软宣布收购GitHub。再来说说PHP。1994年,PHP(超文本预处理器)由Rasmus Lerdorf创建。1995年,PHP/FI加入了对MySQL的支持,从此建立了PHP在动态网页开发上的地位。到了1996年底,有15000个网站使用 PHP/FI。然而,PHP内容付费系统持续进行。还记得被Java统治的时代吗?1995年5月,Sun公司在Sun world会议上正式发布Java和HotJava浏览器。IBM、Apple、DEC、Adobe、HP、Oracle、Netscape和微软等各大公司都纷纷停止了自己的相关开发项目,竞相购买了Java使用许可证,并为自己的产品开发了相应的Java平台。2018年9月,Oracle发布了最新版的java 11,同时也宣布了Java开始收费。如今,Java首度承认失败,愿永久服软Python。1989年,荷兰人Guido van Rossum发明了面向对象的解释型计算机程序设计语言Python。现在,Python已经成为最受欢迎的程序设计语言之一。2018年3月,该语言作者在邮件列表上宣布Python 2.7将于2020年1月1日终止支持。用户如果想要在这个日期之后继续得到与Python 2.7有关的支持,则需要付费给商业供应商。以上主流开发环境都是基于国外开源框架发明的,一直存在国外技术垄断风险。今天,国产原创大数据技术——前嗅大数据已经成熟。颠覆行业标准,面对国外技术垄断,让中国科技不再受制于人。• 前嗅KSP语言:唯一一个以数据库为基础的语言,设计的思想突破了传统计算机架构(冯.诺依曼架构),面向数据,面向智能体(拥有自主功能的个体,类似于细胞,生物器官等等)、功能芯片、智能终端比如手机、手表等。• 前嗅ForeServer服务器:不基于国外开源框架,完全百分百自主的服务器。自带多机并行解决方案,免配置免安装。• 前嗅ForeLib数据库:NoSQL+SQL,支持多机大规模并行存储,支持矩阵运算、数据映射。原创千亿级数据库,大规模无需hadoop。前嗅人工智能语言系统是基于自主开发KSP(知识服务平台语言, Knowledge Service of Platform)语言与数据库相结合的国产原创技术,面向海量数据和智能体(芯片),是未来开发人工大脑及智能系统的基础,也是未来自然人和机器人交流的基本媒介。前嗅人工智能语言系统完全自主的知识产权,拥有最基本的性能安全、自主可控优势,避免漏洞的产生。未来3-5年,前嗅人工智能语言系统将通过自主开发KSP语言与数据库相结合、爬虫、采集、数据挖掘等技术面向所有智能体,应用在所有场景中,面向机器人。就像大脑融入血液,完成对所有器官的管理、控制。毋庸置疑,如今的国产原创技术——前嗅人工智能语言系统青春永驻,成为下一代IT技术的缔造者指日可待! 前嗅如何在短短几年内就打造了如此成熟的人工智能语言系统,你想知道吗?欢迎评论区留言。

May 10, 2019 · 1 min · jiezi

蚂蚁金服开源的机器学习工具-SQLFlow有何特别之处

阿里妹导读:近日,蚂蚁金服副 CTO 胡喜正式宣布开源机器学习工具 SQLFlow,他在大会演讲中表示:“未来三年,AI 能力会成为每一位技术人员的基本能力。我们希望通过开源 SQLFlow,降低人工智能应用的技术门槛,让技术人员调用 AI 像 SQL 一样简单。” SQLFlow 能够抽象出端到端从数据到模型的研发过程,配合底层的引擎及自动优化,具备基础 SQL 知识的技术人员即可完成大部分的机器学习模型训练及预测任务。SQLFlow 由何而来?蚂蚁金服对于 SQLFlow 未来还有哪些规划?一起来深入了解。 SQLFlow 的目标是将 SQL 引擎和 AI 引擎连接起来,让用户仅需几行 SQL 代码就能描述整个应用或者产品背后的数据流和 AI 构造。其中所涉及的 SQL 引擎包括 MySQL、Oracle、Hive、SparkSQL、Flink 等支持用 SQL 或其某个变种语言描述数据,以及描述对数据的操作的系统。而这里所指的 AI 引擎包括 TensorFlow、PyTorch 等深度学习系统,也包括 XGBoost、LibLinear、LibSVM 等传统机器学习系统。 SQLFlow 研发团队认为,在 SQLFlow 和 AI 引擎之间存在一个很大的空隙——如何把数据变成 AI 模型需要的输入。谷歌开源的 TensorFlow 项目开了一个好头,TFX Data Transform 和 feature column API 都是意图填补这个空缺的项目。但是这个空缺很大,是各种 SQL 引擎和各种 AI 引擎的笛卡尔积,远不是 TensorFlow 的这两个子项目就足以填补的,需要一个开源社区才行。要填补好这个空缺,需要先让用户意识到其重要性,这也是蚂蚁金服开源 SQLFlow 的意图之一。 SQLFlow 位于 AI 软件系统生态的最顶端,最接近用户,它也位于数据和数据流软件生态之上。 ...

May 9, 2019 · 3 min · jiezi

初学C选择哪个编译器比较合适为什么

C/C++开发环境,下面说说自己的一些看法,将日常开发中身边人经常使用的环境罗列出来,如果你有不同意见,欢迎留言讨论。最后,如果大家如果在自学遇到困难,想找一个C++的学习环境,可以加入我们的C++学习圈,点击我加入吧,会节约很多时间,减少很多在学习中遇到的难题。Windows平台: 1)VisualStudio系列 宇宙第一IDE,不是吹出来的,配合“番茄插件(Visual Assist)”,写起来爽到爆,谁用谁知道。VisualStudio2013及之后的版本对C++ 11,17等新标准也支持比较好,对于VC6这种老古董,还是早点扔掉吧,千万别用VC6,千万别用VC6,千万别用VC6,重要的事情说三遍。 2)CodeBlocks 当然,和VS相比,肯定是不在同一个重量级上,不过这家伙最大的好处就在于其轻巧方便,安装包也不算很大。对于性能较差的电脑也许是一个不错的选择,而且也支持C++ 11标准,自带智能提示,对于新手学习,完全足矣。而且跨平台支持,完全免费,不用你再去百度各种注册码。 Linux平台: 1)gcc/g++、vim 不懂makefile的C/C++程序猿不是合格的工程师。会不会写Makefile,也许真的是衡量一个人水平的真正标准了,学会了它,你不再是windows下的那个只会点点按钮来完成编译,链接的人了。当然Vim也可以配置的和IDE一般强大的,这需要你有足够强的耐心,下面是一张我在Windows下自己配置使用的gvim截图。如有需要vim配置文件,可私信或楼下留言(PS:已经将Vim的杀手锏插件YouCompleteMe集成进去)。 2)JetBrains CLion JetBrains CLion 是一个收费的、强大的跨平台 C/C++ IDE。它是一个完全整合的 C/C++ 程序开发环境,并提供 cmake 项目模型、一个嵌入式终端窗口和一个主要以键盘操作的编码环境。它还提供了一个智能而现代化的编辑器,内置Git支持,VIM插件,C/C++智能提示等等多个神器。 3)Qt Creator 在Linux平台开发,这款IDE也是很常见的吧,尤其对一些做UI开发的coder来说。它用于创建连接设备、用户界面和应用程序。Qt Creator 可以让用户比应用的编码做到更多的创新。可以用来创建移动和桌面应用程序,也可以连接到嵌入式设备。首先明确你想问的是编译器还是编辑器/IDE? 也和你使用的平台有关。 编译器有mingw、gcc和clang等等; 编辑器有vim、sublime text、vs code等; IDE有visual studio、clion等。

May 9, 2019 · 1 min · jiezi

递归和尾递归的运行流程解释

递归和尾递归的运行流程解释递归定义递归(英语:recursion)在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法。[1] 递归式方法可以被用于解决很多的计算机科学问题,因此它是计算机科学中十分重要的一个概念。[2] 绝大多数编程语言支持函数的自调用,在这些语言中函数可以通过调用自身来进行递归。计算理论可以证明递归的作用可以完全取代循环,因此有很多在函数编程语言(如Scheme)中用递归来取代循环的例子。(摘自维基百科) 尾递归定义在计算机学里,尾调用是指一个函数里的最后一个动作是返回一个函数的调用结果的情形,即最后一步新调用的返回值直接被当前函数的返回结果。[1]此时,该尾部调用位置被称为尾位置。尾调用中有一种重要而特殊的情形叫做尾递归。经过适当处理,尾递归形式的函数的运行效率可以被极大地优化。[1]尾调用原则上都可以通过简化函数调用栈的结构而获得性能优化(称为“尾调用消除”),但是优化尾调用是否方便可行取决于运行环境对此类优化的支持程度如何。(摘自维基百科) 前提知识递归我将它分为两个过程,一个我将它称为递归,另一个我将它称为回溯. 递归的函数的运行主要有这两个流程,递归的进入,回溯的退出,这两个过程的分界是以递归出口为分界的.递归的实现形式是使用栈,递归函数的进入(递归)类似与压栈,递归函数的退出(回溯)类似于出栈.递归样例和解释【编程题】幂运算三(递归函数)题目ID:1137【问题描述】 求x^n。【输入形式】一行2个数,第一个整数表示x,第二个大于等于零的整数表示n,二数之间用空格分隔。【输出形式】一行一个整数,表示x的n次方【样例输入】2 3【样例输出】8 【样例说明】2的3次方结果为8【评分标准】5组测试用例,每组2分,共计10分 【测试用例】1)输入:2 3输出:82)输入:3 5输出:243 3)输入:-17 4输出:83521 4)输入:22 0输出:1 5)输入:-1287 0输出:1 //普通递归#include<stdio.h>long my_pow1(long x,int n){ if(n==0) return 1; //递归出口 return x*(my_pow1(x,--n)); //除了调用自身外还乘多了个x,即一般的递归}int main(){ long x; int n; scanf("%ld%d",&x,&n); printf("%ld\n",my_pow1(x,n)); return 0;} 运行图解解释:普通的递归过程是在一个函数中,结果依靠自身的调用来得出,例如求幂运算,pow(2,3)代表求2的3次方,由于pow(2,3)未知,我们可以把它分解成2*pow(2,2),pow(2,2)也未知,又可分解成2*pow(2,1),以此类推,直到pow(2,0)可知(if中定义0时返回1),即pow(2,0)返回值是1.在这个递归过程中,pow函数的建立就是一个个压栈的过程我把它称为函数栈 压栈压入所以函数后,直到最后一个,可以获得最后一个函数的返回值,由这个返回值可以依次推出栈内所有函数的返回值(回溯),即退栈,pow(2,0)返回1,推的pow(2,1)返回2*pow(2,0),即2*1=2,pow(2,2)返回2*pow(2,1),即2*2=4,直到退到栈内最后一个函数pow(2,3),可获得pow(2,3)的返回值为2*pow(2,2)即8; 尾递归样例和解释【编程题】吃糖(尾递归函数) 题目ID:1135【问题描述】小樱是个爱吃糖的女孩, 哥哥送了她n(1<=n<=30)颗糖,怎么吃?一天吃1颗;一天吃2颗。嗯,那就每天吃一颗或两颗吧。1颗糖,肯定只有(1)一种吃法;2颗糖,有(1,1)和(2)两种吃法;3颗糖,有(1,1,1)、(1,2)和(2,1)三种吃法。注 (2,1)表示第一天吃2颗,第二天吃1颗。*你能帮小樱算出,吃完这n颗糖,有多少种吃法吗?请编写一个尾递归函数来解决此问题【输入形式】 【测试用例】1)输入:1输出:result=1 2)输入:4输出:result=5 3)输入:15输出:result=987 4)输入:20输出:result=10946 5)输入:30输出:result=1346269 实际上这道题是一个斐波那契数列的变体,可用尾递归函数解决 //尾递归#include <stdio.h>int ci(int n,int pre,int next){ int sum; if (n==1){ //递归出口(递归和回溯的分界点) return pre; } return ci(n-1,next,pre+next); //除了调用自身外没有其他操作即为尾递归}int main(){ int n; int sum; scanf ("%d",&n); printf ("result=%d",ci(n,1,2)); return 0;}运行图解 ...

May 7, 2019 · 1 min · jiezi

MaskRCNNBenchmarkPytorch版本训练自己的数据以及避坑指南

一、安装 地址:MaskRCNN-Benchmark(Pytorch版本) 首先要阅读官网说明的环境要求,千万不要一股脑直接安装,不然后面程序很有可能会报错!!! PyTorch 1.0 from a nightly release. It will not work with 1.0 nor 1.0.1. Installation instructions can be found in https://pytorch.org/get-start...torchvision from mastercocoapiyacsmatplotlibGCC >= 4.9OpenCV# first, make sure that your conda is setup properly with the right environment# for that, check that `which conda`, `which pip` and `which python` points to the# right path. From a clean conda env, this is what you need to doconda create --name maskrcnn_benchmarkconda activate maskrcnn_benchmark# this installs the right pip and dependencies for the fresh pythonconda install ipython# maskrcnn_benchmark and coco api dependenciespip install ninja yacs cython matplotlib tqdm opencv-python# follow PyTorch installation in https://pytorch.org/get-started/locally/# we give the instructions for CUDA 9.0conda install -c pytorch pytorch-nightly torchvision cudatoolkit=9.0export INSTALL_DIR=$PWD# install pycocotoolscd $INSTALL_DIRgit clone https://github.com/cocodataset/cocoapi.gitcd cocoapi/PythonAPIpython setup.py build_ext install# install apexcd $INSTALL_DIRgit clone https://github.com/NVIDIA/apex.gitcd apexpython setup.py install --cuda_ext --cpp_ext# install PyTorch Detectioncd $INSTALL_DIRgit clone https://github.com/facebookresearch/maskrcnn-benchmark.gitcd maskrcnn-benchmark# the following will install the lib with# symbolic links, so that you can modify# the files if you want and won't need to# re-build itpython setup.py build developunset INSTALL_DIR# or if you are on macOS# MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py build develop一定要按上面的说明一步一步来,千万别省略,不然后面程序很有可能会报错!!! ...

May 6, 2019 · 6 min · jiezi

多线程读写同一变量引发进程崩溃

一.问题概述两个线程,不加锁的情况下,一个线程读,一个线程写;或者两个线程同时写,会导致进程崩溃。如果两个线程同时读,不加锁的情况也不会出现问题。 二.示例代码#include <boost/thread.hpp>#include <vector>using namespace std;using namespace boost;vector<int> g_vec;void test(){ int a = 0; for(int i = 0; i< 100000000; ++ i) { a = g_vec[0]; }}void test1(){ for(int i = 0; i< 100000000; ++ i) { g_vec.push_back(1); }}int main(int artc, char* argv[]){ g_vec.push_back(1); boost::thread thrd1(test); boost::thread thrd2(test1); thrd1.join(); thrd2.join(); return 0;}三.解决问题的方法首先评估一下是否需要全局变量,其次全局变量的操作尽可能在同一线程内,如果一定在多线程访问,在全局变量g_vec的作用域范围内加锁。注:vector类型的g_vec即使声明为局部变量,存在栈上的也只有32个字节,参考vector的实现其内部是使用指针new在堆上的内存。

May 6, 2019 · 1 min · jiezi

关于-C-vector-的两个小-tips

本来这篇文章标题我想起成《关于 vector 的两个小坑》,后来想想,其实也不算是坑,还是自己对原理性的东西理解的没做那么透彻。工作中遇到的很多问题,后来归根到底都是基础不牢靠。 vector 扩容这个问题很经典了,但还是不小心踩到。有一个需求是要对目标元素进行复制,而目标元素集合是保存在 vector 里面,于是简单思考下就有如下代码(大致含义): void Duplidate(vector<Element>* element_list, Element* element) { element_list.push_back(*element);}void Process() { for (auto& package : package_list) { if (IsNeedDuplicate()) { Duplicate(element_list, package->element); } }}看起来好像没什么问题,就是当前的 package 对象是否满足复制的要求,需要的话,就对 package 的成员 origin_element 进行复制。跑 UT 也正常,然后在测试的时候就 coredump 了。看 core 文件就是挂在了复制的时候。这里我一开始就没明白,一个简单的复制为什么会有 coredump。 检查了很久 element 复制的场景,甚至想要专门写一个拷贝构造函数。最后才恍然大悟,origin_element 指针指向的就是 element_list 里面的元素,element_list 是整体流程的数据源,packge 对象是封装的中间处理对象。之前的开发人员为了方便,直接在 package 对象上保存了原始的 element 指针,而这个指针指向的是一个 vector 里的元素。而我新加的逻辑会往原始的 vector 里面再添加元素,那么就有可能导致 vector 扩容,而 vector 扩容会导致整体的复制,从而导致原来指向这些元素的指针都失效了,靠后的 package 对象再去访问 origin_element 就产生了 coredump。 ...

May 5, 2019 · 1 min · jiezi

Windows-进程的一生实验程序模块

May 2, 2019 · 0 min · jiezi

鄙人在Windows命令行下创建多个进程的动态演示

May 2, 2019 · 0 min · jiezi

leetcode数组array标签题目更新中

905. Sort Array By Parity(easy)题目理解:一个数组中全是非负数,现在要我们把偶数放在前面,奇数放在偶数之后 首先想到的做法:声明一个新的vector,扫描A两遍,第一遍插入偶数,第二遍插入奇数,返回新的vector可能优化方向: 扫描一遍不分配新空间于是想到了新的做法:用两个指针标记位置,两个指针分别从前往后和从后往前扫描比较两个指针所指的数据有4种情况: 情况对应操作前奇后偶交换位置,i向后移,j向前移前偶后奇不用交换,i往后移,j往前移前后全是偶数不用交换,i往后移前后全是奇数不用交换,j往前移其实这种做法的本质就是利用两个指针在O(N)时间里达到了和之前扫描两次同样的效果,并且维护了原数组,就不用再分配新的空间了,这也是一种比较常用的做法。在实际代码里我对i,j移动的时机做了一些改动,结果是一样的,但是看起来可能更简单,也可以分成四种情况逐一用if...else if来写。accept代码 class Solution {public: vector<int> sortArrayByParity(vector<int>& A) { int i = 0, j = A.size() - 1; int n = A.size(); while (i < j) { if (A[i] % 2 != 0 && A[j] % 2 != 1) swap(A[i++], A[j--]); if (A[i] % 2 == 0) i++; if (A[j] % 2 == 1) j--; } return A; }}; ...

May 1, 2019 · 1 min · jiezi

leetcode中的vector常见用法

vector介绍我对vector的认识就是C++提供的包装好的数组,即对象的集合,一般来说,刷题过程中普通数组都可以用vector来代替,毕竟vector有很对简单用法并且不用考虑长度问题。因为是基础用法部分,就不深究vector和数组的区别以及vector的特性了,直接进入使用吧 本vector用法说明由于只是针对leetcode刷题所写的常用用法,所以内容可能比较简略,只是vector的部分内容。 声明虽然leetcode上自动包含了所有头文件,但是如果自己编程使用vector要加上头文件 #include <vector>再加上using namespace std;或者using std::vector;创建,复制vector<int> a; //创建一个空的vectorvector<int> a(n); //创建一个含有n个对象的vector, //此时数据已缺省构造产生,比如现在的数组里全是0vector<int> a(n, elem);//创建一个含有n个elem拷贝的vectorvector<int> a1(a2); //复制一个vectorvector<int> a1(begin, end); //这里的begin和end可以是另一个vector的迭代器 //会将[begin,end)范围内的数据复制过来关于最后一个用法有一个小坑可以注意一下,假设a2是一个含有4个int数据的vector,如果你是这样用的:vector<int> a1(a2.begin(), a2.begin()+3),那么a1中实际有3个对象;但如果你是这样用的:vector<int> a1(a2.begin(), a2.end()),那么a1中实际有4个对象。这是因为a2.end()实际上指向的并不是vector最后一个对象,而是最后一个对象的下一个位置。 一般来说,leetcode刷题过程中都不用手动释放vector内存,所以我也就不写了 访问数据a.begin() 返回指向首个对象的指针,也就是一般所说的迭代器a.end() 返回指向最后一个对象的下一个位置的指针a.begin()+1 返回指向首个对象的下一个对象的指针a.end()-1 返回返回指向最后一个对象的指针a.rbegin() 返回指向最后一个对象的指针,反向迭代器a.rend() 返回指向首个对象的前一个位置的指针,反向迭代器迭代器我个人觉得一开始不用深究,只需要知道他们是指向vector对象的指针即可反向迭代器很容易搞混。。可以不用,如果用的话要记住,rend和end分别在vector的两头 a.front() 返回首个对象的数据,和*a.begin()是一样的a.back() 返回最后一个对象的数据a.at(i) 返回编号i位置处的对象数据,会检查数据是否存在a[i] 返回编号i位置处的对象数据这里建议大家尽量使用a.at(i)来返回数据,因为会检查数据是否存在 插入a.push_back(i); //最简单的插入,直接向vector末尾加入一个数据a.insert(pos,elem); //向pos迭代器指向的对象前插入一个对象,注意是对象前a.insert(pos, n, elem); //向pos迭代器指向的对象前插入n个相同对象a.insert(pos, begin, end); //向pos迭代器指向的对象前插入[begin,end)之间的对象后三个函数都会返回新数据的位置 删除a.clear(); //删除所有对象a.pop_back(); //删除最后一个对象a.erase(pos); //删除pos迭代器对应的对象a.erase(begin, end); //删除[begin,end)之间的对象后两个函数会返回被删除数据的下一个位置 赋值a[1] = 1; //令编号1的对象数据为1a.assign(1, 1); //令a为{1}a.assign(begin,end); //把另一个迭代器[begin,end)中的数据赋值给a要注意第一行和第二行结果是完全不同的,assign函数有点类似复制函数,是对整体的操作 其它常用函数a.size() 返回vector中元素的个数a.empty() 返回vector是否为空,空返回1,不为空返回0a1.swap(a2); //交换a1,a2数据swap(a1, a2); //交换a1,a2数据,同上swap(a1[1], a1[2]); //交换a1的第2个数据和第3个数据 注意,是没有a1[1].swap(a1[2])这种用法的 ...

May 1, 2019 · 1 min · jiezi

求回文数的三种算法的c语言描述

c语言求回文数的三种算法的描述题目描述注意:(这些回文数都没有前导0)1位的回文数有0,1,2,3,4,5,6,7,8,9 共10个;2位的回文数有11,22,33,44,55,66,77,88,99 共9个;* 请问:n位的回文数有多少个?请编写一个递归函数来解决此问题!!! 【输入形式】一行一个正整数,代表多少位【输出形式】一行一个正整数,代表回文诗的个数【样例输入】2【样例输出】9 输入:3输出:90 输入:5输出:900 **输入:10输出:90000** 输入:8输出:9000 输入:1输出:10 思路分析通过for循环读入这个数,通过/和%操作将这个数据逆转,然后再对比逆转后的数字是否和原数字相等 通过for循环读入这个数,每次取头位一个数字和末位一个数字,依次比较这两个数字是否相等,再去掉这两个数字,直到剩下一个数字(位数为奇数)或者剩下两个数字(位数为偶数) . 通过数学关系,直接判断位数,算出这个位数内的回文数个数; 例如:99899可以把它分为两半,取前面一半998,如果是回文数,其后面一半一定是与其相应位置对应,998为3位数字,除第一位(不包含前导0)故与后半对应的位置那个数有9种选择(1-9)外,其他位都与相应的位置有10种选择(0-9),例如第二位和倒数第二位(0-9)所以可以总结出来相同的位数,位数为奇数奇数其回文数有910^(n/2)个,注意n/2是整数,位数为偶数的为910^(n/2-1)个,所以5位数字的的回文数有91010=900个注意位数为1有10个(0-9),需要特殊处理代码描述第一种思路:#include <stdio.h>#include <math.h>int reverse(long int i,long int *terminate) //递归函数求数值的逆序{ if (i<=0){ //递归出口 return 1; } else{ *terminate*=10; //每次乘10升位数 *terminate+=i%10; //加上个位 reverse(i/10,terminate); //递归每次规模缩小 } return 1;}int main (){ int n; scanf ("%d",&n); //读入一个n,表示n位整数 long int i; int count=0; if (n==1){ //如果等于1,则有10个(0-9都是),特殊处理; printf ("10"); return 0; } for (i=pow(10,n-1);i<pow(10,n);i++){ //从第一个n位数开始(10^(n-1)),到(10^n)-1 long int terminate=0; //定义一个逆序目标数 reverse(i,&terminate); //把i和逆序目标数传入 if (terminate==i){ //逆序后还和原数相等,则可计数 count++; } } printf ("%d",count); //输出个数 return 0;}第二种思路:#include <stdio.h>#include <math.h>int judge(int i,int n){ int first,last; if (n<=1){ //规模减小,直到n为1(偶数)或者0 return 1; } else{ first=i/pow(10,n-1); //头位数字 last=i%10; //末位数字 if (first!=last){ //头位末尾不一样直接退出 return 0; } int tem=pow(10,n-1); judge(i%tem/10,n-2); //剔除头尾剩下中间,位数减二 }}int main (){ int n; scanf("%d",&n); if (1==n){ printf ("10"); return 0; } int i; int count=0; long long low=pow(10,n-1); //循环入口 long long high=pow(10,n); //循环出口 for (i=low;i<high;i++){ if ( judge(i,n)==1){ //判断i是否为回文,计数 count++; } } printf ("%d",count); return 0;}第三种思路:#include <stdio.h>#include <math.h>int main (){ int n; scanf ("%d",&n); int ji=9*pow(10,n/2),ou=9*pow(10,n/2-1); if (n==1){ printf ("10"); } else if (n==2){ printf ("%d",9); } else if (n%2==1){ printf ("%d",ji); } else if (n%2==0){ printf("%d",ou); } return 0;}额外疑问第一第二种方法当n=10的时候运算不出来,求解为何如此,是时间复杂度太高了吗?还是爆int了或者爆递归了?

April 30, 2019 · 1 min · jiezi