NEFU C语言大作业总结JSON解析生成器
Posted 鱼竿钓鱼干
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了NEFU C语言大作业总结JSON解析生成器相关的知识,希望对你有一定的参考价值。
【NEFU C语言大作业总结】JSON解析生成器
今天把生成器部分写完了,目前.h 60多行.c库代码500多行,test单元测试400多行(使用网上单元测试框架)。把打印部分写完这个项目应该就完结了。总共应该快1000行了吧,算是大作业及格代码量?
下面进行一些技术总结
到时候如果顺利通过的话我会比较闲,可以私聊我。我可以提供一些简单的代码优化,当然你需要支持小额报酬呵呵。
多看nb的开源项目真滴可以收获很多东西!
新奇操作
内存泄漏检测
在 Windows 下,可使用 Visual C++ 的 C Runtime Library(CRT) 检测内存泄漏。
/*检测内存泄露*/
#ifdef _WIN32
#define _CRTDBG_MAP_ALLOC
#include <crtdbg.h>
#endif
int main() {
#ifdef _WIN32
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
通用堆栈
typedef struct {
const char* json;
/*c语言实现混合类型堆栈,参考RapidJSON库*/
char* stack;
unsigned int size, top;//由于需要动态扩展,top不使用指针类型,并且push和pop操作可以通过简单的+-来实现
}fish_context;
/*压入堆栈,返回值为数据头指针位置*/
static void* fish_context_push(fish_context* c, unsigned int size) {
void* ret;
assert(size > 0);
if (c->top + size >= c->size) {
if (c->size == 0)
c->size = FISH_PARSE_STACK_INIT_SIZE;
while (c->top + size >= c->size)/*参考C++ STL vector 优化 2倍扩展变1.5倍扩展*/
c->size += c->size >> 1;
void* new_ptr= (char*)realloc(c->stack, c->size);
if (new_ptr == NULL) {
free(new_ptr);
puts("FISH_CONTEXT_PUSH_CAN_NOT_REALLOC!");
}
else {
c->stack = new_ptr;
}
}
ret = c->stack + c->top;
c->top += size;
return ret;
}
static void* fish_context_pop(fish_context* c, unsigned int size) {
assert(c->top >= size);
return c->stack + (c->top -= size);
}
代码编写
效率优化
虽然现在编译器基本都可以优化,但是下面这些写法都或许可以显得你比较专业哈哈哈
无限循环
for(;;)优于while(1)
逻辑分支
switch比if else快
内存优化
当结构体存在多选一的数据时候,我们可以采用联合体来优化内存
JSON的数据结构中,string,array,object是多选一的,所以我们可以使用联合体来优化内存。
可以参考一下这篇文章
struct fish_value{
char* s; unsigned int len; //string
fish_value* e; unsigned int arr_size; //array
fish_member* m; unsigned int obj_size; //object
double n;
fish_type type;
};
联合体实现
struct fish_value {
union {
struct { fish_member* m; size_t size; }o; //object
struct { fish_value* e; size_t size; }a; //array
struct { char* s; size_t len; }s; //string
double n;
}u;
lept_type type;
};
可读性优化
枚举类型优化可读性
如果一些数字有特定的意义,我们可以采用枚举类型来增强代码的自说明性
enum {
FISH_PARSE_OK = 0,
FISH_PARSE_EXPECT_VALUE,
FISH_PARSE_INVALID_VALUE,
FISH_PARSE_ROOT_NOT_SINGULAR,
FISH_PARSE_NUMBER_TOO_BIG,
FISH_PARSE_MISS_QUOTATION_MARK,
FISH_PARSE_INVALID_STRING_ESCAPE,
FISH_PARSE_INVALID_STRING_CHAR,
FISH_PARSE_MISS_COMMA_OR_SQUARE_BRACKET,
FISH_PARSE_MISS_KEY,
FISH_PARSE_MISS_COLON,
FISH_PARSE_MISS_COMMA_OR_CURLY_BRACKET
};
试着比较一下下面两个语句
/*如果ch=='} 那么返回解析成功*/
if(ch=='})return FISH_PARSE_OK;
if(ch=='})return 0;
宏优化可读性
do{}while
用do{…}while(0);包裹住要操作的#define,无论你外面怎么操作,都不会影响#define的操作
#define PUTC(c, ch) do { *(char*)fish_context_push(c, sizeof(char)) = (ch); } while(0)
#define PUTS(c,s,len) do { memcpy(fish_context_push(c, len), s, len);} while(0)
各位可以参考这篇博客
使用比较常见的变量名
可以去这个网站查询
ret 返回值
size 大小
length 长度
buffer 缓冲区
init 初始
整齐的排版多使用TAB
安全性优化
防御性编程
EXPECT宏的使用:
虽然在实际测试和使用过程中,调用某一个函数可能已经进行过参数检查或者条件判断了。但是我们在测试和编写的过程中应该尽可能遵守防御性编程的方法。在函数内部对一些指针,参数条件进行检查。常见的检查方法有:使用断言,抛出异常
检查参数是否符合输入
#define EXPECT(c, ch) do { assert(*c->json == (ch)); c->json++; } while(0)
/*解析TRUE*/
static int fish_parse_true(fish_context* c, fish_value* v){
EXPECT(c, 't');//防御性编程,在fish_parse_value中已经判断过了,但是对于功能函数还需要考虑单独应用场景,例如test.c文件中的单元测试。
if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e')
return FISH_PARSE_INVALID_VALUE;
c->json += 3;
v->type = FISH_TRUE;
return FISH_PARSE_OK;
}
检查空指针
/*获取类型*/
fish_type fish_get_type(const fish_value* v) {
assert(v != NULL);
return v->type;
}
自定义类型free
可以专门写一个free函数来释放自己的类型
void fish_free(fish_value* v){
assert(v != NULL);
switch (v->type) {
case FISH_STRING:
free(v->s);
break;
case FISH_ARRAY:
for (unsigned int i = 0; i < v->arr_size; i++)
fish_free(&v->e[i]);
free(v->e);
break;
case FISH_OBJECT:
for (unsigned int i = 0; i < v->obj_size; i++) {
free(v->m[i].key);
fish_free(&v->m[i].v);
}
free(v->m);
break;
default: break;
}
v->type = FISH_NULL;//避免重复释放 狗牌标记
}
测试
TDD测试驱动开发
测试驱动开发(Test-Driven Development)
说白了就是你先把需求中可能用到的数据造出来,然后写个测试(比如最普通的单元测试)。
然后你根据这些测试来编写你的程序,这样保证了你的需求都能被满足。
但是坏处是,这种开发方式,非常耗费时间。
我在写C语言大作业的时候找到了现成的JSON解析生成器测试框架和大量测试数据,所以比较轻松。如果各位找不到足够数据的话,可以尝试写个程序生成合法的随机数据。但是我们要保证这些数据能够满足测试强度需求。
下面给出一些需要考虑的数据
测试数字
极大的数据,极小的数据,整数,小数,0,正数,负数,科学计数法
测试字符串
空格,连续空格,转移字符,utf-8,中文字符,空串,极长的字符串
单元测试框架
平常写锐格的时候,我们测试一个函数或者程序是否允许成功常常会使用printf等来打印输出,然后人工检查结果。但是这对于一个有几十个函数的大作业来说或许已经有一些繁琐了。
一般我们会采用自动的测试方式,例如单元测试(unit testing)。单元测试也能确保其他人修改代码后**,原来的功能维持正确**(这称为回归测试/regression testing)。同时如果后期要重构优化代码,也很轻松。
各位可以了解一下Google的单元测试框架
自定义性
我们当申请一些空间大小的时候,我们可以采用宏的方式定义初始SIZE大小,这样要调整的时候方便调整
#ifndef FISH_PARSE_STACK_INIT_SIZE
#define FISH_PARSE_STACK_INIT_SIZE 256
#endif
#ifndef FISH_PARSE_STRINGIFY_INIT_SIZE
#define FISH_PARSE_STRINGIFY_INIT_SIZE 256
#endif
推荐各位无聊的时候读的几本书/文档
《代码大全2》(本人有纸质版,如果你看的话可以私聊我)
《重构》(纸质版过几天应该到了,你要的话也可以私聊我)
《人月神话》(不是啥技术书,但是有些用处)
Google代码规范
先写这么多吧。
以上是关于NEFU C语言大作业总结JSON解析生成器的主要内容,如果未能解决你的问题,请参考以下文章