狐狸の窝

未信有情皆是累,但能无病便为仙。

0%

试图通过混淆 C/C++ 源代码以达到保护知识产权的目的的做法其实就是自欺欺人,因为不论如何混淆代码,到了编译阶段代码终究是要被还原成它本来的样子,说到底,这只是一层窗户纸而已。

我曾利用 机制实现过一个 C/C++ 代码混淆器,效果乍一看还真能给人一种眼前一亮的神奇,但在 行家 眼里这真就是一层窗户纸而已——捅破它只需一个编译命令 gcc -E 。但若只为了阻挡伸手党白嫖你的代码,嘿嘿,它还是能起到一定的作用滴٩(๑>◡<๑)۶


混淆效果

  • 找一段 C 语言代码测试下混淆效果,下面是原始代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
/********************************************
* 图书信息管理系统
* Copyright (C) i@foxzzz.com
*
* C语言实现的命令行模式下的信息管理系统。
*********************************************/

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <conio.h>

#define BORROW_COUNT_MAX 10 /*最多可借阅的图书数量*/
#define SHOW_BOOK_PAGE_COUNT 20 /*一页显示的图书条目*/

/*用户权限*/
enum UserRank {
NORMAL, /*普通用户*/
MANAGER /*管理员*/
};

/*用户结构体*/
typedef struct _tUserInfo {
char id[128]; /*账号*/
char name[256]; /*姓名*/
char password[256]; /*密码*/
int rank; /*权限*/
struct _tUserInfo* next; /*下一个节点*/
} UserInfo, * pUserInfo;

/*图书结构体*/
typedef struct _tBookInfo {
char id[128]; /*编号*/
char name[256]; /*书名*/
char author[256]; /*作者*/
char press[256]; /*出版社*/
char type[256]; /*类型*/
char time[256]; /*出版时间*/
double price; /*价格*/
int stock; /*库存*/
struct _tBookInfo* next; /*下一个节点*/
} BookInfo, * pBookInfo;

/*借阅记录结构体*/
typedef struct _tRecordInfo {
char user_id[128]; /*借阅人账号*/
char book_id[BORROW_COUNT_MAX][128]; /*图书编号*/
int count; /*借阅数量*/
struct _tRecordInfo* next; /*下一个节点*/
} RecordInfo, * pRecordInfo;

/*清空输入缓冲区*/
void emptyStdin() {
int c;
while ((c = getchar()) != '\n' && c != EOF);
}

/*等待按下任意键*/
void waitingPressAnyKey() {
emptyStdin();
getchar();
}

/*清屏*/
void clearScreen() {
system("cls");
}

/*从标准输入一行文本*/
void inputLine(char* line, int capacity) {
while (1) {
if (fgets(line, capacity, stdin)) {
size_t len = strlen(line);
if (len > 1) {
line[len - 1] = '\0';
break;
}
}
}
}

/*输入密码*/
void inputPassword(char* password, int capacity) {
int index = 0;
while (index < capacity) {
int ch = _getch();
if (ch == '\n' || ch == '\r') {
if (index > 0) {
password[index] = '\0';
putchar('\n');
break;
} else continue;
}
if (ch == '\b') {
if (index > 0) {
--index;
putchar('\b');
putchar(' ');
putchar('\b');
}
} else {
password[index++] = ch;
putchar('*');
}
}
}

/*添加用户节点,返回链表首节点指针*/
pUserInfo addUserInfoNode(pUserInfo head, pUserInfo node) {
if (head) {
pUserInfo cursor = head;
while (cursor->next) {
cursor = cursor->next;
}
/*将新节点插入到链表尾部*/
cursor->next = node;
return head;
} else {
/*链表为空返回该节点*/
return node;
}
}

/*删除用户节点,返回链表首节点指针*/
pUserInfo removeUserInfoNode(pUserInfo head, pUserInfo node) {
if (head) {
if (head == node) {
/*删除节点为首节点*/
head = node->next;
/*删除该节点*/
free(node);
} else {
pUserInfo cursor = head;
while (cursor->next) {
/*找到要删除节点的上一个节点*/
if (cursor->next == node) {
/*将上一个节点指向删除节点的下一个节点*/
cursor->next = node->next;
/*删除该节点*/
free(node);
break;
}
cursor = cursor->next;
}
}
}
return head;
}

/*通过账号查找用户节点*/
pUserInfo findUserInfoNode(pUserInfo head, char* id) {
pUserInfo cursor = head;
while (cursor) {
/*匹配用户*/
if (strcmp(cursor->id, id) == 0) {
return cursor;
}
cursor = cursor->next;
}
return NULL;
}

/*计算用户节点数*/
int countUserInfoNode(pUserInfo head) {
pUserInfo cursor = head;
int count = 0;
while (cursor) {
++count;
cursor = cursor->next;
}
return count;
}

/*添加图书节点,返回链表首节点指针*/
pBookInfo addBookInfoNode(pBookInfo head, pBookInfo node) {
if (head) {
pBookInfo cursor = head;
while (cursor->next) {
cursor = cursor->next;
}
/*将新节点插入到链表尾部*/
cursor->next = node;
return head;
} else {
/*链表为空返回该节点*/
return node;
}
}

/*删除图书节点,返回链表首节点指针*/
pBookInfo removeBookInfoNode(pBookInfo head, pBookInfo node) {
if (head) {
if (head == node) {
/*删除节点为首节点*/
head = node->next;
/*删除该节点*/
free(node);
} else {
pBookInfo cursor = head;
while (cursor->next) {
/*找到要删除节点的上一个节点*/
if (cursor->next == node) {
/*将上一个节点指向删除节点的下一个节点*/
cursor->next = node->next;
/*删除该节点*/
free(node);
break;
}
cursor = cursor->next;
}
}
}
return head;
}

/*通过账号查找图书节点*/
pBookInfo findBookInfoNodeByID(pBookInfo head, char* id) {
pBookInfo cursor = head;
while (cursor) {
/*匹配图书*/
if (strcmp(cursor->id, id) == 0) {
return cursor;
}
cursor = cursor->next;
}
return NULL;
}

/*通过书名查找图书节点*/
pBookInfo findBookInfoNodeByName(pBookInfo head, char* name) {
pBookInfo cursor = head;
while (cursor) {
/*匹配图书*/
if (strcmp(cursor->name, name) == 0) {
return cursor;
}
cursor = cursor->next;
}
return NULL;
}

/*通过作者查找图书节点*/
pBookInfo findBookInfoNodeByAuthor(pBookInfo head, char* author) {
pBookInfo cursor = head;
while (cursor) {
/*匹配图书*/
if (strcmp(cursor->author, author) == 0) {
return cursor;
}
cursor = cursor->next;
}
return NULL;
}

/*通过出版社查找图书节点*/
pBookInfo findBookInfoNodeByPress(pBookInfo head, char* press) {
pBookInfo cursor = head;
while (cursor) {
/*匹配图书*/
if (strcmp(cursor->press, press) == 0) {
return cursor;
}
cursor = cursor->next;
}
return NULL;
}

/*计算图书节点数*/
int countBookInfoNode(pBookInfo head) {
pBookInfo cursor = head;
int count = 0;
while (cursor) {
++count;
cursor = cursor->next;
}
return count;
}

/*添加记录节点,返回链表首节点指针*/
pRecordInfo addRecordInfoNode(pRecordInfo head, pRecordInfo node) {
if (head) {
pRecordInfo cursor = head;
while (cursor->next) {
cursor = cursor->next;
}
/*将新节点插入到链表尾部*/
cursor->next = node;
return head;
} else {
/*链表为空返回该节点*/
return node;
}
}

/*删除记录节点,返回链表首节点指针*/
pRecordInfo removeRecordInfoNode(pRecordInfo head, pRecordInfo node) {
if (head) {
if (head == node) {
/*删除节点为首节点*/
head = node->next;
/*删除该节点*/
free(node);
} else {
pRecordInfo cursor = head;
while (cursor->next) {
/*找到要删除节点的上一个节点*/
if (cursor->next == node) {
/*将上一个节点指向删除节点的下一个节点*/
cursor->next = node->next;
/*删除该节点*/
free(node);
break;
}
cursor = cursor->next;
}
}
}
return head;
}

/*通过账号查找记录节点*/
pRecordInfo findRecordInfoNodeByID(pRecordInfo head, char* id) {
pRecordInfo cursor = head;
while (cursor) {
/*匹配用户*/
if (strcmp(cursor->user_id, id) == 0) {
return cursor;
}
cursor = cursor->next;
}
return NULL;
}

/*计算记录节点数*/
int countRecordInfoNode(pRecordInfo head) {
pRecordInfo cursor = head;
int count = 0;
while (cursor) {
++count;
cursor = cursor->next;
}
return count;
}

/*将用户信息存储到文件*/
void saveUserInfoFile(const pUserInfo head) {
pUserInfo cursor = head;
FILE* file = fopen("userinfo.dat", "wb");
if (file) {
int count = countUserInfoNode(head);
/*将用户节点总数写入文件起始位置*/
fwrite(&count, sizeof(int), 1, file);
while (cursor) {
fwrite(cursor, sizeof(UserInfo), 1, file);
cursor = cursor->next;
}
fclose(file);
} else {
printf("写文件失败!\n");
}
}

/*从文件中加载用户信息*/
pUserInfo loadUserInfoFile() {
pUserInfo head = NULL;
FILE* file = fopen("userinfo.dat", "rb");
if (file) {
int count = 0;
/*读取文件起始位置的节点总数*/
fread(&count, sizeof(int), 1, file);
while (count--) {
pUserInfo user = (pUserInfo)malloc(sizeof(UserInfo));
memset(user, 0, sizeof(UserInfo));
fread(user, sizeof(UserInfo), 1, file);
/*将指向下个节点的指针重置成NULL*/
user->next = NULL;
head = addUserInfoNode(head, user);
}
fclose(file);
} else {
printf("读文件失败!\n");
}
return head;
}

/*清理用户列表,回收内存*/
void clearUserInfoList(pUserInfo head) {
while (head) {
head = removeUserInfoNode(head, head);
}
}

/*将图书信息存储到文件*/
void saveBookInfoFile(const pBookInfo head) {
pBookInfo cursor = head;
FILE* file = fopen("bookinfo.dat", "wb");
if (file) {
int count = countBookInfoNode(head);
/*将用户节点总数写入文件起始位置*/
fwrite(&count, sizeof(int), 1, file);
while (cursor) {
fwrite(cursor, sizeof(BookInfo), 1, file);
cursor = cursor->next;
}
fclose(file);
} else {
printf("写文件失败!\n");
}
}

/*从文件中加载用户信息*/
pBookInfo loadBookInfoFile() {
pBookInfo head = NULL;
FILE* file = fopen("bookinfo.dat", "rb");
if (file) {
int count = 0;
/*读取文件起始位置的节点总数*/
fread(&count, sizeof(int), 1, file);
while (count--) {
pBookInfo book = (pBookInfo)malloc(sizeof(BookInfo));
memset(book, 0, sizeof(BookInfo));
fread(book, sizeof(BookInfo), 1, file);
/*将指向下个节点的指针重置成NULL*/
book->next = NULL;
head = addBookInfoNode(head, book);
}
fclose(file);
} else {
printf("读文件失败!\n");
}
return head;
}

/*清理图书列表,回收内存*/
void clearBookInfoList(pBookInfo head) {
while (head) {
head = removeBookInfoNode(head, head);
}
}

/*将记录信息存储到文件*/
void saveRecordInfoFile(const pRecordInfo head) {
pRecordInfo cursor = head;
FILE* file = fopen("recordinfo.dat", "wb");
if (file) {
int count = countRecordInfoNode(head);
/*将用户节点总数写入文件起始位置*/
fwrite(&count, sizeof(int), 1, file);
while (cursor) {
fwrite(cursor, sizeof(RecordInfo), 1, file);
cursor = cursor->next;
}
fclose(file);
} else {
printf("写文件失败!\n");
}
}

/*从文件中加载记录信息*/
pRecordInfo loadRecordInfoFile() {
pRecordInfo head = NULL;
FILE* file = fopen("recordinfo.dat", "rb");
if (file) {
int count = 0;
/*读取文件起始位置的节点总数*/
fread(&count, sizeof(int), 1, file);
while (count--) {
pRecordInfo book = (pRecordInfo)malloc(sizeof(RecordInfo));
memset(book, 0, sizeof(RecordInfo));
fread(book, sizeof(RecordInfo), 1, file);
/*将指向下个节点的指针重置成NULL*/
book->next = NULL;
head = addRecordInfoNode(head, book);
}
fclose(file);
} else {
printf("读文件失败!\n");
}
return head;
}

/*清理记录列表,回收内存*/
void clearRecordInfoList(pRecordInfo head) {
while (head) {
head = removeRecordInfoNode(head, head);
}
}

/*显示用户信息*/
void showUser(pUserInfo user) {
printf("┌-------------------------------------------------┐\n");
printf(" 账号:%s\n", user->id);
printf(" 姓名:%s\n", user->name);
printf(" 密码:%s\n", user->password);
switch (user->rank) {
case NORMAL:
printf(" 权限:%s\n", "普通用户");
break;
case MANAGER:
printf(" 权限:%s\n", "管理员");
break;
}
printf("└-------------------------------------------------┘\n");
}

/*编辑用户信息*/
void editUser(pUserInfo user) {
printf("┌-------------------------------------------------┐\n");
printf(" 账号:");
scanf("%s", user->id);
printf(" 姓名:");
inputLine(user->name, sizeof(user->name));
printf(" 密码:");
inputPassword(user->password, sizeof(user->password));
printf(" 权限:(0:普通用户, 1:管理员)");
scanf("%d", &user->rank);
if (user->rank != NORMAL && user->rank != MANAGER) {
user->rank = NORMAL;
}
printf("└-------------------------------------------------┘\n");
}

/*显示图书信息*/
void showBook(pBookInfo book) {
printf("┌-------------------------------------------------┐\n");
printf(" 编号:");
printf("%s\n", book->id);
printf(" 书名:");
printf("%s\n", book->name);
printf(" 作者:");
printf("%s\n", book->author);
printf(" 出版社:");
printf("%s\n", book->press);
printf(" 类型:");
printf("%s\n", book->type);
printf(" 出版时间:");
printf("%s\n", book->time);
printf(" 价格:");
printf("%.2lf\n", book->price);
printf(" 库存:");
printf("%d\n", book->stock);
printf("└-------------------------------------------------┘\n");
}

/*编辑图书信息*/
void editBook(pBookInfo book) {
printf("┌-------------------------------------------------┐\n");
printf(" 编号:");
if (strlen(book->id)) {
printf("%s\n", book->id);
} else {
scanf("%s", book->id);
}
printf(" 书名:");
inputLine(book->name, sizeof(book->name));
printf(" 作者:");
inputLine(book->author, sizeof(book->author));
printf(" 出版社:");
inputLine(book->press, sizeof(book->press));
printf(" 类型:");
scanf("%s", book->type);
printf(" 出版时间:");
scanf("%s", book->time);
printf(" 价格:");
scanf("%lf", &book->price);
printf(" 库存:");
scanf("%d", &book->stock);
printf("└-------------------------------------------------┘\n");
}

/*显示用户清单选项*/
void showUserListOption(pUserInfo head) {
pUserInfo cursor = head;
while (cursor) {
showUser(cursor);
cursor = cursor->next;
}
printf("\n按回车键返回上级菜单...\n");
waitingPressAnyKey();
}

/*添加用户选项*/
void createUserOption(pUserInfo* head) {
pUserInfo user = (pUserInfo)malloc(sizeof(UserInfo));
memset(user, 0U, sizeof(UserInfo));
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #添加用户#\n");
printf(" └------------------------┘\n");
editUser(user);
if (findUserInfoNode(*head, user->id)) {
free(user);
printf("\n用户创建失败,存在相同用户!\n");
} else {
*head = addUserInfoNode(*head, user);
/*同步文件信息*/
saveUserInfoFile(*head);
printf("\n用户创建成功!\n");
}
waitingPressAnyKey();
}

/*修改用户选项*/
void updateUserOption(pUserInfo head, pUserInfo me) {
char id[128] = { 0 };
pUserInfo target = NULL;
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #修改用户#\n");
printf("\n");
printf(" 账号:");
scanf("%s", id);
printf(" └------------------------┘\n");
target = findUserInfoNode(head, id);
if (target) {
showUser(target);
printf("┌-------------------------------------------------┐\n");
printf(" 账号:");
printf("%s\n", target->id);
printf(" 姓名:");
inputLine(target->name, sizeof(target->name));
printf(" 密码:");
inputPassword(target->password, sizeof(target->password));
if (target != me) {
printf(" 权限:(0:普通用户, 1:管理员)");
scanf("%d", &target->rank);
if (target->rank != NORMAL && target->rank != MANAGER) {
target->rank = NORMAL;
}
}
printf("└-------------------------------------------------┘\n");
/*同步文件信息*/
saveUserInfoFile(head);
printf("\n用户修改成功!\n");
} else {
printf("\n未找到该用户!\n");
}
waitingPressAnyKey();
}

/*删除用户选项*/
void removeUserOption(pUserInfo* head, pUserInfo me) {
char id[128] = { 0 };
pUserInfo target = NULL;
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #删除用户#\n");
printf("\n");
printf(" 账号:");
scanf("%s", id);
printf(" └------------------------┘\n");
target = findUserInfoNode(*head, id);
if (target) {
if (target == me) {
printf("\n不允许删除自己!\n");
} else {
showUser(target);
*head = removeUserInfoNode(*head, target);
/*同步文件信息*/
saveUserInfoFile(*head);
printf("\n用户删除成功!\n");
}
} else {
printf("\n未找到该用户!\n");
}
waitingPressAnyKey();
}

/*显示图书清单选项*/
void showBookListOption(pBookInfo head) {
if (head) {
pBookInfo cursor = head;
int page_current = 0;
int page_total = countBookInfoNode(head);
page_total = ((page_total - 1) / SHOW_BOOK_PAGE_COUNT) + 1;
while (cursor) {
int count = 0;
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #图书清单#\n");
printf(" └------------------------┘\n");
printf(" 【%2d/%-2d】\n", ++page_current, page_total);
while (cursor) {
showBook(cursor);
cursor = cursor->next;
if (++count == SHOW_BOOK_PAGE_COUNT) {
break;
}
}
if (cursor) {
printf("\n【 1 下一页 | 0 返回上级菜单】\n");
int option;
scanf("%d", &option);
switch (option) {
case 1:
break;
case 0:
return;
}
}
}
}
printf("\n按回车键返回上级菜单...\n");
waitingPressAnyKey();
}

/*添加图书选项*/
void createBookOption(pBookInfo* head) {
pBookInfo book = (pBookInfo)malloc(sizeof(BookInfo));
memset(book, 0U, sizeof(BookInfo));
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #添加图书#\n");
printf(" └------------------------┘\n");
editBook(book);
if (findBookInfoNodeByID(*head, book->id)) {
free(book);
printf("\n图书添加失败,存在相同图书编号!\n");
} else {
*head = addBookInfoNode(*head, book);
/*同步文件信息*/
saveBookInfoFile(*head);
printf("\n图书添加成功!\n");
}
waitingPressAnyKey();
}

/*修改图书选项*/
void updateBookOption(pBookInfo head) {
char id[128] = { 0 };
pBookInfo target = NULL;
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #修改图书#\n");
printf("\n");
printf(" 编号:");
scanf("%s", id);
printf(" └------------------------┘\n");
target = findBookInfoNodeByID(head, id);
if (target) {
showBook(target);
editBook(target);
/*同步文件信息*/
saveBookInfoFile(head);
printf("\n图书修改成功!\n");
} else {
printf("\n未找到该图书!\n");
}
waitingPressAnyKey();
}

/*删除图书选项*/
void removeBookOption(pBookInfo* head) {
char id[128] = { 0 };
pBookInfo target = NULL;
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #删除图书#\n");
printf("\n");
printf(" 编号:");
scanf("%s", id);
printf(" └------------------------┘\n");
target = findBookInfoNodeByID(*head, id);
if (target) {
showBook(target);
*head = removeBookInfoNode(*head, target);
/*同步文件信息*/
saveBookInfoFile(*head);
printf("\n图书删除成功!\n");
} else {
printf("\n未找到该图书!\n");
}
waitingPressAnyKey();
}

/*按编号查询图书选项*/
void searchBookByIDOption(pBookInfo head) {
char id[128] = { 0 };
pBookInfo target = NULL;
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #按编号查询#\n");
printf("\n");
printf(" 编号:");
scanf("%s", id);
printf(" └------------------------┘\n");
target = findBookInfoNodeByID(head, id);
if (target) {
showBook(target);
} else {
printf("\n未找到该图书!\n");
}
waitingPressAnyKey();
}

/*按书名查询图书选项*/
void searchBookByNameOption(pBookInfo head) {
char name[128] = { 0 };
pBookInfo target = NULL;
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #按书名查询#\n");
printf("\n");
printf(" 书名:");
inputLine(name, sizeof(name));
printf(" └------------------------┘\n");
target = findBookInfoNodeByName(head, name);
if (target) {
do {
showBook(target);
target = findBookInfoNodeByName(target->next, name);
} while (target);
} else {
printf("\n未找到该图书!\n");
}
waitingPressAnyKey();
}

/*按作者查询图书选项*/
void searchBookByAuthorOption(pBookInfo head) {
char author[128] = { 0 };
pBookInfo target = NULL;
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #按作者查询#\n");
printf("\n");
printf(" 作者:");
inputLine(author, sizeof(author));
printf(" └------------------------┘\n");
target = findBookInfoNodeByAuthor(head, author);
if (target) {
do {
showBook(target);
target = findBookInfoNodeByAuthor(target->next, author);
} while (target);
} else {
printf("\n未找到该图书!\n");
}
waitingPressAnyKey();
}

/*按出版社查询图书选项*/
void searchBookByPressOption(pBookInfo head) {
char press[128] = { 0 };
pBookInfo target = NULL;
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #按出版神查询#\n");
printf("\n");
printf(" 出版社:");
inputLine(press, sizeof(press));
printf(" └------------------------┘\n");
target = findBookInfoNodeByPress(head, press);
if (target) {
do {
showBook(target);
target = findBookInfoNodeByPress(target->next, press);
} while (target);
} else {
printf("\n未找到该图书!\n");
}
waitingPressAnyKey();
}

/*添加借书记录*/
pRecordInfo addRecord(pRecordInfo* head, char* user_id, char* book_id) {
pRecordInfo target = findRecordInfoNodeByID(*head, user_id);
if (!target) {
target = (pRecordInfo)malloc(sizeof(RecordInfo));
memset(target, 0, sizeof(RecordInfo));
strcpy(target->user_id, user_id);
*head = addRecordInfoNode(*head, target);
}
if (target->count < BORROW_COUNT_MAX) {
int index;
for (index = 0; index < BORROW_COUNT_MAX; ++index) {
if (strlen(target->book_id[index]) == 0) {
strcpy(target->book_id[index], book_id);
++target->count;
saveRecordInfoFile(*head);
return target;
}
}
}
return NULL;
}

/*删除借书记录*/
pRecordInfo removeRecord(pRecordInfo* head, char* user_id, char* book_id) {
pRecordInfo target = findRecordInfoNodeByID(*head, user_id);
if (target) {
int index;
for (index = 0; index < BORROW_COUNT_MAX; ++index) {
if (strcmp(target->book_id[index], book_id) == 0) {
memset(target->book_id[index], 0, 128);
--target->count;
if (target->count == 0) {
*head = removeRecordInfoNode(*head, target);
}
saveRecordInfoFile(*head);
return target;
}
}
}
return NULL;
}

/*借书*/
void borrowBookOption(pBookInfo bookhead, pRecordInfo* recordhead, pUserInfo me) {
char id[128] = { 0 };
pBookInfo target = NULL;
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #借书#\n");
printf("\n");
printf(" 编号:");
scanf("%s", id);
printf(" └------------------------┘\n");
target = findBookInfoNodeByID(bookhead, id);
if (target) {
showBook(target);
if (target->stock > 0) {
if (addRecord(recordhead, me->id, target->id)) {
/*同步文件信息*/
--target->stock;
saveBookInfoFile(bookhead);
printf("\n操作成功!\n");
} else {
printf("\n您的借阅数量已达上限,请先归还图书!\n");
}
} else {
printf("\n没有库存!\n");
}
} else {
printf("\n未找到该图书!\n");
}
waitingPressAnyKey();
}

/*还书*/
void returnBookOption(pBookInfo bookhead, pRecordInfo* recordhead, pUserInfo me) {
char id[128] = { 0 };
pBookInfo target = NULL;
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #还书#\n");
printf("\n");
printf(" 编号:");
scanf("%s", id);
printf(" └------------------------┘\n");
target = findBookInfoNodeByID(bookhead, id);
if (target) {
showBook(target);
if (removeRecord(recordhead, me->id, target->id)) {
/*同步文件信息*/
++target->stock;
saveBookInfoFile(bookhead);
printf("\n操作成功!\n");
} else {
printf("\n您没有借阅该图书!\n");
}
} else {
printf("\n未找到该图书!\n");
}
waitingPressAnyKey();
}

/*借阅清单*/
void showRecordOption(pBookInfo bookhead, pRecordInfo recordhead, pUserInfo me) {
pRecordInfo target = NULL;
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #借阅清单#\n");
printf("\n");
target = findRecordInfoNodeByID(recordhead, me->id);
printf(" %-10s%-10s\n", "编号", "书名");
if (target) {
int index;
for (index = 0; index < BORROW_COUNT_MAX; ++index) {
char* book_id = target->book_id[index];
pBookInfo book = findBookInfoNodeByID(bookhead, book_id);
if (book) {
printf(" %-10s%-10s\n", book_id, book->name);
}
}
}
printf(" └------------------------┘\n");
waitingPressAnyKey();
}

/*用户设置选项*/
void settingUserOption(pUserInfo head, pUserInfo me) {
clearScreen();
printf(" ┌------------------------┐\n");
printf(" #用户设置#\n");
printf(" └------------------------┘\n");
printf("┌-------------------------------------------------┐\n");
printf(" 账号:");
printf("%s\n", me->id);
printf(" 姓名:");
printf("%s\n", me->name);
printf(" 密码:");
inputPassword(me->password, sizeof(me->password));
printf("└-------------------------------------------------┘\n");
/*同步文件信息*/
saveUserInfoFile(head);
printf("\n用户设置成功!\n");
waitingPressAnyKey();
}

/*用户管理菜单*/
void manageUsersOption(pUserInfo* head, pUserInfo me) {
int option;
while (1) {
clearScreen();
printf(" ┌-------------------------------┐\n");
printf(" #用户管理#\n");
printf("\n");
printf(" 【1】 用户清单\n");
printf(" 【2】 添加用户\n");
printf(" 【3】 修改用户\n");
printf(" 【4】 删除用户\n");
printf(" 【0】 返回\n");
printf("\n");
printf(" └-------------------------------┘\n");
printf("\n");
scanf("%d", &option);
switch (option) {
case 1:
showUserListOption(*head);
break;
case 2:
createUserOption(head);
break;
case 3:
updateUserOption(*head, me);
break;
case 4:
removeUserOption(head, me);
break;
case 0:
return;
}
}
}

/*图书浏览菜单*/
void browseBooksOption(pBookInfo head) {
int option;
while (1) {
clearScreen();
printf(" ┌-------------------------------┐\n");
printf(" #图书浏览#\n");
printf("\n");
printf(" 【1】 图书清单\n");
printf(" 【2】 按编号查询\n");
printf(" 【3】 按书名查询\n");
printf(" 【4】 按作者查询\n");
printf(" 【5】 按出版社查询\n");
printf(" 【0】 返回\n");
printf("\n");
printf(" └-------------------------------┘\n");
printf("\n");
scanf("%d", &option);
switch (option) {
case 1:
showBookListOption(head);
break;
case 2:
searchBookByIDOption(head);
break;
case 3:
searchBookByNameOption(head);
break;
case 4:
searchBookByAuthorOption(head);
break;
case 5:
searchBookByPressOption(head);
break;
case 0:
return;
}
}
}

/*图书管理菜单*/
void manageBooksOption(pBookInfo* head) {
int option;
while (1) {
clearScreen();
printf(" ┌-------------------------------┐\n");
printf(" #图书管理#\n");
printf("\n");
printf(" 【1】 添加图书\n");
printf(" 【2】 修改图书\n");
printf(" 【3】 删除图书\n");
printf(" 【0】 返回\n");
printf("\n");
printf(" └-------------------------------┘\n");
printf("\n");
scanf("%d", &option);
switch (option) {
case 1:
createBookOption(head);
break;
case 2:
updateBookOption(*head);
break;
case 3:
removeBookOption(head);
break;
case 0:
return;
}
}
}

/*登录验证*/
pUserInfo checkLogin(pUserInfo head, char* id, char* password) {
pUserInfo target = findUserInfoNode(head, id);
if (target) {
if (strcmp(target->password, password) == 0) {
return target;
}
}
return NULL;
}

/*普通用户系统主菜单*/
void mainNormalOption(pUserInfo* userhead, pUserInfo me, pBookInfo* bookhead, pRecordInfo* recordhead) {
while (1) {
int option;
clearScreen();
printf(" ┌-------------------------------┐\n");
printf(" #图书信息管理系统#\n");
printf("\n");
printf(" 【1】 浏览图书\n");
printf(" 【2】 用户设置\n");
printf(" 【3】 借书\n");
printf(" 【4】 还书\n");
printf(" 【5】 借阅清单\n");
printf(" 【0】 退出系统\n");
printf("\n");
printf(" 账号:%s 姓名:%s 权限:%s\n", me->id, me->name, "普通用户");
printf(" └-------------------------------┘\n");
printf("\n");
scanf("%d", &option);
switch (option) {
case 1:
browseBooksOption(*bookhead);
break;
case 2:
settingUserOption(*userhead, me);
break;
case 3:
borrowBookOption(*bookhead, recordhead, me);
break;
case 4:
returnBookOption(*bookhead, recordhead, me);
break;
case 5:
showRecordOption(*bookhead, *recordhead, me);
break;
case 0:
return;
}
}
}

/*管理员系统主菜单*/
void mainManagerOption(pUserInfo* userhead, pUserInfo me, pBookInfo* bookhead) {
while (1) {
int option;
clearScreen();
printf(" ┌-------------------------------┐\n");
printf(" #图书信息管理系统#\n");
printf("\n");
printf(" 【1】 浏览图书\n");
printf(" 【2】 用户管理\n");
printf(" 【3】 图书管理\n");
printf(" 【0】 退出系统\n");
printf("\n");
printf(" 账号:%s 姓名:%s 权限:%s\n", me->id, me->name, "管理员");
printf(" └-------------------------------┘\n");
printf("\n");
scanf("%d", &option);
switch (option) {
case 1:
browseBooksOption(*bookhead);
break;
case 2:
manageUsersOption(userhead, me);
break;
case 3:
manageBooksOption(bookhead);
break;
case 0:
return;
}
}
}

/*首次登录,初始化管理员账号*/
void firstLogin(pUserInfo* head) {
pUserInfo user = (pUserInfo)malloc(sizeof(UserInfo));
memset(user, 0U, sizeof(UserInfo));
clearScreen();
printf(" ┌-------------------------------┐\n");
printf(" #图书信息管理系统#\n");
printf(" #首次使用,需创建管理员用户#\n");
printf("\n");
printf(" 账号 : ");
scanf("%s", user->id);
printf(" 姓名 : ");
inputLine(user->name, sizeof(user->name));
printf(" 密码 : ");
scanf("%s", user->password);
user->rank = MANAGER;
printf(" └-------------------------------┘\n");
*head = addUserInfoNode(*head, user);
saveUserInfoFile(*head);
}

/*用户登录*/
pUserInfo userLogin(pUserInfo head) {
char id[128] = { 0 };
char password[128] = { 0 };
pUserInfo user = NULL;
clearScreen();
printf(" ┌-------------------------------┐\n");
printf(" #欢迎登录图书信息管理系统#\n");
printf("\n");
printf("\n");
printf(" 账号 : ");
scanf("%s", id);
printf(" 密码 : ");
inputPassword(password, sizeof(password));
printf(" └-------------------------------┘\n");
return checkLogin(head, id, password);
}

/*进入系统*/
void process(pUserInfo* userhead, pBookInfo* bookhead, pRecordInfo* recordhead) {
pUserInfo me = NULL;
do {
me = userLogin(*userhead);
if (!me) {
printf("\n登录失败,账号或密码错误!\n");
waitingPressAnyKey();
}
} while (!me);
switch (me->rank) {
case NORMAL:
mainNormalOption(userhead, me, bookhead, recordhead);
break;
case MANAGER:
mainManagerOption(userhead, me, bookhead);
break;
}
}

