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;
a.下载所需要的rpm包,gcc需要安装比较多的rpm包,而且不同rpm包之间具有依赖关系,需要按照一定顺序安装,这里推荐直接下载附件;
b.把下载好的附件解压,复制到Linux环境下,例如复制到~/home文件夹下;
c.执行 cd /home/gcc-4.8.5 ,进入该目录下,执行 rpm -ivh –force *,等待安装完成即可;
d.执行 gcc –version,出现版本号说明gcc安装成功。
g++ test.cpp --verbose -I/home/lzy
https://bbs.huaweicloud.com/blogs/308450
https://colobu.com/2018/08/28/15-Most-Frequently-Used-GCC-Compiler-Command-Line-Options/
生成一个a.out文件
gcc main.c
使用参数-o, 可以指定输出的文件名。
gcc main.c -o main
gcc -Wall main.c -o main
gcc -E main.c > main.i
gcc命令将结果输出在stdout中,所以你可以把它重定向到任意的文件中,在上面的例子中,重定向到main.i文件中。
gcc -S main.c > main.s
文件main.s包含汇编代码。
gcc -C main.c
代码产生main.o, 包含机器级别的代码或者编译的代码。
-C参数只产生编译的代码(没有链接link)。
$ gcc -save-temps main.c
$ ls
a.out main.c main.i main.o main.s
gcc -Wall main.c -o main -lCPPfile
上面的代码会链接libCPPfile.so,产生可执行文件main。
pthread库不是Linux系统默认的库,连接时需要使用库libpthread.a,所以在使用pthread_create创建线程时,在编译中要加-lpthread参数:
gcc -o test -lpthread test.c
$ gcc -c -Wall -Werror -fPIC Cfile.c
$ gcc -shared -o libCfile.so Cfile.o
产生共享库的时候使用了-fPIC参数。
注意-shared产生共享库
$ gcc -Wall -v main.c -o main
Using built-in specs.
COLLECT_GCC=gcc
……
ISO C89不支持C++风格的注释
$ gcc -Wall -funsigned-char main.c -o main
$ ./main
The Geek Stuff [246]
通过这个参数, char类型被看作为 unsigned char类型
$ gcc -Wall -fsigned-char main.c -o main
$ ./main
The Geek Stuff [-10]
使用这个参数, char类型被看作是有符号的
$ gcc -Wall -DMY_MACRO main.c -o main
$ ./main
可以看到宏MY_MACRO被定义了,并打印出了结果。
$ gcc -Wall -Werror main.c -o main
$ cat opt_file
-Wall -omain
$ gcc main.c @opt_file
gcc -I/home/codeman/include input-file.c
gcc -std=c++11 hello-world.cpp
标准如 c++11, c++14, c90, c89等。
g++ -g -Wall -std=c++11 main.cpp
gcc main.c -static -o main -lpthread
使用-shared使用动态库链接。
如果没有使用-static,默认使用libstdc++共享库,而-static-libstdc++可以指定使用libstdc++静态库。
gcc -M main.c
http://www.runoob.com/w3cnote/gcc-parameter-detail.html
gcc参数详解
选项 | 解释 |
---|---|
-ansi | 只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色, 例如 asm 或 typeof 关键词。 |
-c | 只编译并生成目标文件。 |
-DMACRO | 以字符串”1”定义 MACRO 宏。 |
-DMACRO=DEFN | 以字符串”DEFN”定义 MACRO 宏。 |
-E | 只运行 C 预编译器。 |
-g | 生成调试信息。GNU 调试器可利用该信息。 |
-IDIRECTORY | 指定额外的头文件搜索路径DIRECTORY。 |
-LDIRECTORY | 指定额外的函数库搜索路径DIRECTORY。 |
-lLIBRARY | 连接时搜索指定的函数库LIBRARY。 |
-m486 | 针对 486 进行代码优化。 |
-o FILE | 生成指定的输出文件。用在生成可执行文件时。 |
-O0 | 不进行优化处理。 |
-O 或 -O1 | 优化生成代码。 |
-O2 | 进一步优化。 |
-O3 | 比 -O2 更进一步优化,包括 inline 函数。 |
-shared | 生成共享目标文件。通常用在建立共享库时。 |
-static | 禁止使用共享连接。 |
-UMACRO | 取消对 MACRO 宏的定义。 |
-w | 不生成任何警告信息。 |
-Wall | 生成所有警告信息。 |
-funroll-loops
gcc来检查代码,进行循环展开,减少循环次数提高性能
https://blog.csdn.net/qq_31108501/article/details/51842166
-O0: 不做任何优化,这是默认的编译选项。
-O和-O1: 对程序做部分编译优化。使用本项优化,编译器会尝试减小生成代码的尺
寸,以及缩短执行时间。
打开的优化选项:
-fdefer-pop 延迟栈的弹出时间。
-fmerge-constants 尝试横跨编译单元合并同样的常量
-fthread-jumps 分支重定向
-floop-optimize 常量表达式从循环中移除,简化判断循环的条件
-fif-conversion 将条件跳转转换为等价的无分支型式
-fif-conversion2
-fdelayed-branch 根据指令周期时间重新安排指令
-fguess-branch-probability 采用随机模式猜测分支被执行的可能性
-fcprop-registers 减少调度依赖性并且删除不必要的寄存器复制操作
-O2: 是比O1更高级的选项,进行更多的优化。提高了生成代码的执行效率。
-fforce-mem
-foptimize-sibling-calls
-fstrength-reduce
-fcse-follow-jumps
-fcse-skip-blocks
-frerun-cse-after-loop
-frerun-loop-opt
-fgcse-lm
-fgcse-sm
-fgcse-las
-fdelete-null-pointer-checks
-fexpensive-optimizations
-fregmove
-fschedule-insns
-fschedule-insns2
-fsched-interblock
-fsched-spec-load
-fcaller-saves
-fpeephole2
-freorder-blocks
-freorder-functions
-fstrict-aliasing
-funit-at-a-time
-falign-functions
-falign-jumps
-falign-loops
-falign-labels
-fcrossjumping
-O3:比O2更进一步的进行优化。
-finline-functions
-fweb
-frename-registers
-funswitch-loops
-Os: 主要是对程序的尺寸进行优化。
Os会关闭如下选项: -falign-functions -falign-jumps -falign-loops -falign-labels -freorder-blocks -fprefetch-loop-arrays
O1优化会消耗少多的编译时间,它主要对代码的分支,常量以及表达式等进行优化。
O2会尝试更多的寄存器级的优化以及指令级的优化,它会在编译期间占用更多的内存和编译时间。
O3在O2的基础上进行更多的优化,例如使用伪寄存器网络,普通函数的内联,以及针对循环的更多优化。
Os主要是对代码大小的优化,我们基本不用做更多的关心。 通常各种优化都会打乱程序的结构,让调试工作变得无从着手。并且会打乱执行顺序,依赖内存操作顺序的程序需要做相关处理才能确保程序的正确性。
http://www.runoob.com/w3cnote/gcc-parameter-detail.html
https://wizardforcel.gitbooks.io/100-gcc-tips/content/address-sanitizer.html
1.令牌桶算法
令牌生成算法:
A.投资收益比值法
B.二分逼近法
C.等比法
https://cloud.tencent.com/developer/article/1692032