| 安城小帅's profile佛乃觉悟也BlogLists | Help |
佛乃觉悟也佛法不二之门 看看这段代码请将 结构体 转换成 类:
c 转换成 c++ : 谢谢!!! 80分
#include<stdio.h>
#define MAX 100 typedef int elemtype; typedef struct { elemtype Data[MAX]; int len; }SQlist; int Insert(SQlist *L,int i,elemtype x) /*插入*/ { int j; if(i<1||i>L->len) { printf("\nerror,insert failure!"); return(0); } if(L->len>MAX-1) { printf("\nfull!"); return(0); } for(j=L->len;j>=i;j--) L->Data[j+1]=L->Data[j]; L->Data[i]=x; L->len++; return(1); }
int Delete(SQlist *L,int i) /*删除*/ { int j; if(i<1||i>L->len) { printf("\ndelete error,failure!"); return(0); } if(L->len<1)
{ printf("\nempty,failure!"); return(0); } for(j=i;j<L->len;j++) L->Data[j]=L->Data[j+1]; L->len--; return(1); } int Output(SQlist L) /*输出*/ { int i; if(L.len<1) { printf("\n empty,Output failure!"); return(0); } for(i=1;i<=L.len;i++) printf("%5d",L.Data[i]); return(1); } int Locate(SQlist L,elemtype x) /*查找*/ { int i; L.Data[0]=x; i=L.len; while(x!=L.Data[i]) i--; return(i); } int Create(SQlist *L) /*建表*/
{ int i,n; elemtype x; printf("\nplease input n(n<99) for create data:"); scanf("%d",&n); if(n>MAX-1) { printf("\nerror! n is1~99"); return(0); } L->len=n; for(i=1;i<=n;i++) { scanf("%d",&x); L->Data[i]=x; } return(1); } main()
{ SQlist L; int ch,i,v,a; elemtype x; printf("\n\n1--insert\n2--delete\n3--ouput\n4--locate\n"); Create(&L);
do { printf("\nplease input a number 1--4:\n"); scanf("%d",&ch); switch(ch) { case 1:printf("\nplease input i,x:"); scanf("%d%d",&i,&x); Insert(&L,i,x); Output(L);break; case 2:printf("\nplease input i:"); scanf("%d",&i); Delete(&L,i); Output(L); break; case 3:Output(L);break; case 4:printf("\nplease input x:"); scanf("%d",&x); v=Locate(L,x); printf("\nbe located is %d",v); break; } }while(ch>0&&ch<5); } #include <iostream>
#include <ctime> #include <cstdio> #include <cmath> #include <cstdlib> using namespace std;
template<typename T>
class Vector { public: Vector(); ~Vector(); void insert(T, int); // 插入一个结点在链表中指定的位置 void add(T); // 插入一个结点在链表尾 T remove(int); // 从链表中删除指定位置的元素, 返回删除的元素. int contains(T) const; // 查找给定的元素,如果找到,返回元素在链表中的位置,没找到,返回-1 void sort(); // 对链表进行升序排序 void reverse(); // 反转链表 T get(int) const; // 取得链表中第i个位置的元素 void print() const; // 打印出链表中的所有元素 int getSize() const { // 取得链表中元素的个数 return this->size; } int getCapacity() const { // 取得链表最大能容纳元素的个数 return this->capacity; } private:
int size; // 链表元素的个数 int capacity; // 链表能容纳元素的最大数 T* data; // 存储链表的元素数组 const static int CREASE_LENGTH = 32; // 每次增加链表容量的个数. T NO_ELEMENT; // 当给定非法的下标从链表中取元素时,返回此标志,表示取得的值非法.
}; template<typename T>
Vector<T>::Vector():size(0), capacity(16) { data = new T[capacity]; // 为链表元素初始化一段空间 memset(&NO_ELEMENT, -1111111, sizeof(T)); // 非法链表值 } template<typename T>
Vector<T>::~Vector() { delete[] data; } template<typename T>
void Vector<T>::add(T d) { if (size + 1 > capacity) { // 当增加元素时,可以链表容量不够,重新分配空间 T* temp = new T[capacity + CREASE_LENGTH]; if (NULL == temp) { // 如果分配内存不成功,则函数返回 cout << "Cann't allocation Memory." << endl; return; } capacity += CREASE_LENGTH; memcpy(temp, data, capacity * sizeof(T)); delete[] data; // 记得释放以前链表的空间,以免造成内存泄漏 data = temp; temp = NULL; cout << "reallocation: [capacity " << capacity << ", size" << size << "]" << endl; } data[size++] = d; } template<typename T>
void Vector<T>::insert(T d, int loc) { if (loc < 0 || loc > size) { cout << "Insert Exception: Bad allocation." << endl; return; } if (size + 1 > capacity) { T* temp = new T[capacity + CREASE_LENGTH]; if (NULL == temp) { cout << "Cann't allocation Memory." << endl; return; } capacity += CREASE_LENGTH; memcpy(temp, data, capacity * sizeof(T)); delete[] data; data = temp; temp = NULL; cout << "Reallocation: [capacity " << capacity << ", size" << size << "]" << endl; } for (int i = size; i > loc; i--) { data[i] = data[i - 1]; } data[loc] = d; size++; } template<typename T>
T Vector<T>::remove(int loc) { if (loc < 0 || loc >= size) { // 判断给定的位置是否合法,不合法,则函数返回 cout << "Remove Exception: Bad allocation." << endl; return NO_ELEMENT; } T temp = data[loc]; for (int i = loc; i < size - 1; i++) { data[i] = data[i+1]; // 删除元素后,要把链表中的元素移动. } size--; return temp; } template<typename T>
int Vector<T>::contains(T d) const { for (int i = 0; i < size; i++) { if (data[i] == d) { return i; // 返回给定元素在链表中的下标位置 } } return -1; } template<typename T>
T Vector<T>::get(int loc) const { if (loc < 0 || loc >= size) { cout << "Get Exception: Bad allocation." << endl; return NO_ELEMENT; } return data[loc];
} template<typename T>
void Vector<T>::sort() { // 选择法对链表进行排序. for (int i = 0; i < size - 1; i++) { int k = i; for (int j = i + 1; j < size; j++) { if (data[j] < data[k]) { k = j; } } if (k != i) { T temp = data[k]; data[k] = data[i]; data[i] = temp; } } } template<typename T>
void Vector<T>::reverse() { // 反转链表,只需要计算一半位置,左右两边的值进行交换. int middle = size / 2; T temp; for (int i = 0; i < middle; i++) { temp = data[i]; data[i] = data[size - 1 - i]; data[size - 1 - i] = temp; } } template<typename T>
void Vector<T>::print() const { cout << "["; for (int i = 0; i < size - 1; i++) { cout << data[i] << ", "; } cout << data[size - 1] << "]" << endl; } int main() {
Vector<int> vec; cout << "Initial size: " << vec.getSize() << endl; cout << "Initial capacity: " << vec.getCapacity() << endl; srand(time(0)); for (int i = 0; i < 20; i++) { vec.add(rand() % 100); } vec.print(); cout << "Current size: " << vec.getSize() << endl; cout << "Current capacity: " << vec.getCapacity() << endl; cout << "Random remove: " << endl; vec.remove(vec.getSize() - rand() % (vec.getSize() + 1)); vec.print(); cout << "After sort: " << endl; vec.sort(); vec.print(); cout << vec.get(-1) << endl; cout << "Random insert: " << endl; vec.insert(rand() % 500, rand() % vec.getSize()); vec.insert(rand() % 500, rand() % vec.getSize()); vec.print(); cout << "After sort: " << endl; vec.sort(); vec.print(); cout << "After reverse: " << endl; vec.reverse(); vec.print(); return 0; } 测试自己在工作中,我的朋友总结的:
1。工作中需要不段积累经验,这非常重要,在以后的测试过程,你能发现一些问题,这些问题都是你以前留心过的,思考过的。所以你发现的问题就是比别人多。
2。竞争,这个社会就是在竞争,你可以把这句话放在你的心底,提醒自己,勤奋工作。你只有竞争,你才能发现比别人多的bug。
3。对自己写的一些小自动化测试代码经常进行思考,测试。别认为调用了一个函数那个功能就实现了,别认为30秒的时间长度,就足够了。经常考虑点异常啊。
4。没事的时候,多学习。
5。经常测试自己,改进自己的测试理念,测试方法,测试工具,测试方式,思考方式……让这些达到最优化。 release testing这个阶段平时处理的好的,比较细心的人在这边正常情况下是不会发生什么问题。但是也难免。所以这个release testing也是非常重要的一个步骤。不要小看之。
这个阶段正常会发生什么情况呢:
开发人员少放了一个文件,或者多放了一个文件,没影响功能。(你说是不是问题?)
开发人员放错了文件。
还有的开发人员可能会把以前的版本放进来了。
程序的名字写错了。在微软,有一次错的比较厉害的是:Window Live Hotmail 写成了 Window Live Hotmal,这个问题就比较大啦。
原来我在一家软件公司,开发人员会把自己的公司名字也错过。
有些很明显的地方的标题是不是错了。
还有一些问题,开发人员说已经改好了,但还是错的。(在规范的软件的过程中,正常不会出现这样的问题。但一些不是很规范的公司,还是会出现这样的问题。)
有的时候,在自己的机器上,一点问题都没有,但到了客户那边问题就是100%出现。(我原来就出现过这样的问题,调一个函数中有一点错误,在自己的机器上怎么也不出现,清空在build,还是没问题,但是到了canon,问题就是出现。后来还是直接在客户那边调试的。真怪啊。)
会不会有不一致的地方。
产品安装,不是多了东西,就是少了东西。(安装程序哦)
调试版本。(这个我出现过的,有次不小心把调试的版本发上去了,搞得安装包特大,不说了,脸红了。)
客户可能会要求做一些修改的地方,是不是都修改了。(这肯定要达标啊,不然客户怎么给你钱?)
你的环境很好,但客户的机器就不同了。(是不是再确认一下在各个环境下的测试呢?)
你的版本是不是13或者250。(也许,你觉得无所谓,但是客户可能不这么考虑。)
安全漏洞测试。(这块我接触不多。如果您有这方面的经验,我们可以讨论一下。)
病毒测试。(呵呵,这是最呕心的。你的安装包里携带病毒。别觉得自己的机器没问题啊。我一朋友就发生过这样的问题。)
这么多问题,release testing 重要吧? 成功秘诀1. 先为成功的人打工,再和成功的人做同事,最后雇佣成功的人为自己工作 2. 在积累中获益(古为中用、洋为今用;八面来风、兼收并蓄;每天进步一点点) 3. 做到“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. 为渴望而做事 我的测试人才培养之道
这一两年在浏览各大IT网站的人才相关栏目时,总能发现类似‘测试人员缺口大,未来前景好’的信息。着实让很多正在从事测试行业或即将要从事测试行业的同行们无限憧憬了许久。但日子天天的过去,很多测试人员也没看见自己的待遇有很大的改观,不禁也狐疑起来,真的是这样的吗? 我是2001年进入测试行业的,可以说那时的国内测试还处于很初级的阶段,6年过去了,见证了测试行业的蓬勃发展,自己也由一个初级的软件测试人员升级成为了项目经理,从此也开始了人员招聘和人员培养的工作。这也成为了到目前为止我的项目经理生涯中最值得骄傲自豪的地方了(为什么呢?后面我会说)。 在此,我要先声明,虽然我也曾在大公司做过,但毕竟作为项目管理人员是在小公司,为了避免不必要的争执,我此处提及的经验主要是小公司。而且从很多方面上看小公司确实和大公司在运作上有些区别,直接导致了在人才培养方面的区别。小公司一般人员配备远少于大公司,很多时候一个人要做两个或多个人的事情,但也正是由于这个原因,小公司的人相对独立的时间更短,学到的东西更多;小公司部门精炼,流程相对简单,培养的结果更高效。下面说说我的人才培养思路。 1. 人员配备-招聘刚刚毕业的大学生。本来这只是针对一个小项目的权宜之际,但没想到得到了不少甜头,当然也出现了一些问题。但相比较招聘有测试人员的成本和得到的收益,让我在以后的几年里都是招聘刚毕业的大学生。首先刚毕业的大学生,相对来说成本较低;虽然对测试可能一无所知(当然我们也有简单的面试去挑选一些有思想,仔细的学生),但是也正由于此,他们会更加积极的学习新东西,投入的工作。有些以前在大公司做过的测试人员,来到小公司以后,往往都喜欢比较,越比较越郁闷,导致工作没心情,直接影响了工作。 2. 人员早期培训-强化测试基本理论,讲解公司流程,并用以往有代表性的项目为案例快速进入测试。由于招聘的全部都是毕业生,还有些不是计算机专业的,所以就需要有效的培训让他们快速的了解测试。我们制定一套测试培训课程,有理论知识的讲解,公司流程的讲解,工作英语的培训,最主要的是选取一些以往的项目为案例进行培训,将以往出现的问题,经验告诉他们,以尽快的了解公司的运作,自己的角色及工作的内容等。 3. 工作中的培养方式-采用导师式培养。真正开始工作了,就需要自己多多的言传身教了。虽然多少知道了怎么做测试,但是测试过程中也会有许多异常情况,而且测试也有许多需要利用经验去发现错误的地方,所以在测试中多多的和测试人员一起工作,多多的沟通,有时测试人员测过的地方自己走一遍,看看有什么他们漏掉了没有,有的话就告诉他们,这样下一次他们就知道自己去发现此类问题了。这样相对成熟也会快很多。 4. 辅助活动-多展开讨论,项目总结或课题研究之类的活动。通过这些活动,不断的激发他们的求知欲,并且将成果又运用到工作中去,效果显著。出现问题的项目多多的总结经验,以确保下一次避免出现同样的问题。 5. 人员绩效评核-每个项目或版本结束时,做好项目中人员绩效评核。不要等到公司做评核的时候才去给你的测试人员做考核,因为那时你可能早就忘记了每个人在不同项目中的表现。所以最好在每个项目或版本结束的时候(那时也有充分的时间),可以伴着项目总结一起做一做人员绩效考核,记录结果,等到了公司评核的时候不至于又花脑袋回忆。当然如果有人表现突出,要及时的向公司提出。 6. 人员培养-重点培养项目中突出表现的人员。当发现有人表现突出,或有特殊的潜力时,应适时的调整工作或职位。这样才可以更有效的激发他们的潜能,更加的显现公平的原则。 7. 不要让你的团队成员失望。作为一个项目管理人员,尽可能的保证你的组员可以没有杂念的工作(当然,生活上的除外)。如果他真的做的好,为什么不尽力为他争取他应该得到的呢? 经过这么几年的工作,招聘并培训的人不下于40个,虽然有的人离开了,有的人转行了,有的人继续深造了,有的人去别的项目了,但是他们的表现(不论是在公司,还是离开公司以后的表现)却是非常令我自豪。虽然刚开始会辛苦一些,但是随着培训机制的不断完善,人员的逐渐成熟,就会有那种看到自己种下的种子长成大树的那种欣慰。而且我还发现一个副产品,和他们年轻人在一起,自己也年轻许多呀,哈哈!
遇到你这样的人,算是那些的孩子的运气了。 做技术的我现在是自己做,但我此前有多年在从事软件开发工作,当回过头来想一想自己,觉得特别想对那些初学JAVA/DOT。NET技术的朋友说点心里话,希望你们能从我们的体会中,多少受点启发(也许我说的不好,你不赞同但看在我真心的份上别扔砖头啊)。
一. 在中国你千万不要认为学习技术就可以换来稳定的生活和高的薪水待遇,你更不要认为哪些从事 市场开发,跑腿的人,没有前途。 不知道你是不是知道,咱们中国有相当大的一部分软件公司,他们的软件开发团队都小的可怜,甚至只有1-3个人,连一个项目小组都算不上,而这样的团队却要承担一个软件公司所有的软件开发任务,在软件上线和开发的关键阶段需要团队的成员没日没夜的加班,还需要为测试出的BUG和不能按时提交的软件模块功能而心怀忐忑,有的时候如果你不幸加入现场开发的团队你则需要背井离乡告别你的女友,进行封闭开发,你平时除了编码之外就是吃饭和睡觉(有钱的公司甚至请个保姆为你做饭,以让你节省出更多的时间来投入到工作中,让你一直在那种累了就休息,不累就立即工作的状态)更可怕的是,会让你接触的人际关系非常单一,除了有限的技术人员之外你几乎见不到做其他行业工作和职位的人,你的朋友圈子小且单一,甚至破坏你原有的爱情(想象一下,你在外地做现场开发2个月以上,却从没跟女友见过一面的话,你的女友是不是会对你呲牙裂嘴)。 也许你拿到了所谓的白领的工资,但你却从此失去享受生活的自由,如果你想做技术人员尤其是开发人员,我想你很快就会理解,你多么想在一个地方长期待一段时间,认识一些朋友,多一些生活时间的愿望。 比之于我们的生活和人际关系及工作,那些从事售前和市场开发的朋友,却有比我们多的多的工作之外的时间,甚至他们工作的时间有的时候是和生活的时间是可以兼顾的,他们可以通过市场开发,认识各个行业的人士,可以认识各种各样的朋友,他们比我们坦率说更有发财和发展的机会,只要他们跟我们一样勤奋。(有一种勤奋的普通人,如果给换个地方,他马上会成为一个勤奋且出众的人。) 二。在学习技术的时候千万不要认为如果做到技术最强,就可以成为100%受尊重的人。 有一次一个人在面试项目经理的时候说了这么一段话:我只用最听话的人,按照我的要求做只要是听话就要,如果不听话不管他技术再好也不要。随后这个人得到了试用机会,如果没意外的话,他一定会是下一个项目经理的继任者。 朋友们你知道吗?不管你技术有多强,你也不可能自由的腾出时间象别人那样研究一下LINUX源码,甚至写一个LINUX样的杰作来表现你的才能。你需要做的就是按照要求写代码,写代码的含义就是都规定好,你按照规定写,你很快就会发现你昨天写的代码,跟今天写的代码有很多类似,等你写过一段时间的代码,你将领略:复制,拷贝,粘贴那样的技术对你来说是何等重要。(如果你没有做过1年以上的真正意义上的开发不要反驳我)。 如果你幸运的能够听到市场人员的谈话,或是领导们的谈话,你会隐约觉得他们都在把技术人员当作编码的机器来看,你的价值并没有你想象的那么重要。而在你所在的团队内部,你可能正在为一个技术问题的讨论再跟同事搞内耗,因为他不服你,你也不服他,你们都认为自己的对,其实你们两个都对,而争论的目的就是为了在关键场合证明一下自己比对方技术好,比对方强。(在一个项目开发中,没有人愿意长期听别人的,总想换个位置领导别人。) 三。你更不要认为,如果我技术够好,我就自己创业,自己有创业的资本,因为自己是搞技术的。如果你那样认为,真的是大错特错了,你可以做个调查在非技术人群中,没有几个人知道C#与JAVA的,更谈不上来欣赏你的技术是好还是不好。一句话,技术仅仅是一个工具,善于运用这个工具为别人干活的人,却往往不太擅长用这个工具来为自己创业,因为这是两个概念,训练的技能也是完全不同的。 创业最开始的时候,你的人际关系,你处理人际关系的能力,你对社会潜规则的认识,还有你明白不明白别人的心,你会不会说让人喜欢的话,还有你对自己所提供的服务的策划和推销等等,也许有一万,一百万个值得我们重视的问题,但你会发现技术却很少有可能包含在这一万或一百万之内,如果你创业到了一个快成功的阶段,你会这样告诉自己:我干吗要亲自做技术,我聘一个人不就行了,这时候你才真正会理解技术的作用,和你以前做技术人员的作用。 [小结] 基于上面的讨论,我奉劝那些学习技术的朋友,千万不要拿科举考试样的心态去学习技术,对技术的学习几近的痴迷,想掌握所有所有的技术,以让自己成为技术领域的权威和专家,以在必要的时候或是心里不畅快的时候到网上对着菜鸟说自己是前辈。 技术仅仅是一个工具,是你在人生一个阶段生存的工具,你可以一辈子喜欢他,但最好不要一辈子靠它生存。 掌握技术的唯一目的就是拿它找工作(如果你不想把技术当作你第二生命的话),就是干活。所以你在学习的时候千万不要去做那些所谓的技术习题或是研究那些帽泡算法,最大数算法了,什么叫干活?就是做一个东西让别人用,别人用了,可以提高他们的工作效率,想象吧,你做1万道技术习题有什么用?只会让人觉得酸腐,还是在学习的时候,多培养些自己务实的态度吧,比如研究一下当地市场目前有哪些软件公司用人,自己离他们的要求到底有多远,自己具体应该怎么做才可以达到他们的要求。等你分析完这些,你就会发现,找工作成功,技术的贡献率其实并没有你原来想象的那么高。 不管你是学习技术为了找工作还是创业,你都要对技术本身有个清醒的认识,在中国 不会出现BILL GATES,因为,中国目前还不是十分的尊重技术人才,还仅仅的停留在把软件技术人才当作人才机器来用的尴尬境地——如果你不理解,一种可能是你目前仅仅从事过技术工作,你的朋友圈子里技术类的朋友占了大多数,一种可能是你还没有工作,但喜欢读比尔.盖茨的传记。 计算器本计算器利用堆栈来实现
1、定义后缀式计算器的堆栈结构 因为需要存储的单元不多,这里使用顺序栈,即用一维数组来模拟堆栈: #define MAX 100 int stack[MAX]; int top=0; 因此程序中定义了长度为MAX的一维数组,这里MAX用宏定义为常数100,我们可以修改宏定义而重新定义堆栈的大小。 整型数据top为栈顶指示,由于程序开始时堆栈中并无任何数据元素,因此top被初始化为0。 2、存储后缀式计算器的运算数 我们定义了堆栈stack[MAX]后,就可以利用入栈操作存储先后输入的两个运算数。 下面看一下是如何实现的: int push(int i) /*存储运算数,入栈操作*/ { if(top<MAX) { stack[++top]=i; /*堆栈仍有空间,栈顶指示上移一个位置*/ return 0; } else /*堆栈已满,给出错误信息,返回出错指示*/ { printf("The stack is full"); return ERR; } } 我们在调用函数push时,如果它的返回值为0,说明入栈操作成功;否则,若返回值为ERR(在程序中说明为-1),说明入栈操作失败。 3、从堆栈中取出运算数 当程序中读完了四则运算符后,我们就可以从堆栈中取出已经存入的两个运算数,构成表达式,计算出结果。取出运算数的函数采用的正是出栈算法。在本例中,实现该算法的函数 为pop(): int pop(); /*取出运算数,出栈操作*/ { int var; /*定义待返回的栈顶元素*/ if(top!=NULL) /*堆栈中仍有数据元素*/ { var=stack[top--]; /*堆栈指示下移一个位置*/ return var; } else /*堆栈为空,给出错误信息,并返回出错返回值*/ printf("The stack is cmpty!\n"); return ERR; } 同样,如果堆栈不为空,pop()函数返回堆栈顶端的数据元素,否则,给出栈空提示,并返回错误返回值ERR。 4、设计完整的后缀式计算器 有了堆栈存储运算数,后缀式计算器的设计就很简单了。程序首先提示用户输入第一个运算数,调用push()函数存入堆栈中;而后提示用户输入第二个运算数,同样调用push()函数存入堆栈中。接下来,程序提示用户输入+,-,*,/四种运算符的一种,程序通过switch_case结构判断输入运算符的种类,转而执行不同的处理代码。以除法为例,说明程序的执行流程: case '/': b=pop(); a=pop(); c=a/b; printf("\n\nThe result is %d\n",c); printf("\n"); break; 程序判断用户输入的是除号后,就执行上述代码。首先接连两次调用pop()函数从堆栈中读出先前输入的运算数,存入整型数a和b中;然后执行除法运算,结果存入单元c中。这时需要考虑究竟谁是被除数,谁是除数。由于开始我们先将被除数入栈,根据堆栈“先进后出”的原则,被除数应该是第二次调用pop()函数得到的返回值。而除数则是第一次调用pop()函数得到的返回值。 最后程序打印出运算结果,并示提示用户是否继续运行程序: printf("\t Continue?(y/n):"); l=getche(); if(l=='n') exit(0); 如果用户回答是"n",那么结束程序,否则继续循环。 完整的程序代码如下:
#include<stdio.h> #include<conio.h> #include<stdlib.h> #define ERR -1 #define MAX 100 /*定义堆栈的大小*/ int stack[MAX]; /*用一维数组定义堆栈*/ int top=0; /*定义堆栈指示*/ int push(int i) /*存储运算数,入栈操作*/ { if(top<MAX) { stack[++top]=i; /*堆栈仍有空间,栈顶指示上移一个位置*/ return 0; } else { printf("The stack is full"); return ERR; } } int pop() /*取出运算数,出栈操作*/ { int var; /*定义待返回的栈顶元素*/ if(top!=NULL) /*堆栈中仍有元素*/ { var=stack[top--]; /*堆栈指示下移一个位置*/ return var; /*返回栈顶元素*/ } else printf("The stack is empty!\n"); return ERR; } void main() { int m,n; char l; int a,b,c; int k; do{ printf("\tAriothmatic Operate simulator\n"); /*给出提示信息*/ printf("\n\tPlease input first number:"); /*输入第一个运算数*/ scanf("%d",&m); push(m); /*第一个运算数入栈*/ printf("\n\tPlease input second number:"); /*输入第二个运算数*/ scanf("%d",&n); push(n); /*第二个运算数入栈*/ printf("\n\tChoose operator(+/-/*//):"); l=getche(); /*输入运算符*/ switch(l) /*判断运算符,转而执行相应代码*/ { case '+': b=pop(); a=pop(); c=a+b; printf("\n\n\tThe result is %d\n",c); printf("\n"); break; case '-': b=pop(); a=pop(); c=a-b; printf("\n\n\tThe result is %d\n",c); printf("\n"); break; case '*': b=pop(); a=pop(); c=a*b; printf("\n\n\tThe result is %d\n",c); printf("\n"); break; case '/': b=pop(); a=pop(); c=a/b; printf("\n\n\tThe result is %d\n",c); printf("\n"); break; } printf("\tContinue?(y/n):"); /*提示用户是否结束程序*/ l=getche(); if(l=='n') exit(0); }while(1); } 发布测试你知道哥德堡号是怎样沉没的吗?07年第8期的《读者》上有篇文章引起了我的兴趣。哥德堡号是18世纪瑞典人的希望:他们需要从海上贸易来充实因为战争而濒临枯竭的国库。建造哥德堡号动用了瑞典当时15%的国内生产总值,船坚炮利不在话下。然而在最后一次返航途中离码头900米的地方撞上了当地人再熟悉不过的一块暗礁,在欢迎人群的注视下满载着从中国运来的瓷器、茶叶和丝绸沉入海底。 你的开发工作中也会有平时再熟悉不过的暗礁: 你不会在工作目录少放一两个文件,特别是开发了半年后; 你不会在调试上个星期的版本的时候,心里以为是最新的版本; 你不会把产品的名字都写错... 是的,谁都不会撞上这样的暗礁。不过考虑一下临交货前一天可能发生的事情: 发现一个小bug,顺手改了一把; bug都改完了,开始兴冲冲的写下一个版本; 客户发个email来说某些显眼处的标题要改,他们也很抱歉,说是上头今天异想天开... 如果这时候就打包刻盘,明天交货时会发生哪些事情呢? 出现了一些以前出现过的bug,但是dev说早就改好了; 有些问题在自己的环境里面总没法复现出来,客户那边100%出现,直到有一天发现少了个文件; 被问到“为什么这里说的和那里不一致呢?”... 在把发布测试当一回事来抓之前,客户拿到手的产品可能会有这些问题: 产品安装/上线之后不是多了就是少了些东西; 好像是调试版本; 文档和产品不一致; 有些承诺修改过的bug还在... 所有这一切,都源于开发人员和客户关注角度的差别。作为测试人员,应该站在客户的位置上,可惜他们还是开发团队的一部分,往往还是以开发人员的眼光去看bug。发布阶段的bug,拥有许多不一样的地方: 这不是/这里没有客户需要的东西; 这不影响使用,但影响客户的生意(比如把人家的logo都搞错了); 你会用,但客户不会用; 在你的环境好用,但和客户环境不太兼容; 触了客户的霉头(别笑,你见过主版本号是13的产品吗?)... 成熟的软件工业会进行一系列的发布阶段测试: 安全漏洞测试; 各个语言版本的界面内容(文本,图片,多媒体资源等),用户文档,发布说明的复核,确保没有违反法律和地缘政治文化(想想十字军东征的画面被放在阿拉伯文版里面); 数字签名校验; 病毒扫描(想想熊猫烧香是怎样传播的); 再一次基本功能测试。 噢,忘了说为什么哥德堡号撞上暗礁的根本原因:航海多年的水手看见陆地和欢迎人群,兴奋起来所以提早在船上开庆祝party;舵手的位置在二楼,需要甲板上的人指示方向;本来每条船上都有当地向导作为领航员,但是他去参加party了。 国内软件业期待更多的关注现在提起软件测试的话题并非突然。清华大学教授、著名软件工程专家郑人杰表示:只是在一年多前,当中国软件行业协会在清华大学开办第一期软件测试工程师培训时,国内专业媒体和网上对软件测试的讨论还寥寥无几。但一年后的今天,当他来到上海参加8月22日举办的“首届中国软件测试与软件产业发展战略研讨会”时,现场代表的关注热情让他一下子就感受到这个萌芽中的市场开始成长了。 国外:软件测试已成独特市场软件必需经过测试,测试是验证软件是否能达到期望功能的唯一有效的方法。据了解,目前国内的软件测试一般有下列几种形式:一是软件公司内部进行的功能性测试;二是用户进行的测试;还有就是第三方测试,就是专业软件测试人员运用一定的测试工具对软件的质量进行检测。 在本次大会上,美国著名软件质量分析师贺越明介绍了国外的情况,在软件业较发达的国家,软件测试不仅早已成为软件开发的一个有机组成部分,而且在整个软件开发的系统工程中占据着相当大的比重。以美国的软件开发和生产的平均资金投入为例,通常是:“需求分析”和“规划确定”各占百分之三,“设计”占百分之五,“编程”占百分之七,“测试”占百分之十五,“投产和维护”占百分之六十七。测试在软件开发中的地位,由此可见一斑。 与此同步的是,软件测试市场已成为软件产业中的一个独特市场。在美国硅谷地区,凡是软件开发企业或是设有软件开发部门的公司,都有专门的软件测试单位,其中软件测试人员的数量相当于软件开发工程师的四分之三。在这些公司或部门中,负责软件测试的质量保证经理的职位与软件开发的主管往往是平行的。据了解,在软件产业发展较快的印度,软件测试在软件企业中同样拥有举足轻重的地位。 国内:萌芽中的市场才起步与国外相比,国内的软件测试业状况又如何?上海软件行业协会副理事长朱三元研究员认为,目前,国内软件测试市场表现实在有点令人尴尬。中国市场中的软件开发公司比比皆是,但软件测试公司则如凤毛麟角,“市场化的第三方测试如同刚刚出生的宁馨儿,目前的市场几乎可以忽略不计。” 为什么国内的软件测试市场会如此嬴弱,到现在企业才开始关注呢?以朱三元之见,究其主要原因,首先是因为企业对软件测试的重要性理解不够。很多人认为程序能试运行基本上就已经成功,没有必要成立专门的测试部门或设立测试岗位。 另一方面,软件开发企业在为软件开发支付费用后,就不希望再为软件的测试支付新的成本,而项目甲方则往往认为开发合格的软件是软件开发企业的责任。即使有些项目的开发方或委托方有意对软件进行第三方测试,也会考虑到在测试过程中往往需要软件开发商提供源代码,担心其知识产权遭到侵犯。这是软件测试市场无法长大的又一个重要原因。此外,软件开发企业不重视利用外部的测试力量进行测试也是因素之一。 但从本次会议上提供的信息中,敏锐的人士却可以嗅出,萌芽中的市场正开始起步。就在本次研讨会召开的同时:上海著名的软件企业微创软件就向上海的用户发布了BMS(缺陷跟踪管理系统)2003和TCM(测试用例管理系统)2003,它们的发布填补了国内软件公司目前在测试管理软件市场上的空白。 软件测试国标今年有望出台中国的软件产业要健康发展,软件测试专业公司的成长与专业测试市场的发展是不可跨跃的一步。那么软件测试产业如何才能发展呢? 在此次大会上,与会的专家也对此进行了积极的探讨。与会专家认为:首先需要软件企业提高对于测试环节的重视程度,形成对测试服务的需求市场;其次,需要有企业从事测试理论与技术服务;第三,作为行业的管理者也要制订相应的行业规范,严格软件开发的流程及标准;第四,作为软件测试企业也要关注软件开发商对于知识产权的利益,从而也保障了自身的利益。 朱三元透露,关于软件测试的国家标准今年有望出台。在中国软件行业协会软件测评培训中心授权下,他将作为上海威迅教育讲师团的成员参与上海软件测评工程师的培训。他认为,国家标准的制定,将极大地促进软件测评业的发展。 软件公司是如何倒闭的呢?最近读了一篇非常有趣的文章,原文是英文,和大家共享,其大概意思是:
管程序员就象是养蜂人管蜜蜂一样。养蜂人怎么管的呢?养蜂人其实并不能和蜜蜂沟通,谁会说“蜂语”啊。但他们可以把蜜蜂放在一个舒适的蜂房里,这样他们自动就会采蜜了,你顺手就可以拿走蜂蜜。:)
对程序员是类似的,把他们弄到一个舒服的环境里:工资比他们想要的高,以至于不会用了(当然,比你的预算还是要便宜);这些蜜蜂会嘤嘤嗡嗡自己形成社会,程序员会自己评价自己,就象沉迷在打游戏里的家伙一样,谁的话都听不进去,就只愿意写好的软件-这和艺术家和战士一样。
软件公司怎么失控的和完蛋的?通常是来了一个有个性的管理人员,这老兄一看,这帮程序员怎么这么……不顺眼啊?脏兮兮,乱糟糟,不配合,他们看起来是多无趣的一群人啊!最糟糕的是,他们还笑话你!于是对他们进行管理……这下规范了,但是,程序员们被伤害了,他们被要求要参加会议,做计划,写报告,严格按照流程,千万千万不要去动别人的代码!程序员觉得自己就象过起了外星人的生活……于是,最好的程序员走了,有的开始怠工,甚至破坏……蜂房毁了。管理者舒服了,因为好像事情开始受控了,大家开始打领带了;但是Bug开始成堆出现,市场丢失,最后,关门大吉。
英文原文:
Software - How Software Companies Die
By Orson Scott Card
The environment that nutures creative programmers kills management and marketing types - and vice versa. Programming is the Great Game. It consumes you, body and soul. When you're caught up in it, nothing else matters. When you emerge into daylight, you might well discover that you're a hundred pounds overweight, your underwear is older than the average first grader, and judging from the number of pizza boxes lying around, it must be spring already. But you don't care, because your program runs, and the code is fast and clever and tight. You won. You're aware that some people think you're a nerd. So what? They're not players. They've never jousted with Windows or gone hand to hand with DOS. To them C++ is a decent grade, almost a B - not a language. They barely exist. Like soldiers or artists, you don't care about the opinions of civilians. You're building something intricate and fine. They'll never understand it.
BEEKEEPING
Here's the secret that every successful software company is based on: You can domesticate programmers the way beekeepers tame bees. You can't exactly communicate with them, but you can get them to swarm in one place and when they're not looking, you can carry off the honey. You keep these bees from stinging by paying them money. More money than they know what to do with. But that's less than you might think. You see, all these programmers keep hearing their parents' voices in their heads saying "When are you going to join the real world?" All you have to pay them is enough money that they can answer (also in their heads) "Geez, Dad, I'm making more than you." On average, this is cheap. And you get them to stay in the hive by giving them other coders to swarm with. The only person whose praise matters is another programmer. Less-talented programmers will idolize them; evenly matched ones will challenge and goad one another; and if you want to get a good swarm, you make sure that you have at least one certified genius coder that they can all look up to, even if he glances at other people's code only long enough to sneer at it. He's a Player, thinks the junior programmer. He looked at my code. That is enough. If a software company provides such a hive, the coders will give up sleep, love, health, and clean laundry, while the company keeps the bulk of the money.
OUT OF CONTROL
Here's the problem that ends up killing company after company. All successful software companies had, as their dominant personality, a leader who nurtured programmers. But no company can keep such a leader forever. Either he cashes out, or he brings in management types who end up driving him out, or he changes and becomes a management type himself. One way or another, marketers get control. But...control of what? Instead of finding assembly lines of productive workers, they quickly discover that their product is produced by utterly unpredictable, uncooperative, disobedient, and worst of all, unattractive people who resist all attempts at management. Put them on a time clock, dress them in suits, and they become sullen and start sabotaging the product. Worst of all, you can sense that they are making fun of you with every word they say.
SMOKED OUT
The shock is greater for the coder, though. He suddenly finds that alien creatures control his life. Meetings, Schedules, Reports. And now someone demands that he PLAN all his programming and then stick to the plan, never improving, never tweaking, and never, never touching some other team's code. The lousy young programmer who once worshiped him is now his tyrannical boss, a position he got because he played golf with some sphincter in a suit. The hive has been ruined. The best coders leave. And the marketers, comfortable now because they're surrounded by power neckties and they have things under control, are baffled that each new iteration of their software loses market share as the code bloats and the bugs proliferate. Got to get some better packaging. Yeah, that's it. 再论软件测试的执行虽然我们都认为,有效的测试计划是指导测试用例设计、测试执行的指导性文件,是成功测试的前提和必要条件,测试用例设计是测试工作的核心,测试用例的成功设计已经完成了一半的测试任务,但是测试的执行是基础,是测试计划和测试用例实现的基础,严格的测试执行使测试工作不会半途而废。而且,测试执行的管理相对复杂些,在整个测试执行阶段中,我们需要面对一系列问题,如:如何确保测试环境满足测试用例所描述的要求?
- 如何保证每个测试人员清楚自己的测试任务和要达到的目标? - 如何保证每个测试用例得到百分之百的执行? - 如何保证所报告的软件缺陷正确、描述清楚、没有漏掉信息? - 如何在验证Bug或新功能与回归测试之间寻找平衡? - 如何跟踪Bug处理的进度使严重的Bug及时得到解决? 要实现上述目标,得到一个真实、符合要求的执行过程,需要很好地全程跟踪测试过程、过程度量和评审、借助有效的测试管理系统等来实现。主要的方法和措施有: 1. 执行前,动员会是必要的,如同打战,要鼓舞士气,更重要阐述策略,回答大家的问题,使测试计划、测试范围和所有测试项目的定义都十分清楚。 2. 严格审查测试环境,包括硬件型号、网络拓扑结构、网络协议、防火墙或代理服务器的设置、服务器的设置、应用系统的版本,包括被测系统以前发布的各种版本和不定包、以及相关的或依赖性的产品。 3. 将要执行的所有测试用例进行分类,基于测试策略和历史数据的统计分析,包括测试策略和缺陷的关联关系,构造有效的测试套件(Test Suite),然后在此基础上建立要执行的测试任务,这样任务的分解有助于进度和质量的有效控制,减少风险。 4. 所有测试用例、测试套件、测试任务和测试执行结果,都通过测试管理系统进行管理,使之测试执行的操作、过程记录在案,具有良好的可跟踪性、控制性和追溯性,容易控制好测试进度和质量。 5. 要确保每一个测试人员理解测试策略、测试目标,对测试进程进行审查(Audit),确保测试策略得到执行,可以通过一些奖励手段进行引导。测试经理、组长要用于承担风险,使之测试人员有发挥、想象的空间,但同时也要给予适当的压力,提高工作效率和责任心。 6. 缺陷的跟踪和管理一般由数据库系统来执行,容易对缺陷进行跟踪、统计分析和趋势预测,并设定一些有效的规则和流程来配合测试执行,如通过系统自动发出邮件给相应的开发人员和测试人员,使得任何缺陷都不会错过,并能得到及时处理。而且事先建立基于缺陷跟踪系统的缺陷报表、缺陷趋势曲线,对各模块、各测试人员、整体项目等进行实时跟踪。 7. 进行常规的缺陷审查,如Daily Bg review, bug scrub meeting,包括Bug的严重性、Bug的描述、Bug修正的反应速度等,及时发现问题、纠正问题,使整个测试进程在控制轨道上发展。 8. 对每个阶段的测试结果进行分析,保证阶段性的测试任务得到完整的执行并达到预定的目标。 9. 良好的沟通,不仅和测试人员保持经常的沟通,还要求和项目组的其他人员保持有效的沟通,如每周例会,可以及时发现测试中问题或不正常的现象。 sizeof的函数使用 一 sizeof的使用方法 1、用于数据类型 sizeof使用形式:sizeof(type) 数据类型必须用括号括住。如sizeof(int)。 2、用于变量 sizeof使用形式:sizeof(var_name)或sizeof var_name 变量名可以不用括号括住。如sizeof (var_name),sizeof var_name等都是正确形 式。带括号的用法更普遍,大多数程序员采用这种形式。 注意:sizeof操作符不能用于函数类型,不完全类型或位字段。不完全类型指具有未知存储大小的数据类型,如未知存储大小的数组类型、未知内容的结构或联合类型、void类型等。 如sizeof(max)若此时变量max定义为int max(),sizeof(char_v) 若此时char_v定义为char char_v [MAX]且MAX未知,sizeof(void)都不是正确形式。 二、sizeof的结果 sizeof操作符的结果类型是size_t,它在头文件 中typedef为unsigned int类型。该类型保证能容纳实现所建立的最大对象的字节大小。 1、若操作数具有类型char、unsigned char或signed char,其结果等于1。 ANSI C正式规定字符类型为1字节。 2、int、unsigned int 、short int、unsigned short 、long int 、unsigned long 、float、double、long double类型的sizeof 在ANSI C中没有具体规定,大小依赖于实现,一般可能分别为2、2、2、2、4、4、4、8、10。 3、当操作数是指针时,sizeof依赖于编译器。例如Microsoft C/C++7.0中,near类指针字节数为2,far、huge类指针字节数为4。一般Unix的指针字节数为4。 4、当操作数具有数组类型时,其结果是数组的总字节数。 5、联合类型操作数的sizeof是其最大字节成员的字节数。结构类型操作数的sizeof是这种类型对象的总字节数,包括任何垫补在内。 让我们看如下结构: struct {char b; double x;} a; 在某些机器上sizeof(a)=12,而一般sizeof(char)+ sizeof(double)=9。 这是因为编译器在考虑对齐问题时,在结构中插入空位以控制各成员对象的地址对齐。如double类型的结构成员x要放在被4整除的地址。 6、如果操作数是函数中的数组形参或函数类型的形参,sizeof给出其指针的大小。 HTMLQAHtml 文件是常用的文件格式,很多软件的联机帮助都是一系列 Html 文件组成的。另外,网站本地化测试经常要测试本地化的 Html 文件。 HtmlQA 就是用于测试本地化 Html 文件的通用工具之一。 1.简介 HtmlQA 是 SDL International (思迪)公司针对软件本地化行业开发的商业工具,用于测试源语言和本地化语言的项目文件的本地化质量,每个项目文件包含一系列 Html 文件。 HtmlQA 可以执行一系列本地化 Html 文件检查,确定本地化的 Html 文件与源语言对应的 Html 文件具有一致的功能。 经过本地化翻译的 Html 文件经常会产生影响文档系统功能的缺陷。例如删除或增加了链接、格式标识符,遗漏图像引用( Reference ),未翻译的文字等。 HtmlQA 对源语言和本地化语言的两个 Html 项目文件执行一致性完整检查,确保本地化项目与源语言项目的功能保持一致。 2.安装和运行 现在使用最多的是 HtmlQA1.4 ,它有两种运行模式:演示模式和全功能模式。默认安装后是演示模式,只能打开有限数量的文件,对于打开的文件大小也受限制。全功能模式需要购买许可文件 (License) 和 / 或硬件加密锁 (Dongle) 。 HtmlQA1.4 的安装非常简单,双击安装文件“ HtmlQA_v1_4_FSL.exe ”,然后根据屏幕提示即可完成。 HtmlQA1.4 安装后,在 Windows 的桌面上自动创建一个快捷方式,双击此快捷方式,可以运行 HtmlQA1.4 ,运行界面如下图所示: HtmlQA1.4 的运行程序窗口包括四个标签:“ Source ”、“ Target ”、“ Project Compare ”和“ Configuration ”,分别完成不同的功能,下面介绍本地化测试中的常见操作方法。 3.打开项目文件 在使用 HtmlQA1.4 进行任何比较和测试之前,首先需要打开包含一系列源语言和本地化 Html 文件的项目文件。操作步骤如下: 运行 HtmlQA1.4 ; 从“ Source ”标签,单击“ File ” > “ Open Project ”; 选择并打开一个扩展名为“ prj ”或“ hhp ” 项目文件。 打开项目文件后的界面如下图所示: 4.项目比较 使用 HtmlQA1.4 进行 Html 文件的本地化测试主要是在“ Project Compare ”标签页面完成的。 “ Project Compare ”标签页面的左边分成两个部分:“ Compare ”和“ Visual Compare ”。“ Compare ”是本地化测试最经常使用的部分,因此,需要详细介绍。 在“ Compare ”部分,包括 6 个按钮,每个按钮可以测试 Html 文件的特定类型的错误。 下面分别介绍这些按钮的测试功能: Links (URLs) 这个按钮检查和测试源语言和本地化文件包含 URL 引用 (References) 的标识 (Tags) 属性的一致性。 Images 这个按钮检查和测试源语言项目出现的图像也出现在目标语言项目中。 Untranslated Text 这个按钮查看未翻译的 Html 文件的主体 (Body) 文本和属性 (Attribute) 文本。 Formatting 这个按钮检查和测试源语言和目标语言文件的特定标识和标识的数量。逐个比较源语言文件和目标语言文件的标识数量,如果数量不同,则在错误列表视图控件中显示出来。 Inconsistencies 这个按钮验证和测试相同 URL 的链接(或“锚 (Anchor) ”)文字在目标语言 URL 的各处链接中都保持一致。 Group Verify “ Group Verify ”按钮可以执行全部或多项上述条目的测试,如下图所示: 5.保存测试报告结果 HtmlQA1.4 不仅可以包测试结果显示在右边的列表视图控件中,还可以保存到结果文件,以便以后查看、打印和分发。 单击“ Save Report ”按钮,输入文件名可以保存上述测试结果,保存的文件类型可以是 html 或 txt 格式。 下图是保存的 html 格式的测试结果文件在浏览器中打开后的部分内容。 HtmlQA 对测试发现的每个问题按行显示,包括错误的位置,问题描述和标识内容。本地化测试人员可以逐条分析和判断哪些结果属于真正的缺陷,并且报告到缺陷跟踪数据库中。 HtmlQA 1.4 还包括很多其它的辅助功能,可以单击“ Source ”或“ Target ”标签页面的“ Tools ”按钮,选择不同的功能。由于这些功能在本地化测试中应用较少,在此不再一一介绍。 手机测试面临改革越来越多的手机终端正在涌入市场,在令消费者体验到更多更新功能的同时,也给手机厂商带来了自己所生产的手机是否能满足消费者需求的困惑。什么样的手机能获得消费者的欢迎?往往在投放市场前无法得知。手机厂商们测试重点更多放在手机的技术性能指标上,而非站在用户角度的体验。 事实上我们看到目前手机发展的趋势有两大特点:一是手机往小、薄、轻的方向发展;二是手机跟电脑的应用充分融合,这使手机功能变得越来越复杂。这就令手机出厂前的测试更为复杂。 结合以上两点,对手机测试提出了两个问题:如何站在用户体验角度对手机进行测量,如何能令越来越复杂的测试变得简单?这两个问题促使手机测试领域面临一场改革。对此,C114记者专程采访了TestQuest的首席市场营销官John F. Yuzdepski先生。 自动测试代替手工测试 C114:我们知道现在随着手机功能越来越复杂、手机品种越来越多,对于手机测试的难度也增加了不少,您能否针对目前测试方面存在的问题给我们做个介绍? John:现在的手机厂商都希望领先对手把产品提供给客户,这样就必须缩短手机测试的时间,因此如何缩短手机的测试时间就是我们的迫切需要解决的问题。我知道目前中国手机市场是非常快速发展的市场,每年有成千上万的手机推向市场。我希望TestQuest公司的测试方案能够帮助国内的厂商提高测试的效率。 手机测试的复杂程度可以通过一些简单的公式表现出现:首先很多手机厂商使用成千上万个手机工程师,然后每一款手机要经过成千上万的测试,从这种描述中我们可以想象整个手机测试的复杂程度,超出所有人的想象。 具体来讲,手机测试过程中会遇到哪些难题和挑战呢?手机的平台变得越来越多样化、手机的功能元器件变得越来越复杂、手机的应用也越来越多样化(比如说短信、彩信等等)、同时手机的验证过程和认证过程也越来越复杂,这些复杂的因素带来一个问题,那就是在手机的整个产业价值链中,从上游的芯片厂商到中游的运行厂商,再到下游的网络运营商,整个测试的架构变得异常的烦琐,效率也非常低下。我们TestQuest希望通过提供一个自动测试平台,以提高手机产业链中不同的公司不同组织的测试效率。 C114:换句话说目前手机测试存在的最主要问题是效率低下,无法有效缩短手机生产周期。那么TestQuest公司有什么方法来解决这个问题? John:TestQuest最终的愿景希望以最先进的理念和服务加速手机厂商和移动应用软件开发商产品推向市场的速度,同时保证用户的最终体验和产品的满意度。 在我们见到的很多手机厂商,在手机的测试过程中,很大的程度上是手工测试。我们去过的手机厂商经常看到公司里面有几十个上百个,最多的研发公司有三百个工程师在做手工测试。但实际上手工测试有很多的局限性,例如,有时候人的双眼很难辨别很微小的差异,比如说字母“O”和数字“0”。其次,手工测试有最大的问题就是不可重复性,比如说通过手工测试在测试过程中出现的一个问题,却很难回到当时的场景。第三,手工测试效率低因此,可以看出手工测试存在这样那样的问题,我们就想用机器改善手工测试的局限性。机器可以24小时测试,可以辨别0和O这样的差别,同时机器可以更快的进行测试。 TestQuest实际上是通过自动测试这样一个方案代替手工测试。第一个优势是可以最大程度提高测试的效率,缩短测试的时候,使产品可以更快的推向市场,获得更大的盈利空间。第二个就是通过自动测试有可能发现更多我们手工测试不能发现的问题,这样可以减少手机厂商因为产品召回和维修所造成的直接经济损失,大大降低了有失去客户的风险,并且减少了对品牌的影响程度。同时,也可以降低我们对手工测试人员支付的工资和成本。 C114:这种使用自动测试代替传统手工测试的方式是否已经为业界所接受?或者说TestQuest的自动测试平台/解决方案是否得到用户肯定? John:是的,目前成为TestQuest用户的企业有很多,包含很多种类。比如说世界上知名的电信运营商包括美国的Wireless、T-Mobile、包括德国电信等等。我们还包括了世界上很多的手机生产厂商,比如说摩托罗拉、诺基亚、LG、三星、以及国内的中兴等等。为了最大限度满足客户的需要,我们跟世界上很多知名的软件测试公司建立了合作关系,一起给客户提供完整的解决方案。 与众不同的测试平台CountDown C114:同一领域内独立的提供测试解决方案的公司有多少? John:我曾经预期会有很多的公司进入同一个行业,有很多类似的产品,事实上情况不是这样。我很惊讶,这个行业相对于PC的自动测试有更多的复杂性,因为手机多种多样,技术要求非常的高。到目前为止,我没有发现特别明显的竞争对手在行业中,也许这个对TestQuest是好消息,在这个领域里我们不仅是领先的公司,也是最主要的公司。 C114:TestQuest的测试平台CountDown在国际上拥有较高的知名度,但国内对此并不是很了解,您能否就CountDown是个怎样的平台,贯彻怎样的理念,为我们简单介绍? John:TestQuest的自动测试平台名字叫做CountDown.这是一个终极化的适用非常广泛的一个自动测试系统的平台。CountDown的自动测试系统是一个模块化的设计,它包括开发工具TestDesigner,运行测试的工具TestRunner,测试管理工具TestManager以及资产管理工具AssetManager.我们可以根据不同的客户的不同需要,将系统分别安装在不同的地点,最大限度的满意客户全球话,分布式的管理的需求。比如说我们测试项目的开发可能在北京、测试项目的运行可能美国,测试的管理可能在上海。 测试软件开发的工具TestDesigner是一个全图形化的界面,用户不需要做任何的编程就可以很轻松快速的生成所需要的图形。测试管理的工具TestManager是一个完全基于IE浏览器的工具,用户可以在任何一个PC上,只要能连接互联网,就可以随时监控、规划和管理测试的过程。 我们之所以认为CountDown是终极的测试平台,最重要的原因就是他可以适用于各种各样的手机的测试。第一,适用于任何的应用服务,比如说短信、彩信、移动多媒体等等。第二,适用于任何手机的平台,比如说,摩托罗拉、LG等等。第三,适用于任何的操作系统,比如说Windows Moblie,Symbian, Brew等等。第四,适用于任何制式的手机,比如说GSM手机,CDMA手机等等。同时,CountDown可以广泛使用于手机产业链的各个环节,比如说上游的芯片厂商,到中游的开发商以及网络的运营商等等,都可以通过CountDown测试平台,提高整个产业价值链中的测试的效率。 为了实现一款测试软件,可以在不同的手机之间分享和共用,TestQuest提供了一个非常重要的概念,就是自适应的自动测试系统。这意味着用户在一款手机上开发的所有的测试软件可以很轻松的移动到另外的手机上面,这样可以提高手机测试的开发的效率。其次,CountDown支持用户做端到端的测试,用户可以把不同操作系统的手机连接到自动测试系统上面,然后来测试不同手机的兼容性。第三,通过CountDown这样的自动测试平台,用户可以很好的保存自己在以往的测试过程中积累的经验。第四,CountDown还可以支持用户分布式的测试,可以最大限度的满足客户全求化分布式测试的要求。比如说很多美国公司把研发中心转移到印度、中国和南美洲的很多国家,在这种情况下,怎么样协调不同的部门、不同的研发团队的步伐,就显得越来越重要。最后,TestQuest对中国市场非常的重视,我们现在提供的产品可以完全的支持中文的识别和输入,这样可以更好的适应本地用户的需求,同时TestQuest在北京建立了团队。 C114:但是有很多的大公司有自己的测试系统? John:为了回答你提的问题,我举个最简单的例子,比如说摩托罗拉、诺基亚和高通,这三个公司是移动通信里面比较知名的公司。对摩托罗拉来讲,其本身就是TestQuest的用户,因为我们要保护他们的利益,所以我们不能解释更多的合作内容。诺基亚有自己内部的测试工具,同时他们也在和TestQuest合作采用自动测试的平台,对于高通来讲,我们会在较短的时间内宣布我们与高通的合作,现在因具体的协议没有确定,所以不方便透露其具体的内容。我们的自动测试方案会用于高通的手机平台。有一个很浅显的道理就是说TestQuest有一百个工程师来开发和维护自动测试的工具,但是对任何一个厂商,包括诺基亚、高通这样的厂商他很难集中一百个工程师来开发测试工具,所以这就是说为什么全球有很多的大公司采用TestQuest的测试方案的原因。 C114:在开发这种具有革新性的测试平台你们投入了多少人员? John:从软件工程的角度来讲,一般认为以多少个人员来这样定义,对TestQuest来讲,从90年代中期我们公司总部的投入超过了1千个人员,在CountDown投入的人员上,过去的三两年中我们投入两百到三百个人员,这样一个工作量。 本土化策略为手机厂商进入中国提供便利 C114:刚才我听您提到TestQuest在北京而已有建立团队,我们知道有许多在国外有分支机构的成熟的企业都会考虑到本土化问题,那么TestQuest对进入中国或者其他国家,有怎样的本土化策略或规划呢? John:实际上TestQuest的业务在过去的几年来以每年提高15%的速度增长,对于中国市场的承诺,TestQuest的产品可以对中文字体进行识别和输入,用户可以很轻松的在自动测试平台上测试中国本土的手机。我在中国出差得到深刻的印象是:中国的市场远远超出我最初的预计。我们最近在北京新建了本地的团队,我希望在将来在沿海的一些城市,包括上海、深圳建立新的销售和服务的团队。 从一个产品的技术层面来考虑,对于相对于底层平台的部分,与美国、欧洲和亚太地区这些层面的需求是非常相似的。但是在上层的用户体验、应用的软件,比如说包括游戏、包括应用服务各个国家有不同的需求,实际上在这个部分,为了适应本地用户的需求的开发,TestQuest在中国会象在南美洲、欧洲一样,和国内业界的公司进行紧密合作,共同推出适应本地用户需求的软件。 C114:TestQuest在中国遇到哪些方面的挑战?比如说品牌化,或者说产品对中国市场的适应程度? John:在中国拓展业务,中国有众多的手机厂商,为了给厂商提供更多的服务,现在最重要的是找到更多的培训过的工程技术和销售人员,以更好的服务把产品提供给中国的手机厂商。我们遇到的最重要的问题就是如何培训这些销售和技术人员,如何把我们的产品提供给手机厂商。 20年前我来中国拓展业务的时候,我发现当时的中国商业环境并不是非常的理想,有很多这样那样的问题。但是今天,大家可以想象很多的中国年轻人去美国硅谷、去斯坦福学习,然后在硅谷工作,进行创业,然后还有很多人自己带着风险投资回到国内创业。大家可以看到,今天的社会全球化趋势越来越明显,实际上在中国服务于用户与在美国、欧洲和其他国家没有什么本质的区别,最重要的就是要提供好的解决方案,提供用户所需要的方案及服务。 C114:跟中国运营商的合作情况如何? John:我提到为什么我们现在在移动网络运营商方面主要用户是国外的运营商,很重要的原因在欧洲、和美国实际上是网络运营商来负责采购手机,定制手机,与自己的业务进行捆绑提供给用户。而国内绝大部分的手机是厂商,分销商来销售的,而不是网络运营商。但是这情况我认为是会慢慢改变的,随着3G的到来将来的运营商除了通话之外,还会提供应用、内容等服务给用户,我认为运营商在手机销售这个过程中会推演越来越重要的角色。 C114:那么你们计划今年在中国取得的目标是? John:我们的计划是希望在这个年度结束的时候我们的用户上升到10-15家,我希望在未来的3-4个月内我们能够增加3-4个国内的用户。 关于TestQuest TestQuest公司是提供移动设备及应用软件自动化测试和管理解决方案的业界领头羊。我们的独特之处在于将自动化测试技术、服务和方法结合起来,帮助移动设备制造商、运营商、组件提供商和应用软件开发商应对日益复杂的移动和无线产业的挑战。利用TestQuest解决方案,公司能够缩短测试循环周期,提高测试精确度,提高团队合作效率,加速新品市场投放,并确保高品质的终端用户体验。TestQuest是一家私营的全球公司,总部设在San Mateo, CA (加利福尼亚州圣马特奥县) (二)我就设计了另一个测试边际问题的测试,代码如下:void UnitTestCase1(HWND hWnd, HICON handleIcon)
{ gSysTrayIcon.AddIconToSysTray(); //没有进行初始化设定 } BOOL SysTrayIcon::AddIconToSysTray() void UnitTestCase1(HWND hWnd, HICON handleIcon) void SysTrayIcon::SetTrayIconTip(LPCTSTR szMsg) 测试驱动的程序设计进行测试为先测试驱动的程序设计是确保敏捷开发顺进行的有效措施。这篇案例将为读者提供详细的开发历程,来分析测试为先测试驱动的程序设计的过程。本文的重点:
*简要重复叙述一下测试为先/测试驱动得好处(优点)。 *简要介绍一下案例中的项目。 *没有利用测试为先/测试驱动设计的单元代码是什么样的? *没有利用测试为先/测试驱动设计的单元代码里有什么样的问题? *针对单元代码设计的手动单元测试。 *查找出毛病后的改进代码。 测试为先/测试驱动的好处 传统的瀑布型软件开发是先从客户那里获得需求,然后进行纸上谈兵的设计,接着是程序源码写作构建,最后才是测试者对质量进行检评。从需求的分析到最后的测试,两者的相隔往往有好几个月。等到测试发现结构性问题时,重新设计已经成为一个无法完成的任务。设计者程序员已经无法回到几个月前推翻纸上谈兵的错误设计,重新用新的方式进行代码编写组合。测试为先/测试驱动和瀑布型软件开发不同是: 测试模拟用户的使用组件的应用方式,为开发者提供解决方案; //这个就需要测试者具备很强的编程能力,才能做这些事情。 测试驱使开发者开发可以测试的部件; 及早测试,尽快排除设计中的各种微小问题;//测试,排除各种微小的问题 测试为开发者提供质保底线,每次的部件更改都能利用测试来检测修改后质量。 以上这些都是我以前重复过的。这些说的容易但是想像起来是比较难一点。我下面所要谈到的案例并不是教科书里的完美案例,所谓的完美案例是完全可以自动化,完全可以进行单元测试的程序组件。举个例子说,想像你要设计一个类来代表“复数”(imaginary number),这样一个类是可以完全进行自动化单元测试。这种情况只能算得上百分之五十的现实情况,在其他百分之五十的状况下,一些手动测试和一些自动化测试都是必要的。还有很多情况下,手动测试是唯一的选择。半自动和手动测试并不代表整个开发不算作测试驱动开发。测试驱动的多数人都会说手动测试和半自动化测试并不能代表团队在进行测试驱动的开法。我觉得这种说法是偏见,只要测试组和开发组能够配合,尽可能地在最早时间将用户需求确定后,让测试组开始针对用户需求,设计思路进行测试用例设计,开发和测试能同时进行,开发出的部件能够迅速进行测试,测试用例能够经常地运行确保开发的质量不受变化的影响。这就是测试为先/测试驱动的开发。 本文的案例简介 用来演示测试为先/测试驱动的开发,我将使用我最近设计的一个将应用程序图标加入System Tray里的类。然后在应用程序退出后,自动将图标从System Tray里删除。这样的类,你如果知道Windows系统对System Tray里的图标管理,就知道设计这么一个类的自动化测试并不简单。我觉得这种和图形界面打交道的类,也没有必要100%地进行自动化测试。所以我对这个类的测试驱动采取手工测试为主的测试,以测试者甚至开发者本身用用户的需求,先用例程作为基础,来设计图标管理类的单元测试。 案例的用户需求 我是这个类的唯一用户,对于我要设计的程序,我的使用是很简单的。下面的列表就是我的需求: System Tray里的应用图标的数据管理和图标的加入删除都由类对象来进行; 类对象能够设定视窗柄; 类对象能够设定图标的独特ID; 类对象能够设定图标对系统信息处理的消息ID; 类对象能够设定图标在鼠标指向后能够显示有关程序的信息(程序的名称,设计公司和其他信息); 类对象必须在调用者的指示下将图标放入System Tray。 类对象必须处理让调试者能够删除System Tray里的图标。 类对象在自我摧毁的时候自动删除System Tray里的图标。 这些用户需求就是我要设计使用案例,在敏捷中,这些案例就是一个个故事。我在设计每一个故事的编码之前就先设计一个测试案例。每个测试案例都在设计完成之前会运行失败。设计完成后,这些测试案例才能顺利运行。 为了调试图标的加入和删除都能正确运行,我决定使用一个简单的Win32视窗程序来作为我的单元测试温床,我的测试是手动测试。我的目标是用单元测试来尽可能地覆盖我设计的代码面积。第一步我设计了以下的单元测试: void UnitTestCase0(HWND hWnd, HICON handleIcon) //视窗句柄 图标ID { // a normal core functionality test. gSysTrayIcon.SetTrayIconID(11200); gSysTrayIcon.SetNotifyWindow(hWnd); gSysTrayIcon.SetTrayIcon(handleIcon); gSysTrayIcon.SetTrayIconTip(_T("SysTrayIcon")); gSysTrayIcon.SetTrayIconWmMsg(WM_TRAYICON_MSGS); if (!gSysTrayIcon.AddIconToSysTray()) { ::MessageBox(hWnd, _T("Unable to add Icon to System Tray."), _T("Error:"), MB_OK); return; } } 这是一个很简单的函数,我假设我有一个全局变量叫gSysTrayIcon。它是一个类对象;它有至少六个函数;它的五个函数是数据设定函数;它的最后一个函数是让调用者告诉它把图像加入System Tray。根据我自己设计的单元测试案例,我设计了以下的类: #ifndef SYS_TRAY_ICON_H_ #define SYS_TRAY_ICON_H_ #include "shellapi.h" class SysTrayIcon { private: NOTIFYICONDATA niData; public: SysTrayIcon(); ~SysTrayIcon(); void SetTrayIconID(UINT iconID); void SetNotifyWindow(HWND hWnd); void SetTrayIcon(HICON iconHandle); void SetTrayIconTip(LPCTSTR szMsg); void SetTrayIconWmMsg(UINT wmMsg); BOOL AddIconToSysTray(); BOOL DeleteIconFromSysTray(); }; #endif 我的类成员设计如下,这里面有很多我无意中犯下的错误,也有我故意设置的错误,后面我用单元测试一点点地查找出一些常见的问题。为了顺利通过我上面的单元测试,首先看看我的设计初稿: #include "StdAfx.h" #include "SysTrayIcon.h" SysTrayIcon::SysTrayIcon() { ZeroMemory(&niData, sizeof(NOTIFYICONDATA)); niData.cbSize = (DWORD)sizeof(NOTIFYICONDATA); niData.uFlags = NIF_ICON NIF_MESSAGE NIF_TIP; } SysTrayIcon::~SysTrayIcon() { DeleteIconFromSysTray(); } void SysTrayIcon::SetTrayIconID(UINT iconID) { niData.uID = iconID; } void SysTrayIcon::SetNotifyWindow(HWND hWnd) { niData.hWnd = hWnd; } void SysTrayIcon::SetTrayIcon(HICON iconHandle) { niData.hIcon = iconHandle; } void SysTrayIcon::SetTrayIconWmMsg(UINT wmMsg) { niData.uCallbackMessage = wmMsg; } void SysTrayIcon::SetTrayIconTip(LPCTSTR szMsg) { _tcscpy(niData.szTip, szMsg); //查一下这个函数 } BOOL SysTrayIcon::AddIconToSysTray() { Shell_NotifyIcon(NIM_ADD, &niData); return TRUE; } BOOL SysTrayIcon::DeleteIconFromSysTray() { return Shell_NotifyIcon(NIM_DELETE, &niData); } 敏捷的宗旨是,在最短的时间内为客户提供完整的设计,让客户能够看到期待的价值,让客户能迅速反馈,并把反馈意见转变为设计改进。我以上的代码给我自己提供一个可以测试的机会。我用我的测试案例来实践我的设计,测试程序是一个SDI视窗程序。程序运行开始先把一个图标放入System Tray,然后,用户可以按在程序的缩小按钮上,程序会消失,但是System Tray里的程序图标。用户用鼠标左键双击System Tray里的程序图标,程序视窗会重新出现在桌面上。用户把鼠标光标移到System Tray里的程序图标上,一秒钟后就会一个提示标题出现,显示程序的名称。当我关闭程序视窗,视窗消失,System Tray里的程序图标也一并消失。这就是我的第一个测试。这个测试案例运行,不会出现任何问题。 我写的第一个案例是开发者通常会做的测试,一个简单的案例保证设计到达最基本的用户需求。作为认真的开发者,和有专业意识的QA,这样简单的测试根本不够。各种各样的边界问题会通过设计的空隙造成程序运行异常。我就设计了另一个测试边际问题的测试。 软件测试尽早测试、连续测试、自动化测试,并在此基础上提供了完整的软件测试流程和一整套的软件自动化测试工具,组建一个测试团队,基于一套完整的软件测试流程,使用一套完整的自动化软件测试工具,完成全方位的软件质量验证。
别去“挖东墙补西墙”。由于项目研发期的“缺斤短两”,使项目实施和投入运行的初期 漏洞百出,时间一长用户会发疯,项目实施者也会发疯,国内前几年的众多的ERP项目失败的原因多出于此。项目实施的遥遥无期,将严重挫伤用户的耐性和信心。
代码与文档哪个值钱?多数项目管理者忽视了文档的重要性。对于大型软件的研发项目,还需要专业的测试过程管理软件来支撑大规模的信息交流和自动测试、代码的更新和版本的提交。这些文档和信息的价值从某种意义上甚至超出了程序代码本身。
全程还是后期?软件的设计阶段往往没有软件测试人员的参与,事实上设计上的缺陷往往是耗用成本最高,也是最难在开发后期修复的缺陷。而一个软件的质量与它有多大的设计缺陷有着密不可分的联系。而有经验的测试人员的质量意识,安全意识,对用户需求的了解及分析能力,对于打造高品质的软件设计都有着不可忽视的作用。
由于缺乏必要的配置管理和变更控制,测试工作根本无法提出具体的测试要求,加之开发人员的遮丑,测试工作往往是走走过场,测试结果既无法考核又无法量化,当然就无法对以后的开发工作起指导作用。事实上,每个软件项目都需要专业的测试人员进行相对独立的测试工作,从而保证软件项目的质量。
居安思危,控制风险。需求变更给测试带来的问题可能是灾难性的,客户需求不是变动的唯一来源。有时团队自身也能引起范围变动。团队的成员可能听说或“假设”解决方案因客户的实际要求而发生了变动。加强沟通和协作,随时了解变更的状态。
谁为产品质量买单?质量和质量控制应该是软件项目的的一项重要内容。但是,无论在消费类软件还是大型软件的测试领域,国内软件产品的质量掌控体系和标准都很模糊。质量控制越来越依托于公司在产品交付用户之前的测试工作的成败。
没有厚度就没有重心。软件测试过程的历史数据缺失是大多数软件项目失败的关键所在,这样的结论也许使很多人感到吃惊,但事实就是如此。因为这些历史数据是反映软件项目实施轨迹的第一手资料,是项目延续和反馈的基石。
一是满足用户需求,提高产品的竞争力,最终提高产品的销售量。二是尽早发现缺陷,降低售后服务成本。而软件测试的最终目的就是使它带来的经济效益最大化。有一些专业的测试工具的购买、测试人员的配备和培训还需要一定的经济投入,项目决策者们可以选择适合自己的配置,但决不能没有这些方面的投入。
沟通还是对立?沟通是开发和测试人员必备的素质。但传统的思想认为,测试人员是找麻烦,是开发的“克星”。其实,项目管理者应该清楚,为软件的质量和品质努力的工作目标是一致的。沟通和建立沟通渠道是项目管理者的重要工作。
某著名国际软件企业的软件测试人员与软件开发人员的比率达到了3:5左右,并且在实践过程已经证明了这种人员结构的合理性。但国内公司显然一时很难达到,但更重要的是重视程度,在这个基础上壮大软件测试队伍,提高测试人员的素质。
其次是要学习借鉴国外完善的测试机制,包括丰富的软件测试经验,强大的测试工具,优秀的测试管理水平。真正解决测试手段落后、测试方法单一和测试工具欠缺的问题,在企业内部形成一个严密有效的纠错系统,使国内的测试工作流程、 技术水平接近国外先进水平,这样才能提高国内软件开发与测试的整体管理水平,增加软件产品的竞争力。
要重视第三方的测试力量。第三方的专业测试企业是靠技术与服务来赢得客户信任的,也因此更加注重测试方法与质量。对于软件企业来说,从无到有地去建立测试部门,并完善测试体系,需要较大投入,将研发出来的软件产品交给实力强劲的第三方专业测试公司,在提高软件产品的质量问题同时,还节约了产品测试成本。 staticinlcude的作用
给你看下这文章
1、概述 static 声明的变量在C语言中有两方面的特征:
1)、变量会被放在程序的全局存储区中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。
2)、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
2、问题:Static的理解
关于static变量,请选择下面所有说法正确的内容:
A、若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
B、若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
C、设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题;
D、静态全局变量过大,可那会导致堆栈溢出。
答案与分析:
对于A,B:根据本篇概述部分的说明b),我们知道,A,B都是正确的。
对于C:根据本篇概述部分的说明a),我们知道,C是正确的(所谓的函数重入问题,下面会详细阐述)。
对于D:静态变量放在程序的全局数据区,而不是在堆栈中分配,所以不可能导致堆栈溢出,D是错误的。
因此,答案是A、B、C。
3、问题:不可重入函数
曾经设计过如下一个函数,在代码检视的时候被提醒有bug,因为这个函数是不可重入的,为什么?
unsigned int sum_int( unsigned int base )
{ unsigned int index; static unsigned int sum = 0; // 注意,是static类型的。 for (index = 1; index <= base; index ) { sum = index; } return sum; } 答案与分析:
所谓的函数是可重入的(也可以说是可预测的),即:只要输入数据相同就应产生相同的输出。
这个函数之所以是不可预测的,就是因为函数中使用了static变量,因为static变量的特征,这样的函数被称为:带“内部存储器”功能的的函数。因此如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量,这种函数中的static变量,使用原则是,能不用尽量不用。
将上面的函数修改为可重入的函数很简单,只要将声明sum变量中的static关键字去掉,变量sum即变为一个auto 类型的变量,函数即变为一个可重入的函数。
当然,有些时候,在函数中是必须要使用static变量的,比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。 一道程序题/*
*************************************************************** * *Project.h * **************************************************************** */ // Include files
#include <string> using std::string;
// Global Const variables. These variables determine
// the maximum lengths of fields and member variables. const int MAX_FIELD_SIZE= 35;
const int HEADER_SIZE= 15; const int SURNAME_SIZE= 21;
const int FIRST_NAME_SIZE= 21; const int ADDRESS_SIZE= 31; const int CITY_SIZE= 21; const int STATE_SIZE= 4; const int ZIP_CODE_SIZE= 7; const int PHONE_NUMBER_SIZE= 9; /*
*************************************************************** * *In this file we declare the member functions and member *variables of the "Person" base class, as well as it's *derived classes. * **************************************************************** */ /*
**************************************************************** * *The "Person" Class. This is the base class for *both the Student and Teacher class. * **************************************************************** */ class Person {
private: // Private member variables
string first_name; string surname; string address1; string address2; string address3; string city; string state; int zip; string phone; // Private virtual member functions
// virtual void set_other_info() = 0; public:
// Function to get the data for the object virtual void setup(); // Public Get member functions
string get_first_name() const{ return first_name; } string get_surname() const { return surname; } string get_address1() const {return address1; } string get_address2() const { return address2; } string get_address3() const { return address3; } string get_city() const { return city; } string get_state() const { return state; } int get_zip() const { return zip; } string get_phone() const { return phone; } void display_info() const;
virtual void dump_info(std::ostream& os) const; virtual void load_info(std::ifstream& is); bool validate_input(const string& input, bool is_string = true); // Public Set member functions
void set_first_name(); void set_surname(); void set_address(); void set_city(); void set_state(); void set_zip(); void set_phone(); // Public virtual member functions
virtual void display_other_info() const = 0; }; /*
**************************************************************** * *The "Student" Class inherited from the *"Person" base Class. * **************************************************************** */ class Student : public Person {
private: // Private member variables
string student_ID; int grade; public:
// Function to get the data for the object virtual void setup(); // Public virtual member functions
virtual void display_other_info() const; virtual void dump_info(std::ostream& os) const; virtual void load_info(std::ifstream& is); }; /*
**************************************************************** * *The "Teacher" Class inherited from the *"Person" base Class. * **************************************************************** */ class Teacher: public Person {
private: // Private member variables
int years_experience; long salary; public:
// Function to get the data for the object virtual void setup(); // Public virtual member functions
virtual void display_other_info() const; virtual void dump_info(std::ostream& os) const; virtual void load_info(std::ifstream& is); }; // Exception class class create_ex: public std::exception { private: string msg; public:
create_ex(const string& m) { msg = m; } const char* what() const throw() { return msg.c_str(); } }; /*
*************************************************************** * *Project.cpp * **************************************************************** */ // standard library includes
#include <iostream>
#include <iomanip> #include <cstdlib> #include <fstream> #include <cctype> // private includes
#include "project.h"
using std::cout; using std::cin; using std::endl; using std::string; using std::setw; /*
*************************************************************** * *In this file we define and implement the member functions *of the "Person" class, as well as it's derived classes. * *The derived classes are the "Teacher" class and the "Student" *class. Only the virtual functions inherited from the *"Person" class, along with their constructors are defined *and implemented in this file. * **************************************************************** */ /*
**************************************************************** * *The "Person" Class Member Functions * **************************************************************** */ // Set member functions for the base Person Class.
void Person::setup() {
// Validate the object, and prompt user to enter values // for all the attributes. set_first_name();
set_surname(); set_address(); set_city(); set_state(); set_zip(); set_phone(); } // This function determines whether the user input should consist of digits,
// alphabetic characters, or a mixture bool Person::validate_input(const string& input, bool is_string) {
// If is_string is true, we test for characters. If not we test for digits. if (is_string) {
for (unsigned int i = 0; i < input.length(); i++) { if (!std::isalpha(input[i])) { cout << endl << "The input contained invalid characters. Try Again." << endl; return false; } } } else { for (size_t i = 0; i < input.length(); i++) { if (!std::isdigit(input[i])) { cout << endl << "The input contained invalid characters. Try Again." << endl; return false; } } } return true;
} // Set member functions of the Person Abstract Class
void Person::set_first_name() {
string temp; // initialize boolean variables to test the user inputs.
bool result = false; while (!result) {
cout << endl << "Please enter the First Name. " << endl; cin >> temp; if (temp.length() >= FIRST_NAME_SIZE) {
cout << endl << "The input must be less than " << FIRST_NAME_SIZE << " characters. " << endl; continue; } if (validate_input(temp, true))
result = true; } // Now we can assign the input variable to the member variable
first_name = temp; } void Person::set_surname() {
string temp; // initialize boolean variables to test the user inputs.
bool result = false; while (!result) {
cout << endl << "Please enter the Surname. " << endl; cin >> temp; if (temp.length() >= SURNAME_SIZE) {
cout << endl << "The input must be less than " << SURNAME_SIZE << " characters. " << endl; continue; } if (validate_input(temp, true))
result = true; } // Now we can assign the input variable to the member variable
surname = temp; } void Person::set_address() {
string temp; // initialize boolean variables to test the user inputs.
bool result = false; while (!result) {
cout << endl << "Enter line 1 of the street address (or '.' to exit) " << endl; std::ws(cin); // Skip any whitespace left on cin std::getline(cin, temp, '\n'); if (temp == ".")
return; address1 = temp;
result = true; } // Re-initialize result to validate user input
result = false; while (result == false) {
cout << endl << "Enter line 2 of the street address (or '.' to exit) " << endl; std::ws(cin); // Skip any whitespace left on input std::getline(cin, temp, '\n'); if (temp == ".")
return; address2 = temp;
result = true; } // Re-initialize result to validate user input
result = false; while (result == false) {
cout << endl << "Enter line 3 of the street address (or '.' to exit) " << endl; std::ws(cin); // Skip whitepsace on input std::getline(cin, temp, '\n'); if (temp == ".")
return; address3 = temp;
result = true; } } void Person::set_city() {
string temp; // initialize boolean variables to test the user inputs.
bool result = false; while (result == false) {
cout << endl << "Please enter the City. " << endl; cin >> temp; if (temp.length() >= CITY_SIZE ) {
cout << endl << "The input must be less than " << CITY_SIZE << " characters. " << endl; continue; } if (validate_input(temp, true))
result = true; } // Now we can assign the input variable to the member variable
city = temp; } void Person::set_state() {
string temp; // initialize boolean variables to test the user inputs.
bool result = false; while (result == false) {
cout << endl << "Please enter the State. " << endl; cin >> temp; if (temp.length() >= STATE_SIZE ) {
cout << endl << "The input must be less than " << STATE_SIZE << " characters. " << endl; continue; } if(validate_input(temp, true))
result = true; } // Now we can assign the input variable to the member variable
state = temp; } void Person::set_zip() {
string temp; // initialize boolean variables to test the user inputs.
bool result = false; while (result == false) {
cout << endl << "Please enter the 6 digit Zip Code. " << endl; cin >> temp; if (temp.length() != ZIP_CODE_SIZE - 1) {
cout << endl << "The Zip Code must be 6 digits long. Try Again." << endl; continue; } if (validate_input(temp, false))
result = true; } // Now we can assign the input variable to the member variable zip = std::atoi(temp.c_str()); } 一段小程序#include <iostream>
#include <string> using namespace std; void main() { char *p="henry"; int i; cout<<p; cout << endl; for(i=strlen(p)-1;i>-1;i--) cout<<p[i]; cout << endl; } 堆栈在计算机领域,堆栈是一个不容忽视的概念,但是很多人甚至是计算机专业的人也没有明确堆栈其实是两种数据结构。
要点:
堆:顺序随意
栈:先进后出
堆和栈的区别
一、预备知识—程序的内存分配
一个由c/C++编译的程序占用的内存分为以下几个部分
1、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
2、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。
3、全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后有系统释放
4、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
5、程序代码区—存放函数体的二进制代码。
二、例子程序
这是一个前辈写的,非常详细
//main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456\0在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10);
p2 = (char *)malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "123456"); 123456\0放在常量区,编译器可能会将它与p3所指向的"123456"优化成一个地方。
}
二、堆和栈的理论知识
2.1申请方式
stack:
由系统自动分配。 例如,声明在函数中一个局部变量 int b; 系统自动在栈中为b开辟空间
heap:
需要程序员自己申请,并指明大小,在c中malloc函数
如p1 = (char *)malloc(10);
在C++中用new运算符
如p2 = (char *)malloc(10);
但是注意p1、p2本身是在栈中的。
2.2
申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,
会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 测试方法的综合策略1.测试方法的综合策略:
1) 在任何情况下都必须使用边界值分析方法,经验表明用这种方法设计出测试用例发现程序错误的能力最强。 2) 必要时用等价类划分方法补充一些测试用例。 3) 用错误推测法再追加一些测试用例。 4) 对照程序逻辑,检查已设计出的测试用例的逻辑覆盖程度,如果没有达到要求的覆盖标准,应当再补充足够的测试用例。 5) 如果程序的功能说明中含有输入条件的组合情况,则一开始就可选用因果图法。 2.测试用例的设计步骤 1) 构造根据设计规格得出的基本功能测试用例; 2) 边界值测试用例; 3) 状态转换测试用例; 4) 错误猜测测试用例; 5) 异常测试用例; 6) 性能测试用例; 7) 压力测试用例。 3.优化测试用例的方法 1) 利用设计测试用例的8种方法不断的对测试用例进行分解与合并; 2) 在测试时利用发散思维构造测试用例。 软件测试工程师一、市场需求分析,中国IT行业最紧缺的人才是什么? 无忧指数显示,软件测试工程师已经成为2006年最紧缺的人才,该类职位的需求主要集中在沿海发达城市,其中北京和上海的需求量分别占去了33%和29%;而从企业分布来看,民企需求量最大,占了19%,外商独资欧美类企业需求排列第二,占了15%。中国青年报2005年12月22日报道: 据了解,目前我国软件从业人员的缺口高达40万之多,其中软件测试人才的缺口将超过20万,在未来5-10年中这一数字还将继续增大。目前,在软件企业中,软件测试人员的薪水主要看其工作经验及能力,有两年工作经验的软件测试人员的月薪一般都能达到5000--7000元。中国软件行业协会游戏软件分会副会长刘金华在接受记者采访时说,在企业内部,软件测试工程师基本处于“双高”地位,即地位高、待遇高,有的人月薪可高达上万元。可以说他们的职业前景非常广阔,从近期的企业人才需求和薪金水平来看,软件测试工程师的年工资有逐年上升的明显迹象。 根据以上分析,我们无庸置疑软件测试行业的职业发展前景。从当今大学计算机专业的知识结构来看,软件测试只占其中很少的章节,是作为计算机专业主要技能辅助知识设立的。系统化的软件测试专业只是在近两年才被分离出的,而且只在屈指可数的几个院校设立,一方面是需求骤增,一方面是供应有限,由此造成了软件测试人才极度“抢手”的局面。 二、企业对软件测试人员的职位要求 以下是来自某知名招聘网站的一组招聘信息: 软件测试工程师 10人 (工作地点:北京) 工作性质:全职 截止日期: 招聘人数:10人 月薪范围:4000--6000 工作经验:1年 学历:本科以上 职位描述及要求 (补充:此公司业务范围通信与软件开发和外包,当属专业类公司) 1. 计算机相关专业本科以上学历,性别不限; 2. 熟悉WinNT/2000操作系统,有在上述操作系统上进行软件开发或系统配置的经验 3. 熟悉大型数据库,对MS SQL SERVER等数据库有使用经验; 4. 熟练掌握软件测试的方法和技巧;能独立制定测试计划、测试用例、测试规范,并能编制规范的测试文档; 5. 精通1~2种开发语言,熟悉规范化的软件开发流程; 6. 有二年以上正规商用软件软件测试的实际经验。 7. 最好有.NET开发经验的,熟悉C#、ASP.NET、IIS、XML、ASP.NET等 从企业现状来看,由于软件的使用者千差万别,软件在使用过程中遇到的各种现象也是千差万别的,所以要求软件测试工程师需要具有一些逆向思维的能力。这是作为一名优秀的软件测试工程师最基本的素质。同时要有一种穷追到底的精神和善于沟通能力,软件测试人员与开发人员搞好关系,这对于提高整个软件项目质量是十分重要的。软件测试技术随着时间的变化也在做一些提高和改进,作为一名优秀的测试人员要善于利用书籍、网站、论坛、交流等各种途径不断提高自己的软件测试水平。当软件测试人员发现软件中存在缺陷的时候,往往要书写缺陷报告,作为一名优秀的测试人员提高自己的写作能力是非常必要的。 雅虎中国品质保证部经理鲍海燕表示:仔细分析企业对测试人员职位要求不难发现,企业更希望接受符合自己专业的复合型人才。因此软件测试人员不仅要掌握软件测试技术相关知识,对产品相关的业务知识也要学习。这很好理解,如果从事财务软件的测试工作,一定要学习财务知识;如果从事通讯产品测试工作,那么相关的通讯理论知识也是必须的;如果从事银行软件的测试,银行的业务流程也是不可或缺的知识点。 因此,在学习软件测试技术的同时,千万不要忽略产品相关业务知识的学习。如果你是一个软件测试技术专家,但是对产品业务知识一无所知,那么也只能测试出来纯粹的软件缺陷,而面对眼前出现的产品业务相关的缺陷,很可能是视而不见,如此这般,软件测试的效果会大打折扣。一般来说,每个软件公司的项目可以分为固定的几大类。可以按业务类型划分,比如 ERP 软件、产品数据管理软件、通信软件、地理信息系统软件等等;可以按软件结构来划分,比如 B/S 架构的软件、 C/S 架构的软件、嵌入式软件等等。 三、面对“形势一片大好”的职业前景与现实的重压,我们的应对之策 一方面,软件产业是国家鼓励发展的朝阳产业,软件产业要发展,提高软件质量势所必然,这样就产生了软件测试工程师的大量需求。另一方面,很多开发人员和应届毕业生有志从事软件测试工作,却因为没有软件测试经验而被用人企业拒之门外。当前国内软件企业非常缺乏测试人员,特别是经过一定测试专业培训、进入企业后马上就能开展测试执行工作的测试执行人员。 孙子兵法 中的“天时”、“地利”、“人和”,是实战与商战中立于不败之地的法宝。对个人而言,在做一个“艰难”选择(择业方向的确立)的时候,这难道不是一个“战胜自我”的依据吗!对软件测试岗位而言,目前可以说占尽“天时和地利”,就看你能否赢得“人和”。毫无疑问,软件测试工程师是一条通往高薪的捷径,当然世界上没有免费的午餐,想要挑战高薪,必须先付出辛勤的努力! 软件测试谈起测试项目经历 感觉走在开发人员前面 李和恒最近比较关注的是跨浏览器、跨平台的web应用测试架构,这也是为他正在参与的产品项目服务。很多web应用程序在浏览器上只运行HTML和 JavaScript,软件测试工程师还是要花不少时间在各种浏览器和操作系统上进行手工测试。他希望通过良好地支持自动化测试来减轻这方面的负担。目前一个从发动测试到结果收集全程自动化运行、支持多浏览器端并行协作的系统已经在支撑两个项目的自动化测试工作,下一步是构建web应用测试的编程接口、支持代码注入。将来和恒的目标是在微软把web应用测试做得和桌面及服务器应用的测试一样完美。 谈到最满意的项目,和恒告诉记者我他对在线会议系统的记录模块进行测试是最令他满意的工作经历。在那里,他接触并实践了微软最先进的测试理念。微软内部进行了大量的“模型驱动测试”实践:对产品建模进而自动产生测试用例。依靠这种技术得到的测试用例,自动运行以后覆盖了接近90%的产品代码,而他自己的工作量只是五六百行的代码、一个Visio文件外加必要的维护。翘着脚看软件开发工程师在用那堆测试用例暴露出来的bug里面扑腾的时候,他有时会想:“总算跑到你们前头去了”。 回顾所有的项目,和恒认为其共通之处在于对测试工作的热情和责任感。实际工作中软件测试工程师的每一步工作都会引发更多的工作:暴露一个bug,修改好,再检查,还是有bug,再修改......有可能会像擦地毯上的奶油一样越擦越大。在没有交付到客户手上之前,测试工作的效率看上去是自我递减的。这跟开发工作不同,他们是自我递增的。没有极大的热情和责任感,很难想象这样的工作得以维持下去。 测试就像寻宝一样 需要在最短时间内找到最值钱的宝物 每个测试人员在特质上可能有共同之处,但成长的经历却是不同的,李和恒在选择测试的工作的时候既有偶然的成分也有自主的选择。来微软面试的过程中,他从面试题里面感觉到一点不平常:总是在问如何测试自己写的代码。最后一关经理终于问他对软件测试工程兴趣如何,这着实出乎意料。不过和恒回想起所有面试题的意图,如果是从事简单的工作,犯不着问这么难,所以就答应了。三年之后的现在,经历了若干项目之后,如果再选择一次,和恒觉得自己还是会选择测试工作。 具备什么样的素质才是合格的软件测试工程师?软件测试的具体工作内容包括:理解用户的需求和体验,校正设计和项目计划,运用良好的测试方法和实践,撰写有效的测试计划,设计有效的测试用例,推动自动化测试,调查分析bug的根本病因,追求卓越的技术和业务能力,充分的团队合作,以及紧密地联系和关注用户和合作伙伴。 李和恒个人的理解是,软件测试就像沙滩上的寻宝人,你不可能知道沙里埋了些什么、有多少、在哪里。寻宝人要在尽量短的时间里面挖出尽量值钱的宝物。但极为讽刺的是,你不可能挖出所有的宝物,而且所有的宝物日后都会浮现出来,比如地震海啸地质运动什么的。在这里,测试工程师就是寻宝人,宝物就是bug。至于用什么办法寻宝,那是技术上的问题了。技术总是日新月异的,所以我对合格的软件测试工程师的期望是:狂热追求宝物,具有大局观,充分了解沙滩,最后才是了解并改革寻宝工具。 测试在架构下更简单 在沙滩上寻宝也是有沙滩的宽度的限制,同样测试工作也是需要在一定的规则下进行的。李和恒平时对架构设计很感兴趣,他告诉记者其实架构和测试是有相通之处的。他所理解的架构是一组游戏规则,在这组规则的保护下人们可以关注更有趣的事情,这个理解对测试工作来说是一样的。软件测试工程师在架构的保护下可以关心更值得注意的事情。举个例子,篮球规则让进攻队员可以专心投篮而不用担心被推拉,违反规则的行为可以被清晰的观察出来。另一个技术上的例子是.NET framework 3.0里面的插件开发模型,以前软件测试工程师可能需要测试不同类型的插件实现方法,现在只需要留意插件相对于产品的功能。换句话说,架构或者说是规则,已经被良好的测试过了,值得信赖。 采访后记 与和恒聊了很久,话语间提到在微软工作的收获,他认为最大的收获莫过于发现测试工作是一个未被开发的金山,莫过于跟一群具有智慧和热情的人们共同开发这座金山,莫过于发现自己还挖了不少好东西。最后记者希望他推荐本书给广大的程序员,和恒拿出一本John Lakos的Large-Scale C++ Software Design,大规模C++程序设计,告诉记者说那里面介绍的系统分析方法使他从只关注自动化测试转向了解系统结构。 The 4th generation white-box-testing MethodolodyThe 4th generation white-box-testing Methodology aims at the defects of its ancestors. But inherits many philosophies of the 2GWM and 3GWM. The following table lists out major differences between the Methodolodies:
All in one and debugging testing.
In the above table, Test effect accessment means whether it support coverage or index to access to test effect."Automatic testing" means that whether it can formalize the testing operations and using them for next testing."continue testing" means whether it can carry out the testing in continue testing integration mode."all in one debugging and testing"means whether it integrates testing design into routine product coding and debugging in a high effective way.
The watershed between the 2GWM and 3GWM is "Continue testin". Maybe you will say that your project is often upgraded and added with new test cases.Please note that they are two concepts. Among the 12 steps in improving codes in Joel testing.Thers is one sentence "Do you correct faults before adding(writing) new codes ?"Correcting faults beforing writing new codes is for quality-first project.Otherwise, It for process-first project.There are two totally different styles.In the former continue testing, test case will be added after even one or two functions and test practice is melted into the development progress. The latter follows the scheduel and testing si the only a tast of a certain stage.
Why do we relate the testing methodologies and software development? It because the testing the not isolated. The effectiveness of the testing strongly relies on the software engineering method. In the ealier development language, only assert statements have relation with testing.In today’s C#, unit testing frameworks have become fixed components of the language. Testing scripts are also a type of product codes. Testing methodology has close relations with software development methodology in effect,which is well reflected in the 3GWM and 4GWM.
Compared with the 3GWM, the 4GWM has melted the testing process (including testing design, implementing, and improving) into the whole course of development in a highly efficient way. Here, “highly efficient” is the key words. What does “highly efficient” mean?We’ll learn 9 key features of the 3 key fields of the 4GWM at first, as follows: B. The 2nd key field: grey-box debugging C. The 3rd key field: continuous testing |
||||
|
|