int main() {
/*从文件中加载用户数据*/
pUserInfo userhead = loadUserInfoFile();
/*从文件中加载图书数据*/
pBookInfo bookhead = loadBookInfoFile();
/*从文件中加载记录数据*/
pRecordInfo recordhead = loadRecordInfoFile();
/*首次登录,设置管理员用户*/
if (!userhead) {
firstLogin(&userhead);
}
/*进入系统*/
process(&userhead, &bookhead, &recordhead);
/*清理用户列表*/
clearUserInfoList(userhead);
/*清理图书列表*/
clearBookInfoList(bookhead);
/*清理记录列表*/
clearRecordInfoList(recordhead);
return 0;
}
  • 下面是混淆后的代码,它跟上面的原始代码一样都能顺利编译,执行效果并无差别,但是已经不可阅读了
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
/*!
**************************************
* # C/C++代码混淆器 #
*
* --- 狐狸の窝 ---
* Copyright (C) https://foxzzz.com
**************************************
*/
#define J27B printf(" 账号:%s 姓名:%s 权限:%s\n", me->id, me->name, "普通用户");
#define J27A printf(" 账号:%s 姓名:%s 权限:%s\n", me->id, me->name, "管理员");
#define J279 printf(" 【%2d/%-2d】\n", ++page_current, page_total);
#define J278 printf("\n您的借阅数量已达上限,请先归还图书!\n");
#define J277 printf("└-------------------------------------------------┘\n");
#define J276 printf("┌-------------------------------------------------┐\n");
#define J275 printf(" #首次使用,需创建管理员用户#\n");
#define J274 printf("\n图书添加失败,存在相同图书编号!\n");
#define J273 printf(" 权限:(0:普通用户, 1:管理员)");
#define J272 printf(" #欢迎登录图书信息管理系统#\n");
#define J271 char user_id[128]; /*借阅人账号*/
#define J270 struct _tRecordInfo* next; /*下一个节点*/
#define J26F printf(" ┌-------------------------------┐\n");
#define J26E printf(" └-------------------------------┘\n");
#define J26D /*将上一个节点指向删除节点的下一个节点*/
#define J26C printf("\n用户创建失败,存在相同用户!\n");
#define J26B printf("\n【 1 下一页 | 0 返回上级菜单】\n");
#define J26A char book_id[BORROW_COUNT_MAX][128]; /*图书编号*/
#define J269 int count; /*借阅数量*/
#define J268 printf(" %-10s%-10s\n", book_id, book->name);
#define J267 printf(" %-10s%-10s\n", "编号", "书名");
#define J266 printf("\n登录失败,账号或密码错误!\n");
#define J265 printf(" #图书信息管理系统#\n");
#define J264 printf(" 【5】 按出版社查询\n");
#define J263 /*添加图书节点,返回链表首节点指针*/
#define J262 /*添加记录节点,返回链表首节点指针*/
#define J261 /*删除用户节点,返回链表首节点指针*/
#define J260 /*删除图书节点,返回链表首节点指针*/
#define J25F /*添加用户节点,返回链表首节点指针*/
#define J25E /*删除记录节点,返回链表首节点指针*/
#define J25D printf(" └------------------------┘\n");
#define J25C printf(" ┌------------------------┐\n");
#define J25B printf(" 【4】 按作者查询\n");
#define J25A printf(" 【2】 按编号查询\n");
#define J259 printf(" 【3】 按书名查询\n");
#define J258 /*将用户节点总数写入文件起始位置*/
#define J257 printf(" #按出版神查询#\n");
#define J256 printf("\n按回车键返回上级菜单...\n");
#define J255 printf(" 【1】 添加图书\n");
#define J254 printf(" 【1】 用户清单\n");
#define J253 printf(" 【2】 修改图书\n");
#define J252 struct _tUserInfo* next; /*下一个节点*/
#define J251 /*将指向下个节点的指针重置成NULL*/
#define J250 struct _tBookInfo* next; /*下一个节点*/
#define J24F printf(" 【1】 浏览图书\n");
#define J24E printf(" 【4】 删除用户\n");
#define J24D printf(" 【2】 用户管理\n");
#define J24C printf(" 【2】 添加用户\n");
#define J24B printf(" 【5】 借阅清单\n");
#define J24A printf(" 【3】 图书管理\n");
#define J249 printf(" 【2】 用户设置\n");
#define J248 printf(" 【3】 删除图书\n");
#define J247 printf(" 【1】 图书清单\n");
#define J246 printf(" 【3】 修改用户\n");
#define J245 printf(" 【0】 退出系统\n");
#define J244 printf(" #按编号查询#\n");
#define J243 printf(" #按书名查询#\n");
#define J242 printf(" #按作者查询#\n");
#define J241 char time[256]; /*出版时间*/
#define J240 printf(" #图书管理#\n");
#define J23F printf(" #用户管理#\n");
#define J23E printf(" #图书浏览#\n");
#define J23D while ((c = getchar()) != '\n' && c != EOF);
#define J23C printf(" 权限:%s\n", "普通用户");
#define J23B printf(" 密码:%s\n", user->password);
#define J23A /*读取文件起始位置的节点总数*/
#define J239 /*找到要删除节点的上一个节点*/
#define J238 FILE* file = fopen("recordinfo.dat", "wb");
#define J237 FILE* file = fopen("recordinfo.dat", "rb");
#define J236 /*首次登录,初始化管理员账号*/
#define J235 printf(" #删除用户#\n");
#define J234 printf(" #修改用户#\n");
#define J233 printf(" #删除图书#\n");
#define J232 printf(" #借阅清单#\n");
#define J231 printf(" #图书清单#\n");
#define J230 printf(" #用户设置#\n");
#define J22F printf(" #添加用户#\n");
#define J22E printf(" #修改图书#\n");
#define J22D printf("\n您没有借阅该图书!\n");
#define J22C printf(" #添加图书#\n");
#define J22B FILE* file = fopen("userinfo.dat", "wb");
#define J22A FILE* file = fopen("bookinfo.dat", "wb");
#define J229 char press[256]; /*出版社*/
#define J228 printf(" 【3】 借书\n");
#define J227 printf(" 【4】 还书\n");
#define J226 printf(" 【0】 返回\n");
#define J225 FILE* file = fopen("userinfo.dat", "rb");
#define J224 FILE* file = fopen("bookinfo.dat", "rb");
#define J223 printf(" 权限:%s\n", "管理员");
#define J222 /*首次登录,设置管理员用户*/
#define J221 (pRecordInfo)malloc(sizeof(RecordInfo));
#define J220 printf(" 姓名:%s\n", user->name);
#define J21F printf("\n不允许删除自己!\n");
#define J21E char type[256]; /*类型*/
#define J21D char id[128]; /*编号*/
#define J21C char name[256]; /*书名*/
#define J21B char name[256]; /*姓名*/
#define J21A char id[128]; /*账号*/
#define J219 char author[256]; /*作者*/
#define J218 findBookInfoNodeByAuthor(target->next,
#define J217 int rank; /*权限*/
#define J216 char password[256]; /*密码*/
#define J215 printf(" 账号:%s\n", user->id);
#define J214 double price; /*价格*/
#define J213 int stock; /*库存*/
#define J212 /*通过出版社查找图书节点*/
#define J211 findBookInfoNodeByPress(target->next,
#define J210 /*清理图书列表,回收内存*/
#define J20F /*清理用户列表,回收内存*/
#define J20E /*将新节点插入到链表尾部*/
#define J20D /*清理记录列表,回收内存*/
#define J20C printf("\n图书修改成功!\n");
#define J20B findBookInfoNodeByName(target->next,
#define J20A printf("\n图书添加成功!\n");
#define J209 printf("\n未找到该图书!\n");
#define J208 printf("\n图书删除成功!\n");
#define J207 printf("\n用户设置成功!\n");
#define J206 printf(" 出版社:");
#define J205 printf("\n用户删除成功!\n");
#define J204 (pUserInfo)malloc(sizeof(UserInfo));
#define J203 printf("\n用户创建成功!\n");
#define J202 (pBookInfo)malloc(sizeof(BookInfo));
#define J201 printf(" #借书#\n");
#define J200 printf("\n未找到该用户!\n");
#define J1FF printf(" #还书#\n");
#define J1FE printf("\n用户修改成功!\n");
#define J1FD /*从文件中加载图书数据*/
#define J1FC findBookInfoNodeByAuthor(pBookInfo
#define J1FB searchBookByAuthorOption(pBookInfo
#define J1FA findRecordInfoNodeByID(recordhead,
#define J1F9 /*从文件中加载用户信息*/
#define J1F8 /*从文件中加载记录数据*/
#define J1F7 /*从文件中加载用户数据*/
#define J1F6 /*通过账号查找记录节点*/
#define J1F5 /*从文件中加载记录信息*/
#define J1F4 /*通过书名查找图书节点*/
#define J1F3 /*通过作者查找图书节点*/
#define J1F2 /*通过账号查找用户节点*/
#define J1F1 /*通过账号查找图书节点*/
#define J1F0 /*按出版社查询图书选项*/
#define J1EF findRecordInfoNodeByID(pRecordInfo
#define J1EE /*将记录信息存储到文件*/
#define J1ED /*将用户信息存储到文件*/
#define J1EC /*将图书信息存储到文件*/
#define J1EB printf(" 编号:");
#define J1EA searchBookByPressOption(pBookInfo
#define J1E9 printf(" 密码 : ");
#define J1E8 printf(" 作者:");
#define J1E7 printf(" 书名:");
#define J1E6 printf(" 姓名 : ");
#define J1E5 findBookInfoNodeByPress(pBookInfo
#define J1E4 printf(" 账号:");
#define J1E3 printf(" 账号 : ");
#define J1E2 searchBookByNameOption(pBookInfo
#define J1E1 clearRecordInfoList(recordhead);
#define J1E0 findBookInfoNodeByName(pBookInfo
#define J1DF removeRecordInfoNode(pRecordInfo
#define J1DE searchBookByAuthorOption(head);
#define J1DD (strlen(target->book_id[index])
#define J1DC /*按书名查询图书选项*/
#define J1DB (strcmp(target->book_id[index],
#define J1DA /*按编号查询图书选项*/
#define J1D9 clearRecordInfoList(pRecordInfo
#define J1D8 printf("%.2lf\n", book->price);
#define J1D7 /*普通用户系统主菜单*/
#define J1D6 /*从标准输入一行文本*/
#define J1D5 inputPassword(target->password,
#define J1D4 if (ch == '\n' || ch == '\r') {
#define J1D3 /*链表为空返回该节点*/
#define J1D2 countRecordInfoNode(pRecordInfo
#define J1D1 /*按作者查询图书选项*/
#define J1D0 printf("读文件失败!\n");
#define J1CF printf("写文件失败!\n");
#define J1CE findBookInfoNodeByID(pBookInfo
#define J1CD searchBookByPressOption(head);
#define J1CC findBookInfoNodeByAuthor(head,
#define J1CB printf("\n操作成功!\n");
#define J1CA printf("\n没有库存!\n");
#define J1C9 findBookInfoNodeByID(bookhead,
#define J1C8 memset(target->book_id[index],
#define J1C7 printf(" 出版时间:");
#define J1C6 strcpy(target->book_id[index],
#define J1C5 searchBookByIDOption(pBookInfo
#define J1C4 findBookInfoNodeByPress(head,
#define J1C3 addRecordInfoNode(pRecordInfo
#define J1C2 printf("%s\n", book->author);
#define J1C1 inputPassword(user->password,
#define J1C0 findRecordInfoNodeByID(*head,
#define J1BF browseBooksOption(*bookhead);
#define J1BE searchBookByNameOption(head);
#define J1BD settingUserOption(*userhead,
#define J1BC /*删除节点为首节点*/
#define J1BB scanf("%s", user->password);
#define J1BA removeUserInfoNode(pUserInfo
#define J1B9 removeBookInfoNode(pBookInfo
#define J1B8 showBookListOption(pBookInfo
#define J1B7 /*管理员系统主菜单*/
#define J1B6 manageBooksOption(bookhead);
#define J1B5 manageBooksOption(pBookInfo*
#define J1B4 /*显示图书清单选项*/
#define J1B3 (findBookInfoNodeByID(*head,
#define J1B2 mainManagerOption(pUserInfo*
#define J1B1 printf("%s\n", book->press);
#define J1B0 printf("%d\n", book->stock);
#define J1AF clearBookInfoList(bookhead);
#define J1AE findBookInfoNodeByName(head,
#define J1AD manageUsersOption(pUserInfo*
#define J1AC NORMAL, /*普通用户*/
#define J1AB clearUserInfoList(userhead);
#define J1AA /*显示用户清单选项*/
#define J1A9 showUserListOption(pUserInfo
#define J1A8 printf(" 出版社:");
#define J1A7 printf("%s\n", book->name);
#define J1A6 printf("%s\n", book->time);
#define J1A5 printf("%s\n", target->id);
#define J1A4 removeBookOption(pBookInfo*
#define J1A3 removeRecordInfoNode(*head,
#define J1A2 removeUserOption(pUserInfo*
#define J1A1 returnBookOption(*bookhead,
#define J1A0 saveBookInfoFile(bookhead);
#define J19F scanf("%d", &target->rank);
#define J19E scanf("%lf", &book->price);
#define J19D searchBookByIDOption(head);
#define J19C settingUserOption(pUserInfo
#define J19B showRecordOption(*bookhead,
#define J19A borrowBookOption(*bookhead,
#define J199 browseBooksOption(pBookInfo
#define J198 clearBookInfoList(pBookInfo
#define J197 clearUserInfoList(pUserInfo
#define J196 countBookInfoNode(pBookInfo
#define J195 countUserInfoNode(pUserInfo
#define J194 createBookOption(pBookInfo*
#define J193 createUserOption(pUserInfo*
#define J192 findBookInfoNodeByID(*head,
#define J191 printf("%s\n", book->type);
#define J190 inputPassword(me->password,
#define J18F mainManagerOption(userhead,
#define J18E mainNormalOption(pUserInfo*
#define J18D manageUsersOption(userhead,
#define J18C findBookInfoNodeByID(head,
#define J18B sizeof(target->password));
#define J18A mainNormalOption(userhead,
#define J189 showUserListOption(*head);
#define J188 countRecordInfoNode(head);
#define J187 findUserInfoNode(pUserInfo
#define J186 removeRecordInfoNode(head,
#define J185 returnBookOption(pBookInfo
#define J184 borrowBookOption(pBookInfo
#define J183 updateUserOption(pUserInfo
#define J182 showRecordOption(pBookInfo
#define J181 saveRecordInfoFile(*head);
#define J180 scanf("%d", &book->stock);
#define J17F updateBookOption(pBookInfo
#define J17E (strcmp(target->password,
#define J17D MANAGER /*管理员*/
#define J17C showBookListOption(head);
#define J17B /*等待按下任意键*/
#define J17A /*借阅记录结构体*/
#define J179 /*清空输入缓冲区*/
#define J178 removeBookInfoNode(*head,
#define J177 (removeRecord(recordhead,
#define J176 printf("%s\n", me->name);
#define J175 addBookInfoNode(pBookInfo
#define J174 removeRecord(pRecordInfo*
#define J173 addUserInfoNode(pUserInfo
#define J172 removeUserInfoNode(*head,
#define J171 printf("%s\n", book->id);
#define J170 /*计算记录节点数*/
#define J16F /*计算用户节点数*/
#define J16E /*计算图书节点数*/
#define J16D scanf("%d", &user->rank);
#define J16C countBookInfoNode(head);
#define J16B scanf("%s", book->time);
#define J16A saveUserInfoFile(*head);
#define J169 addRecordInfoNode(*head,
#define J168 saveRecordInfoFile(const
#define J167 scanf("%s", book->type);
#define J166 printf(" 价格:");
#define J165 removeUserInfoNode(head,
#define J164 removeBookInfoNode(head,
#define J163 saveBookInfoFile(*head);
#define J162 printf(" 账号:");
#define J161 (findUserInfoNode(*head,
#define J160 (strcmp(cursor->user_id,
#define J15F printf(" 编号:");
#define J15E printf(" 类型:");
#define J15D printf(" 库存:");
#define J15C printf(" 密码:");
#define J15B printf(" 书名:");
#define J15A updateBookOption(*head);
#define J159 printf(" 姓名:");
#define J158 sizeof(user->password));
#define J157 countUserInfoNode(head);
#define J156 printf(" 作者:");
#define J155 inputLine(book->author,
#define J154 updateUserOption(*head,
#define J153 strcpy(target->user_id,
#define J152 addRecordInfoNode(head,
#define J151 target->book_id[index];
#define J150 inputPassword(password,
#define J14F saveUserInfoFile(head);
#define J14E (strcmp(cursor->author,
#define J14D saveBookInfoFile(head);
#define J14C createUserOption(head);
#define J14B inputLine(target->name,
#define J14A createBookOption(head);
#define J149 removeBookOption(head);
#define J148 printf("%s\n", me->id);
#define J147 password[index] = '\0';
#define J146 findUserInfoNode(*head,
#define J145 /*用户管理菜单*/
#define J144 /*显示图书信息*/
#define J143 /*显示用户信息*/
#define J142 /*添加借书记录*/
#define J141 /*添加图书选项*/
#define J140 sizeof(me->password));
#define J13F sizeof(book->author));
#define J13E /*编辑图书信息*/
#define J13D scanf("%s", user->id);
#define J13C /*编辑用户信息*/
#define J13B /*修改用户选项*/
#define J13A scanf("%s", book->id);
#define J139 addBookInfoNode(*head,
#define J138 (strcmp(cursor->press,
#define J137 /*清理记录列表*/
#define J136 /*添加用户选项*/
#define J135 saveUserInfoFile(const
#define J134 sizeof(target->name));
#define J133 /*用户设置选项*/
#define J132 addRecord(pRecordInfo*
#define J131 addUserInfoNode(*head,
#define J130 saveBookInfoFile(const
#define J12F firstLogin(&userhead);
#define J12E /*清理用户列表*/
#define J12D findUserInfoNode(head,
#define J12C removeUserOption(head,
#define J12B (addRecord(recordhead,
#define J12A /*删除借书记录*/
#define J129 inputLine(book->press,
#define J128 /*清理图书列表*/
#define J127 /*修改图书选项*/
#define J126 /*同步文件信息*/
#define J125 /*图书浏览菜单*/
#define J124 /*删除图书选项*/
#define J123 /*图书管理菜单*/
#define J122 /*删除用户选项*/
#define J121 loadRecordInfoFile();
#define J120 scanf("%d", &option);
#define J11F addUserInfoNode(head,
#define J11E inputLine(book->name,
#define J11D (strcmp(cursor->name,
#define J11C userLogin(*userhead);
#define J11B inputLine(user->name,
#define J11A waitingPressAnyKey();
#define J119 addBookInfoNode(head,
#define J118 sizeof(book->press));
#define J117 firstLogin(pUserInfo*
#define J116 line[len - 1] = '\0';
#define J115 SHOW_BOOK_PAGE_COUNT)
#define J114 checkLogin(pUserInfo
#define J113 sizeof(book->name));
#define J112 sizeof(RecordInfo));
#define J111 loadRecordInfoFile()
#define J110 sizeof(user->name));
#define J10F waitingPressAnyKey()
#define J10E inputPassword(char*
#define J10D /*图书结构体*/
#define J10C /*用户结构体*/
#define J10B loadBookInfoFile();
#define J10A sizeof(RecordInfo),
#define J109 userLogin(pUserInfo
#define J108 loadUserInfoFile();
#define J107 /*删除该节点*/
#define J106 (strcmp(cursor->id,
#define J105 editBook(pBookInfo
#define J104 editUser(pUserInfo
#define J103 sizeof(password));
#define J102 sizeof(UserInfo));
#define J101 sizeof(BookInfo));
#define J100 loadBookInfoFile()
#define JFF loadUserInfoFile()
#define JFE showUser(pUserInfo
#define JFD (strlen(book->id))
#define JFC process(pUserInfo*
#define JFB process(&userhead,
#define JFA showBook(pBookInfo
#define JF9 editBook(target);
#define JF8 showUser(cursor);
#define JF7 sizeof(UserInfo),
#define JF6 password[index++]
#define JF5 BORROW_COUNT_MAX)
#define JF4 sizeof(BookInfo),
#define JF3 showBook(target);
#define JF2 showBook(cursor);
#define JF1 BORROW_COUNT_MAX;
#define JF0 inputLine(author,
#define JEF showUser(target);
#define JEE if (ch == '\b') {
#define JED /*匹配图书*/
#define JEC /*匹配用户*/
#define JEB /*用户权限*/
#define JEA /*用户登录*/
#define JE9 /*登录验证*/
#define JE8 /*输入密码*/
#define JE7 /*进入系统*/
#define JE6 checkLogin(head,
#define JE5 inputLine(press,
#define JE4 scanf("%s", id);
#define JE3 sizeof(author));
#define JE2 ++target->count;
#define JE1 ++target->stock;
#define JE0 --target->count;
#define JDF --target->stock;
#define JDE /*借阅清单*/
#define JDD editBook(book);
#define JDC sizeof(press));
#define JDB inputLine(name,
#define JDA editUser(user);
#define JD9 inputLine(char*
#define JD8 putchar('\b');
#define JD7 sizeof(name));
#define JD6 memset(target,
#define JD5 fwrite(&count,
#define JD4 fwrite(cursor,
#define JD3 putchar('\n');
#define JD2 clearScreen();
#define JD1 (cursor->next)
#define JD0 (target->stock
#define JCF (target->count
#define JCE system("cls");
#define JCD &recordhead);
#define JCC clearScreen()
#define JCB (cursor->next
#define JCA putchar(' ');
#define JC9 emptyStdin();
#define JC8 putchar('*');
#define JC7 fclose(file);
#define JC6 (target->rank
#define JC5 printf("\n");
#define JC4 password[128]
#define JC3 cursor->next;
#define JC2 fread(&count,
#define JC1 strlen(line);
#define JC0 memset(user,
#define JBF page_current
#define JBE memset(book,
#define JBD _tRecordInfo
#define JBC target->id))
#define JBB ((page_total
#define JBA sizeof(int),
#define JB9 target->rank
#define JB8 emptyStdin()
#define JB7 *recordhead,
#define JB6 (user->rank)
#define JB5 (fgets(line,
#define JB4 cursor->next
#define JB3 recordhead);
#define JB2 pRecordInfo;
#define JB1 pRecordInfo*
#define JB0 recordhead)
#define JAF (user->rank
#define JAE recordhead,
#define JAD author[128]
#define JAC (!userhead)
#define JAB RecordInfo,
#define JAA pRecordInfo
#define JA9 node->next;
#define JA8 fread(book,
#define JA7 fread(user,
#define JA6 free(book);
#define JA5 free(node);
#define JA4 free(user);
#define JA3 bookhead);
#define JA2 pBookInfo*
#define JA1 (me->rank)
#define JA0 page_total
#define J9F &bookhead,
#define J9E press[128]
#define J9D /*还书*/
#define J9C pUserInfo;
#define J9B book->id))
#define J9A /*借书*/
#define J99 /*清屏*/
#define J98 getchar();
#define J97 user->rank
#define J96 user->next
#define J95 user->id))
#define J94 _tUserInfo
#define J93 _tBookInfo
#define J92 book->next
#define J91 password);
#define J90 recordhead
#define J8F pBookInfo;
#define J8E continue;
#define J8D (count--)
#define J8C (!target)
#define J8B password,
#define J8A BookInfo,
#define J89 (target);
#define J88 capacity,
#define J87 capacity)
#define J86 _getch();
#define J85 UserInfo,
#define J84 password)
#define J83 bookhead,
#define J82 pBookInfo
#define J81 pUserInfo
#define J80 bookhead)
#define J7F book_id);
#define J7E user_id);
#define J7D userhead,
#define J7C name[128]
#define J7B bookhead
#define J7A MANAGER;
#define J79 ++index)
#define J78 (target)
#define J77 (option)
#define J76 UserRank
#define J75 me->id);
#define J74 target);
#define J73 author);
#define J72 ++count;
#define J71 (cursor)
#define J70 MANAGER:
#define J6F (++count
#define J6E MANAGER)
#define J6D user_id,
#define J6C userhead
#define J6B book_id)
#define J6A --index;
#define J69 return;
#define J68 option;
#define J67 typedef
#define J66 me->id,
#define J65 id[128]
#define J64 press);
#define J63 stdin))
#define J62 NORMAL:
#define J61 target;
#define J60 NORMAL;
#define J5F book_id
#define J5E (target
#define J5D cursor;
#define J5C author)
#define J5B size_t
#define J5A return
#define J59 (!me);
#define J58 press)
#define J57 (book)
#define J56 (file)
#define J55 user);
#define J54 target
#define J53 file);
#define J52 head);
#define J51 index;
#define J50 book);
#define J4F cursor
#define J4E main()
#define J4D (head)
#define J4C NORMAL
#define J4B (index
#define J4A count;
#define J49 switch
#define J48 struct
#define J47 name);
#define J46 break;
#define J45 node)
#define J44 head)
#define J43 index
#define J42 head,
#define J41 line,
#define J40 head;
#define J3F char*
#define J3E count
#define J3D book)
#define J3C name)
#define J3B 128);
#define J3A node;
#define J39 (head
#define J38 *head
#define J37 (!me)
#define J36 user)
#define J35 NULL;
#define J34 while
#define J33 id);
#define J32 user
#define J31 head
#define J30 me);
#define J2F case
#define J2E enum
#define J2D else
#define J2C char
#define J2B void
#define J2A (len
#define J29 book
#define J28 me,
#define J27 for
#define J26 0U,
#define J25 id)
#define J24 int
#define J23 me)
#define J22 id,
#define J21 (1)
#define J20 ch;
#define J1F len
#define J1E 3:
#define J1D 1:
#define J1C 2:
#define J1B do
#define J1A if
#define J19 me
#define J18 ch
#define J17 &&
#define J16 1,
#define J15 1)
#define J14 1;
#define J13 ==
#define J12 0,
#define J11 0)
#define J10 c;
#define JF !=
#define JE 0:
#define JD 5:
#define JC 4:
#define JB 0;
#define JA };
#define J9 /
#define J8 <
#define J7 +
#define J6 -
#define J5 0
#define J4 *
#define J3 >
#define J2 {
#define J1 }
#define J0 =
/********************************************
* 图书信息管理系统
* Copyright (C) i@foxzzz.com
*
* C语言实现的命令行模式下的信息管理系统。
*********************************************/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include <conio.h>
#define BORROW_COUNT_MAX 10 /*最多可借阅的图书数量*/
#define SHOW_BOOK_PAGE_COUNT 20 /*一页显示的图书条目*/
#define J27C JEB J2E J76 J2 J1AC J17D JA J10C J67 J48
#define J27D J94 J2 J21A J21B J216 J217 J252 J1 J85 J4
#define J27E J9C J10D J67 J48 J93 J2 J21D J21C J219 J229
#define J27F J21E J241 J214 J213 J250 J1 J8A J4 J8F J17A
#define J280 J67 J48 JBD J2 J271 J26A J269 J270 J1 JAB
#define J281 J4 JB2 J179 J2B JB8 J2 J24 J10 J23D J1
#define J282 J17B J2B J10F J2 JC9 J98 J1 J99 J2B JCC
#define J283 J2 JCE J1 J1D6 J2B JD9 J41 J24 J87 J2
#define J284 J34 J21 J2 J1A JB5 J88 J63 J2 J5B J1F
#define J285 J0 JC1 J1A J2A J3 J15 J2 J116 J46 J1
#define J286 J1 J1 J1 JE8 J2B J10E J8B J24 J87 J2
#define J287 J24 J43 J0 JB J34 J4B J8 J87 J2 J24
#define J288 J18 J0 J86 J1D4 J1A J4B J3 J11 J2 J147
#define J289 JD3 J46 J1 J2D J8E J1 JEE J1A J4B J3
#define J28A J11 J2 J6A JD8 JCA JD8 J1 J1 J2D J2
#define J28B JF6 J0 J20 JC8 J1 J1 J1 J25F J81 J173
#define J28C J42 J81 J45 J2 J1A J4D J2 J81 J4F J0
#define J28D J40 J34 JD1 J2 J4F J0 JC3 J1 J20E JB4
#define J28E J0 J3A J5A J40 J1 J2D J2 J1D3 J5A J3A
#define J28F J1 J1 J261 J81 J1BA J42 J81 J45 J2 J1A
#define J290 J4D J2 J1A J39 J13 J45 J2 J1BC J31 J0
#define J291 JA9 J107 JA5 J1 J2D J2 J81 J4F J0 J40
#define J292 J34 JD1 J2 J239 J1A JCB J13 J45 J2 J26D
#define J293 JB4 J0 JA9 J107 JA5 J46 J1 J4F J0 JC3
#define J294 J1 J1 J1 J5A J40 J1 J1F2 J81 J187 J42
#define J295 J3F J25 J2 J81 J4F J0 J40 J34 J71 J2
#define J296 JEC J1A J106 J25 J13 J11 J2 J5A J5D J1
#define J297 J4F J0 JC3 J1 J5A J35 J1 J16F J24 J195
#define J298 J44 J2 J81 J4F J0 J40 J24 J3E J0 JB
#define J299 J34 J71 J2 J72 J4F J0 JC3 J1 J5A J4A
#define J29A J1 J263 J82 J175 J42 J82 J45 J2 J1A J4D
#define J29B J2 J82 J4F J0 J40 J34 JD1 J2 J4F J0
#define J29C JC3 J1 J20E JB4 J0 J3A J5A J40 J1 J2D
#define J29D J2 J1D3 J5A J3A J1 J1 J260 J82 J1B9 J42
#define J29E J82 J45 J2 J1A J4D J2 J1A J39 J13 J45
#define J29F J2 J1BC J31 J0 JA9 J107 JA5 J1 J2D J2
#define J2A0 J82 J4F J0 J40 J34 JD1 J2 J239 J1A JCB
#define J2A1 J13 J45 J2 J26D JB4 J0 JA9 J107 JA5 J46
#define J2A2 J1 J4F J0 JC3 J1 J1 J1 J5A J40 J1
#define J2A3 J1F1 J82 J1CE J42 J3F J25 J2 J82 J4F J0
#define J2A4 J40 J34 J71 J2 JED J1A J106 J25 J13 J11
#define J2A5 J2 J5A J5D J1 J4F J0 JC3 J1 J5A J35
#define J2A6 J1 J1F4 J82 J1E0 J42 J3F J3C J2 J82 J4F
#define J2A7 J0 J40 J34 J71 J2 JED J1A J11D J3C J13
#define J2A8 J11 J2 J5A J5D J1 J4F J0 JC3 J1 J5A
#define J2A9 J35 J1 J1F3 J82 J1FC J42 J3F J5C J2 J82
#define J2AA J4F J0 J40 J34 J71 J2 JED J1A J14E J5C
#define J2AB J13 J11 J2 J5A J5D J1 J4F J0 JC3 J1
#define J2AC J5A J35 J1 J212 J82 J1E5 J42 J3F J58 J2
#define J2AD J82 J4F J0 J40 J34 J71 J2 JED J1A J138
#define J2AE J58 J13 J11 J2 J5A J5D J1 J4F J0 JC3
#define J2AF J1 J5A J35 J1 J16E J24 J196 J44 J2 J82
#define J2B0 J4F J0 J40 J24 J3E J0 JB J34 J71 J2
#define J2B1 J72 J4F J0 JC3 J1 J5A J4A J1 J262 JAA
#define J2B2 J1C3 J42 JAA J45 J2 J1A J4D J2 JAA J4F
#define J2B3 J0 J40 J34 JD1 J2 J4F J0 JC3 J1 J20E
#define J2B4 JB4 J0 J3A J5A J40 J1 J2D J2 J1D3 J5A
#define J2B5 J3A J1 J1 J25E JAA J1DF J42 JAA J45 J2
#define J2B6 J1A J4D J2 J1A J39 J13 J45 J2 J1BC J31
#define J2B7 J0 JA9 J107 JA5 J1 J2D J2 JAA J4F J0
#define J2B8 J40 J34 JD1 J2 J239 J1A JCB J13 J45 J2
#define J2B9 J26D JB4 J0 JA9 J107 JA5 J46 J1 J4F J0
#define J2BA JC3 J1 J1 J1 J5A J40 J1 J1F6 JAA J1EF
#define J2BB J42 J3F J25 J2 JAA J4F J0 J40 J34 J71
#define J2BC J2 JEC J1A J160 J25 J13 J11 J2 J5A J5D
#define J2BD J1 J4F J0 JC3 J1 J5A J35 J1 J170 J24
#define J2BE J1D2 J44 J2 JAA J4F J0 J40 J24 J3E J0
#define J2BF JB J34 J71 J2 J72 J4F J0 JC3 J1 J5A
#define J2C0 J4A J1 J1ED J2B J135 J81 J44 J2 J81 J4F
#define J2C1 J0 J40 J22B J1A J56 J2 J24 J3E J0 J157
#define J2C2 J258 JD5 JBA J16 J53 J34 J71 J2 JD4 JF7
#define J2C3 J16 J53 J4F J0 JC3 J1 JC7 J1 J2D J2
#define J2C4 J1CF J1 J1 J1F9 J81 JFF J2 J81 J31 J0
#define J2C5 J35 J225 J1A J56 J2 J24 J3E J0 JB J23A
#define J2C6 JC2 JBA J16 J53 J34 J8D J2 J81 J32 J0
#define J2C7 J204 JC0 J12 J102 JA7 JF7 J16 J53 J251 J96
#define J2C8 J0 J35 J31 J0 J11F J55 J1 JC7 J1 J2D
#define J2C9 J2 J1D0 J1 J5A J40 J1 J20F J2B J197 J44
#define J2CA J2 J34 J4D J2 J31 J0 J165 J52 J1 J1
#define J2CB J1EC J2B J130 J82 J44 J2 J82 J4F J0 J40
#define J2CC J22A J1A J56 J2 J24 J3E J0 J16C J258 JD5
#define J2CD JBA J16 J53 J34 J71 J2 JD4 JF4 J16 J53
#define J2CE J4F J0 JC3 J1 JC7 J1 J2D J2 J1CF J1
#define J2CF J1 J1F9 J82 J100 J2 J82 J31 J0 J35 J224
#define J2D0 J1A J56 J2 J24 J3E J0 JB J23A JC2 JBA
#define J2D1 J16 J53 J34 J8D J2 J82 J29 J0 J202 JBE
#define J2D2 J12 J101 JA8 JF4 J16 J53 J251 J92 J0 J35
#define J2D3 J31 J0 J119 J50 J1 JC7 J1 J2D J2 J1D0
#define J2D4 J1 J5A J40 J1 J210 J2B J198 J44 J2 J34
#define J2D5 J4D J2 J31 J0 J164 J52 J1 J1 J1EE J2B
#define J2D6 J168 JAA J44 J2 JAA J4F J0 J40 J238 J1A
#define J2D7 J56 J2 J24 J3E J0 J188 J258 JD5 JBA J16
#define J2D8 J53 J34 J71 J2 JD4 J10A J16 J53 J4F J0
#define J2D9 JC3 J1 JC7 J1 J2D J2 J1CF J1 J1 J1F5
#define J2DA JAA J111 J2 JAA J31 J0 J35 J237 J1A J56
#define J2DB J2 J24 J3E J0 JB J23A JC2 JBA J16 J53
#define J2DC J34 J8D J2 JAA J29 J0 J221 JBE J12 J112
#define J2DD JA8 J10A J16 J53 J251 J92 J0 J35 J31 J0
#define J2DE J152 J50 J1 JC7 J1 J2D J2 J1D0 J1 J5A
#define J2DF J40 J1 J20D J2B J1D9 J44 J2 J34 J4D J2
#define J2E0 J31 J0 J186 J52 J1 J1 J143 J2B JFE J36
#define J2E1 J2 J276 J215 J220 J23B J49 JB6 J2 J2F J62
#define J2E2 J23C J46 J2F J70 J223 J46 J1 J277 J1 J13C
#define J2E3 J2B J104 J36 J2 J276 J162 J13D J159 J11B J110
#define J2E4 J15C J1C1 J158 J273 J16D J1A JAF JF J4C J17
#define J2E5 J97 JF J6E J2 J97 J0 J60 J1 J277 J1
#define J2E6 J144 J2B JFA J3D J2 J276 J15F J171 J15B J1A7
#define J2E7 J156 J1C2 J1A8 J1B1 J15E J191 J1C7 J1A6 J166 J1D8
#define J2E8 J15D J1B0 J277 J1 J13E J2B J105 J3D J2 J276
#define J2E9 J15F J1A JFD J2 J171 J1 J2D J2 J13A J1
#define J2EA J15B J11E J113 J156 J155 J13F J1A8 J129 J118 J15E
#define J2EB J167 J1C7 J16B J166 J19E J15D J180 J277 J1 J1AA
#define J2EC J2B J1A9 J44 J2 J81 J4F J0 J40 J34 J71
#define J2ED J2 JF8 J4F J0 JC3 J1 J256 J11A J1 J136
#define J2EE J2B J193 J44 J2 J81 J32 J0 J204 JC0 J26
#define J2EF J102 JD2 J25C J22F J25D JDA J1A J161 J95 J2
#define J2F0 JA4 J26C J1 J2D J2 J38 J0 J131 J55 J126
#define J2F1 J16A J203 J1 J11A J1 J13B J2B J183 J42 J81
#define J2F2 J23 J2 J2C J65 J0 J2 J5 JA J81 J54
#define J2F3 J0 J35 JD2 J25C J234 JC5 J1E4 JE4 J25D J54
#define J2F4 J0 J12D J33 J1A J78 J2 JEF J276 J162 J1A5
#define J2F5 J159 J14B J134 J15C J1D5 J18B J1A J5E JF J23
#define J2F6 J2 J273 J19F J1A JC6 JF J4C J17 JB9 JF
#define J2F7 J6E J2 JB9 J0 J60 J1 J1 J277 J126 J14F
#define J2F8 J1FE J1 J2D J2 J200 J1 J11A J1 J122 J2B
#define J2F9 J1A2 J42 J81 J23 J2 J2C J65 J0 J2 J5
#define J2FA JA J81 J54 J0 J35 JD2 J25C J235 JC5 J1E4
#define J2FB JE4 J25D J54 J0 J146 J33 J1A J78 J2 J1A
#define J2FC J5E J13 J23 J2 J21F J1 J2D J2 JEF J38
#define J2FD J0 J172 J74 J126 J16A J205 J1 J1 J2D J2
#define J2FE J200 J1 J11A J1 J1B4 J2B J1B8 J44 J2 J1A
#define J2FF J4D J2 J82 J4F J0 J40 J24 JBF J0 JB
#define J300 J24 JA0 J0 J16C JA0 J0 JBB J6 J15 J9
#define J301 J115 J7 J14 J34 J71 J2 J24 J3E J0 JB
#define J302 JD2 J25C J231 J25D J279 J34 J71 J2 JF2 J4F
#define J303 J0 JC3 J1A J6F J13 J115 J2 J46 J1 J1
#define J304 J1A J71 J2 J26B J24 J68 J120 J49 J77 J2
#define J305 J2F J1D J46 J2F JE J69 J1 J1 J1 J1
#define J306 J256 J11A J1 J141 J2B J194 J44 J2 J82 J29
#define J307 J0 J202 JBE J26 J101 JD2 J25C J22C J25D JDD
#define J308 J1A J1B3 J9B J2 JA6 J274 J1 J2D J2 J38
#define J309 J0 J139 J50 J126 J163 J20A J1 J11A J1 J127
#define J30A J2B J17F J44 J2 J2C J65 J0 J2 J5 JA
#define J30B J82 J54 J0 J35 JD2 J25C J22E JC5 J1EB JE4
#define J30C J25D J54 J0 J18C J33 J1A J78 J2 JF3 JF9
#define J30D J126 J14D J20C J1 J2D J2 J209 J1 J11A J1
#define J30E J124 J2B J1A4 J44 J2 J2C J65 J0 J2 J5
#define J30F JA J82 J54 J0 J35 JD2 J25C J233 JC5 J1EB
#define J310 JE4 J25D J54 J0 J192 J33 J1A J78 J2 JF3
#define J311 J38 J0 J178 J74 J126 J163 J208 J1 J2D J2
#define J312 J209 J1 J11A J1 J1DA J2B J1C5 J44 J2 J2C
#define J313 J65 J0 J2 J5 JA J82 J54 J0 J35 JD2
#define J314 J25C J244 JC5 J1EB JE4 J25D J54 J0 J18C J33
#define J315 J1A J78 J2 JF3 J1 J2D J2 J209 J1 J11A
#define J316 J1 J1DC J2B J1E2 J44 J2 J2C J7C J0 J2
#define J317 J5 JA J82 J54 J0 J35 JD2 J25C J243 JC5
#define J318 J1E7 JDB JD7 J25D J54 J0 J1AE J47 J1A J78
#define J319 J2 J1B J2 JF3 J54 J0 J20B J47 J1 J34
#define J31A J89 J1 J2D J2 J209 J1 J11A J1 J1D1 J2B
#define J31B J1FB J44 J2 J2C JAD J0 J2 J5 JA J82
#define J31C J54 J0 J35 JD2 J25C J242 JC5 J1E8 JF0 JE3
#define J31D J25D J54 J0 J1CC J73 J1A J78 J2 J1B J2
#define J31E JF3 J54 J0 J218 J73 J1 J34 J89 J1 J2D
#define J31F J2 J209 J1 J11A J1 J1F0 J2B J1EA J44 J2
#define J320 J2C J9E J0 J2 J5 JA J82 J54 J0 J35
#define J321 JD2 J25C J257 JC5 J206 JE5 JDC J25D J54 J0
#define J322 J1C4 J64 J1A J78 J2 J1B J2 JF3 J54 J0
#define J323 J211 J64 J1 J34 J89 J1 J2D J2 J209 J1
#define J324 J11A J1 J142 JAA J132 J42 J3F J6D J3F J6B
#define J325 J2 JAA J54 J0 J1C0 J7E J1A J8C J2 J54
#define J326 J0 J221 JD6 J12 J112 J153 J7E J38 J0 J169
#define J327 J74 J1 J1A JCF J8 JF5 J2 J24 J51 J27
#define J328 J4B J0 JB J43 J8 JF1 J79 J2 J1A J1DD
#define J329 J13 J11 J2 J1C6 J7F JE2 J181 J5A J61 J1
#define J32A J1 J1 J5A J35 J1 J12A JAA J174 J42 J3F
#define J32B J6D J3F J6B J2 JAA J54 J0 J1C0 J7E J1A
#define J32C J78 J2 J24 J51 J27 J4B J0 JB J43 J8
#define J32D JF1 J79 J2 J1A J1DB J6B J13 J11 J2 J1C8
#define J32E J12 J3B JE0 J1A JCF J13 J11 J2 J38 J0
#define J32F J1A3 J74 J1 J181 J5A J61 J1 J1 J1 J5A
#define J330 J35 J1 J9A J2B J184 J83 JB1 JAE J81 J23
#define J331 J2 J2C J65 J0 J2 J5 JA J82 J54 J0
#define J332 J35 JD2 J25C J201 JC5 J1EB JE4 J25D J54 J0
#define J333 J1C9 J33 J1A J78 J2 JF3 J1A JD0 J3 J11
#define J334 J2 J1A J12B J66 JBC J2 J126 JDF J1A0 J1CB
#define J335 J1 J2D J2 J278 J1 J1 J2D J2 J1CA J1
#define J336 J1 J2D J2 J209 J1 J11A J1 J9D J2B J185
#define J337 J83 JB1 JAE J81 J23 J2 J2C J65 J0 J2
#define J338 J5 JA J82 J54 J0 J35 JD2 J25C J1FF JC5
#define J339 J1EB JE4 J25D J54 J0 J1C9 J33 J1A J78 J2
#define J33A JF3 J1A J177 J66 JBC J2 J126 JE1 J1A0 J1CB
#define J33B J1 J2D J2 J22D J1 J1 J2D J2 J209 J1
#define J33C J11A J1 JDE J2B J182 J83 JAA JAE J81 J23
#define J33D J2 JAA J54 J0 J35 JD2 J25C J232 JC5 J54
#define J33E J0 J1FA J75 J267 J1A J78 J2 J24 J51 J27
#define J33F J4B J0 JB J43 J8 JF1 J79 J2 J3F J5F
#define J340 J0 J151 J82 J29 J0 J1C9 J7F J1A J57 J2
#define J341 J268 J1 J1 J1 J25D J11A J1 J133 J2B J19C
#define J342 J42 J81 J23 J2 JD2 J25C J230 J25D J276 J162
#define J343 J148 J159 J176 J15C J190 J140 J277 J126 J14F J207
#define J344 J11A J1 J145 J2B J1AD J42 J81 J23 J2 J24
#define J345 J68 J34 J21 J2 JD2 J26F J23F JC5 J254 J24C
#define J346 J246 J24E J226 JC5 J26E JC5 J120 J49 J77 J2
#define J347 J2F J1D J189 J46 J2F J1C J14C J46 J2F J1E
#define J348 J154 J30 J46 J2F JC J12C J30 J46 J2F JE
#define J349 J69 J1 J1 J1 J125 J2B J199 J44 J2 J24
#define J34A J68 J34 J21 J2 JD2 J26F J23E JC5 J247 J25A
#define J34B J259 J25B J264 J226 JC5 J26E JC5 J120 J49 J77
#define J34C J2 J2F J1D J17C J46 J2F J1C J19D J46 J2F
#define J34D J1E J1BE J46 J2F JC J1DE J46 J2F JD J1CD
#define J34E J46 J2F JE J69 J1 J1 J1 J123 J2B J1B5
#define J34F J44 J2 J24 J68 J34 J21 J2 JD2 J26F J240
#define J350 JC5 J255 J253 J248 J226 JC5 J26E JC5 J120 J49
#define J351 J77 J2 J2F J1D J14A J46 J2F J1C J15A J46
#define J352 J2F J1E J149 J46 J2F JE J69 J1 J1 J1
#define J353 JE9 J81 J114 J42 J3F J22 J3F J84 J2 J81
#define J354 J54 J0 J12D J33 J1A J78 J2 J1A J17E J84
#define J355 J13 J11 J2 J5A J61 J1 J1 J5A J35 J1
#define J356 J1D7 J2B J18E J7D J81 J28 JA2 J83 JB1 JB0
#define J357 J2 J34 J21 J2 J24 J68 JD2 J26F J265 JC5
#define J358 J24F J249 J228 J227 J24B J245 JC5 J27B J26E JC5
#define J359 J120 J49 J77 J2 J2F J1D J1BF J46 J2F J1C
#define J35A J1BD J30 J46 J2F J1E J19A JAE J30 J46 J2F
#define J35B JC J1A1 JAE J30 J46 J2F JD J19B JB7 J30
#define J35C J46 J2F JE J69 J1 J1 J1 J1B7 J2B J1B2
#define J35D J7D J81 J28 JA2 J80 J2 J34 J21 J2 J24
#define J35E J68 JD2 J26F J265 JC5 J24F J24D J24A J245 JC5
#define J35F J27A J26E JC5 J120 J49 J77 J2 J2F J1D J1BF
#define J360 J46 J2F J1C J18D J30 J46 J2F J1E J1B6 J46
#define J361 J2F JE J69 J1 J1 J1 J236 J2B J117 J44
#define J362 J2 J81 J32 J0 J204 JC0 J26 J102 JD2 J26F
#define J363 J265 J275 JC5 J1E3 J13D J1E6 J11B J110 J1E9 J1BB
#define J364 J97 J0 J7A J26E J38 J0 J131 J55 J16A J1
#define J365 JEA J81 J109 J44 J2 J2C J65 J0 J2 J5
#define J366 JA J2C JC4 J0 J2 J5 JA J81 J32 J0
#define J367 J35 JD2 J26F J272 JC5 JC5 J1E3 JE4 J1E9 J150
#define J368 J103 J26E J5A JE6 J22 J91 J1 JE7 J2B JFC
#define J369 J7D JA2 J83 JB1 JB0 J2 J81 J19 J0 J35
#define J36A J1B J2 J19 J0 J11C J1A J37 J2 J266 J11A
#define J36B J1 J1 J34 J59 J49 JA1 J2 J2F J62 J18A
#define J36C J28 J83 JB3 J46 J2F J70 J18F J28 JA3 J46
#define J36D J1 J1 J24 J4E J2 J1F7 J81 J6C J0 J108
#define J36E J1FD J82 J7B J0 J10B J1F8 JAA J90 J0 J121
#define J36F J222 J1A JAC J2 J12F J1 JE7 JFB J9F JCD
#define J370 J12E J1AB J128 J1AF J137 J1E1 J5A JB J1
#define J371 J27C J27D J27E J27F J280 J281 J282 J283 J284 J285
#define J372 J286 J287 J288 J289 J28A J28B J28C J28D J28E J28F
#define J373 J290 J291 J292 J293 J294 J295 J296 J297 J298 J299
#define J374 J29A J29B J29C J29D J29E J29F J2A0 J2A1 J2A2 J2A3
#define J375 J2A4 J2A5 J2A6 J2A7 J2A8 J2A9 J2AA J2AB J2AC J2AD
#define J376 J2AE J2AF J2B0 J2B1 J2B2 J2B3 J2B4 J2B5 J2B6 J2B7
#define J377 J2B8 J2B9 J2BA J2BB J2BC J2BD J2BE J2BF J2C0 J2C1
#define J378 J2C2 J2C3 J2C4 J2C5 J2C6 J2C7 J2C8 J2C9 J2CA J2CB
#define J379 J2CC J2CD J2CE J2CF J2D0 J2D1 J2D2 J2D3 J2D4 J2D5
#define J37A J2D6 J2D7 J2D8 J2D9 J2DA J2DB J2DC J2DD J2DE J2DF
#define J37B J2E0 J2E1 J2E2 J2E3 J2E4 J2E5 J2E6 J2E7 J2E8 J2E9
#define J37C J2EA J2EB J2EC J2ED J2EE J2EF J2F0 J2F1 J2F2 J2F3
#define J37D J2F4 J2F5 J2F6 J2F7 J2F8 J2F9 J2FA J2FB J2FC J2FD
#define J37E J2FE J2FF J300 J301 J302 J303 J304 J305 J306 J307
#define J37F J308 J309 J30A J30B J30C J30D J30E J30F J310 J311
#define J380 J312 J313 J314 J315 J316 J317 J318 J319 J31A J31B
#define J381 J31C J31D J31E J31F J320 J321 J322 J323 J324 J325
#define J382 J326 J327 J328 J329 J32A J32B J32C J32D J32E J32F
#define J383 J330 J331 J332 J333 J334 J335 J336 J337 J338 J339
#define J384 J33A J33B J33C J33D J33E J33F J340 J341 J342 J343
#define J385 J344 J345 J346 J347 J348 J349 J34A J34B J34C J34D
#define J386 J34E J34F J350 J351 J352 J353 J354 J355 J356 J357
#define J387 J358 J359 J35A J35B J35C J35D J35E J35F J360 J361
#define J388 J362 J363 J364 J365 J366 J367 J368 J369 J36A J36B
#define J389 J36C J36D J36E J36F J370
#define J38A J371 J372 J373 J374 J375 J376 J377 J378 J379 J37A
#define J38B J37B J37C J37D J37E J37F J380 J381 J382 J383 J384
#define J38C J385 J386 J387 J388 J389
#define J38D J38A J38B J38C
#define J38E(__FOX__) __FOX__
J38E(J38D)

