前段时间一直在学习内核监控进程创建的知识,虽然成功监视,但一直在内核输出到 DebugView 中,不能通知我们的应用程序来显示指定内容,无论如何也不方便,所以赶在周末参考了 Windows 内核安全与驱动开发
中第五章 “应用与内核通讯” 制作了以下程序。程序主要使用了内核事件 KEVENT
实现同步,更多请参考 Windows 内核安全与驱动开发
,程序运行后的效果如下:
完成端口实现高性能服务端通信层的关键问题
在网络上翻阅过的一份资料,所谈之处皆是痛点,如因处理大量请求可能会导致内存不足的情况、多线程情况下数据包排序紊乱等问题,非常值得参考和学习,其中引入的其他文献有微软官方和非官方文章,附带有非常可靠的代码提供学习,此文非常值得记录。特此分享!
ASN.1 编码基础数据类型
这几天开始做项目了,涉及到项目传输时的报文设计,在C/S架构的项目设计中,传递数据一定要有一定的格式,这样服务端和客户端才能区分开来。除了格式以外还要考虑到传递的数据如果是指针怎么办?如果是NULL怎么办?等等问题,这些问题其实有很多中解决方案,本文就介绍一种 ASN.1 编码格式,当然本文没办法大篇幅的介绍 ASN.1 编码的格式、好处等等内容,网络上的资料有很多,本文主要是记录代码上如何实现对基础数据类型的编码,以备以后忘记了具体细节时回来查看。
Qt 中的数据结构 QVector、QList 使用
与 stl 和 boost 库中的链表差不多,都有我们常见的属性,如at、push_back、push_front、erase 等等,操作与我们以前学习过的都大相径庭,所以我只贴出代码,有更多需要可以参考 Qt 帮助文档。下面只是 QList 的代码,其实你把里面 QList 的关键字替换成 QVector 就可以切换成数组形式了。这是 Qt 对为了让大家更方便的使用,所以让接口风格装都保持一致性,只不过在遍历时,vector使用at方法要比遍历链表速度快。
模版类实现线性表链式储存
同上一篇文章,我们一样是把以前使用C语言实现的单向链表用模版实现了一次,进一步让我们对模版和C++的封装特性有了了解。对于链表的操作我们不过多介绍了,如果有还不清楚操作的,请看以前介绍链表的文章。
队列(queue)的概念及常见应用
队列是一种先进先出的数据模型,它的应用场景比较常见,比如日常生活中我们打10086的电话需要排队时、吃饭排号时的小票、Windows GUI程序的消息队列等等都是基于队列实现的。先进入队列的也是先从队列出去的。他的模型如下:
计算机是如何基于后缀表达式计算的
前一篇文章我们讨论了计算机是如何将中缀表达式转换为后缀表达式的,那么转换后到底计算机是如何计算的呢?本文就来讨论这个主要话题。我们首先来看一下其计算的规则:
栈的应用中缀转后缀表达式
后缀表达式,由波兰科学家在20世纪50年代提出,将运算符放在数字后面,更便于计算机去计算,而我们平常看到的 1 + 2、5 * 10 等,都是中缀表达式,这种方式,符合人类的思考方式。举几个例子:
栈应用代码检测就近匹配
你在使用编辑器写代码的时候是否思考过这个问题:如果少写了一个大括号或中括号,编辑器就会提示错误,这种做法是怎么做到的呢?
其实这个检测就可以通过栈模型来实现,括号的数量总是匹配出现的,并且都是与最近的一个匹配。我们可以编写代码来实现这个检测的功能。具体实现思路如下:
从第一个字符开始扫描, 当遇见普通字符时忽略, 当遇见左符号时压入栈中 当遇见右符号时从栈中弹出栈顶符号,并进行匹配. 匹配成功:继续读入下一个字符 匹配失败:立即停止,并报错 结束. ------成功: 所有字符扫描完毕,且栈为空 ------失败:匹配失败或所有字符扫描完毕但栈非空
【实现代码】
以下代码需要用到栈模型链式存储的 LinkStack.h 和 LinkStack.c 头文件:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include "LinkStack.h" /***************** 算法思路 ***************** 从第一个字符开始扫描, 当遇见普通字符时忽略, 当遇见左符号时压入栈中 当遇见右符号时从栈中弹出栈顶符号,并进行匹配. 匹配成功:继续读入下一个字符 匹配失败:立即停止,并报错 结束. ------成功: 所有字符扫描完毕,且栈为空 ------失败:匹配失败或所有字符扫描完毕但栈非空*/ int match(char left, char right) { int ret = 0; switch (left) { case '<': //左尖括号 ret = (right == '>'); break; case '(': //左小括号 ret = (right == ')'); break; case '[': //左中括号 ret = (right == ']'); break; case '{': //左大括号 ret = (right == '}'); break; case '\'': //左单引号 ret = (right == '\''); break; case '\"': //左双引号 ret = (right == '\"'); break; default: ret = 0; break; } //匹配成功返回1,不成功返回0 return ret; } int isRight(char right) { int ret = 0; switch (right) { case '>': //右尖括号 case ')': //右小括号 case ']': //右中括号 case '}': //右大括号 case '\'': //右单引号 case '\"': //右双引号 ret = 1; //是需要检测的符号返回1 break; default: ret = 0; //不是需要检测的符号返回0 break; } return ret; } int isLeft(char left) { int ret = 0; switch (left) { case '<': //左尖括号 case '(': //左小括号 case '[': //左中括号 case '{': //左大括号 case '\'': //左单引号 case '\"': //左双引号 ret = 1; //是需要检测的符号返回1 break; default: ret = 0; //不是需要检测的符号返回0 break; } return ret; } int read(const char* code) { int i = 0; LinkStack* stack = LinkStack_Create(); while (code[i]) { // 判断是否是左符号 if (isLeft(code[i])) { // 是的话就压如栈中 printf("push = %c\n", code[i]); LinkStack_Push(stack, (void*)&code[i]); //continue; } // 判断是否是右符号 if (isRight(code[i])) { // 如果是则取出栈顶的符号与这个右符号对比 char left = *(char*)LinkStack_Top(stack); if (match(left, code[i])) { // 匹配成功,从栈中弹出匹配过的左符号 printf("pop = %c\n", code[i]); LinkStack_Pop(stack); } else { // 匹配失败直接报错并终止循环 printf("数据异常,匹配失败! left = %c, right = %c\n", left, code[i]); break; } } i++; } // 最后判断栈中是否还有数据,如果还有证明缺少右符号 if (!LinkStack_Size(stack)) { printf("匹配成功!\n"); } else { char ch = *(char*)LinkStack_Top(stack); printf("缺少匹配 %c\n", ch); } // 销毁 LinkStack_Destroy(stack); return 0; } int main(int argc, char* argv[]) { const char* code = "#include <stdio.h> int main() { int a[4][4]; int (*p)[4]; p = a[0]; return 0;}"; read(code); return 0; }
Stack 栈模型的链式存储实现
栈模型使用顺序存储的方式就相当于在数组上进行操作,而本文介绍的则是通过链式存储来实现栈的模型,那么我们就要思考一个问题了。栈只是栈顶来做插入和删除操作,栈顶放在链表的头部还是尾部呢?
Stack 栈模型的顺序存储实现
栈(Stack)也是数据存储的一种方式,我们可以将其理解为一种线性的表,只不过他是前去后继的关系,他只能在线性表的尾部插入和取出数据,这个尾部所指的就是栈的栈顶,而最先被存入的数据则是栈底。它具有后进先出、先进后出的特性。表示图如下:
循环链表解决约瑟夫问题
循环链表的存在很难想象他的应用范围到底是哪里,本文主要介绍的是通过循环链表处理解决约瑟夫问题,让大家更深刻的理解循环链表的使用和应用场景。
循环链表的增删改查
循环链表与单向链表十分相似,两者唯一不同之处就是,循环链表的尾节点的next属性指向了链表的首节点(非头节点,头节点是没有数据的,头节点的下一个有数据的节点我们称为首节点)。他的表现形式有常见的两种,如下图: