2020/11/4更

没想到这么多人点赞收藏的,继续分享一个,编译期解析执行BrainFuck代码,生成"Hello World!"字元串并列印出来:

怎么使C++用最复杂的方法打hello world??

www.zhihu.com图标

-----------原文-------------

编译时快速排序。

template &
struct TypeList {
using type = TypeList&;

template &
using append = TypeList&;

template & typename T&>
using exportTo = T&;
};

template& typename P,
typename S = TypeList&,
typename R = TypeList&&>
struct Partition {
struct type {
using satisfied = S;
using rest = R;
};
};

template& class P&>
using Partition_t = typename Partition&::type;

template& typename P, typename S, typename R&>
struct Partition&, P, S, R&>: std::conditional_t&

::value,
Partition&, P, typename S::template append&, R&>,
Partition&, P, S, typename R::template append&&>&> {};

template&
using Concat_t = IN2::template exportTo&;

template& class CMP&>
struct QuickSort: TypeList& {};

template& class CMP&>
using QuickSort_t = typename QuickSort&::type;

template& class CMP, typename H, typename ...Ts&>
class QuickSort&, CMP&> {
template& using LT = CMP&;
using P = Partition_t&, LT&>;
using SmallerSorted = QuickSort_t&;
using BiggerSorted = QuickSort_t&;
public:
using type = Concat_t&,
BiggerSorted&>;
};

可以用在编译时图的拓扑排序中,更多细节请见:

https://zhuanlan.zhihu.com/p/185034212?

zhuanlan.zhihu.com图标

更多请关注我的专栏:

魅力C++?

www.zhihu.com图标

纯C语言实现 生成二维码

Update:今晚更新下原理.

二维码图片会被知乎自动识别,上动图。

写这段代码的原因是看到用c语言代码可以形成那些有趣的图? 这个问题,想著怎么才能用有限的代码画出多样的世界呢,画出二维码不就好了吗!

于是打开搜索引擎,从二维码原理学起,耗时三小时完成这个生成QR二维码的程序。很多细节都是一把梭的,成功扫描出HELLO WORLD的那一刻,心里还是很激动的。

有人想学原理的话留个言,我之后更新一下。

===== 分割线 ,以下为原理

评论区有很多小伙伴要求补充原理,我就在这里简单讲一下吧,由于网上类似的文章比较多,但是大都是转载或者翻译官方文档,并没有指出真正实现应该怎么做,所以下文著重讲生成最简单的QR码需要具备的知识以及如何真正用代码实现。

实现整个代码的过程可以分为,准备数据码,准备纠错码,输出二维码三个部分,下面会通过"HELLO WORLD"这个例子展开讲解每个部分。

0.准备数据码

0.1.确定纠错等级

纠错等级分为 L, M, Q, H,越高的纠错等级拥有更强的恢复数据的能力,更高级别的错误校正需要更多的位元组,因此更高级别的错误校正将必须具有更大的QR码。 这里我们选择M纠错级别。

0.2.确定数据最小版本

不同大小的QR码称为版本。二维码目前有四十个版本。最小的版本是版本1,大小为21像素x 21像素。版本2为25像素乘25像素。最大的版本是40版,大小为177 x 177像素。每个版本比以前的版本大4个像素。每个版本具有最大容量,具体取决于使用的模式。同时对于纠错码的选择也会对版本的最大容量有影响,具体的官方给出了一张表可以查看,容量表。在我是实现代码时,目标是简单实现,所以选择了最小的21x21版本。

0.3.模式指示器

模式指示器就是说使用什么编码形式,编码形式主要有数值模式、字母数字模式、位元组模式、汉字模式、ECI模式。我为了扫描出HELLO WORLD,选择了比较简单的字母数字模式,关于模式与对应的编码可以参考下表。

模式名称 模式编码
数值模式 0001
字母数字模式 0010
位元组模式 0100
汉字模式 1000
ECI模式 0111

