早在《C专家编程》一书中就听说过lint程序,但是一直没有用过,今天看一篇有关C的文章,这个名字再次出现在我的眼前,于是下载了一个破解版玩了下,而且还测试了下我以前写的代码,发现bug多多呀!
PC-lint for C/C++是由Gimpel软件公司于1985年开发的代码静态分析工具,它能有效地发现程序语法错误、潜在的错误隐患、不合理的编程习惯等。
C语言的灵活性带来了代码效率的提升,但相应带来了代码编写的随意性,另外C编译器不进行强制类型检查,也带来了代码编写的隐患。PCLint能识别并报告C语言中的编程陷阱和格式缺陷的发生。它进行程序的全局分析,能识别没有被适当检验的数组下标,报告未被初始化的变量,警告使用空指针,冗余的代码,等等。软件除错是软件项目开发成本和延误的主要因素。PClint能够帮你在程序动态测试之前发现编码错误,这样消除错误的成本更低。使用PC-Lint在代码走读和单元测试之前进行检查,可以提前发现程序隐藏错误,提高代码质量,节省测试时间。并提供编码规则检查,规范软件人员的编码行为。
在vs2005下配置如下:
准备工作当然是下载并安装PC-lint(有需要可以找我
,我用的是最新版8.00w),例如:安装目录为C:\Program Files\pclint\,该目录下肯定有一个子目录lnt,里面存放了很多*.lnt文件,它们是语法配置规则,发现bug就是根据这些的文件来的。
在lnt目录下新建两个文件:std.lnt和options.lnt,std.lnt文件的内容如下:
au-sm.lnt
co-msc80.lnt
lib-mfc.lnt
lib-stl.lnt
lib-w32.lnt
lib-wnt.lnt
lib-atl.lnt
options.lnt -si4 -sp4
-i"C:\Program Files\Microsoft Visual Studio 8\VC\include"
-i"C:\Program Files\Microsoft Visual Studio 8\VC\atlmfc\include"
-i"C:\Program Files\Microsoft Visual Studio 8\VC\PlatformSDK\include"
-i"C:\Program Files\Microsoft Visual Studio 8\SDK\v2.0\include"
而options.lnt文件可以为空。
配置vs2005方法:工具->外部工具,点击“添加”,新建一个外部工具。标题可以任意,如取(pc_lint);命令为:D:\Program Files\pclint\LINT-NT.EXE; 参数为:-i”C:\Program Files\pclint” std.lnt “$(ItemFileName)$(ItemExt)”;初始目录为:$(ItemDir),并将下面的“使用输出窗口”勾选上。
接下来,你就可以这个pc_lint工具测试你以前写的代码了!!如果你的程序有bug,pc_lint会报告在输出窗口中。
Tags: soft, tools
Posted in 计算机技术, 软件交流 | 2 Comments »
异常(Exception)是程序可能检测到,运行时刻不正常的情况,如被0除、数组越界访问或者堆空间申请失败等等。
标准C并没有提供异常处理机制,但是提供了两个特殊的函数:setjmp()和longjmp(),这两个函数是结构化异常的基础,C里可以利用这两个函数的特性来实现异常。在头文件setjmp.h里有两个函数一个类型(jmp_buf):
int setjmp ( jmp_buf env );
void longjmp (jmp_buf env, int val);
C.1 jmp_buf是一个结构体,用于保存当前程序现场(保存当前需要用到的寄存器的值),结构如下:
- typedef struct
- {
- unsigned j_sp; // 堆栈指针寄存器
- unsigned j_ss; // 堆栈段
- unsigned j_flag; // 标志寄存器
- unsigned j_cs; // 代码段
- unsigned j_ip; // 指令指针寄存器
- unsigned j_bp; // 基址指针
- unsigned j_di; // 目的指针
- unsigned j_es; // 附加段
- unsigned j_si; // 源变址
- unsigned j_ds; // 数据段
- } jmp_buf;
由上述结构可知,jmp_buf保存程序当前寄存器的值,以确保使用longjmp()后可以调回到该执行点上继续执行。
C.2 setjmp()和longjmp()组合其实很类似于goto语句,setjmp()首先设置一个跳转点,相当于goto里为goto语句设置一个label一样,然后在其后的代码中任意地方调用longjmp()就会跳转回到那个跳转点上,就像使用goto语句回到label处一样。不同的是,goto只能在局部域内跳转,而setjmp/longjmp可以跨函数跨文件跳转。setjmp() 与 longjmp() 函数详细说明如下:
首先调用 setjmp() 函数来初始化 jmp_buf 结构变量 jmpb,将当前CPU中的大部分影响到程序执行的寄存器的值存入 jmpb,为 longjmp() 函数提供跳转,setjmp() 函数是一个有趣的函数,它能返回两次,它应该是所有库函数中唯一一个能返回两次的函数,第一次是初始化时,返回零,第二次遇到 longjmp() 函数调用后,longjmp() 函数使 setjmp() 函数发生第二次返回,返回值由 longjmp() 的第二个参数给出(整型,这时不应该再返回零)。
在使用 setjmp() 初始化 jmpb 后,可以在其后的程序中任意地方使用 longjmp() 函数跳转回setjmp() 函数的位置,若想跳转回刚才设置的 setjmp() 处,则 longjmp() 函数的第一个参数是 setjmp() 所初始化的 jmpb 变量,这也说明一件事,即 jmpb 这个变量,一般需要定义为全局变量,否则,若是局部变量,当跨函数调用时就几乎无法使用(除非每次遇到函数调用都将 jmpb 以参数传递,然而明显地,是不值得这样做的);longjmp() 函数的第二个参数是传给 setjmp() 的第二次返回值,这在介绍 setjmp() 函数时已经介绍过。
这两个函数的使用例子如下: Continue Reading »
Tags: C&C++, 备忘
Posted in C&C++, 计算机技术 | No Comments »
最近我过着猪一般的幸福生活。
记得以前本科最后一学期就听说过:保研的丫们过着猪一般的生活,找工作的丫们过着狗一般的生活,考研的丫们是在猪狗不如活着。现在,在偶即将离开学校的最后半个月,我终于体会到了猪一般的美好生活是啥滋味拉。
由于6月初开始我就决定去实验室再也不刷卡了,所以每天早上都是睡到自然醒。由于天气转热,小家里又没有冰箱,所以头一天买回来的馒头在第二天早上就不能吃了,于是我和king开始吃水果早餐。水果最新鲜拉,通常我们一起要吃一个大青瓜,一个大黄金瓜,削皮切好放在小盘子里,然后冲一杯凉凉的果珍,再加上一些咸菜,有时是咸蛋,有时是那种真空包装的小熟食。呵呵,爽呀。
平时偶尔去去实验室,看看小书拉,上上网拉,生活原来可以如此之high。好好享受现在的最后半个月吧,以后去北京工作就要吃苦咯……
Tags: happy
Posted in 生活点滴 | 3 Comments »
马上就要毕业了,写这篇文章的主要目的是为了纪念去年学习研究jsvm的那段时光。其中回头想想发现硕士研究生阶段好好学习的日子还真的不多:第一学期上上课就过去了,第二学期开始好好学习视频编码,第三学期忙碌着找工作,到第四学期就开始写论文搞答辩盼毕业。哎,原来自己两年来脚踏实地好好学习的时间才那么几个月……每每想到这些,心里还是忍不住要感谢实验室给我这样一个紧张的学习平台,avs会议3个月一次,每次都像打仗时的慌着做提案。虽然很累,那段日子天天晚上加班,也不记得暗地里骂了多少次,但是回头想想,不是那样一种环境,那样一种压力,自己如今可能仍然是一无所获。
以后可能不会再继续做视频压缩方面的东东了,所以在对jsvm、jm、rm、sm等暂时告别之际,不舍之情还是有一些的,平时学习的资料可能到最后压个盘,也不会再拿出来好好的研究学习了,在ksarea里面放着或许还会偶尔翻翻。不管怎样,相信这段学习的经历永远是偶一笔不小的财富。
本人学习jsvm的时间不长,但是感触还是有一些的。在正式看jsvm代码之前,做了一段时间的理论学习工作,当然主要是针对可伸缩视频编码技术。在后期看代码的时候,才发觉得前段时间的理论学习是很重要的。看jsvm的速度完全取决于你对可伸缩编码技术的了解程度和对c++编程的熟练程度,当然,后者相对而言次要些,但是如果你要在jsvm上做一定修改的话,那么后者就显得相当重要了。
下面本人就自己对jsvm的认识做一个简单的介绍。由于当时看jsvm代码也不到1个月,所以认识很浅,文字也不够深入或许还存在一些认识上的错误,但本人的初衷是好的,希望能对读者有个抛砖引玉的作用吧,呵呵。
Continue Reading »
Tags: jsvm
Posted in Video Coding | 14 Comments »
前段时间,我和king过着一段很阴郁的日子,事事不顺,心情差到极点,但一直都强忍的控制着自己的情绪,希望能够给对方以支持和鼓励。昨天晚上king和他同学一起在外面吃饭,喝了一些酒,想必也是发泄一番心中压制的不悦吧,我也一个人在小家忍不住的cry了一场。哎,我们就是以一个这样的状况来迎接我们love4周年的到来。说实话,最近状态真的很差,及其的差,整个天空都是一直灰暗的。曾经很努力的尝试改变,但是就像中了邪一样,bad luck的邪,笼罩在我们周围,挥之不去。
昨晚和king深谈了一次,觉得很多东西我们应该暂时的放下了,我们太需要把心里的包袱卸下歇一歇了。就像弹簧被拉得太长太久,必须得恢复到初始状态。第二天就是我们4周年庆,或许可以把这样的一天,当作目前的一个转折点,但愿能够换取一些good luck来吧。
说实在的,坏运气这个东西可能是人们虚构出来、为自己的失败而寻找的借口、为自己的忧伤而开辟的出路,也可能真的存在我们周围,比如一种让人感觉紧张的电磁波什么的……
今天是我们的4周年庆,按昨天的计划,我们很懒的睡到自然醒,先是去仟吉买了偶最爱的全麦,本来打算还买恩爱的,但貌似仟吉不产恩爱了。然后去博客食府爽爽的吃了一顿。吃博客的瓜瓜鱼时,我们都说这个就是俺们的lucky fish,吃完就要转大运,嘿嘿,于是很努力的把那条大鱼吃完。吃饱喝足,买单走人时,king说要找收银台索取发票刮奖。还真的很邪门哈,前段时间的运气一直差,今天我们开玩笑的说非要把运气扭转过来时,运气就变得不是一般的好!我拿的是50的发票,刮出来一瞧,熟悉的“护税光荣”,king拿的是10块,刮完大喊:“中了十元!”,我还不信:“不会吧,你刮的10元发票,就中了十元?太假了!”事实还真的就是那么假,果然是中了10元,呵呵。走在路上我感叹说:“这是偶平生中过的最大的奖,真的!貌似上天在和我们开玩笑,应验了转运这一说哈。”
下午偶回实验室参加了师弟师妹们办的求职交流会,会上谈了谈自己去年找工作的一些感想和经验。本来打算不去的,自己也很水,能够给别人的经验确实很少。不过因为先前答应过人家,所以最后还是去了。但是去这么一趟收获还真不少。一开始是听了一个工作了2年现因项目而回来一阵子的师姐的报告,貌似小有醍醐灌顶的感觉。然后又听了一下其他同学找工作的经验之谈,也是收获颇多。回去就自己听到的一些东西和king随便侃了侃,发现其实我们在很多地方认识都是存在误区的,自身也有很多不足、需要改进的地方,说着说着心中也仿佛感觉宽敞起来。前段时间阴郁的日子对于我们而言或许是一段打击,或许是一笔财富,关键取决于自己的心态。如果我们能够从中找到自己的问题所在,我想,在我们还年轻的时候多经历一些小小的挫折又何妨呢?
今天是我和king 的4周年庆,虽然庆祝的方式很简单,也很俗套,吃吃喝喝,呵呵,不过也算很happy拉。晚上9点半的时候,我突发奇想,想出去给上次买的粉色裤子配一件上衣,king还在嘀咕那么晚了,人家做生意的都关门了,哪里有卖衣服的呀。不过俺们运气好哦,一出去逛了还在营业的两三家后,就看中了一件偶很喜欢的T恤,新衣服穿在身上,价钱都没还,就欢欢喜喜的付了钱。
所以今天也算是bad luck的一个转折吧,呵呵,偶们的大运 is coming~ 中奖为证哦,10元发票中10元现金,这可不是假的。
Tags: love
Posted in 生活点滴 | 3 Comments »
RTTI全称是RunTime Type Identification,即运行时类型识别,这是C++的一个新特性。程序能够使用基类类型指针或引用来检索这些指针或引用在运行时所指对象的实际的类型:派生类或者基类。
通过两个操作符来提供RTTI:
(1)typeid操作符,返回指针或者引用的实际类型;
(2)dynamic_cast操作符,将基类类型指针或者引用安全的转换位派生类类型的指针或者引用。
1. typeid操作符
该操作符的使用形如:typeid(e),其中e是任意表达式或者类型名。
如果表达式的类型是类类型且这个类中包含一个或者多个虚函数,则表达式的动态类型可能不同于它的静态类型;如果表达式的类型是内置类型或者是类类型但是没有包含任何虚函数,则表达式的动态类型和静态类型一致。例如,如果表达式是基类类型引用或者是对基类指针解引用,则表达式的静态编译时类型是基类型,但如果指针或引用实际指向派生类对象,则typeid就会告诉我们表达式的类型是派生类类型。
typeid操作符的结果是名为type_info的标准库类型的对象引用。要使用这个类,必须include头文件typeinfo。type_info这个类的默认构造函数、复制构造函数以及赋值操作符都定义成了private,所以不能定义或者复制type_info类型对象,只能通过typeid操作符来创建type_info类对象。这个类里有一个函数是经常用的name(),它返回C风格字符串,是类型名字的可显示版本。
常见用途如下:
derived *pd=new derived;
base *pb=pd;
//比较两个表达式的类型
if (typeid(*pb) == typeid(*pd))
{
//do something
}
//比较表达式类型和特定类型
if (typeid(*pb) == typeid(derived))
{
//do something
}
注意:只有当typeid的操作数是带虚函数的类类型的对象的时候,才返回动态类型信息。测试指针只返回指针的静态类型。
所以,如下测试用用于是失败的:
if (typeid(pb) == typeid(derived))
{
//do something
}
2. dynamic_cast操作符
它实际上是C++中4中强制类型转换操作符之一,但是dynamic_cast涉及运行时类型检查,所以它能安全的将基类类型对象的引用或指针转换为同一继承层次中其他类型的引用或者指针。和typeid操作符有点不同,dynamic_cast待转换的基类类型中必须包含一个或者多个虚函数,否则不能使用dynamic_cast操作符(编译都通过不了)。
转换指针方式如下:
derived * pd = dynamic_cast<derived*>(pb);
在运行时检查,如果pb指向继承类对象,那么转换成功;如果pb 指向基类对象,那么转换的结果是0,即pd为0。
转换引用方式如下:
derived & rd=dynamic_cast<derived&>(rb);
当rb实际引用的是一个基类对象,则转换失败,抛出一个std::bad_cast异常;当rb实际引用的是一个继承类对象,则转换成功。
在实际的应用中,当我们不能修改基类,但能在继承类中增加函数,此时我们无法使用虚函数来调用继承类的新增函数,但是可以使用dynamic_cast来将基类指针或者引用安全的转换为继承类指针或者引用,这样就可以访问新增的函数。
Tags: C&C++
Posted in C&C++, 计算机技术 | No Comments »
这里讲重载(overload)、覆盖(override)、隐藏是基于OOP的继承、多态来阐述。 三者有一点是完全相同的,那就是讨论的函数都是同名的。
1、重载比较好理解,是指与许存在多个同名函数,而这些函数的参数表不同(或者参数个数不同、或者参数类型不同、或者两者皆不同)。注意:仅仅是函数的返回值不同的重载,编译器是会嗷嗷叫的!
类成员函数被重载的特征:
- 相同的范围(在同一个类中);
- 函数名字相同;
- 参数不同(或者参数个数不同、或者参数类型不同、或者两者皆不同);
- virtual 关键字可有可无。
这里要偏离下主题,备案一个刚刚获知的知识点。一直以来,我以为复制构造函数有以下两种重载原型:
Myclass(Myclass &);
Myclass(const Myclass &);
今天又学一手,还有第三种复制构造函数原型:
Myclass(volatile Myclass &);
2、言归正传,而覆盖是指派生类函数覆盖基类函数,它总是和继承、多态扯上千丝万缕的关系,可谓你中有我,我中有你
!它的特征如下:
- 不同的范围(分别位于派生类与基类);
- 函数名字相同;
- 参数相同(vs2005里两个函数的返回值也要一样,否则报error);
- 基类函数必须有virtual 关键字。
也就是说分别处于互为继承关系的类中,两个完全相同的一模一样的虚函数才有覆盖的概念!
看如下代码:
#include <iostream>
using namespace std;
class base
{
public:
void f(int x) { cout<< “base::f(int)” << endl; }
void f(float y) { cout << “base::f(float)” << endl;}
virtual void g(void) { cout << “base::g(void)” << endl; }
};
class derived:public base
{
public:
//这里的virtual可要可不要,因为基类函数有virtual,继承类同名同参数函数自动加上virtual
virtual void g(void) { cout << “derived::g(void)” << endl;}
};
int main()
{
derived d;
base * pb=&d;
pb->f(10); //base::f(int)
pb->f(3.1415f); //base::f(float)
pb->g(); //derived::g(void)
}
base类中void f(int x)和void f(float y)就属于重载,而derived::g(void)就是覆盖base::g(void)。
3、前面两者还算好区分,C++里的隐藏才是令人迷惑。隐藏指派生类的函数屏蔽了与其同名的基类函数,其规则如下:
- 如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。
- 如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
这两句话可以这么理解:通过继承类对象、指针或者引用没有办法调用到基类中的那个函数,这叫继承类隐藏了基类的函数;反之亦然!也就是说隐藏在继承层次中是相互的,你隐藏我,我也隐藏你!
一句话概括:如果两个函数分别在继承关系的类中,只要不是覆盖关系,那么一定就是屏蔽关系!!
将上述两个类代码换成如下形式:
class base
{
public:
void f(int x) { cout<< “base::f(int)” << endl; }
void g(void) { cout << “base::g(void)” << endl; }
};
class derived:public base
{
public:
void f(float y) { cout << “base::f(float)” << endl;}
void g(void) { cout << “derived::g(void)” << endl;}
};
这里derived::f(float y)就是第一种方式隐藏base::f(int x);derived::g(void)就是第二种方式隐藏base::g(void)
Tags: C/C++ 备忘
Posted in C&C++, 计算机技术 | No Comments »
在所有编译预处理指令中,#pragma指令是最复杂的,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。它的格式一般为:#pragma para 其中para为参数。下面罗列一些我见到过的参数。
1.message 参数
使用方法:#pragma message(”文本消息”)
作用:当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。注意:这里的编译输出窗口不是指打印程序输出结果的那个黑黑控制台窗口,而是指IDE下面输出编译信息,例如:给你报编译错误的那个窗口。
2. warning 参数
例如:#pragma warning(diable:4507;once:4385;error:164)
#pragma warning(disable:4507 34) // 不显示4507和34号警告信息
#pragma warning(once:4385) // 4385号警告信息仅报告一次
#pragma warning(error:164) // 把164号警告信息作为一个错误。
同时这个pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
这里n代表一个警告等级(1~4)。
#pragma warning( push )保存所有警告信息的现有的警告状态。
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告
等级设定为n。
#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的
一切改动取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//…….
#pragma warning( pop )
在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。
3. comment 参数
原型:#pragma comment( comment-type [,”commentstring“] )
该指令将一个注释记录放入一个对象文件或可执行文件中。 常用的comment-type有lib、compiler等。
例如在我的大数运算库里使用的一条指令:#pragma comment(lib,”winmm.lib”),目的就是导入winmm.lib库,连接器能连接这个库,以便能调用该静态库里的函数。
4. pack 参数
有如下几种形式:
#pragma pack() //取消对齐,恢复最原始缺省对齐
#pragma pack(n) /*指定按n字节对齐,其取值为1、2、4、8、16,默认是8,如果这个值比结构体成员的sizeof值小,那么该成员距结构体起始位置的偏移量应该以 此值为准,即是说,结构体成员的偏移量应该取二者的最小值,公式如下:
offsetof( item ) = min( n, sizeof( item )) */
#pragma pack(push) //保存当前对其方式到packing stack
#pragma pack(push,n) 等效于 #pragma pack(push) #pragma pack(n) //n=1,2,4,8,16保存当前对齐方式,设置按n字节对齐
#pragma pack(pop) //packing stack 出栈,并将对其方式设置为出栈的对齐方式
Tags: C/C++ 备忘
Posted in C&C++, 计算机技术 | No Comments »