https://blog.51cto.com/waleon/5743410
依赖:分为so依赖和.h依赖
模块依赖:回调
说明:
在正确的做法中,函数与函数之间有且仅有一行空行,文件尾有且仅有一行空行。
说明:
魔鬼数字的定义是:在代码中没有具体含义的数字、字符串。魔鬼数字主要影响了代码可读性, 阅读者可能无法理解字符的含义, 从而难以理解程序的意图。当程序中的魔鬼数字过多时,代码的可维护性将会下降。
说明:
不要使用短时间内难以明白的技巧性很高的语句,高技巧的语句不一定是高效率的,高效率的关键在于算法设计本身。
说明:
默认的优先级可能与程序设计的思路或思想不符合导致出错。
说明:
程序文件、头文件、源文件的命名采用小写加下划线的形式,注意命名应与文件功能高度匹配。
说明:
goto语句是一种无条件转移语句,goto语句的使用格式为:goto 语句标号。执行goto语句后,程序将跳转到该标号处并执行其后的语句。goto语句会扰乱程序逻辑,使程序的可读性变差。
说明:
通用的缩写的使用应该参考abbr.md。程序中用于处理逻辑的语句应该少用缩写,不用无意义和无法理解的缩写,同一单词保持一致的缩写。
说明:增强程序的可读性。
说明:
增强程序的可读性。
说明:
#define发生在预处理阶段采用符号直接替换原则,不能进行参数有效性的测试。当设计这方面代码出错,有时候会令人费解。对于单纯常量,最好以const对象替代#difine,对于形似函数的宏,最好改用inline函数替换#define
说明:
宏只是简单的代码替换,不会像函数一样先将参数计算后,再传递。在使用带参数的宏的时候要注意:所有的参数都加括号,然后整个表达式在加上一个括号
说明:
为了不遗漏调用函数返回的错误信息,从而导致遗漏相关错误处理。需要对全面地对错误码进行处理。
说明:
用户输入的合法性通常难以保证,因为存在恶意攻击和误操作导致的非法输入,因此通常需要校验来保证安全,特别是当用户输入作为循环条件、数组下标、格式化字符串、拼装sql语句等情况。
说明:
统一规定由调用者负责检查参数合理性,避免遗漏合法性检查和重复检查造成冗余代码。
说明:
函数名应该尽量清晰明了,使用完整的单词或可以理解的缩写,避免产生误解和歧义
说明:
函数设计的过多,会使得函数间的接口变得复杂,降低代码可读性,所以过小的函数和功能不明确的函数不需要单独存在。
说明:
一个函数最好只实现一个对应的功能,将没有关联的语句放在用一个函数中会导致函数职责不明确,难以理解
说明:
如果参数是指针型参数,且内容不会被修改,请定义为const类型,以避免指针在运行时被篡改。
说明:
inline函数带来的运行效率提升是以代码膨胀为代价的,如果执行函数体内的代码的时间比函数调用的开销大,那么inline的收益就会很小。此外,每一处inline函数的调用都是以代码复制方式,将导致生成的可执行文件体积变大。
说明:
资源的申请和释放必须在同一逻辑层,谁申请,谁释放。请保持良好的资源释放习惯,避免出现内存泄露和资源泄露。
说明:
圈复杂度反应了一个模块判定结构的复杂程度,圈复杂度大说明该段程序代码的判断逻辑复杂,难以维护和测试。一般认为圈复杂度在10以下可认为代码质量较好,10到20可能需要拆分或重构。
圈复杂度的计算方法有很多,这里给出两种常用的计算方法,记圈复杂度为V(G)。
示例:
//下列代码的圈复杂度为 1(for) + 2(if) + 1 = 4
int find (int match)
{
for (int var in list) {
if (var == match && var != NAN) {
return var;
}
}
}
说明:
每级嵌套都会增加代码阅读的难度,因为需要记忆上下文代码的逻辑关系。对于超过5层的嵌套进行进一步的分解,保证代码可读性。
说明:
禁止在.c文件中直接通过extern int foo(int input)的写法来引用其他文件的函数和变量。这种写法会导致代码混乱,同时在函数改变时导致声明和定义不一致。
说明:
对于可能存在较多递归层数的函数,需要使用ANL_STACK_SAFE_CALL添加栈保护,防止系统栈溢出。
说明:
在Release版本中DEBUG宏的代码段会被直接去掉,如果其中包含业务逻辑,会导致相关的业务逻辑丢失,影响正常功能。
说明:
协议、接口、持久化等方面若存在差异,会导致release版本无法正常解析传入的变量。
说明:
如果某变量只在Debug模式下使用,但是未使用Debug声明,那么在Release版本中会成为一个未被引用的变量,会导致代码冗余,降低代码可读性。
说明:
执行结构体对齐后可以减少读取内存的次数,I/O操作耗时很高,通过对齐可以减少I/O操作
说明:
在代码中使用了许多XXXassist、XXXinfo格式的结构体,请谨慎命名结构体,避免名称混淆和混乱。
说明:
内存结构体要求8字节对齐,对于没有对齐的结构体,需要手动添加unused。
说明:
COD_ASSERT通过定义ifndef,只在测试版本出现,最终发布版本不允许出现assert函数
说明:
对于外部接口参数,必须进行合法性判断,严禁在API实现过程中产生crash。
说明:
某些函数在运行时结果可能会有变化,如果写死断言会导致程序运行时crash。
说明:
断言在release时并不会写入,在断言内不能包含任何主体逻辑(赋值,修改变量,内存申请等),否则会导致代码逻辑遗漏。
说明:
断言只能判断真假,并在假时宕掉,缺少一些异常处理。因此严禁简单地用断言来代替一些需要异常处理的逻辑。
在release版本应当不启用断言,这主要是为了防止运行时宕机。在COD_ASSERT处已经实现了相关宏定义,因此只需要保证调用COD_ASSERT即可。
说明:
变量在声明的时候,不要给变量赋无效的值。在实际需要给变量赋值的时候,再赋予相应的值。
说明:
当变量为常量的时候,应当要在变量声明前面加const。这样可以提高代码的安全性、可读性和可维护性。
说明:
函数内的局部变量保存在栈中,局部变量过大的时候会导致栈空间不足,引起stackoverflow异常。
说明:
禁止未经评审增加全局变量和线程私有全局变量。
说明:
代码禁止使用malloc/realloc/alloca系统函数分配内存。
说明:
在进行memcpy时,如果源内存区域[src, src + count)和目标内存区域[dst, dst + count)有重叠,同时src < dst < src + count时,就会发生内存覆盖问题。src给dst赋值不超过dst的偏移量时没有什么问题,但是一旦超过偏移量,由于前面的赋值操作已经将内存给改变了,然后再用改变过后的内存再给dst赋值,这就导致了前面内容复制重复了,也就是发生内存覆盖了。
说明:
switch语句中需要强制加上default分支,default建议处理异常。
说明:
字符串的操作必须使用相应的安全函数库内定义的函数,如:strcpy_s、strcat_s和strtok_s等。
说明:
当整数之间进行运算时,可能会超过整数的最大固定长度,导致整数溢出或反转,使得预期结果不符。 如果涉及到除法或者求余操作,必须确保除数不为0。
说明:
为了防止在整数表达式比较或赋值为一种更大的类型时,计算出的结果与预期不符。建议先用更大的类型对它进行求值。
说明:
位操作符(~、»、«、&、^、|)应该只用于无符号整型操作数
说明:
内存申请的大小可能来自于外部传入的参数,必须检查其合法性,防止非法地申请内存。
Commit:提交代码到本地仓库。
Fetch(pull):获取主仓库的更新,同步到个人本地仓库。
Merge:合并主仓库的代码和本地的开发代码。代码会自动合并,但如果有冲突,需手动合并代码解决冲突。
Push:提交代码到个人远程仓库。
Merge Request:在GitLab的个人主页,发起Merge Request请求将个人远程仓库代码合并到主仓库的对应分支。
Code Review:处理Merge Request请求,进行code review。如果一切正常,最终合并到主仓库,打Tag上线。如果有问题,则拒绝Merge Request,要求修改。
http://www.runoob.com/lua/lua-tutorial.html
lua是一个非常轻量级的脚本语言,它由C语言编写而成,可以很容易被C/C++代码调用,同时也可以很方便地反向调用C/C++函数,介于这样地特性,我们可以选择将Lua脚本嵌入应用程序中,从而为应用程序提供灵活地扩展性和功能定制,这便是Lua语言诞生地意义。
Lua是类C的,所以,他是大小写字符敏感的。
Lua版本低会导致精度缺失。
1.https://blog.csdn.net/u013625451/article/details/84644839
2.https://www.codingnow.com/2000/download/lua_manual.html
int main() { char a[10]; a[10] = 0; return 0; }
文件名保存为file1.c
执行cppcheck file1.c
输出Checking file1.c…
[file1.c:4]: (error) Array ’a[10]’ index 10 out of bounds
命令行方式:
cppcheck --force --enable=all --template vs -I ../../../inc -I ../../../inc/tinyxml . 2>err.txt
参数说明:
error
严重问题
warning
预防bug的一些建议
style
编码格式问题(未使用的函数, 冗余代码等)
performance
使代码更快的建议
github
https://github.com/danmar/cppcheck/releases
man手册
https://linux.die.net/man/1/cppcheck
Using STL algorithms with cppcheck
开发者所做的测试。
可控制性,可观测性,可定位性。
可定位性:不管故障被自愈,容错,还是最终失败,异常都需要输出,不能被内部逻辑完全消化。
日志要分层(INFO/WARN/ERROR),日志量与可定位性要权衡,太少不足以定位问题,太多影响性能,浪费磁盘空间。
有时过程中的交互消息/事件、状态、性能、资源等过程信息输出对问题定位也有非常关键的作用。
测试分层:UT:IT:ST = 7:2:1
单元:最小粒度的功能单元/工作单元。
单元测试:一段自动化的代码。
单元测试(Unit Testing)是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。
一个单元测试是一段自动化的代码,这段代码调用被测试的工作单元,之后对这个单元的单个最终结果的某些假设进行校验。单元测试几乎都是用单元测试框架编写的;只要产品代码不发生变化,单元测试的结果是稳定的。
CUnit
测试框架gtest/gmock
https://github.com/google/googletest/tree/main/googletest/samples
https://github.com/google/googletest/tree/main/googlemock/test
DECLARE_FUNCTION_MOCK
IMPLEMENT_FUNCTION_MOCK
EXPECT_FUNCTION_CALL
(WillOnce,WillRepeatedly,Invoke)
https://www.cnblogs.com/welkinwalker/archive/2011/11/29/2267225.html
https://www.cnblogs.com/coderzh/archive/2009/04/06/1426755.html
gtest的官方网站是: http://code.google.com/p/googletest/
测试框架关键字简介:
1.TEST_F宏:每个宏定义一个测试用例,宏的两个参数分别代表测试套名和测试用例名
2.SetUp和TearDown: 在每个测试用例调用前和调用后执行
3.SetUpTestCase和TearDownTestCase:在测试套调用前和调用后执行,必须为static void类型
4.EXPECT_EQ:预期结果检查
5.MOCK_METHOD1,EXPECT_CALL:对一个类的接口进行打桩/模拟
单元测试原则FIRST:
F-FAST(快速原则)
I-Independent(独立原则)
R-Repeatable(可重复原则)
S-Self Validating(自我验证原则)
T-Timely(及时原则)
单元测试用例需要符合BCDE原则:
B:Border 边界值测试
C:Correct 正确的输入
D:Design 与设计文档相结合
E:Error 强制的错误输入
单元测试的作用: 1.单元测试描述了代码的预期行为,可以最有效地保证代码正确运行 2.由于单元规模较小,当因为代码变更出现问题的时候,可以帮助我们快速定位问题 3.有单元测试覆盖的代码,让我们更有信心,敢于放心做代码重构
集成测试也叫组装测试或联合测试,按照设计要求将各个单元,组件或子系统集成到一起测试是否能正确运行。
系统测试是对整个系统的测试,将硬件,软件,操作人员看作一个整体,检验它是否有不符合系统说明书的地方。
TDD = TFD + Refactoring 即测试先行加上重构。
测试驱动开发是一种由一系列短小迭代组成的软件开发技术。
测试对象概述
(单选题)1.软件测试是软件质量保证的重要手段,( )是软件测试中的最基础环节
2.关于软件测试,以下说法错误的是( )
(多选题)1.关于可测试性,以下描述正确的是:( )
2.关于单元测试,以下说法正确的是:( )
3.关于测试分层,以下说法正确的是:( )
• C/C++单元测试框架gtest
• Java单元测试框架JUnit
• Python单元测试框架PyUnit
• go单元测试框架可以用go自带的gotest包
相关书籍
• 《软件测试基础教程(第2版)》
• 《有效的单元测试》
• 《JUnit Recipes-程序员使用测试技巧》(JAVA)
• 《软件测试》
• 《Clean Code》
• 《重构—改善既有代码的设计》
• 《xUnit测试模式–测试码重构 》
• 《单元测试之道》
• 《实战Infusion 代码坏味道浅析》(JAVA)
相关链接
• 白盒测试覆盖技术:https://blog.csdn.net/virus2014/article/details/51217026
• JUnit4(JAVA):https://github.com/junit-team/junit4/wiki
• Mockito(JAVA):http://static.javadoc.io/org.mockito/mockito-core/2.25.1/org/mockito/Mockito.html
• 《Debugging with GDB》:https://www.gnu.org/software/gdb/documentation/
strlen功能:计算给定字符串的长度,不包括‘\0’
strlen是一个函数,参数是char *
sizeof是关键字,取字节运算符
sizeof功能:返回一个对象或者类型所占的内存字节数
str.length()和str.size() 是求string类型对象的成员函数
#include <cstring>
#include <iostream>
#include <stdio.h>
using namespace std;
#define test "test string";
int main() {
char *s = "test string"; //warning: deprecated conversion from string constant to ‘char*’
cout << "strlen(s)=" << strlen(s) << endl; //strlen(s)=11
cout << "sizeof(s)=" << sizeof(s) << endl; //sizeof(s)=8
cout << endl;
char mystr[100] = "test string";
cout << "strlen(mystr)=" << strlen(mystr) << endl; //strlen(mystr)=11
cout << "sizeof(mystr)=" << sizeof(mystr) << endl; //sizeof(mystr)=100
cout << endl;
char ss[] = "test string";
cout << "strlen(ss)=" << strlen(ss) << endl; //strlen(ss)=11
cout << "sizeof(ss)=" << sizeof(ss) << endl; //sizeof(ss)=12
cout << endl;
char *p = mystr;
cout << "strlen(p)=" << strlen(p) << endl; //strlen(p)=11
cout << "sizeof(p)=" << sizeof(p) << endl; //sizeof(p)=8
cout << endl;
string cur = "test string";
cout << "strlen(cur)=" << strlen(cur.c_str()) << endl; //strlen(cur)=11
cout << "cur.size()=" << cur.size() << endl; //cur.size()=11
cout << "cur.length()=" << cur.length() << endl; //cur.length()=11
cout << "sizeof(cur)=" << sizeof(cur) << endl; //sizeof(cur)=8
cout << endl;
string now = test;
cout << "strlen(now)=" << strlen(now.c_str()) << endl;//strlen(now)=11
cout << "now.size()=" << now.size() << endl;//now.size()=11
cout << "now.length()=" << now.length() << endl;//now.length()=11
cout << "sizeof(now)=" << sizeof(now) << endl;//sizeof(now)=8
cout << endl;
return 0;
}
运行结果:
strlen函数求得的字符串长度是从字符串第一个元素到第一个’\0’之间元素的个数(如果字符串中间有’\0’,则结果不是整个字符串的长度),同时不包括该’\0’
sizeof求得的结果是存储该字符串的变量占用的空间大小,因而一定会包括’\0’.若’\0’后还有空余的空间,也会包含到结果里面
char * strcpy ( char * destination, const char * source );
strcpy 是依据 “\0” 作为结束判断的,如果 destination 的空间不够,则会引起 buffer overflow。
char str1[100];
char *s = "test string";
cout << "strlen(s)=" << strlen(s) << endl; //strlen(s)=11
cout << "sizeof(s)=" << sizeof(s) << endl; //sizeof(s)=8
strcpy(str1, s);
cout << "strcpy str1:" << str1 << endl; //strcpy str1:test string
cout << "strlen(str1)=" << strlen(str1) << endl; //strlen(str1)=11
cout << "sizeof(str1)=" << sizeof(str1) << endl; //sizeof(str1)=100
cout << endl;
char * strncpy ( char * destination, const char * source, size_t num );
strncpy要安全一些。
拷贝你指定的个数或者碰到’\0’,不验证源缓冲区长度,可能造成越界。
如果遇到空字符(’\0’),则空字符后面为原来的字符。
char str2[100];
char str3[10];
char mystr[100] = "test string";
cout << "strlen(mystr)=" << strlen(mystr) << endl;//strlen(mystr)=11
cout << "sizeof(mystr)=" << sizeof(mystr) << endl;//sizeof(mystr)=100
strncpy(str2, mystr, sizeof(str2));
cout << "strncpy str2:" << str2 << endl;//strncpy str2:test string
cout << "strlen(str2)=" << strlen(str2) << endl; //strlen(str2)=11
cout << "sizeof(str2)=" << sizeof(str2) << endl; //sizeof(str2)=100
strncpy(str3, mystr, 4);
str3[4] = '\0';
cout << "strncpy str3:" << str3 << endl; //strncpy str3:test
cout << "strlen(str3)=" << strlen(str3) << endl;//strlen(str3)=4
cout << "sizeof(str3)=" << sizeof(str3) << endl;//sizeof(str3)=10
cout << endl;
编译选项
g++ main.cpp -std=c++11 /home/lzy/code/taurus-root/src/3rdparty/securec/lib/libsecurec.a
头文件:#include “securec.h”
strncpy_s 不以零填充目标数组。这是转换既存代码到边界检查版本的常见错误源。
| dest | - | 指向要复制到的字符数组的指针 |
|---|---|---|
| src | - | 指向复制来源的字符数组的指针 |
| count | - | 要复制的最大字符数 |
| destsz | - | 目标缓冲区的大小 |
errno_t strncpy_s(char *restrict dest, rsize_t destsz,
const char *restrict src, rsize_t count);
char str4[100];
char str5[10];
char ss[] = "test string";
cout << "strlen(ss)=" << strlen(ss) << endl; //strlen(ss)=11
cout << "sizeof(ss)=" << sizeof(ss) << endl; //sizeof(ss)=12
strncpy_s(str4, sizeof(str4), ss, sizeof(ss));
cout << "strncpy_s str4:" << str4 << endl; //strncpy_s str4:test string
cout << "strlen(str4)=" << strlen(str4) << endl; //strlen(str4)=11
cout << "sizeof(str4)=" << sizeof(str4) << endl; //sizeof(str4)=100
strncpy_s(str5, 10, ss, 4);
str5[4] = '\0';
cout << "strncpy_s str5:" << str5 << endl; //strncpy_s str5:test
cout << "strlen(str5)=" << strlen(str5) << endl; //strlen(str5)=4
cout << "sizeof(str5)=" << sizeof(str5) << endl; //sizeof(str5)=10
cout << endl;
memcpy:不理’\0’,只拷贝你指定的个数,故strcpy可以不指定字符串长度,实现整串copy,而memcpy必定要指定长度。
void * memcpy(void * dest,const void * src,size_t count);
char str6[100];
char str7[4];
char *p = mystr;
cout << "strlen(p)=" << strlen(p) << endl; //strlen(p)=11
cout << "sizeof(p)=" << sizeof(p) << endl; //sizeof(p)=8
memcpy(str6, p, strlen(p) + 1);
cout << "memcpy str6:" << str6 << endl; //memcpy str6:test string
cout << "strlen(str6)=" << strlen(str6) << endl; //strlen(str6)=11
cout << "sizeof(str6)=" << sizeof(str6) << endl; //sizeof(str6)=100
memcpy(str7, p, 4);
cout << "memcpy str7:" << str7 << endl; //memcpy str7:test
cout << "strlen(str7)=" << strlen(str7) << endl; //strlen(str7)=4
cout << "sizeof(str7)=" << sizeof(str7) << endl; //sizeof(str7)=4
cout << endl;
count 大于 destsz (会发生缓冲区溢出)
| dest | - | 指向要复制的对象的指针 |
|---|---|---|
| destsz | - | 目标中要修改的最大字节数(典型地为目标对象的大小) |
| src | - | 指向复制来源对象的指针 |
| count | - | 复制的字节数 |
errno_t memcpy_s( void *restrict dest, rsize_t destsz,
const void *restrict src, rsize_t count );
char str8[100];
char str9[4];
char str10[4];
string cur = "test string";
string cur2 = "ab";
cout << "strlen(cur)=" << strlen(cur.c_str()) << endl;//strlen(cur)=11
cout << "cur.size()=" << cur.size() << endl; //cur.size()=11
cout << "cur.length()=" << cur.length() << endl; //cur.length()=11
cout << "sizeof(cur)=" << sizeof(cur) << endl; //sizeof(cur)=8
memcpy_s(str8, sizeof(str8), cur.c_str(), strlen(cur.c_str()) + 1);
cout << "memcpy_s str8:" << str8 << endl; //memcpy_s str8:test string
cout << "strlen(str8)=" << strlen(str8) << endl; //strlen(str8)=11
cout << "sizeof(str8)=" << sizeof(str8) << endl; //sizeof(str8)=100
memcpy_s(str9, 4, cur.c_str(), 4);
cout << "memcpy_s str9:" << str9 << endl; //memcpy_s str9:test
cout << "strlen(str9)=" << strlen(str9) << endl; //strlen(str9)=4
cout << "sizeof(str9)=" << sizeof(str9) << endl; //sizeof(str9)=4
memcpy_s(str10, 4, cur.c_str(), 3);
cout << "memcpy_s str10:" << str10 << endl; //memcpy_s str10:tes
cout << "strlen(str10)=" << strlen(str10) << endl; //strlen(str10)=3
cout << "sizeof(str10)=" << sizeof(str10) << endl; //sizeof(str10)=4
cout << endl;