软件使用

CodeDisorder

  1. 选择 C/C++ 源文件进行混淆
  2. 为混淆后的代码添加的声明信息
  3. 勾选 行分模式 将以 为颗粒拆分代码,否则以 为颗粒拆分代码
  4. 混淆后的代码是否保留注释
  5. 代码的部分混淆,0 表示所有代码都混淆,50 表示50行之前的代码保持原状,50行之后的代码进行混淆
  6. 设置混淆后的代码的使用期限,0 表示混淆后的代码无使用期限,1440 表示混淆后的代码自混淆时间起有1440分钟(24小时)的使用期限
  7. 查看每次操作的混淆代码和原始代码的存档
  8. 若不想指定源文件进行混淆,可直接粘贴代码快速混淆

软件下载

下载链接:CodeDisorder

几年前制作的一套 C/C++ 程序题库,其目的是为了使学生在课堂上熟悉掌握 C/C++ 基础知识。程序自带检验和错误反馈功能,并有积分激励机制,当时学校机房电脑只配置了 Dev-C++ 开发环境, 所以为了便于学生使用,题库的设计也就以在 Dev-C++ 开发环境上使用方便为目的。学生使用效果不错,课上的学习氛围有所提高,后来也给过一些想学 C/C++ 的朋友,都觉得的确对于 C/C++ 初学者有一定的帮助。