所以这里我们的模式编码为0010,记住这个数字,这是最终二维码黑白块的一部分~

0.4.字元长度指示器

这一步是展示你的字元是多长的,对于不同的版本需要把字元长度转成不同的二进位编码长度,可以参考下面这张表。

版本模式 数值模式 字母数字模式 位元组模式 日文模式
版本1 - 9 10位 9位 8位 8位
版本10 - 26 12位 11位 16位 10位
版本27-40 14位 13位 16位 12位

上文提到我们使用的是字母数字模式,"HELLO WORLD" 的长度为11,转成二进位为1011,我们需要补充位数到九位,得到000001011

把上一步得到的模式指示器编码0010与字元长度指示器编码接到一起,得到0010 000001011。

0.5.字元编码

上文中提到我们使用的是字母数字编码模式,于是我们参考字母数字编码规范进行字元编码,首先根据字元索引表查到每个字元对应的索引值,将整个字元串从左到右两两分组,每组中第一个字元索引值乘以 45 加上第二个字元索引值,将结果转化为 11 位的二进位数,不足 11 位在左侧补 0 以达到长度。如果字元串长度为奇数,将最后的单身狗字元转换成6位二进位。

最终,"HELLO WORLD"可以得到以下字元编码值:

HE -&> 17*45 + 14 -&> 779 -&> 01100001011

LL -&> 01111000110

O(空格) -&>10001011100

WO -&> 10110111000

RL -&> 10011010100

D -&> 001101

将以上内容拼接起来,我们得到61位字元编码,根据QR二维码规范,版本1-M的二维码需要128位的数据编码,目前我们已经有 4 位编码指示符(0010),9 位字元计数符(000001011) ,和 61 位的字元编码,共计74位,还差54位。根据规则,当长度不达到128位时,我们需要做如下流程。

如果相差位数大于等于4位,那么增添一个4位的终止符, 0000,我们距离128位还差54位,显然是大于4的,于是加上4位终止符,长度来到78位。

如果增添终止符之后仍然达不到128位,那么要先将整个数据编码补充0,直到长度为8的倍数,于是我们加上 00,长度来到80位。

如果当前长度仍然达不到128位,则在整个字元串之后交替添加1110110000010001直到字元串长度达到128位,我们依次添加上述字元串,得到最终的数据编码:

00100000 01011011 00001011 01111000 11010001 01110010 11011100 01001101 01000011 01000000 11101100 00010001 11101100 00010001 11101100 00010001

截止到目前,相信各位只要跟著一步步算下来,应该都是可以理解的,不理解的可以评论区评论下我的置顶评论,我会一一回复,上述的流程编码起来也就是一些字元串处理,相信各位都可以随便写完,不妨试一试,看看能不能得到一样的数据码。

1.准备纠错码

我们目前已经拿到了128位数据码,还差一步就可以开始画图了,就是纠错码。纠错码生成的步骤是比较繁琐的,查阅比较多的中文博客大部分都是跳过的...这里我会展开讲一下版本1-M纠错码的生成细节,如果各位觉得繁琐可以直接借鉴我的GetErrorCode函数。

1.0 前置知识

多项式除法

Galois Field

用log及反log的乘法运算

(先上班了,晚上接著更~)

===== 分割线 , 以下为代码

#include &
#include &
#include &
#include &

#define QR_SIZE 21
#define PRINT_SIZE 31
#define GF_SIZE 256
#define DATACODE_SIZE 129
#define DEBUG 0

int mat[QR_SIZE][QR_SIZE];
int vis[QR_SIZE][QR_SIZE];
int output[PRINT_SIZE][PRINT_SIZE];
int GF256[GF_SIZE], RGF256[GF_SIZE];

int posMat[9][9];
int errorCodeNum[12];