这套程序题库有点类似于 Online Judge ,只不过它是在 Dev-C++ 本机环境中使用(其实也可以在其他开发环境例如 visual studiocode::blocks 中使用,但是没有 Dev-C++ 那么方便)。但与 Online Judge 的设计思想完全不同,Online Judge 是黑盒测试,并不会给使用者过多的反馈,代码粘贴到网页中执行也缺乏本机环境的编辑调试能力,它是为算法竞赛而设计;但这套程序题库的设计思想是教会 C/C++ 初学者基础知识,所以题库设计难度循序渐进,起到对初学者逐步建立起程序思维的作用,直接在开发环境中编辑和调试代码,有助于初学者对开发环境的熟悉掌握, 并且在程序执行结果不对的时候给予充足的提示信息以帮助初学者纠正错误。

虽然题库的使用非常简单,但每次还是少不了要教人一遍用法,为方便日后有人再问起,我写个使用教程。


  • 安装 Dev-C++ 开发环境

  • 下载题库并解压(先解压,别直接在压缩包中使用)

下载链接:cpp-quiz

  • 解压目录下的每个 *.cpp源文件是独立的一道题,启动 Dev-C++ 开发环境,拖拽其中一个文件到 Dev-C++ 开发环境的窗口界面

*.cpp to Dev-C++

  • 根据题目要求,补充代码并编译运行

build & run Dev-C++

  • 程序测试给出的反馈

test list

  • 如果全部测试通过,可按下回车查看总得分

score

  • 如果发生部分测试未通过,可按下回车查看错误提示

error

说起 Huffman 的算法原理其实很简单,难在实现过程中对细节的控制,比如 字串流 转换成 比特流比特流 转换回 字串流 ,这类操作极易出错;再比如要使 解码 过程效率更高,需要让 游标指针 在逐个获取 比特位 的过程中高效地从根节点移动到目标节点,从而获取目标节点对应的解码字符;再就是针对解码所必须的 字符集频率表 如何设计才能最大限度减少体积。总之我的体会就是,若要亲手实现一个各方面鲁棒性良好的 Huffman Code Program 其过程并不那么轻松。


输入数据测试

为检测 Huffman Tree 的构建是否正常,我写了一个测试功能,可以输入字符以及频率来构建一颗 Huffman Tree ,并打印 Huffman Tree树形图编码表,下面展示的是我以数据 {A:1, B:2, C:3, D:4} 的构建效果:

  • 输入数据构建 Huffman Tree

Input data to test

  • Huffman Tree树形图 展现

Print Huffman Tree

  • Huffman Tree编码表 展现

Print Huffman Code List


文件的字符集频率表的设计

下面说说文件的压缩和解压,文件存储不光要存储压缩数据,还需要在文件头部额外存储 字符集频率表 ,目的是为了文件解压时,可根据 字符集频率表 重新构建回 Huffman Tree ,进而在构建的 Huffman Tree 上将压缩数据解码成原始数据。 字符集频率表 应最大限度减少体积,这样才能降低文件的总体积。所以根据上述说法,文件内容将分为两部分: 文件头部信息块数据区文件头部信息块 内含 文件头标识字符集频率表

还是拿上面的数据 {A:1, B:2, C:3, D:4} 为案例,我的 文件头部信息块 设计如下:
File Header Info

文件头的两个字节是类型标识 :FX ,用来标识这是一个压缩文件,通过扫描文件头标识,可判断对该文件的操作是压缩还是解压。
文件头标识之后是 字符集频率表 ,第一个字节是表长,特别注意,它的值在 0 ~ 255 之间,表示的表长的范围是 1 ~ 256 之间,所以字符集 {A:1, B:2, C:3, D:4} 的表长是3而不是4。接下来以每5个字节代表一个字符信息块,其中1个字节存储字符编码剩下4个字节存储该字符的频率,例如 A 的频率是1,所以4个字节中存放的是 {0,0,0,1},由此可见我的设计尚有压缩空间,如果我用2个 比特位 标识该字符的频率所占用的字节数,那么4个字节的占用将压缩到1个字节,整个 字符集频率表 在理想状况下能缩小一倍以上。如我目前存储上述 字符集频率表 信息需要 1+5*4=21 字节,采用这种方式能压缩到 1+2*4=9 字节。这点留待以后再优化吧。


文件的压缩和解压测试

拿源文件本身来测试压缩和解压:

  • 读取 demo.c 源文件,构建 Huffman Tree 耗时1毫秒

Read File

  • Huffman Tree树形图 展现

Print Huffman Tree

  • Huffman Tree编码表 展现

Print Huffman Code List

  • 压缩程序源文件 demo.c 耗时9毫秒, 字符集频率表803 字节 ,原始数据 23565 字节,压缩后 15134 字节,压缩率 64.22% , 可见 Huffman 算法的压缩率取决于字符频次,如果频次差距越大压缩率越理想,总体来说,对二进制文件的压缩率偏低,对文本的压缩率在 50% 左右,其实也不算很高,所以压缩软件并不会单纯只用 Huffman 算法,而是多种压缩算法协同使用

Encode

  • 解压产生的压缩文件 encodefile ,耗时0.89毫秒

Decode


源程序

程序仅使用C的常用标准库函数,且编写采用 C89 标准,其目的是为了让程序拥有更广泛的适应性,以下是程序的完整代码,可供参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
/********************************************
* Huffman Code Demo
* Copyright (C) i@foxzzz.com
*
* C implementation of the Huffman Code's
* encoding and decoding.
*********************************************/

#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <sys/time.h>

/*数据列表长度*/
#define LIST_SIZE 256
/*构建Huffman树需要产生的森林长度*/
#define FOREST_SIZE (LIST_SIZE * 2 - 1)
/*单个数据产生的Huffman编码文本串的最大容量*/
#define CODE_MAX 2048
/*文件路径长度*/
#define PATH_MAX 1024
/*文件头标识*/
const char FILE_HEADER_FLAG[] = { 'F', 'X' };

/*节点标识*/
enum {
NODE_FLAG_ROOT, /*根节点*/
NODE_FLAG_LEFT, /*左孩子节点*/
NODE_FLAG_RIGHT /*右孩子节点*/
};

/*节点类型*/
enum {
NODE_TYPE_DATA, /*数据节点*/
NODE_TYPE_BLANK /*空节点*/
};

/*文件类型*/
enum {
FILE_TYPE_NULL, /*读取出错*/
FILE_TYPE_ENCODE, /*原始数据文件*/
FILE_TYPE_DECODE, /*压缩数据文件*/
};

/*字节类型*/
typedef unsigned char Byte;

/*Huffman树节点*/
typedef struct _tNode {
int type; /*节点类型*/
int data; /*节点数据*/
int weight; /*节点权重*/
char code[CODE_MAX]; /*Huffman编码*/
struct _tNode* left; /*左孩子*/
struct _tNode* right; /*右孩子*/
}Node, * pNode;

/*Huffman树信息*/
typedef struct _tHuffmanTree {
pNode root; /*根节点*/
int total /*总字节数*/;
}HuffmanTree, * pHuffmanTree;


/*得到当前时间戳*/
struct timeval startTimestamp() {
struct timeval stamp;
gettimeofday(&stamp, NULL);
return stamp;
}

/*计算从时间戳到当前时间的毫秒*/
double endTimestamp(struct timeval start) {
int diff_sec = 0;
double start_msec = 0;
double end_msec = 0;
struct timeval end;
gettimeofday(&end, NULL);
diff_sec = (int)(end.tv_sec - start.tv_sec);
start_msec = (double)start.tv_usec / 1000.0;
end_msec = (diff_sec * 1000) + ((double)end.tv_usec / 1000.0);
return (end_msec - start_msec);
}

/*创建Huffman树的数据节点*/
pNode createDataNode(int data, int weight) {
pNode node = (pNode)malloc(sizeof(Node));
memset(node, 0, sizeof(Node));
node->type = NODE_TYPE_DATA;
node->data = data;
node->weight = weight;
return node;
}

/*创建Huffman树的空节点*/
pNode createBlankNode(int weight) {
pNode node = (pNode)malloc(sizeof(Node));
memset(node, 0, sizeof(Node));
node->type = NODE_TYPE_BLANK;
node->weight = weight;
return node;
}

/*将Huffman树节点添加到森林列表*/
void addNodeToList(pNode nodelist[], int size, pNode node) {
int index;
for (index = 0; index < size; ++index) {
if (nodelist[index] == NULL) {
/*从表中找到空位放入新节点*/
nodelist[index] = node;
break;
}
}
}

/*从森林列表弹出权重最低的Huffman树节点*/
pNode popMinNodeFromList(pNode nodelist[], int size) {
int min = -1;
int index;
for (index = 0; index < size; ++index) {
if (nodelist[index]) {
if (min == -1) {
min = index;
} else {
if (nodelist[min]->weight > nodelist[index]->weight) {
/*当发现存在更小权重节点时候更新记录*/
min = index;
}
}
}
}
if (min != -1) {
pNode node = nodelist[min];
nodelist[min] = NULL;
return node;
}
return NULL;
}

/*通过递归遍历方式为Huffman树中的的所有节点产生Huffman编码*/
void generateHuffmanCode(pNode root) {
if (root) {
if (root->left) {
strcpy(root->left->code, root->code);
strcat(root->left->code, "0");
generateHuffmanCode(root->left);
}
if (root->right) {
strcpy(root->right->code, root->code);
strcat(root->right->code, "1");
generateHuffmanCode(root->right);
}
}
}

/*传入权重表构建Huffman树*/
pNode buildHuffmanTree(int times[]) {
pNode nodelist[FOREST_SIZE] = { NULL };
pNode root = NULL;
struct timeval startstamp = startTimestamp();
int index;
/*创建森林表*/
for (index = 0; index < LIST_SIZE; ++index) {
if (times[index] > 0) {
/*将所有节点逐个放入森林表*/
addNodeToList(nodelist, FOREST_SIZE, createDataNode(index, times[index]));
}
}
/*构建Huffman树*/
while (1) {
pNode left = popMinNodeFromList(nodelist, FOREST_SIZE);
pNode right = popMinNodeFromList(nodelist, FOREST_SIZE);
if (right == NULL) {
/*当森林中只剩下一颗树节点的时候表示整个Huffman树构建完成*/
root = left;
break;
} else {
pNode node = createBlankNode(left->weight + right->weight);
node->left = left;
node->right = right;
/*每次从森林表中取出两个最小的节点,并创建新节点重新放入森林表*/
addNodeToList(nodelist, FOREST_SIZE, node);
}
}
generateHuffmanCode(root);
printf(" bulid huffman tree : %lf msc\n", endTimestamp(startstamp));
return root;
}

/*在Huffman树中前进一步*/
pNode setpHuffmanTree(pNode root, int flag) {
switch (flag) {
case NODE_FLAG_LEFT:
return root->left;
case NODE_FLAG_RIGHT:
return root->right;
}
return NULL;
}

/*通过后序遍历的方式销毁Huffman树*/
void destroyHuffmanTree(pNode root) {
if (root) {
destroyHuffmanTree(root->left);
destroyHuffmanTree(root->right);
free(root);
}
}

/*从文件构建Huffman树*/
pNode buildHuffmanTreeFromFile(FILE* input) {
int times[LIST_SIZE] = { 0 };
Byte byte;
while (fread(&byte, sizeof(byte), 1, input) == 1) {
++times[byte];
}
return buildHuffmanTree(times);
}

/*计算Huffman树的权重总值*/
int countHuffmanTreeWeightTotal(pNode root) {
int total = 0;
if (root) {
/*只获取有效字符节点*/
if (root->type == NODE_TYPE_DATA) {
total = root->weight;
}
total += countHuffmanTreeWeightTotal(root->left);
total += countHuffmanTreeWeightTotal(root->right);
}
return total;
}

/*通过递归遍历将Huffman树转换成Huffman表*/
void convertTreeToList(pNode root, pNode nodelist[]) {
if (root) {
/*只获取有效字符节点*/
if (root->type == NODE_TYPE_DATA) {
nodelist[root->data] = root;
}
convertTreeToList(root->left, nodelist);
convertTreeToList(root->right, nodelist);
}
}

/*清理Huffman表中的空指针,并返回实际的表元素数量*/
int trimNodeList(pNode nodelist[], int size) {
int count = 0;
int index;
for (index = 0; index < size; ++index) {
pNode node = nodelist[index];
if (node) {
nodelist[count++] = node;
}
}
return count;
}

/*对文件数据进行Huffman编码*/
int encodeFileData(pNode root, FILE* input, FILE* output) {
int total = 0;
int count = 0;
if (root) {
Byte byte;
int buffer = 0;
pNode nodelist[LIST_SIZE] = { NULL };
/*将Huffman树转换成Huffman表*/
convertTreeToList(root, nodelist);
while (fread(&byte, sizeof(byte), 1, input) == 1) {
char* cursor = nodelist[byte]->code;
while (*cursor) {
buffer <<= 1;
if (*cursor == '0') {
buffer |= 0;
} else {
buffer |= 1;
}
++count;
if (count == 8) {
Byte byte = (Byte)buffer;
fwrite(&byte, sizeof(byte), 1, output);
count = 0;
buffer = 0;
++total;
}
++cursor;
}
}
if (count > 0) {
buffer <<= (8 - count);
char byte = (char)buffer;
fwrite(&byte, 1, 1, output);
++total;
}
}
return total;
}

/*对文件数据进行Huffman解码*/
void decodeFileData(pNode root, FILE* input, FILE* output, int count) {
if (root) {
Byte byte;
pNode cursor = root;
while (fread(&byte, sizeof(byte), 1, input) == 1) {
int buffer = byte;
int index;
for (index = 0; index < 8; ++index) {
buffer <<= 1;
if (!cursor->left || !cursor->right) {
Byte data = (Byte)cursor->data;
fwrite(&data, sizeof(data), 1, output);
if (--count == 0) {
break;
}
cursor = root;
}
if (buffer & ~0xff) {
cursor = setpHuffmanTree(cursor, NODE_FLAG_RIGHT);
} else {
cursor = setpHuffmanTree(cursor, NODE_FLAG_LEFT);
}
buffer &= 0xff;
}
}
}
}

/*检测是否是可显示字符*/
int canShowChar(char ch) {
return (ch > 32 && ch < 127);
}

/*通过递归遍历方式打印Huffman树*/
void outputHuffmanTree(FILE* output, pNode root, int flag) {
if (root) {
int index;
char content[128] = { 0 };
const char* flagname[] = { "ROOT", "LEFT", "RIGHT" };
int offset = (int)strlen(root->code) - 1;
for (index = 0; index < offset; ++index) {
if (root->code[index] == '0') {
fprintf(output, " │ ");
} else {
fprintf(output, " ");
}
}
switch (root->type) {
case NODE_TYPE_DATA:
sprintf(content, "> %-6s #%-4d 0x%02X : '%c'", flagname[flag], root->weight, root->data, canShowChar((char)root->data) ? root->data : ' ');
break;
case NODE_TYPE_BLANK:
sprintf(content, "[+] %-6s #%-4d", flagname[flag], root->weight);
break;
}
switch (flag) {
case NODE_FLAG_ROOT:
fprintf(output, "%s", content);
break;
case NODE_FLAG_LEFT:
fprintf(output, " ├─%s", content);
break;
case NODE_FLAG_RIGHT:
fprintf(output, " └─%s", content);
break;
}
if (root->type == NODE_TYPE_DATA) {
fprintf(output, " CODE : %s\n", root->code);
} else {
fprintf(output, "\n");
}
outputHuffmanTree(output, root->left, NODE_FLAG_LEFT);
outputHuffmanTree(output, root->right, NODE_FLAG_RIGHT);
}
}

/*打印Huffman树*/
void printHuffmanTree(FILE* output, pNode root) {
fprintf(output, " *******************************\n");
fprintf(output, " Print Huffman Tree\n");
fprintf(output, "\n");
outputHuffmanTree(output, root, NODE_FLAG_ROOT);
fprintf(output, "\n");
}

/*将Huffman表中的数据输出成编码和权重统计表*/
void printHuffmanList(FILE* output, pNode root) {
pNode nodelist[LIST_SIZE] = { NULL };
int index;
int listcount = 0;
/*将Huffman树转换成Huffman表*/
convertTreeToList(root, nodelist);
listcount = trimNodeList(nodelist, LIST_SIZE);
fprintf(output, " *******************************\n");
fprintf(output, " # Print Huffman Code List #\n");
fprintf(output, "\n");
fprintf(output, " Total : %d\n", listcount);
fprintf(output, "\n");
fprintf(output, " %-7s%-10s%-10s%s\n", "ASCII", "Char", "Weight", "Code");
for (index = 0; index < listcount; ++index) {
pNode node = nodelist[index];
Byte ch = (Byte)node->data;
if (canShowChar((char)ch)) {
/*可显示字符的输出*/
fprintf(output, " %-7d%-10c%-10d%s\n", ch, ch, node->weight, node->code);
} else {
/*不可显示字符的输出*/
fprintf(output, " %-7d%-10s%-10d%s\n", ch, "NOShow", node->weight, node->code);
}
}
printf("\n");
}