char dataCode[DATACODE_SIZE];
char errorCodeChar[DATACODE_SIZE];
const char blue[] = "101010000010010";
const char dict[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
const int codePoly[] = {0, 251, 67, 46, 61, 118, 70, 64, 94, 32, 45};
const int bluePos[2][15][2] = {
8, 0, 8, 1, 8, 2, 8, 3, 8, 4, 8, 5, 8, 7, 8, 8, 7, 8, 5, 8, 4, 8, 3, 8, 2, 8, 1, 8, 0, 8,
20, 8, 19, 8, 18, 8, 17, 8, 16, 8, 15, 8, 14, 8, 8, 13, 8, 14, 8, 15, 8, 16, 8, 17, 8, 18, 8, 19, 8, 20
};

void PrepareDingWei() {
for (int i = 1; i &< 8; i++) { for (int j = 1; j &< 8; j++) { if (i == 1 || j == 1 || i == 7 || j == 7) { posMat[i][j] = 1; } } } for (int i = 3; i &<= 5; i++) { for (int j = 3; j &<= 5; j++) { posMat[i][j] = 1; } } } void DrawDingWei(int x1, int y1, int x2, int y2) { for (int i = x1, i2 = x2; i &< 9; i++, i2++) { if (i2 &>= QR_SIZE) continue;
for (int j = y1, j2 = y2; j &< 9; j++, j2++) { if (j2 &>= QR_SIZE) continue;
mat[i2][j2] = posMat[i][j];
vis[i2][j2] = 1;
}
}
}

// 最终的列印,显示构造二维码矩阵,然后用一个大的矩阵包住小矩阵
void Print() {
int sum = 0;
for (int i = 0; i &< QR_SIZE; i++) { for (int j = 0; j &< QR_SIZE; j++) { if (vis[i][j]) { sum++; } } } int id = 0; int x = QR_SIZE - 1, y = QR_SIZE - 1, flag = 1; for (int j = QR_SIZE - 1; j &>= 0; j--) {
int pos = 0;
if (flag == 1) {
for (;;) {
mat[x][y] = dataCode[id++] - 0;
if (pos == 0) {
y--;
} else {
if (x == 0) {
y--;
flag = 0;
break;
} else if (vis[x - 1][y + 1]) {
if (!vis[x - 2][y + 1]) {
x -= 2;
y += 1;
} else {
y--;
flag = 0;
break;
}
} else {
x--;
y++;
}
}
pos = 1 - pos;
}
} else {
for (;;) {
mat[x][y] = dataCode[id++] - 0;
if (pos == 0) {
y--;
} else {
if (x == QR_SIZE - 1) {
if (y == 9) {
x = 12;
y--;
flag = 1;
break;
} else {
y--;
flag = 1;
break;
}
} else if (vis[x + 1][y + 1]) {
if (!vis[x + 2][y + 1]) {
x += 2;
y += 1;
} else {
flag = 1;
break;
}
} else {
x += 1;
y += 1;
}
}
pos = 1 - pos;
}
}
if (j == 8) j -= 2, y--;
else j--;
}
for (int i = 0; i &< QR_SIZE; i++) { for (int j = 0; j &< QR_SIZE; j++) { if (!vis[i][j] (i + j) % 2 == 0) { mat[i][j] = 1 - mat[i][j]; } } } char black = ; char white = 219; for (int i = 0; i &< PRINT_SIZE; i++) { for (int j = 0; j &< PRINT_SIZE; j++) { output[i][j] = 0; } } for (int i = 0; i &< QR_SIZE; i++) { for (int j = 0; j &< QR_SIZE; j++) { output[i + 5][j + 5] = mat[i][j]; } } for (int i = 0; i &< PRINT_SIZE; i++) { for (int j = 0; j &< PRINT_SIZE; j++) { if (output[i][j]) { printf("%c%c", black, black); } else { printf("%c%c", white, white); } } printf(" "); } } // 画出二维码中固定的部分,例如定位符和掩码 void DrawBackground() { // 准备定位符的一份矩阵,然后直接copy三份到左上,右上,右下 PrepareDingWei(); DrawDingWei(1, 1, 0, 0); DrawDingWei(1, 0, 0, 13); DrawDingWei(0, 1, 13, 0); // 开始画背景中的掩码 { for (int i = 0; i &< 2; i++) { for (int j = 0; j &< 15; j++) { vis[bluePos[i][j][0]][bluePos[i][j][1]] = 1; mat[bluePos[i][j][0]][bluePos[i][j][1]] = blue[j] - 0; } } for (int i = 8; i &<= 12; i++) { mat[6][i] = !((i + 6) 1); vis[6][i] = 1; mat[i][6] = !((i + 6) 1); vis[i][6] = 1; } } } // 查找字元c所对应的权值 int FindIndex(char c) { int len = strlen(dict); for (int i = 0; i &< len; i++) { if (dict[i] == c) { return i; } } return -1; } // 得到数值num所对应的len长度的二进位编码 char *GetLenBinString(int num, int len) { char *rtn = (char *) malloc(len); rtn[len] = ; int idx = len - 1; while (idx &>= 0) {
rtn[idx] = (num % 2) + 0;
num /= 2;
idx--;
}
return rtn;
}

// 得到数据的数据码 数据码 + 补码
void GetDataCode(char *text) {
int len = strlen(text);
char headCode[14] = "0010";
char *textLen = GetLenBinString(strlen(text), 9);
strcat(headCode, textLen);
strcat(dataCode, headCode);
for (int i = 0; i &< len - 1; i += 2) { char *result = GetLenBinString(FindIndex(text[i]) * 45 + FindIndex(text[i + 1]), 11); strcat(dataCode, result); free(result); } if (len 1) { char *result = GetLenBinString(FindIndex(text[len - 1]), 6); strcat(dataCode, result); free(result); } if (DATACODE_SIZE - strlen(dataCode) &>= 4) {
strcat(dataCode, "0000");
}
int currentLen = strlen(dataCode);
while (currentLen % 8 != 0) {
strcat(dataCode, "0");
currentLen++;
}
char appendCode1[9] = "11101100";
char appendCode2[9] = "00010001";
while (currentLen &< 128) { strcat(dataCode, appendCode1); currentLen += 8; if (currentLen &< 128) { strcat(dataCode, appendCode2); currentLen += 8; } } } void PrepareGF256() { GF256[0] = 1; RGF256[1] = 0; for (int i = 1; i &< GF_SIZE; i++) { GF256[i] = GF256[i - 1] * 2; if (GF256[i] &> 255) {
GF256[i] = (GF256[i] ^ 285);
}
RGF256[GF256[i]] = i;
}
}

// Reed-Solomon纠错码 「HELLO WORLD」 -&> 196 35 39 119 235 215 231 226 93 23
// 演算法细节参考: https://www.thonky.com/qr-code-tutorial/error-correction-coding
void GetErrorCode() {
int infoPolyCoefficient[30];
for (int i = 0; i &<= 25; i++) { infoPolyCoefficient[i] = 0; } for (int i = 0; i &< strlen(dataCode); i += 8) { int num = 0; for (int j = i; j &< i + 8; j++) { num = num * 2 + (int) (dataCode[j] - 0); } infoPolyCoefficient[i / 8] = num; } int GenPolyCoefficient[15]; for (int i = 0; i &<= 10; i++) { GenPolyCoefficient[i] = codePoly[i]; } for (int i = 0; i &< 16; i++) { int mul = RGF256[infoPolyCoefficient[i]]; for (int j = i; j &<= i + 10; j++) { infoPolyCoefficient[j] = (infoPolyCoefficient[j] ^ GF256[(GenPolyCoefficient[j - i] + mul) % 255]); } } for (int j = 0; j &< 10; j++) { errorCodeNum[j] = infoPolyCoefficient[j + 16]; } if (DEBUG) { for (int i = 0; i &< 10; i ++) { printf("%d ",errorCodeNum[i]); } printf(" "); } for (int i = 0; i &< 10; i++) { char *result = GetLenBinString(errorCodeNum[i], 8); strcat(errorCodeChar, result); free(result); } } int main(int argc, char **argv) { SetConsoleOutputCP(437); char *text = argv[1]; DrawBackground(); GetDataCode(text); PrepareGF256(); GetErrorCode(); strcat(dataCode, errorCodeChar); if (DEBUG) { printf("ErrorCode : %s len : %llu ",dataCode, strlen(errorCodeChar)); printf("DataCode : %s len : %llu ",dataCode, strlen(dataCode)); } Print(); return 0; }