/*统计输入的字符权重*/
void contUserInputTimes(int times[]) {
int index, count;
printf(" *******************************\n");
printf(" # Input data to test #\n");
printf("\n");
printf(" Number of input nodes : ");
scanf("%d", &count);
printf(" Enter the letters and weights for each node : \n");
for (index = 0; index < count; ++index) {
char str[128] = { 0 };
int weight = 0;
printf(" Char : ");
scanf("%s", str);
printf(" Weight : ");
scanf("%d", &weight);
times[(int)str[0]] = weight;
}
}

/*输入数据构建Huffman树选项*/
pNode inputDataToBuildHuffmanTreeOption() {
int times[LIST_SIZE] = { 0 };
contUserInputTimes(times);
return buildHuffmanTree(times);
}

/*获取输入选项*/
int inputOption(int begin, int end) {
do {
int opt;
if (scanf("%d", &opt) == 1) {
if (opt >= begin && opt <= end) {
return opt;
} else {
printf(" error : The input range should be between %d and %d.\n", begin, end);
}
} else {
printf(" error : Please enter integer type.\n");
/*清空缓冲区*/
setbuf(stdin, NULL);
}
} while (1);
}

/*检测文件类型*/
int getFileType(const char filename[]) {
int type = FILE_TYPE_NULL;
FILE* input = fopen(filename, "rb");
if (input) {
char buffer[2] = { 0 };
type = FILE_TYPE_ENCODE;
if (fread(buffer, 2, 1, input) == 1) {
if (buffer[0] == FILE_HEADER_FLAG[0] && buffer[1] == FILE_HEADER_FLAG[1]) {
type = FILE_TYPE_DECODE;
}
}
fclose(input);
}
return type;
}

/*写入文件头信息(文件头含文件头标识和字符权重集)*/
int writeFileHeader(pNode root, FILE* output) {
pNode nodelist[LIST_SIZE] = { NULL };
Byte total = 0;
int index;
/*写入文件头标识*/
fwrite(FILE_HEADER_FLAG, 2, 1, output);
convertTreeToList(root, nodelist);
/*
* 为节省空间字符集总量存储为1个字节
* 总量1用0表示,总量256用255表示
* 所以总量 - 1
*/
total = (Byte)(trimNodeList(nodelist, LIST_SIZE) - 1);
/*写入字符集总数*/
fwrite(&total, sizeof(total), 1, output);
/*写入每个字符以及权重*/
for (index = 0; index <= total; ++index) {
pNode node = nodelist[index];
Byte byte = (Byte)node->data;
fwrite(&byte, sizeof(byte), 1, output);
fwrite(&node->weight, sizeof(node->weight), 1, output);
}
/*返回写入的文件头总字节数*/
return (total * 5 + 1 + 2);
}

/*读取文件头信息(读取字符权重集)*/
void readFileHeader(FILE* input, int times[]) {
Byte total;
int index;
/*跳过文件头*/
fseek(input, 2, SEEK_CUR);
fread(&total, sizeof(total), 1, input);
for (index = 0; index <= total; ++index) {
Byte byte;
int weight;
fread(&byte, sizeof(byte), 1, input);
fread(&weight, sizeof(weight), 1, input);
times[byte] = weight;
}
}

/*对文件进行编码*/
void toEncode(pNode root, FILE* input) {
char filename[PATH_MAX] = { 0 };
FILE* output = NULL;
printf(" *******************************\n");
printf(" # Write File #\n");
printf("\n");
printf(" write file name : ");
scanf("%s", filename);
output = fopen(filename, "wb");
if (output) {
int rawsize = countHuffmanTreeWeightTotal(root);
int header_size = writeFileHeader(root, output);
if (input) {
struct timeval startstamp = startTimestamp();
int compressedsize = encodeFileData(root, input, output);
printf("\n");
printf(" Elapsed Time : %lf msc\n", endTimestamp(startstamp));
printf(" Character Set : %d Bytes\n", header_size);
printf(" Compressed Data : %d Bytes\n", compressedsize);
printf(" Raw Data : %d Bytes\n", rawsize);
printf(" Compression Ratio : %.2lf%%\n", (compressedsize / (double)rawsize) * 100);
printf("\n");
printf(" Execute successfully.\n");
} else {
printf(" error : Failed to read file.\n");
}
fclose(output);
} else {
printf(" error : Failed to write file.\n");
}
printf("\n");
}

/*对文件进行解码*/
void toDecode(pNode root, FILE* input) {
char filename[PATH_MAX] = { 0 };
FILE* output = NULL;
printf(" *******************************\n");
printf(" # Write File #\n");
printf("\n");
printf(" write file name : ");
scanf("%s", filename);
output = fopen(filename, "wb");
if (output) {
struct timeval startstamp = startTimestamp();
decodeFileData(root, input, output, countHuffmanTreeWeightTotal(root));
printf(" Elapsed Time : %lf msc\n", endTimestamp(startstamp));
printf("\n");
printf(" Execute successfully.\n");
fclose(output);
} else {
printf(" error : Failed to write file.\n");
}
printf("\n");
}

/*文件编码选项*/
void encodeFileOption(const char filename[]) {
FILE* input = fopen(filename, "rb");
if (input) {
pNode root = buildHuffmanTreeFromFile(input);
if (root) {
do {
int option;
printf(" *******************************\n");
printf(" # Encode File #\n");
printf("\n");
printf(" 1 > Print Huffman Tree\n");
printf(" 2 > Print Huffman Code List\n");
printf(" 3 > Encode File\n");
printf(" 0 > Back\n");
printf("\n");
option = inputOption(0, 3);
if (option == 0) break;
switch (option) {
case 1:
printHuffmanTree(stdout, root);
break;
case 2:
printHuffmanList(stdout, root);
break;
case 3:
/*重置文件指针到文件头*/
fseek(input, 0, SEEK_SET);
toEncode(root, input);
break;
}
} while (1);
destroyHuffmanTree(root);
} else {
printf(" error : Failed to build Huffman tree.\n");
}
fclose(input);
} else {
printf(" error : Failed to read file.\n");
}
}

/*文件解码选项*/
void decodeFileOption(const char filename[]) {
FILE* input = fopen(filename, "rb");
if (input) {
int tell = 0;
int times[LIST_SIZE] = { 0 };
readFileHeader(input, times);
/*记录文件数据区位置*/
tell = (int)ftell(input);
pNode root = buildHuffmanTree(times);
if (root) {
do {
int option;
printf(" *******************************\n");
printf(" # Decode File #\n");
printf("\n");
printf(" 1 > Print Huffman Tree\n");
printf(" 2 > Print Huffman Code List\n");
printf(" 3 > Decode File\n");
printf(" 0 > Back\n");
printf("\n");
option = inputOption(0, 3);
if (option == 0) break;
switch (option) {
case 1:
printHuffmanTree(stdout, root);
break;
case 2:
printHuffmanList(stdout, root);
break;
case 3:
/*将文件指针定位到数据区*/
fseek(input, tell, SEEK_SET);
toDecode(root, input);
break;
}
} while (1);
destroyHuffmanTree(root);
} else {
printf(" error : Failed to build Huffman tree.\n");
}
fclose(input);
} else {
printf(" error : Failed to read file.\n");
}
}

/*读文件选项*/
void readFileOption() {
char filename[PATH_MAX] = { 0 };
printf(" *******************************\n");
printf(" # Read File #\n");
printf("\n");
printf(" input file name : ");
scanf("%s", filename);
printf("\n");
switch (getFileType(filename)) {
case FILE_TYPE_ENCODE:
encodeFileOption(filename);
break;
case FILE_TYPE_DECODE:
decodeFileOption(filename);
break;
default:
printf(" error : Failed to read file.\n");
}
}

/*测试选项*/
void inputDataToTestOption() {
pNode root = inputDataToBuildHuffmanTreeOption();
if (root) {
do {
int option;
printf(" *******************************\n");
printf("\n");
printf(" 1 > Print Huffman Tree\n");
printf(" 2 > Print Huffman Code List\n");
printf(" 0 > Back\n");
printf("\n");
option = inputOption(0, 2);
if (option == 0)break;
switch (option) {
case 1:
printHuffmanTree(stdout, root);
break;
case 2:
printHuffmanList(stdout, root);
break;
}
} while (1);
destroyHuffmanTree(root);
}
}

/*Huffman功能演示*/
void huffmanDemo() {
do {
int option;
printf(" *******************************\n");
printf(" # Huffman Tree Demo #\n");
printf("\n");
printf(" 1 > Read file\n");
printf(" 2 > Input data to test\n");
printf(" 0 > Quit\n");
printf("\n");
option = inputOption(0, 2);
if (option == 0) break;
switch (option) {
case 1:
readFileOption();
break;
case 2:
inputDataToTestOption();
break;
}
} while (1);
}

int main() {
huffmanDemo();
return 0;
}

数据结构有 堆排序 ,但 堆排序 在众多排序算法中的能力并不突出,这是因为排序并不体现 的优势,而只能算是它附带的能力。

的优势在于当从 中弹出最小值(针对 小根堆 而言)时,或将某个数值放入 中时,相比其他数据结构,它需要检索和调整其他数据元素的操作最少,效率最优,所以它更适合用来实现 优先队列,因为 优先队列 每次只会获取队列中优先级最高的数据元素,而不会操作其他数据元素,这和 小跟堆 每次只能弹出最小值而不能弹出其他值的行为一致。因此如 Dijkstra 算法在面临规模上万的顶点个数的时候,其内部权值检索环节采用 优先队列 实现的话能显著提升算法效率。除 Dijkstra 算法外,还有像 Huffman 树的构建过程中,需要频繁从当前森林中获取根节点权值最小的树,直到森林中只剩一棵树为止,这个过程若采用 优先队列 也能显著改善性能,总之, 优先队列 在各类算法中的应用非常广泛。

堆通过两个关键性操作 shift up(上浮)和 shift down (下沉)维护堆内数据,shift up 用于往堆中插入新的数据元素,shift down 用于从堆中弹出数据元素。

以下是我用C语言实现的 小根堆 的完整代码,以供参考。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/********************************************
* Min-Heap
* Copyright (C) i@foxzzz.com
*
* C implementation of the Min-Heap.
*********************************************/

#define _CRT_SECURE_NO_WARNINGS /*for visual studio*/

#include <stdio.h>
#include <memory.h>
#include <malloc.h>
#include <assert.h>

/*!
* @brief 堆结构体
* @data 数据元素
* @capacity 数组容量
* @len 元素数量
*/
typedef struct _tHeap {
int* data;
int capacity;
int len;
} Heap, * pHeap;

/*!
* @brief 创建堆结构体
* @param[in] capacity 指定数组容量
* @return 空指针失败 其他成功
*/
pHeap createHeap(int capacity) {
pHeap heap = (pHeap)malloc(sizeof(Heap));
assert(heap);
assert(capacity > 0);
memset(heap, 0, sizeof(Heap));
heap->capacity = capacity;
heap->data = (int*)malloc(sizeof(heap->data[0]) * heap->capacity);
assert(heap->data);
return heap;
}

/*!
* @brief 销毁堆结构体
* @param[in] heap 需要销毁的结构体指针
*/
void destroyHeap(pHeap heap) {
if (heap) {
if (heap->data) {
free(heap->data);
heap->data = NULL;
}
free(heap);
}
}

/*!
* @brief 元素上浮
* @param[in] heap 需要操作的堆结构指针
* @param[in] position 指定要上移的元素
* - position 是元素编号,所以对应到数组元素时候需要 -1
*/
void shiftUp(pHeap heap, int position) {
while (position >> 1) {
int parent = position >> 1;
/*与父元素进行比较,如果大于父元素则进行交换*/
if (heap->data[parent - 1] > heap->data[position - 1]) {
int temp = heap->data[parent - 1];
heap->data[parent - 1] = heap->data[position - 1];
heap->data[position - 1] = temp;
position = parent;
} else break;
}
}

/*!
* @brief 元素下沉
* @param[in] heap 需要操作的堆结构指针
* @param[in] position 指定要下沉的元素
* - position 是元素编号,所以对应到数组元素时候需要 -1
*/
void shiftDown(pHeap heap, int position) {
while (position << 1 <= heap->len) {
int target = position << 1;
/*指定左右孩子中的较小者*/
if (target + 1 <= heap->len) {
if (heap->data[target] < heap->data[target - 1]) {
target += 1;
}
}
/*父节点大于较小者则交换*/
if (heap->data[position - 1] > heap->data[target - 1]) {
int temp = heap->data[target - 1];
heap->data[target - 1] = heap->data[position - 1];
heap->data[position - 1] = temp;
position = target;
} else break;
}
}

/*!
* @brief 插入元素
* @param[in] heap 需要操作的堆结构指针
* @param[in] v 需要插入的元素
*/
void insertHeap(pHeap heap, int v) {
/*容量满了按1倍扩容*/
if (heap->capacity == heap->len) {
heap->capacity <<= 1;
heap->data = (int*)realloc(heap->data, heap->capacity);
assert(heap->data);
}
/*插入到堆尾*/
heap->data[heap->len++] = v;
/*调整堆结构,对堆尾元素进行上浮*/
shiftUp(heap, heap->len);
}

/*!
* @brief 返回对顶元素,此操作要求堆不为空
* @param[in] heap 需要操作的堆结构指针
* @return 返回对顶元素
*/
int topHeap(pHeap heap) {
return heap->data[0];
}

/*!
* @brief 弹出对顶元素
* @param[in] heap 需要操作的堆结构指针
*/
void popHeap(pHeap heap) {
if (heap->len > 0) {
/*将堆尾元素放入堆顶*/
heap->data[0] = heap->data[heap->len - 1];
--heap->len;
/*调整堆结构,对新的堆顶元素进行下沉*/
shiftDown(heap, 1);
}
}

int main() {
/*create heap*/
pHeap heap = createHeap(16);

/*insert value*/
insertHeap(heap, 3);
insertHeap(heap, 1);
insertHeap(heap, 4);
insertHeap(heap, 7);
insertHeap(heap, 6);
insertHeap(heap, -4);

/*pop value & print*/
while (heap->len) {
int v = topHeap(heap);
popHeap(heap);
printf("%d ", v);
}

/*destroy heap*/
destroyHeap(heap);
return 0;
}

今天又看到群里有人讨论 C++deletedelete[] 的区别,表层原因大家都了解,因为教科书上说得很明白:newdelete 需配对使用, new[]delete[] 需配对使用。

但若问起在什么情况下针对 new[] 申请的资源可以使用 delete 释放而不会有任何问题,能讲清楚这点的人就很少了。因为这涉及到对 newdeletenew[]delete[] 内部实现机制的理解。

根本原因在于, delete 需要调用内存中一个元素的析构函数,而 delete[] 需要调用内存中若干个元素的析构函数,这里就牵涉出一个问题—— delete[] 是如何知道内存中元素的数量的?我们知道 delete[] 中的 [] 并不会传入参数,所以这个数量不会是 delete[] 传过来的,而是在 new[] 的时候保存的,只有这样才得以在 delete[] 的时候依据元素数量逐个调用析构函数。

接下来说 new[] 如何存储这个数量,首先它会动态申请一段内存,然后在这段内存的首地址空间中存入元素数量,在这个空间之后的内存分配给各元素,new[] 的返回值并不是这段动态内存空间的首地址,而是动态内存空间中存放第一个元素的内存地址。

以上说的是 delete[] 需要调用元素析构函数的情况,但是C++的哲学是 Zero-cost Abstraction,所以对于并没有显式定义析构函数的 struct/class 的对象元素来说,并不需要为其产生析构函数的代码,也就不需要在 delete[] 的时候调用元素的析构函数以增加无谓的运算开销,那么, new[] 也就不用存储这个元素数量。还有一种情况就是如 int 等基本类型作为空间元素的时候,也不存在析构函数的调用,所以跟没有显示定义析构函数的对象元素一样:在 new[] 时候不需要存储元素数量,在 delete[] 时候不需要调用析构函数。

综上所述, new[]delete[] 的具体行为受对象元素是否存在必须调用析构函数而有所不同。

一图胜千言,我画了三张图来展现上面说的三种元素情况:

  • int 作为基本类型:

int *ptr = new int[5]

  • 定义了一个 class A ,但是 A 并没有显式定义析构函数:

A *ptr = new A[5]

  • 定义了一个 class B,并且 B 显式定义了析构函数:

B *ptr = new B[5]

可以看出,对于 int *ptr = new int[5]A *ptr = new A[5] ,因为不涉及存储元素数量和对析构函数的调用,所以 deletedelete[] 的操作都仅仅是将传入的地址进行释放而不做其他额外事情。这种情况下,你使用 delete 或者 delete[] 都不会存在任何问题。

但是对于 B *ptr = new B[5] 却一定要使用 delete[] ,因为传过来的并不是真正的动态内存首地址, delete[] 的内部处理就会变成从传入的内存地址往前偏移获取真正的动态内存首地址,从该首地址空间获取到元素的数量,然后通过数量逐个调用元素的析构函数,完了再用得到的内存首地址释放动态内存。但若使用 delete 就会只调用第一个元素的析构函数,并且将第一个元素的地址作为动态内存首地址进行释放,但是释放错误的内存地址(非申请时候动态内存的首地址)将发生严重错误,如在 visual studio 中会直接触发程序异常并崩溃。

接下来思考另一种情况,如果 B *ptr = new B 操作后使用 delete[] 释放呢?这也会产生非常严重的错误,因为它会根据这个内存地址往前偏移获取数量,但是这个数量值是个不确定的值,所以接下来发生的行为就是在指针越界访问的情况下调用了无数次析构函数,而这些内存空间中并不存在有效元素,该行为将发生程序崩溃,即便该过程程序照常执行,接下来用偏移地址释放内存也会崩溃,总之,程序执行到此已经走火入魔了。

这几天需要使用 pthread 实现了一个线程同步功能的 Queue,过程中踩了两个坑:

  • pthread_cond_wait() 需要放在 pthread_mutex_lock()pthread_mutex_unlock() 之中,而不是之外,否则将发生死锁,这里的概念有点绕,需要理解 pthread_cond_wait() 是会释放当前的 lock,以便其他线程进入临界区,当其他线程 pthread_cond_signal() 的时候,wait 线程被唤醒,又重新获得锁;

  • pthread_cond_wait() 唤醒后需要再次条件判断,并且条件判断形式必须是 while 而不能是 if,之所以必须这样做的原因是pthread_cond_signal() 可能唤醒多个正处于 wait 状态的线程(多cpu情况),所以被唤醒的线程需要再次检测是否真有数据需要处理,如不需要处理应当继续进入 wait 以等待下次唤醒。

我将 Queue 实现成可支持一对一、一对多、多对一、多对多的线程同步机制,并写了一个简单的生产者消费者模型用以测试。完整程序如下,测试环境是 ubuntu 20.04

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
/********************************************
* Queue with thread synchronization
* Copyright (C) i@foxzzz.com
*
* Using pthread implementation.
* Can be used in the producer-consumer model
* of one-to-one, one-to-many, many-to-one,
* many-to-many patterns.
*********************************************/

#include <iostream>
#include <sstream>
#include <vector>
#include <cstdio>
#include <sys/time.h>
#include <pthread.h>
#include <unistd.h>

/*!
* @brief queue with thread synchronization
*/
template<typename T>
class Queue {
public:
Queue(int capacity) :
front_(0),
back_(0),
size_(0),
capacity_(capacity),
cond_send_(PTHREAD_COND_INITIALIZER),
cond_receive_(PTHREAD_COND_INITIALIZER),
mutex_(PTHREAD_MUTEX_INITIALIZER) {
arr_ = new T[capacity_];
}

~Queue() {
delete[] arr_;
}

public:
/*!
* @brief data entry queue
* @param[in] data needs to be put into the queue
*/
void send(const T& data) {
pthread_mutex_lock(&mutex_);
while (full()) {
pthread_cond_wait(&cond_receive_, &mutex_);
}
enqueue(data);
pthread_mutex_unlock(&mutex_);
pthread_cond_signal(&cond_send_);
}

/*!
* @brief retrieve data from the queue
* @param[out] data retrieved from the queue
*/
void receive(T& data) {
pthread_mutex_lock(&mutex_);
while (empty()) {
pthread_cond_wait(&cond_send_, &mutex_);
}
dequeue(data);
pthread_mutex_unlock(&mutex_);
pthread_cond_signal(&cond_receive_);
}

private:
void enqueue(const T& data) {
arr_[back_] = data;
back_ = (back_ + 1) % capacity_;
++size_;
}

void dequeue(T& data) {
data = arr_[front_];
front_ = (front_ + 1) % capacity_;
--size_;
}

bool full()const {
return (size_ == capacity_);
}

bool empty()const {
return (size_ == 0);
}

private:
T* arr_;
int front_;
int back_;
int size_;
int capacity_;
pthread_cond_t cond_send_;
pthread_cond_t cond_receive_;
pthread_mutex_t mutex_;
};

/*!
* @brief a demonstration of queue operations
*/
template<typename T, typename Make>
class Demo {
public:
Demo(int capacity) :
queue_(capacity) {
start();
}

public:
/*!
* @brief generate the data and queue it(for producer thread)
* @param[in] origin The starting value of the data
* @param[in] count The amount of data to be generated
* @param[in] interval the time interval(ms) to enter the queue
*/
void send(int origin, int count, int interval) {
Make make;
while (count--) {
T data = make(origin);
queue_.send(data);
print("send", data);
usleep(interval * 1000);
}
}

/*!
* @brief retrieve data from the queue(for consumer thread)
* @param[in] interval the time interval(ms) to enter the queue
*/
void receive(int interval) {
while (true) {
T data;
queue_.receive(data);
print("receive", data);
usleep(interval * 1000);
}
}

private:
void print(const char* name, const T& data) {
char buffer[256] = { 0 };
sprintf(buffer, "[%-4lu ms][pid %lu][%-10s] ", elapsedMS(), pthread_self(), name);
std::stringstream ss;
ss << buffer;
ss << data;
ss << "\n";
std::cout << ss.str();
}

private:
void start() {
gettimeofday(&start_time_, nullptr);
}

long elapsedMS() const {
struct timeval current;
gettimeofday(&current, nullptr);
return diffMS(start_time_, current);
}

static long diffMS(const timeval& start, const timeval& end) {
long seconds = end.tv_sec - start.tv_sec;
long useconds = end.tv_usec - start.tv_usec;
return (long)(((double)(seconds) * 1000 + (double)(useconds) / 1000.0) + 0.5);
}

private:
timeval start_time_;
Queue<T> queue_;
};

/*!
* @brief generates integer data
*/
class IntMake {
public:
IntMake() : count_(0) {

}

public:
int operator() (int origin) {
return (origin + count_++);
}

private:
int count_;
};

/*!
* @brief thread type
*/
enum {
TYPE_THREAD_SEND,
TYPE_THREAD_RECEIVE
};

/*!
* @brief thread arguments
*/
struct Args {
Args(Demo<int, IntMake>& demo, int type, int interval) :
demo(demo),
type(type),
interval(interval),
origin(0),
count(0) {

}

Args(Demo<int, IntMake>& demo, int type, int interval, int origin, int count) :
demo(demo),
type(type),
interval(interval),
origin(origin),
count(count) {

}

Demo<int, IntMake>& demo;
int type;
int interval;
int origin;
int count;
};

/*!
* @brief thread info
*/
struct ThreadInfo {
ThreadInfo(const Args& args) :
tid(0),
args(args) {

}
pthread_t tid;
Args args;
};

/*!
* @brief producer thread function
*/
void* thread_func_send(void* arg) {
Args* args = (Args*)arg;
args->demo.send(args->origin, args->count, args->interval);
return nullptr;
}

/*!
* @brief consumer thread function
*/
void* thread_func_receive(void* arg) {
Args* args = (Args*)arg;
args->demo.receive(args->interval);
return nullptr;
}

/*!
* @brief start to work
*/
void work(std::vector<ThreadInfo>& list) {
for (auto& it : list) {
switch (it.args.type) {
case TYPE_THREAD_SEND:
pthread_create(&it.tid, nullptr, thread_func_send, &it.args);
break;
case TYPE_THREAD_RECEIVE:
pthread_create(&it.tid, nullptr, thread_func_receive, &it.args);
break;
}
}

for (auto& it : list) {
pthread_join(it.tid, nullptr);
}
}

int main() {
Demo<int, IntMake> demo(10);

//configuration of the threads
std::vector<ThreadInfo> list = {
ThreadInfo(Args(demo, TYPE_THREAD_SEND, 2, 1, 50)),
ThreadInfo(Args(demo, TYPE_THREAD_RECEIVE, 2)),
};

work(list);
return 0;
}

程序配置了一个生产端线程,一个消费端线程,生产端和消费端效率都设置成2ms:

1
2
3
4
5
6
7
Demo<int, IntMake> demo(10);

//configuration of the threads
std::vector<ThreadInfo> list = {
ThreadInfo(Args(demo, TYPE_THREAD_SEND, 2, 1, 50)),
ThreadInfo(Args(demo, TYPE_THREAD_RECEIVE, 2)),
};

从程序打印输出结果看,生产端和消费端类似于回合制:
One to One

修改配置,降低消费端效率,由原来的2ms修改为20ms:

1
2
3
4
5
6
7
Demo<int, IntMake> demo(10);

//configuration of the threads
std::vector<ThreadInfo> list = {
ThreadInfo(Args(demo, TYPE_THREAD_SEND, 2, 1, 50)),
ThreadInfo(Args(demo, TYPE_THREAD_RECEIVE, 20)),
};

从程序打印输出结果看,当队列满后(队列容量设置为10),生产端需要等待消费端从队列中拿走数据后方可再生产:
One to One

修改配置,降低生产端效率,由原来的2ms修改为20ms:

1
2
3
4
5
6
7
Demo<int, IntMake> demo(10);

//configuration of the threads
std::vector<ThreadInfo> list = {
ThreadInfo(Args(demo, TYPE_THREAD_SEND, 20, 1, 50)),
ThreadInfo(Args(demo, TYPE_THREAD_RECEIVE, 2)),
};

从程序打印输出结果看,生产端和消费端又类似于回合制,这是因为消费端效率高,它得等到生产端生产:
One to One

修改配置,生产端数量增加到3,消费端数量不变,生产端效率是20ms,消费端效率是2ms:

1
2
3
4
5
6
7
8
9
Demo<int, IntMake> demo(10);

//configuration of the threads
std::vector<ThreadInfo> list = {
ThreadInfo(Args(demo, TYPE_THREAD_SEND, 20, 1, 50)),
ThreadInfo(Args(demo, TYPE_THREAD_SEND, 20, 51, 50)),
ThreadInfo(Args(demo, TYPE_THREAD_SEND, 20, 101, 50)),
ThreadInfo(Args(demo, TYPE_THREAD_RECEIVE, 2)),
};

从程序打印输出结果看,消费端效率还是远高于生产端,即便生产端有3个,但无法填满队列:
Many to One

修改配置,生产端数量依然是3,消费端数量变为2,生产端效率是10ms,消费端效率是2ms:

1
2
3
4
5
6
7
8
9
10
Demo<int, IntMake> demo(10);

//configuration of the threads
std::vector<ThreadInfo> list = {
ThreadInfo(Args(demo, TYPE_THREAD_SEND, 10, 1, 50)),
ThreadInfo(Args(demo, TYPE_THREAD_SEND, 10, 51, 50)),
ThreadInfo(Args(demo, TYPE_THREAD_SEND, 10, 101, 50)),
ThreadInfo(Args(demo, TYPE_THREAD_RECEIVE, 2)),
ThreadInfo(Args(demo, TYPE_THREAD_RECEIVE, 2)),
};

程序打印输出呈现多对多模式:
Many to One

以上是几个示例的演示,配置比较简单,可按自己意思设计(创建生产端另外的两个参数一个是起始数值,一个是生产数量)。

对习惯了 windows 的我来说,vs2019 有个绝妙的功能就是可以进行 linux 环境下的 c/c++ 开发,而在此之前我只能是在 windows 上编辑代码,然后通过 github 同步到 ubuntu 上进行编译,整个过程需要来来回回折腾,所以 vs2019 的这项功能极大提高了我的生产力。

以下记录一些使用过程中遇到的问题和解决方法。

linux头文件同步到本地

unistd.h 等头文件是unix/linux环境特有,windows上并不存在,所以在编写代码的时候用不了 vs 的智能感知,而且提示出错的红色波浪线让人看着很不舒服,解决办法就是将这类头文件从远程的 linux 机器同步过来,做法如下:

  • 从菜单栏中选择 Debug->Options…

Debug->Options...

  • 进入 Cross Platform->Connection Manager->Remote Headers IntelliSense Manager,从服务器列表勾选远程的 linux 服务器(因为我的 linux 放在本机的虚拟机中,所以是127.0.0.1)
    Cross Platform->Connection Manager->Remote Headers IntelliSense Manager

  • 可以看到智能感知起作用了
    Linux IntelliSense

pthread

pthrad 是外部库,如工程中用到,需要在编译的时候手动加上 -lpthread 选项,否则会导致编译出错。但vs2019中没找到添加编译选项的设置,后来发现只要添加库依赖项就行,做法如下:

  • 从菜单栏中选择 Project->xxx Properties

Project->xxx Properties

  • 进入 Linker->Input->Library Dependencies,添加上 pthread,重新编译就OK了
    Linker->Input->Library Dependencies

QT 连接 MySQL 的坑不少,记录自己的踩坑经历以备后查。

驱动文件

驱动是指 QSqlDatabase::drivers() 输出的列表项,以下是输出项与对应驱动文件所在位置:

Qt/Qt5.12.10/5.12.10/mingw73_64/plugins/sqldrivers/*.dll

这里的5.12.10是我安装的 QT 版本,mingw73 是我安装的 mingw 版本,64是采用64位编译,每个人的配置会有所不同,可根据给出的模式检查对应 QT 目录是否存在以下两个dll:

qsqlmysql.dll
qsqlmysqld.dll

如果缺少,那么 QSqlDatabase::drivers() 输出项将不会出现 ‘QMYSQL’ ,这是造成无法连接的原因之一,这时候我们就需要自己编译这两个文件,如何编译后面再说。

客户端动态库

如果驱动文件存在,但是无法连接 MySQL ,那就是缺少 MySQL 的客户端动态库,找到 MySQL 的安装目录所对应的 libmysql.dll 文件:

mysql-8.0.21-winx64/lib/libmysql.dll

这里的8.0.21是我的 MySQL 版本,winx64是64位环境,可与之对照,将 libmysql.dll 复制到:

Qt/Qt5.12.10/5.12.10/mingw73_64/bin/

目录下,注意一点,x86的客户端动态库要放在对应的x86目录,x64的客户端动态库要放在对应的x64目录,别放错了。

重新编译执行对应版本的 QT 程序,如果能成功连接 MySQL ,那么问题解决,如果不行,说明 驱动文件客户端动态库 版本不兼容,需要自己重新编译驱动文件(这里的原因多半是 MySQL 的客户端动态库是8.x.xx版本而QT中的驱动文件是针对 MySQL 的5.x.xx版本编译的)。如果不想亲自编译驱动文件,也有快速解决方法,就是下载低版本的 MySQL,从中找出 libmysql.dll 替换。需要说明一点, MySQL 客户端动态库需要与 QT 中的x86/x64对应,但是无须跟安装的 MySQL 服务端对应,换言之,x86的客户端动态库是可以连接x64的 MySQL 服务端的。

编译驱动

这里是坑最多的地方,QT 有些版本是自带 MySQL 驱动文件的,但有些版本却又不带,原因可能是 MySQL 版本更新导致客户端动态库变化, QT 预制的编译好的版本太老无法兼容新的 MySQL 版本,所以干脆把这件事留给工程师自己做。

找到如下工程:

Qt/Qt5.12.10/5.12.10/Src/qtbase/src/plugins/sqldrivers/mysql/mysql.pro

启动 Qt Creator,将 mysql.pro 中的这行语句注释:

1
# QMAKE_USE += mysql

加入两行新语句,注意一点,以下的 C:/mysql-8.0.16-winx64/ 是我机器的 MySQL 安装路径,需要换成你自己的,另外,如果路径中含有空格,需要将路径首尾加上双引号:

1
2
INCLUDEPATH += C:/mysql-8.0.16-winx64/include/
LIBS += C:mysql-8.0.16-winx64/lib/libmysql.lib

有人说要再加上 DESTDIR 去指定编译的驱动文件生成位置,这个我测试没有效果,不加的话会自动生成到QT安装目录的磁盘根目录下,比如我的 QT 安装在C盘,那么就是生成在:

C:/plugins/sqldrivers/

在QT中只需要执行 编译 操作而不需要执行 编译运行 操作,因为它编译的是dll,不是可执行文件,如果 编译运行 将出现找不到执行文件的错误提示。

编译过程中可能出现找不到 qtsqldrivers-config.pri 的错误提示,这时候可以找一找

Qt/Qt5.12.10/5.12.10/Src/qtbase/src/plugins/sqldrivers/qsqldriverbase.pri

所在的同级目录是否有一个 configure.pri文件存在,如果存在,则打开 qsqldriverbase.pri 作以下修改:

1
2
# include($$shadowed($$PWD)/qtsqldrivers-config.pri)
include(./configure.pri)

也就是注释第一句,添加第二句。操作成功会在目录

C:/plugins/sqldrivers/

生成以下两个dll

qsqlmysql.dll
qsqlmysqld.dll

将之拷贝到以下目录即可:

Qt/Qt5.12.10/5.12.10/mingw73_64/plugins/sqldrivers/