我是biubiubiu,一只认真写回答的程序猿!


当初研究状态机的时候用一堆模板什么的堆了个 XML 解析器出来,真佩服自己当初能把状态理清楚调通,现在估计再也写不出这种东西了,调模板编程就不是人应该干的事。

https://github.com/jadedrip/lugce/blob/master/lugce/xml/simple_sax.hpp?

github.com

还有个东西是我当初比较得意的,用了一些模板元的技巧,搞出的 C++ 事件机制,还特地写了篇博客:

CSDN?

mp.csdn.net

代码在这里:

https://github.com/jadedrip/lugce/blob/master/lugce/observer.hpp?

github.com


捂脸,没错,就是下面这行。

printf("古时的风筝 JDK
");

这是在学了 JVM 好长时间之后在 JDK 11 启动方法上加的一行代码。别小看这行代码,此行代码的作用是向全世界宣告这个 JDK 是属于我自己一个人的版本。

完整代码是加在这里的:

int JNICALL
JavaMain(void * _args)
{
JavaMainArgs *args = (JavaMainArgs *)_args;
printf("古时的风筝 JDK
");
int argc = args-&>argc;
char **argv = args-&>argv;
int mode = args-&>mode;
char *what = args-&>what;
InvocationFunctions ifn = args-&>ifn;

JavaVM *vm = 0;
JNIEnv *env = 0;
jclass mainClass = NULL;
jclass appClass = NULL; // actual application class being launched
jmethodID mainID;
/* 省略 */
}

当使用这个 JDK 版本运行程序。

public static void main(String[] args) {
System.out.println("Hello JVM");
}

运行之后的效果是这样的:

作为一个 Java 开发者,虽然自认 JVM 原理都掌握的比较熟练了,但是对 JVM 源码还是敬而远之的,主要是 C++ 完全不在射程范围内。

虽然简单的编译一下源码,改一行代码,翻翻源码并没有什么实际能力的提升,但是心理上会觉得,嗯,我又行了。

补充一下编译 JDK 源码的完整过程:

古时的风筝:做 Java 这么久了,你编译过 JDK 源码吗?

zhuanlan.zhihu.com图标

既然C++也兼容C代码,我就贴一个18年用C给我的操作系统写的键盘驱动吧

#include &
#include &
#include &
#include &
#include &
#include &
#include &
#include &
#include &
#include &
#include &

#define CHECK_TTY(a)
if((a)==NULL){
(a)=tty_current();
}
current_tty=(a);

static struct tty *current_tty=NULL; // Caching the last-used TTY structure
char init=0;

static void kbd_irq(struct registers regs);

struct tty *tty_current(void)
{
return current_tty;
}

void tty_enable_cursor(uint8_t start,uint8_t end)
{
outb(VGA_CMD,0xA);
outb(VGA_DATA,(inb(VGA_DATA) 0xC0) | start);
outb(VGA_CMD,0xB);
outb(VGA_DATA,(inb(VGA_DATA) 0xE0) | end);
}

void tty_disable_cursor(void)
{
outb(VGA_DATA,0xA);
outb(VGA_DATA,0x20);
}

uint16_t tty_get_cursor(void)
{
uint16_t ret;
outb(VGA_CMD,0xE);
ret=inb(VGA_DATA) &&> 8) 0xFF);
ptr-&>cursor=offset;
}

size_t tty_write(struct tty *ptty,const char *data,size_t len)
{
size_t i;
for(i=0;i&

相关文章