人人范文网 其他工作总结

对编程的总结范文(精选多篇)

发布时间:2022-09-29 12:02:48 来源:其他工作总结 收藏本文 下载本文 手机版

推荐第1篇:编程题总结

C作业汇总

1.short a,b=32767; /*short类型在内存中占2B*/ a=b+1; 问:a的值是多少?并分析原因。

2.有一4位数整数,假设用abcd表示,请把这个4位数的每个数位用表达式表示出来。3.从键盘输入圆的半径r,计算并输出圆的面积s (要求:半径r定义为float型;圆周率定义为符号常量;面积s保留2位小数) #define PI 3.14159 #include void main() { float r,s; printf(\"请输入半径r:\\n\"); scanf(\"%f\",&r); s=PI*r*r; printf(\"面积是:%.2f\\n\",s); }

4.输入m>=3的正整数,判断m是否素数。画出算法流程图及NS图

5.有一函数:

x1 x y2x1 1x10

3x-11 x10 写一段程序,输入x,输出y值。

要求x,y声明为float类型,y保留2位小数。 #include void main() { float x,y; printf(\"请输入x的值:\\n\"); scanf(\"%f\",&x);

if(x

y=x; else

if(x

y=2*x-1;

else

y=3*x-11;

}

printf(\"y=%.2f\\n\",y);

x3x5x7x9,6.课后习题4.17(P159)利用泰勒级数sinxx计算sinx的3!5!7!9!值。要求最后一项的绝对值小于10,并统计出此时累加了多少项。 #include #include void main() { float x,sinx=0; float term;

/*记录每个项数*/ int n=1,count=0; /*count记录累加了多少项*/

printf(\"请输入x值(弧度):\\n\"); scanf(\"%f\",&x);

term=x/n; while(fabs(term)>1e-5)

/* while循环*/ {

sinx+=term;

count++;

n+=2;

term=-term*x*x/((n-1)*n); }

/* do

/*do while循环*/ {

sinx+=term;

count++;

n+=2;

term=-term*x*x/((n-1)*n); }while(fabs(term)>1e-5);

*/

printf(\"sin(%.2f)=%.4f\\n\",x,sinx); printf(\"一共累加了:%d项。\\n\",count); }

7.用牛顿迭代法求下面方程在1.5附近的根:

2x4x3x60 325

牛顿迭代公式:

x1x0f(x0)f(x0)

#include #include void main() { float x0,x1=1.5; float y1,y2;

/*y1记录f(x0),y2记录f(x0)的导数*/

do {

x0=x1;

y1=2*x0*x0*x0-4*x0*x0+3*x0-6;

y2=6*x0*x0-8*x0+3;

x1=x0-y1/y2; }while(fabs(x1-x0)>1e-5);

printf(\"the root is:%.2f\\n\",x1); }

8.写一函数,输入一个16进制整数,输出相应的10进制数。例:从键盘输入2a,输出结果是42。

要求:若输入数据不合法,则输出提示信息。如输入了35g,输出“您输入的16进制数不合法!”。

#include #include void main() { char c; int sum=0;

printf(\"请输入一个16进制数字:\\n\");

while((c=getchar())!=\'\\n\') {

if(c>=\'0\' && c

sum=sum*16+c-\'0\';

else

if(c>=\'a\' && c

sum=sum*16+c-87;

else

if(c>=\'A\' && c

sum=sum*16+c-55;

else

{

printf(\"您输入的16进制不合法.\\n\");

exit(0);

} }

printf(\"相应的10进制数是:%d\\n\",sum); } 方法2:用字符串处理的方式 #include #include void main() { char str[20]; int i,sum=0;

printf(\"请输入一个16进制数字:\\n\"); gets(str);

for(i=0;str[i];i++) {

if(str[i]>=\'0\' && str[i]

sum=sum*16+str[i]-\'0\';

else

if(str[i]>=\'a\' && str[i]

sum=sum*16+str[i]-87;

else

if(str[i]>=\'A\' && str[i]

sum=sum*16+str[i]-55;

else

{

printf(\"您输入的16进制不合法.\\n\");

exit(0);

} }

printf(\"相应的10进制数是:%d\\n\",sum); } 方法3:用字符数组及指针处理的方式 #include #include void main()

{ char str[20],*p=str; int sum=0;

printf(\"请输入一个16进制数字:\\n\"); gets(p);

while(*p) {

if(*p>=\'0\' && *p

sum=sum*16+*p-\'0\';

else

if(*p>=\'a\' && *p

sum=sum*16+*p-87;

else

if(*p>=\'A\' && *p

sum=sum*16+*p-55;

else

{

printf(\"您输入的16进制不合法.\\n\");

exit(0);

}

p++; }

printf(\"相应的10进制数是:%d\\n\",sum); } 9.编写一个小函数,其功能是计算两个整数的平均值,该函数要在主函数中调用。

#include void main() { int x,y; float avg; float average(int,int); printf(\"输入x,y的值,用空格分隔两个数:\\n\"); scanf(\"%d%d\",&x,&y);

avg=average(x,y);

printf(\"%d,%d的平均值是:%.2f\\n\",x,y,avg); }

float average(int x,int y)

{ return (x+y)/2.0; }

10.有N(N用宏定义为符号常量)个元素的一维整型数组,该数组中各元素值从键盘随机输入。然后,将这个整型数组中的值逆序存放。例如,原来5个元素的顺序为

8、

1、

4、

6、5,逆序之后各元素的值是

5、

6、

4、

1、8 #define N 5 #include void main() { int a[N]; int i,t;

printf(\"输入%d个整数,用空格或回车分隔:\\n\",N); for(i=0;i

scanf(\"%d\",&a[i]);

printf(\"数组原来的值是:\\n\"); for(i=0;i

printf(\"%d\\t\",a[i]);

for(i=0;i

t=a[i];

a[i]=a[N-1-i];

a[N-1-i]=t; }

printf(\"\\n逆序之后数组的值是:\\n\"); for(i=0;i

printf(\"%d\\t\",a[i]);

printf(\"\\n\"); } 11.有N(N用宏定义为符号常量)个元素的一维整型数组,该数组中各元素值从键盘随机输入。然后,对该数组元素进行由小到大排序(要求,该功能用函数实现),输出数组中各元素值。最后,从键盘随机输入一个整数,并把该整数插入上述数组中(该功能用函数实现),使得插入该整数后的数组仍然有序,输出数组中各元素的值。 #define N 5 #include void main() { int a[N+1];

int i,x; void sort(int array[],int n); void insert(int array[],int n,int x);

printf(\"输入%d个整数,用空格或回车分隔:\\n\",N); for(i=0;i

scanf(\"%d\",&a[i]);

sort(a,N);

/*调用sort对数组进行排序*/

printf(\"\\n升序排序之后数组的值是:\\n\"); for(i=0;i

printf(\"%d

\",a[i]);

printf(\"\\n输入一个x值插入到数组中:\\n\"); scanf(\"%d\",&x);

insert(a,N,x);

printf(\"\\n插入%d之后数组的值是:\\n\",x); for(i=0;i

printf(\"%d

\",a[i]);

printf(\"\\n\"); }

void sort(int array[],int n) /*用选择法对数组array升序排序*/ { int i,j,t,min;

for(i=0;i

min=i;

for(j=i+1;j

if(array[j]

min=j;

if(min!=i)

{

t=array[i];

array[i]=array[min];

array[min]=t;

} } }

void insert(int array[],int n,int x) { int i,pos;

for(i=0;i

pos=i;

for(i=n-1;i>=pos;i--)

array[i+1]=array[i];

array[pos]=x; }

12.有一整型数组,N(N用宏定义为符号常量)个元素,该数组中各元素值从键盘随机输入。从键盘随机输入一个整数x,删除该数组中值与x相同的所有元素(该功能用函数实现),输出数组中各元素的值。 #define N 5 #include void main() { int a[N]; int i,x,n; int delet(int a[],int n,int x);

printf(\"输入%d个整数,用空格或回车分隔:\\n\",N); for(i=0;i

scanf(\"%d\",&a[i]);

printf(\"数组原来的值是:\\n\"); for(i=0;i

printf(\"%d\\t\",a[i]);

printf(\"\\n请输入要删除的值x:\\n\"); scanf(\"%d\",&x);

n=delet(a,N,x);

/*n值是删除与x相同的元素后,数组剩余元素的个数。*/

printf(\"删除%d之后数组的值是:\\n\",x); for(i=0;i

printf(\"%d

\",a[i]);

printf(\"\\n\");

}

int delet(int a[],int n,int x) { int i,j;

for(i=0,j=0;i

if(a[i]!=x)

a[j++]=a[i];

return j; }

13.从键盘随机输入一字符串,将所有ASCII值为偶数的字符输出。例如:输入abc123,输出结果是b2(因为b的ASCII值是98,2的ASCII值是50,其他字符的ASCII值都是奇数)

#include #define N 40 void main() { char str[N]; int i;

printf(\"输入字符串:\\n\"); gets(str);

printf(\"ASCII码是偶数的字符有:\"); for(i=0;str[i];i++)

if(str[i]%2==0) putchar(str[i]);

printf(\"\\n\"); }

14.从键盘输入两个字符串s1,s2,把s2连接到s1的末尾。不能用strcat函数 #include #define N 40 void main() { char str1[N],str2[N]; void mystrcat(char *p1,char *p2);

printf(\"输入两个字符串,输入回车键结束:\\n\"); gets(str1); gets(str2);

mystrcat(str1,str2);

printf(\"连接在一起的字符串是:\\n\"); puts(str1);

}

void mystrcat(char *p1,char *p2) { while(*p1) p1++; while(*p2)

*p1++=*p2++; *p1=\'\\0\'; } 15.从键盘输入一个字符串,把其中最长的单词输出。单词定义为连续的一串英文字母。如输入I am a student.输出结果为student #include void main()

{

char str[80]; int i,start,len=0,lenthest=0,lenstart=0; int word=0;

printf(\"input a string:\\n\"); gets(str);

for(i=0;str[i];i++) {

if(str[i]>=\'a\' && str[i]=\'A\'&&str[i]

if(!word)

{

word=1;

start=i;

}

else

{

len++;

}

else

if(word)

{

word=0;

}

} if(len>lenthest) { lenthest=len; lenstart=start; } len=0;

printf(\"the lenthest word is:\\n\"); for(i=lenstart;i

putchar(str[i]);

printf(\"\\n\"); }

16.有一整型数组,N(N用宏定义为符号常量)个元素,该数组中各元素值从键盘随机输入。编写函数calculate,计算出所有元素的最大值、最小值、平均值,结果在主函数中输出。 #define N 5 #include void main() { int a[N]; int i,max,min; float avg; void calculate(int a[],int *pmax,int *pmin,float *pavg);

printf(\"输入%d个整数,用空格或回车分隔:\\n\",N); for(i=0;i

scanf(\"%d\",&a[i]);

calculate(a,&max,&min,&avg);

printf(\"数组中所有元素的最大值、最小值、平均值分别是:\\n\"); printf(\"最大值max=%d,\\n\",max); if(word) if(len>lenthest) {

lenthest=len;

lenstart=start; }

printf(\"最小值min=%d,\\n\",min); printf(\"平均值avg=%.2f,\\n\",avg);

}

void calculate(int a[],int *pmax,int *pmin,float *pavg) { int i; int sum;

*pmax=*pmin=sum=a[0];

for(i=1;i

if(a[i]>*pmax)

*pmax=a[i];

if(a[i]

*pmin=a[i];

sum=sum+a[i]; }

*pavg=(float)sum/N; }

推荐第2篇:编程题总结

大家必须掌握每种类型的1-3题。

题号考点要求

48、将两个两位数的整数合并成一个整数

65、两个正整数合并成一个整数

71、两个正整数合并成一个整数

77、两个正整数合并成一个整数

81、两个正整数合并成一个整数

84、两个正整数合并成一个整数

87、两个正整数合并成一个整数

91、两个正整数合并成一个整数

8、s=1+1/(1+2)+1/(1+2+3)+...+1/(1+2+3+.....+n)

9、p=m!/(n!*(m-n)!)

26、连加

68、s=1+1/1!+1/2!+1/3!+........

70、s=1/(1*2)+1/(2*3)+...+1/(n*(n+1))

76、s=1+0.5*x+(0.5*(0.5-1)*x*x/2!+......

86、连加

90、连加

92、连加

93、连加

97、连加

100、连加

2、)

类似素数或者闰年算法

2、求出1到1000之间能被7或11整除、但不能同时被7和11整除的所有整数,并将他们放在a所指的数组中,通过n返回这些数的个数。

58、求n以内同时能被3与7整除的所有自然数之和的平方根s

3、求出能整除x且不是偶数的各整数,并按从小到大的顺序放在pp所指的数组中,这些除数的个数通过形参n返回。

27、求出1到m之间能被7或11整除的整数,放在数组中,通过n返回这些数的个数

63、求n的所有因子(不包括1与自身)

98、计算n以内能被5或9整除的所有自然数的倒数之和

15、w是大于10的无符号整数,若w是n(n>=2)位的整数,则函数求出w的后n-1位的数作为函数值返回。

24、将数字字符串转化为一个整数

16、对变量中的值保留两位小数,对第三位四舍五入

3、)

1、一维数组中,将低于平均分的人数作为函数值返回,低于平均分的分数放在below所指的数组中。

82、计算一维数组中n门课程的平均分

96、一维数组中每相邻元素之平均值的平方根之和

28、找出一维数组中最大的值和它所在的下标,两者通过形参返回

7、求出数组的最大元素在数组中下标并存放在k所指的存贮单元中。

43、从num个字符串中找出最长的一个字符串,并通过形参指针传回

39、移动数组中的内容,前p个数移动到数组后面

40、移动数组中的内容,前m个字符移动到后面

5、将大于整数m且紧靠m的k个素数存入xx所指的数组中。

20、求出小于变量m的所有素数并放在一个数组中

51、将大于1小于m的非素数存入xx所指数组中,个数通过形参返回

99、计算3到n之间所有素数的平方根之和

29、将字符串中下标为奇数位置上的字母转化为大写

47、字符串中所有下标为奇数的字母转为大写

31、将字符串中除了下标为偶数、同时ASCII码也为偶数的字符外,其余的全都删除;串中剩余字符放在t字符串中

32、除了下标为奇数、同时ASCII码也为奇数的字符外,其余的全都删除;串中剩余字符放在t字符串中

49、将字符串中下标为偶数且ASCII码为奇数的字符删除

19、从字符串中删除指定字符

64、将字符串中ASCII值为奇数的字符删除,剩余字符放在t所指字符串中

72、将字符串中下标为偶数的字符删除

78、字符串中ASCII为偶数的字符删除

35、删除字符串中所有空格

6、删除一个字符串中指定下标的字符。

60、删去一维有序数组中所有相同的数,使之只剩一个

33、删除字符串中尾部*号,使不超过n个

36、把字符串中的前导*号全部移到字符串尾部

45、删除字符串中前导和尾部的*号

56、字符串中,除了尾部*号外,字符串中其余*号全部删除

73、删除字符串中除了前导和尾部*号外的所有*号

83、字符串中尾部*号删除

88、字符串中除了前导*外,删除其余*号

94、字符串中,前导*号全部删除

95、字符串中前导*号不得多于n个

66、删除字符串中所有*号

(

4、)

17、字符串内容逆置

23、判断字符串是否为回文

25、比较两个字符串的长度(不能用strlen)

52、实现两个字符串连接

59、将字符串数组中的字符串依次合并到一个字符串中

21、字符串中字符按降序排列(选择法排序)

4、统计在tt字符串中“a”到“z”26个字母各自出现的次数,并依次放在pp所指数组中。

38、求出字符串中指定字符的个数

44、统计长度为2的字符串在另一个字符串出现的次数

62、统计一行字符串中单词个数

11、二维数组构成矩阵,使数组左下半三角元素中的值全部置成0

42、二维数组,将右上半角元素中的值乘以m

80、二维数组中左下半三角中元素中的值乘以3

12、求出数组周边元素的平均值

14、求出二维数组周边元素之和

18、矩阵行列互换

53、矩阵B=A+A1

30、求出二维数组中最大元素的值,此值返回给调用函数

55、M行N列的二维数组中的数据,按行的顺序依次放到一维数组中

41、将二维数组中的字符,按列的顺序依次放到一个字符串中

75、二维数组中数按列的顺序放到一维数组中

13、求出二维数组每列中最小元素,并依次放在pp所指一维数组中

37、结构体数组(学号、8门成绩、平均成绩),求出每个学生的平均成绩

34、把结构体数组(学号、成绩)中分数最高的学生放到h数组中,函数返回人数

74、把结构体数组(学号、成绩)中分数最低的学生数据放在h所指数组中

50、结构体数组(学号、成绩),找出成绩最高的学生记录,通过形参返回

54、结构体数组(学号、成绩),低于平均分的学生数据放在b所指数组中

89、结构体数组(学号、成绩),高于等于平均分的学生人数通过形参返回

57、结构体数组(学号、姓名),把指定范围内的学生数据放在所指数组中

67、结构体数组(学号、成绩)返回指定学号的学生数据(查找)

46、结构体数组(学号、成绩),按分数降序排列(选择法排序)

22、链表结构中,找出数值最大的结点,并由函数值返回

85、链表中,求出平均成绩

10、迭代法求方程的实根

61、统计各年龄段人数(建议采用if else)

69、递归函数

推荐第3篇:描述性编程总结

一、描述性编程

1、QTP的运行原理

封装被测对象(TO)到对象仓库

对比对象仓库里的对象属性(TO)和运行时的真实被测对象的属性(RO) 对比一致后,找得到相应的对象(RO),按照脚本驱动对象

2、RO & TO

TO:Test object 仓库对象

Ro:Runtime object 运行时对象

TO包含RO

Cancel button

text:TO:Cancel|取消

text:RO:Cancelor取消

3、GetTOProperty获取TO的属性值

属性值=GetTOProperty(“属性名”)

4、GetROProperty 获得RO的属性值

属性值=GetROProperty(“属性名”)

5、GetTOProperties 返回值是对象的集合 (Set)

Set pops=GetTOProperties

For i=0 to pops.count-1

Pops(i).name获得属性名

Pops(i).value获得属性值

Next

.Count获得组合的总数

6、SetToProperty设置TO属性值

SetTOProperty “属性名”,”属性值”(runtime)

二、描述性编程

1、初级描述性编程(直描)

Object.(“属性名:=属性值”,” 属性名:=属性值”…)

Object:被测对象(类型)

Index

2、代码简化

A.赋值给变量(set)

B.With…End With

With object

.statements

End With

3、高级描述性编程(description)

Set mydes=description.create()

Mydes(“属性名”).value=”属性值”

Mydes(“属性名”).value=”属性值”

Dialog(“Login”).WinButton(mydes).Click

4、ChildObjects

Set ChildO=ChildObjects(mydes)

Set ChilO=Dialog(“Login”).ChildObjects(mydes) .count

For i=0 to ChildO.count-1

ChildO(i).click

next

推荐第4篇:编程入门基础知识总结

编程入门基础知识总结

基本知识

程序

= 算法 + 数据结构,算法是对操作的描述,数据结构是对数据的描述。 伪代码:pseudo code 程序一般包括:

(1)预处理命令:#include等 (2)全局声明部分

(3)函数:函数首部(声明)、函数体(局部声明+执行部分) 程序执行过程

源程序-->编译成目标程序obj-->连接目标程序成可执行文件 类:数据成员+成员函数 命名空间

实际上就是一个由程序设计者命名的内存区域。程序员可以根据需要指定一些有名字的空间域,把一些全局实体分别放在各个命名空间中,从而与其它全局实体分隔开,解决名字冲突,防止全局命名空间污染。

C++库的所有标识符(标准头文件中函数、类、对象、类模版)都是在std命名空间定义的 A 的ASCII码值65

a 的97 1.数据类型

•基本类型:整型、浮点型、字符型、布尔型

•构造类型:枚举、数组、结构体类型(struct)、公用体(union)、类类型 •指针类型 •引用类型 •空类型:null int 4

unsigned int 4

short int 2

unsigned short int 2

long int 4 unsigned long int 4 char 1

uchar 1 float 4

double 8

long double 8 无符号:不用保存符号位,故比有符号多了一位

有符号:数值用补码存放(无论正负),最高位为符号位

VC中,long double与double字节数相同,故long double是无用的 GCC中,long double则有12个字节

无负值的量可以定义为unsigned int,如年龄、学号等,以增大存储范围。 short int,long int,unsigned int这些类型后面的int可以省略 整型表示:

十进制,后面加L 或 l 八进制,前面加0 十六进制,前面加0x 浮点数表示:

默认为双精度型double 后面加 f 或 F 则为单精度float 后面加 l 或 L 则为长精度型long double 指数形式 数符

数字部分

指数部分

a = 3.14e2

a=0.314e4

a=31.4e1

a=3140e-1

浮点数,不管是用小数形式还是指数形式表示,在内存中都是以指数形式表示的,且数字部分必须小于1 如3.14159 在内存中表示为

+ .314159

3 ,数字部分为.314159,是小于1的 字符型

字符型在内存中存储的是ASCII码,也是整型,故而可以把字符型赋给整型 字符串后面,编译系统会加个\'\\0\'作为结束符 符号常量

用符号代表一个常量,如#define

PI 3.14159 在编译时,系统会把符号常量替换成具体的值

2.变量

变量:程序运行期间,值可变的量

变量名:代表内存中的一个存储单元,在编译连接时由系统给每一个变量分配一个地址 标识符:标识实体名字的有效字符序列,字母、数字、下划线

常变量:定义变量时加上const,运行期间值不可变,也即只读变量 区别#define 定义的符号常量与const定义的常变量 符号常量只是用一个符号代表一个常量,在编译时把所有符号常量替换为指定的值,它没有类型,在内存中不存在以符号常量命名的存储单元 常变量具有变量的特征,具有类型,在内存中有以它命名的存储单元,与一般变量不同的是,常变量代表的存储单元值不可变。

强定义的好处

1.保证变量名使用的正确,不正确会在编译时报错

2.每个变量指定为一确定类型,在编译时就能为其分配存储单元 3.编译时即可检查变量进行的运算是否合法。

3.运算符 二进制位运算 & 二进制 按位与 |

二进制按位或 ^ 按位异或 >右移位

++ -- 自加自减运算符 ++i 使用i之前,i先自加 i++ 使用i之后,i再自加 ++ -- 结合方向为自右向左 例: !x++

先取非,再++ (i++)+6 先i+6,后i++,不管有无括号,都是先运算,再自加

(++i)+6 先自加,再加6 -i++ :i左面是符号运算符,右面是自加运算符,设i等于3,运算过程相当于 -(i++),即 -i=-3 -3+1=-2 a = -i++:a结果为-3,先赋值,再自加; 5-i++ =?

答案2

?待测试

i=1,j=2,则a = i+++j ?

答案:3 因为相当于a=(i++)+j; i=1,j=2,则a=i+(++j)?

答案:4

赋值运算符=的优先级小于++ -- 运算符

示例:

int arr[] = {6,7,8,9,10}; sint *ptr = arr; *(ptr++) += 123; printf(\"%d,%d\",*ptr,*(++ptr)); 解:

*(ptr++) += 123; 相当于 *ptr +=123; *ptr++; 故arr[0] = 129; ptr指向arr[1] printf函数从右向左压栈,运算顺序:++ptr; 输出结果: 8 8 cout也是自右向左压栈。

逗号运算符

又称顺序求值运算符

a = 3*4,a*5 则

a=? 答案 12 ?

注意逗号运算符优先级最低。注:整个逗号表达式的值为60(a=3*4=12,12*5 =60) x=(a=3,6*3)

则 x=18 x=a=3,6*a

则 x=3

3.内存存储

浮点数在内存里和整数存储方式不同

float a =1.0f; cout

(int)a显示转换等于1

4.类型转换

转换本质:按存储单元中的存储形式直接传送

(1)浮点型赋给整型,舍弃小数部分

(2)double型赋给float,防止溢出错误

(3)int, short, long赋给char,原封不动取低八位

(4)signed赋给unsigned,符号位照搬。负数以补码形式存放,-1赋给unsigned int,结果为65536.整数不会变,除非两个长度不同(int,long)发生截断

5.C++输入输出

C++输入输出包含三个方面的内容

(1)对系统指定的标准设备的输入输出。即从键盘输入、输出到显示器屏幕,称标准输入输出,简称标准I/O

(2)以外出磁盘文件为对象进行输入输出,称文件I/O

(3)对内存中指定空间进行输入输出,通常指定一个字符数组作为存储空间,称字符串输入输出,简称串I/O

在C++中,输入输出流被定义为类。C++的I/O库中的类称为流类,用流类定义的对象称流对象。

C++中输入输出是调用输入输出流库中的流对象cin、cout实现的,即I/O不是C++本身定义的,而是编译系统的I/O库中定义的。

I/O控制符#include

setprecision(n) 设置精度为n,

十进制输出时,n代表有效数字位数包括整数和小数;

fixed(固定小数位数)和scientific(指数)形式输出时,n指小数位数(不包括整数部分)

double a = 123.4567890123456

cout

//十进制输出,输出123.456

默认精度为6

cout

//十进制,输出123.456789

精度为9

cout

//恢复默认精度

cout

//固定位数,123.456789 ,此时精度表示小数位数,精度为默认的6

6.getchar() 和 putchar()

cout

//读入的实际是ASCII码,故输出为字符ASCII码,97,而非读入的字符

cout

while((c=getchar())!=\'\\n\'){}

//循环读入字符的控制

7.函数与模版

函数原型:不同具体形参的函数声明

如:float add(float,float)

主要作用:根据函数原型,在程序编译阶段对调用函数的合法性进行全面检查。

函数重载:同一函数名定义的多个函数,这些函数的参数个数,参数类型,参数顺序至少有一项不同,一个函数不能既是重载函数,又是带默认参数的函数。

函数模版:适用于函数个数相同,函数体相同,而只有类型不同的情况

[cpp] view plain copy 01.#include 02.using namespace std;

03.template // template 04.T max(T a, T b, T c)

05.{

06.

if(b>a) a = b;

07.

if(c>a)

a = c;

08.

return a;

09.}

10.int main()

11.{

12.

int a=1,b=2,c=3;

13.

cout

14.

float d=1.1,e=2.2,f=3.3;

15.

cout

16.}

变量作用域:变量有效范围,有四种:

文件作用域 file scope

函数作用域

function scope

块作用域 block scope

函数原型作用域 function prototype scope

递归函数:函数体中,直接或间接调用函数本身的函数。

变量的存储类别:外部、局部(相对于函数而言)

全局变量(也即外部变量)

静态局部变量(static)

自动变量(不加static的局部变量)

寄存器变量(register) 存放在CPU中,而非内存中,这样在使用时就省去了从内存中载入寄存器的过程。但只是建议性的,优化编译系统自动识别使用频繁的变量,从而自动将变量放入寄存器中。

extern声明的外部变量

即提前引用申明,表示该变量是将在下面定义的全局变量或在其它文件中定义(本文件中不用extern,引用该变量的外部文件用extern进行声明)

static声明的静态外部变量

只能被本文件使用,而不能被外部文件使用的全局变量,这样不同文件中的全局变量不会相互干扰,为程序模块化、通用化提供方便。

注:全局变量也是静态存储的。

内部函数与外部函数

内部函数:static 声明,文件内使用

外部函数:另一个文件里extern声明,表示是其它文件的函数,extern可以省略

宏定义

#define PI 3.14

定义符号常量

#define Area(a,b)

a*b

//定义函数功能,下面当作函数来用。

#include \'iostream\' 区别

在系统目录寻找,找不到报错

\' \'

在当前目录寻找,找不到报错

字符数组

(1) char str[] = \"i am happy\";

数组长度为11,而非10,因为后面有一个\'\\0\'

(2) char str[] = {\'i\',\'a\',\'m\',\'h\',\'y\'}

数组长度为5,系统不会自动为其在后面加\'\\0\'

因此,(1),(2)是不同的

输出字符数组:cout

string类

字符数组是C中的处理方式,C++中用string类,#include

字符串变量中不会存放\'\\0\',只存放字符串本身,故string str =\"hello\"; 的长度为5,要注意。

字符串变量存储的实际是字符串的指针,4个字节,

sizeof(string) = 4;

string name[]={\"i\",\"am\",\"happy\"};

sizeof(name) = 3*4 = 12;

变量与指针:

int *p = &i;

//int型指针变量,定义时的*只是表示指针类型

p是指针变量,其值是变量i的地址,*p则是存储单元,*&a与*p同,都表示变量a

指向数组的指针

int a[10] = {};

int *p;

p = &a[0]; //与下面等价,都指向数组第一个元素,因为数组名本身就是表示数组的地址

p = a; *************************

*p++ 相当于*(p++),先得到*p的值,p再++移到下一个元素

(*p)++则是使*p的值+1

二维数组的指针表示: *(*(p+i)+j)

表示 a[i,j]

*************************

int (*p)[n]

p为指向含n个元素的数组的指针

int *p[n]

定义指针数组P

函数指针 指向函数的指针,主要用作形参

int max(int,int);

int (*p)(int,int);

p = max; 赋值(地址)

p(1,2) //调用

引用

int a = 10;

int &b = a; b是对a的引用

引用传递,引用型变量作为函数形参

[cpp] view plain copy 01.//值会改变

02.main{

03. int i=5,j=6;

04. swap(i,j)

05.}

06.void swap(int &a, int &b) //这里的&不是“a的地址”,而是指“a是一个引用型变量”,&是个声明符

07.{

08.

int temp;

09.

temp = a;

10.

a= b;

11.

b=temp;

12.}

传递变量地址:形参是指针变量,实参是变量地址,这种虚实结合的方法仍然是“值传递”方式,只是实参的值是变量地址而已。

[cpp] view plain copy 01.//值会改变

02.main()

03.{

04.

int i=5,j=6;

05.

swap(&i,&j)

06.}

07.void swap(int *p1, int *p2) //这里的*号也只表示是指针类型

08.{

09.

int temp;

10.

temp = *p1;

11.

*p1 = *p2;

12.

*p2 = temp;

13.}

结构体变量 struct

作函数参数时有三种形式

(1)用结构体变量名作形参

这时形参要开辟内存单元,实参中全部内容通过值传递方式一一传递给形参,时空开销大,效率低

(2)用指向结构体变量的指针作形参,只有四个字节

void printed(student *p) {

coutnum;

}

(3)用结构体变量的引用变量作形参

Student stu;

print(stu);

void print(Student &stu) {

cout

}

new delete 动态内存分配

new 类型(初值),返回地址,分配失败返回NULL

float *f = new float(3.14);

int *i = new int(6);

int *k = new int[5][4]; //数组

delete 变量

delete f; delete i;

数组

delete [] k;

Enum 枚举类型

声明枚举类型

enum weekday{sun,mon,tue,wed,thu,fri,sat}; 定义枚举变量

weekday workday,week_end; 变量赋值: workday = mon;

workday = weekday(2); 枚举常量对应值从0开始,0,1,2,3..

typedef 声明新类型

typedef int INTEGER ,相当于为int起了个别名,下面程序中就可以直接用INTEGER作int用了

主要为了方便系统移植,如long int在VC中4个字节,在GCC中占8个字节,直接用int的话可能会溢出,用INTEGER就不用担心了,只要把ypedef int INTEGER 声明语句一改就行

运算符重载

方法是定义一个重载运算符的函数,在需要执行被重载的运算符时,系统就自动调用该函数,以实现响应运算。即运算符重载是通过定义函数实现的,运算符重载实际上是函数重载。 格式:

函数类型 operator 运算符名称 (形参表列)

{对运算符的重载处理}

例如:将 + 用于Complex类(复数)的加法运算,函数原型如下:

Complex operator + (Complex& c1, Complex& c2);

注:operator 是关键字, operator + 就是函数名,可以描述为: 函数operator +重载了运算符+

整数加可以想象为调用如下函数

int operator + (int a,int b)

{ return a+b; }

[cpp] view plain copy 01.#include 02.using namespace std;

03.cla Complex

04.{

05. public:

06.

Complex(){real=0;imag=0;}

07.

Complex(double r, double r){real=r;imag=i;}

08.

Complex operator + (Complex &c2); //声明重载运算符的函数

09.

void display();

10. private:

11.

double rea;

12.

double imag;

13.};

14.Complex Complex::operator + (Complex &c2) //定义重载运算符的函数

15.{

16. Complex c;

17. c.real = real + c2.real;

18. c.imag = imag + c2.imag;

19. return c;

20.}

21.void Complex::display()

22.{

23. cout

24.}

25.int main()

26.{

27. Complex c1(3,4), c2(5,-10),c3;

28. c3 = c1 +c2;

//运算符+ 用于复数运算

29. cout

30. cout

31. cout

32. return 0;

33.}

34.运行结果:

35.c1 = (3,4i)

36.c2 = (5,-10i)

37.c1+c2 = (8,-6i)

重载运算符的规则

(1)C++不允许用户自己定义新的运算符,只能对已有的C++运算符进行重载。

(2)C++允许重载绝大部分的运算符

(3)重载不能改变运算符运算对象的个数

(4)重载不能改变运算符的优先级别

(5)重载不能改变运算符的结合性

(6)重载运算符的函数不能有默认的参数 (7)重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少一个是类对象(或类对象的引用)。也就是说参数不能全部是C++的标准类型,以防止用户修改用于标准类型数据的运算符的性质。

(8)用于类对象的运算符一般必须重载,但有两个例外,运算符“=”和“&”不必用户重载

(9)应当使重载运算符的功能类似于该运算符作用于标准类型数据时所实现的功能,否则不易使人理解程序

(10)运算符重载函数可以是类的成员函数,也可以是类的友元函数,还是计费成员函数也非友元函数的普通函数。

推荐第5篇:《C专家编程》总结

《C专家编程》总结

开始读《C专家编程》之前,有一个很担心的问题:94年出的讲语言的书,在现在(2012)还有多少是适用的。因此,一边读,一边用VS2010做实验。最后发现大部分内容都还在用。读完后,觉得最精彩的部分有二:一是讲解如何理解声明,二是深入地讲解数组名与指针。下文是将看书过程中所做的笔记进行的整理。

p.s: 以下代码均在VS2010测试过

1.使用无符号数时要特别注意(不推荐使用无符号数) 当无符号数与有符号数在同一条表达式中出现时,有符号数会被转换为无符号数。e.g:

int feng = -1; unsigned int yan = 5; bool result = (feng

char *str[] = {\"feng\" \"yan\", \"zero\"}; //\"feng\"和\"yan\"被合并成一个了:\"fengyan\" 3.易出错的优先级

.高于* e.g: *p.f 正确的理解:*(p.f)

[]高于* e.g: int *ap[] 正确的理解:int *(ap[]) ap是个数组,其元素为int* 函数()高于* e.g: int *fp() 正确的理解:int* fp() fp是返回int*的函数

==和!=高于位操作符 e.g: val & mask != 0 正确的理解:val & (mask != 0) ==和!=高于赋值符 e.g: c = getchar() != EOF 正确的理解:c = (getchar() != EOF)

算术运算高于移位运算 e.g: msb

a.找标识符

b.找被括号括起来的部分

c.找后缀操作符,如果是(),则表示是函数;如果是[],则表示是数组 d.找前缀操作符,如果是*,则表示“指向XX的指针”

e.找const和volatile,如果const,volatile后面紧跟类型(如int,long),那么它作用于类型,其它情况下,作用于它左边紧邻的项 e.g:

int const * zero; //zero是一个指针,指向一个常量整形

char (*zero)[20]; //zero是一个指针,指向一个有20个char元素的数组

typedef void (*ptr_to_func)(int); //ptr_to_func是新类型名,这种类型是一个函数指针,指向接收一个int参数,并返回void的函数

char* const * (*zero)(); //zero是一个函数指针,该函数无参数,并返回一个指针,返回的指针指向一个常量指针

char* (*zero[10])(int **p); //zero是一个数组,元素是函数指针,其指向的函数授受一个二维指针,并返回一个指向char的指针 void (*signal(int sig, void (*func)(int)))(int); //signal是一个函数,该函数接收一个int,一个函数指针,并返回一个函数指针

5.左值与右值

左值通常表示存储结果的地方(地址),其值在编译时可知 右值通常表示地址的内容,其值通常要到运行时才知道

6.指针与数组名不等同的情况(定义为数组,却声明为指针,或者反过来) 前提知识(假设有定义:int array[10], *ptr;):

a.使用数组名下标访问(如:array[1]),会直接将数组名的地址加上偏移值作为变量的地址(即array[1]的地址)

b.使用指针下标访问(如:ptr[1]),会先取指针指向的内容,然后将这个内容加上偏移值作为变量的地址(即ptr[1]的地址)

不等同的原因:

当定义为数组,却声明为指针时,相当于用指针下标访问的方法来解析一个数组名下标,即先取数组第0号元素的内容,然后将这个内容加上偏移值作为变量的地址,从而访问了不该访问的东西。反之亦然。 7.指针与数组等同的情况

a.编译器将表达式中的数组名当作指向该数组第0号元素的指针,下标当作指针的偏移量,即array[i]会被当作*(array + i)

b.编译器将函数参数声明中的数组名当作指向该数组第0号元素的指针,即在函数内部得到的是指针,而不是数组名

基于a情况,可知这条谣言是假的(至少在一维数组中一定不成立): 用指针迭代数组比用下标迭代数组更快

基于b情况,可解释为什么在传递数组后,不能用以下方法计算数组长度

int ArrayLength(int arr[]) { return sizeof(arr) / sizeof(arr[0]); //返回值必定是1,因为此时的arr是一个指针,而不是数组名 } 注意b情况的将数组改写为指针并不是递归定义的,e.g:

实参 char zero[10][10] 被改写为 char (*zero)[10],这里将数组的数组改写为数组的指针

实参 char *zero[10] 被改写为 char **zero,这里将指针数组改写为指针的指针

实参 cahr (*zero)[10] 不改变,因为此时的zero是指针,而不是数组 8.interposition interposition指用户定义的函数取代库中声明完全相同的函数,注意这不是指重载,而是指下面这种:

void zero(); //user defined function void zero(); //library function 出现interposition时,要特别注意以下情况:

void zero(); //user defined function int main() { zero(); //调用用户定义的函数zero,而不是库函数zero

FengYan(); //假设这是另一个库函数,并且函数内调用库函数zero,此时由于interposition,变成调用用户定义的zero return 0; } 备注:

出现interposition时,在VS2010会出现warning: inconsistent dll linkage 9.堆栈段作用

a.存储局部变量

b.函数调用时,存储有关的维护信息

c.用作暂时存储区。e.g: 计算一个很长的表达式时,会把部分结果先压到堆栈中

推荐第6篇:plc编程学习总结

1:PLC编程语言有5种,即顺序功能图(SFC),梯形图(STL),功能块图,指令表,和结构文本。其中顺序功能图(SFC)是最容易理解的,按照时间的先后顺序执行。 然后转换成梯形图,因为梯形图是PLC普遍采用的编程语言。不过SFC转换梯形图是很简单的。

2:就好比是开关在合上时两触点接触的一瞬间继电器就有动作了,这叫上升沿。

下降沿就好比开关两触点始终保持接触时,继电器没有动作,只有开关两点断开时的瞬间继电器才动作。

推荐第7篇:刀具和编程总结

① 白钢刀(即高速钢刀具)因其通体银白色而得名,主要用于直壁加工。白钢刀价格便宜,但切削寿命短、吃刀量小、进给速度低、加工效率低,在数控加工中较少使用。

② 飞刀(即镶嵌式刀具)主要为机夹式可转位刀具,这种刀具刚性好、切削速度高,在数控加工中应用非常广泛,用于模胚的开粗、平面和曲面粗精加工效果均很好。

③ 合金刀(通常指的是整体式硬质合金刀具)精度高、切削速度高,但价格昂贵,一般用于精加工。

数控刀具与普通机床上所用的刀具相比,有以下不同的要求。

(1)刚性好(尤其是粗加工刀具)、精度高、抗振及热变形小。

(2)互换性好,便于快速换刀。

(3)寿命高,切削性能稳定、可靠。

(4)刀具的尺寸便于调整,以减少换刀调整时间。

(5)刀具应能可靠地断屑或卷屑,以利于切屑的排除。 (6)系列化、标准化,以利于编程和刀具管理。

① 刀具直径越大,转速越慢;同一类型的刀具,刀杆越长,吃刀量就要减小,否则容易弹刀而产生过切。

② 白钢刀转速不可过快,进给速度不可过大。

③ 白钢刀容易磨损,开粗时少用白钢刀。

① 以上的飞刀参数只能作为参考,因为不同的飞刀材料其参数值也不相同,不同的刀具厂生产的飞刀其长度也略有不同。另外,刀具的参数值也因数控铣床或加工中心的性能和加工材料的不同而不同,所以刀具的参数一定要根据工厂的实际情况来设定。

② 飞刀的刚性好,吃刀量大,最适合模胚的开粗。另外,飞刀精加工陡峭面的质量也非常好。 ③ 飞刀主要是镶刀粒的,没有侧刃,

① 合金刀刚性好,不易产生弹刀,用于精加工模具的效果最好。

② 合金刀和白钢刀一样有侧刃,精铣铜公直壁时往往使用其侧刃。

① 刀具的名称一般根据刀具的直径和圆角半径来定义,例如,直径为30,圆角半径为5的飞刀,其名称定义为D30R5;直径为12的平底刀,其名称定义为D12;半径为5的球刀,其名称定义为R5。

② 输入刀具名称时,只需要输入小写字母即可,系统会自动将字母转为大写状态。 ③ 设置刀具参数时,只需要设置刀具的直径和底圆角半径即可,其他参数按默认即可。加工时,编程人员还需要编写加工工艺说明卡,注明刀具的类型和实际长度。

机床坐标一般在工件顶面的中心位置,所以创建机床坐标时,最好先设置好当前坐标,然后在〖CSYS〗对话框中设置“参考”为WCS。

加工模具时,其开粗余量多设为0.5,但如果是加工铜公余量就不一样了,因为铜公(铜公是火花机放电加工用的电极)最后的结果是要留负余量的。

模具加工要求越高时,其对应的公差值就应该越小。

进行实体模拟验证前,必须设置加工工件和毛坯,否则无法进行实体模拟。

推荐第8篇:软件编程规范总结

软件编程规范总结

本规范的内容包括:基本原则、布局、注释、命名规则、变量常量与类型、表达式与语句、函数与过程、可靠性、可测性、断言与错误处理等。

一、基本原则

1.2.3.4.5.6.7. 保持代码的简明清晰,避免过分的编程技巧。 所有的代码尽量遵循ANSI C标准。

编程时首先达到正确性,其次考虑效率。 避免或少用全局变量。 尽量避免使用GOTO语句。 尽可能重用、修正老的代码。 尽量减少同样的错误出现的次数。

二、文件布局

1.头文件必须要避免重复包含。

2.包含标准库头文件用尖括号 ,包含非标准库头文件用双引号 “”。 3.遵循统一的顺序书写类的定义及实现。 类的定义(在定义文件中)按如下顺序书写:

公有属性

公有函数

保护属性

保护函数

私有属性

私有函数

类的实现(在实现文件中)按如下顺序书写:

构造函数

析构函数 公有函数 保护函数 私有函数

4.程序中一行的代码和注释不能超过80列。5.定义指针类型的变量,*应放在变量前。

6.源程序中关系较为紧密的代码应尽可能相邻。iLength iWidth = 10;

= 5; // 矩形的长与宽关系较密切,放在一起。

StrCaption = “Test”;

7.禁止使用TAB键,必须使用空格进行缩进。缩进为4个空格。

8.程序的分界符‘{’和‘}’应独占一行并且位于同一列,同时与引用它们的语句左对齐。{ }之内的代码块使用缩进规则对齐。

9.if、else、else if、for、while、do等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加 { }。

if (varible1

varible1 = varible2; }

10.

11.声明类的时候,public、protected、private关键字与分界符{} 对齐,这些部分的内容要进行缩进。

12.结构型的数组、多维的数组如果在定义时初始化,按照数组的矩阵结构分行书写。13.相关的赋值语句等号对齐。

14.在switch语句中,每一个case分支和default要用{ }括起来,{ }中的内容需要缩进。

15.不同逻辑程序块之间要使用空行分隔。16.一元操作符如“!”、“~”、“++”、“--”、“*”、“&”(地址运算符)等前后不加空格。“[]”、“.”、“->”这类操作符前后不加空格。

17.多元运算符和它们的操作数之间至少需要一个空格。18.关键字之后要留空格。(if、for、while等关键字之后应留一个空格再跟左括号‘(’,以突出关键字。)

19.函数名之后不要留空格。(函数名后紧跟左括号‘(’,以与关键字区别。) 20.(’向后紧跟,‘)’、‘,’、‘;’向前紧跟,紧跟处不留空格。‘,’之后要留空格。‘;’不是行结束符号时其后要留空格。

21.长表达式(超过80列)要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐。

22.函数声明时,类型与名称不允许分行书写。

三、注释

1.一般情况下,源程序有效注释量必须在20%以上。2.注释符与注释内容之间要用一个空格进行分隔。

3.文件头部必须进行注释,包括:.h文件、.c文件、.cpp文件、.inc文件、.def文件、编译说明文件.cfg等。

4.函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、访问和修改的表、修改信息等。

5.包含在{ }中代码块的结束处应加注释,便于阅读。特别是多分支、多重嵌套的条件语句或循环语句。

void Main() {

if (…) {

… while (…)

{

} /* end of while (…) */ …

} /* end of if (…) */ // 指明是哪条语句结束 } /* end of void main()*/

// 指明函数的结束 // 指明该条while语句结束

6.保证代码和注释的一致性。修改代码同时修改相应的注释,不再有用的注释要删除。 7.注释应与其描述的代码相近,对代码的注释应放在其上方或右方(对单条语句的注释)相邻位置,不可放在下面,如放于上方则需与其上面的代码用空行隔开。

8.全局变量要有详细的注释,包括对其功能、取值范围、访问信息及访问时注意事项等的说明。

9.注释与所描述内容进行同样的缩排。

10.对分支语句(条件分支、循环语句等)必须编写注释。11.尽量避免在注释中使用缩写,特别是不常用缩写。

四、命名规则

1.标识符要采用英文单词或其组合,便于记忆和阅读,切忌使用汉语拼音来命名。严格禁止使用连续的下划线,下划线也不能出现在标识符头或结尾(预编译开关除外)。

2.程序中不要出现仅靠大小写区分的相似的标识符。

3.用正确的反义词组命名具有互斥意义的变量或相反动作的函数等。

4.宏、常量名都要使用大写字母, 用下划线 ‘_’分割单词。预编译开关的定义使用下划线 ‘_’开始。

5.变量名长度应小于31个字符,以保持与ANSI C标准一致。不得取单个字符(如i、j、k等)作为变量名,但是局部循环变量除外。

6.程序中局部变量不要与全局变量重名。7.使用一致的前缀来区分变量的作用域。

g_

:全局变量 s_

:模块内静态变量

空:局部变量不加范围前缀

8.使用一致的小写类型指示符作为前缀来区分变量的类型。说明:常用变量类型前缀列表如下:

i

: int

f

: float d : double c

: char uc

: unsigned char 或 BYTE l : long p

: pointer b

: BOOL h : HANDLE w

: unsigned short 或 WORD dw : DWORD或 unsigned long a

:数组,array of TYPE str

:字符串 t :结构类型

9.完整的变量名应由前缀+变量名主体组成,变量名的主体应当使用“名词”或者“形容词+名词”,且首字母必须大写。

float g_fValue; 10.函数名用大写字母开头的单词组合而成,且应当使用“动词”或者“动词+名词”(动宾词组)。

11.结构名、联合名、枚举名由前缀T_ 开头。事件名由前缀EV_ 开头。 12.标识符前最好不加项目、产品、部门的标识。

五、变量常量与类型

1.定义全局变量时必须仔细分析,明确其含义、作用、取值范围及与其它全局变量间的关系。

2.明确全局变量与操作此全局变量的函数或过程的关系。3.一个变量有且只有一个功能,不能把一个变量用作多种用途。 4.循环语句与判断语句中,不允许对其它变量进行计算与赋值。 5.宏定义中如果包含表达式或变量,表达式和变量必须用小括号括起来。 6.使用宏定义多行语句时, 必须使用 { } 把这些语句括起来。

建议:

 尽量构造仅有一个模块或函数可以修改、创建的全局变量,而其余有关模块或函数只能访问。

 对于全局变量通过统一的函数访问。

 尽量使用const说明常量数据,对于宏定义的常数,必须指出其类型。  最好不要在语句块内声明局部变量。

7.结构和联合必须被类型化。typedef struct {

char acName[NAME_SIZE]; WORD wScore; } T_Student;

T_Student *ptStudent;

建议:

 使用严格形式定义的、可移植的数据类型,尽量不要使用与具体硬件或软件环境关系密切的变量。

 结构是针对一种事务的抽象,功能要单一,不要设计面面俱到的数据结构。  不同结构间的关系要尽量简单,若两个结构间关系较复杂、密切,那么应合为一个结构。

 结构中元素的个数应适中。若结构中元素个数过多可考虑依据某种原则把元素组成不同的子结构,以减少原结构中元素的个数。

 仔细设计结构中元素的布局与排列顺序,使结构容易理解、节省占用空间,并减少引起误用现象,对于结构中未用的位明确地给予保留。

 结构的设计要尽量考虑向前兼容和以后的版本升级,并为某些未来可能的应用保留余地(如预留一些空间等)。

 合理地设计数据并使用自定义数据类型,尽量减少没有必要的数据类型默认转换与强制转换。

 当声明数据结构时,必须考虑机器的字节顺序、使用的位域及字节对齐等问题 。

六、表达式与语句

1.在表达式中使用括号,使表达式的运算顺序更清晰。

if (((iYear % 4 == 0) && (iYear % 100 != 0)) || (iYear % 400 == 0)) 2.避免表达式中的附加功能,不要编写太复杂的复合表达式。 3.不可将浮点变量用“==”或“!=”与任何数字比较。

4.应当将指针变量用“==”或“!=”与NULL比较。

5.在switch语句中,每一个case分支必须使用break结尾,最后一个分支必须是default分支。

6.不可在for 循环体内修改循环变量,防止for 循环失去控制。

建议:

 循环嵌套次数不大于3次。

 do while语句和while语句仅使用一个条件。  当switch语句的分支比较多时,采用数据驱动方式。

如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面。

 for语句的循环控制变量的取值采用“半开半闭区间”写法。  在进行“==”比较时,将常量或常数放在“==”号的左边。

七、参数

1.如果函数没有参数,则用void填充。

void SetValue(int iWidth, int iHeight); float GetValue(void); 2.如果参数是指针,且仅作输入用,则应在类型前加const。(防止该指针在函数体内被意外修改。)

3.当结构变量作为参数时,应传送结构的指针而不传送整个结构体,并且不得修改结构中的元素,用作输出时除外。

4.不要省略返回值的类型,如果函数没有返回值,那么应声明为void类型。5.对于有返回值的函数,每一个分支都必须有返回值。(为了保证对被调用函数返回值的判断,有返回值的函数中的每一个退出点都需要有返回值)

6.对输入参数的正确性和有效性进行检查。7.防止将函数的参数作为工作变量。

void SumData(int iNum, int *piData, int *piSum ) {

int iCount ;

int iSumTmp; // 存储“和”的临时变量

iSumTmp = 0;

for (iCount = 0; iCount

{

iSumTmp += piData[iCount]; }

*piSum = iSumTmp; } 反例:

void SumData(int iNum, int *piData, int *piSum ) {

int iCount;

*piSum = 0;

for (iCount = 0; iCount

*piSum += piData[iCount]; // piSum成了工作变量,不好。 } }

8.必须对所调用函数的错误返回值进行处理。(函数返回错误,往往是因为输入的参数不合法,或者此时系统已经出现了异常。如果不对错误返回值进行必要的处理,会导致错误的扩大,甚至导致系统的崩溃。)

八、可靠性

1.在程序编制之前,必须了解编译系统的内存分配方式,特别是编译系统对不同类型的变量的内存分配规则,如局部变量在何处分配、静态变量在何处分配等。

2.防止内存操作越界。

3.必须对动态申请的内存做有效性检查,并进行初始化;动态内存的释放必须和分配成对以防止内存泄漏,释放后内存指针置为NULL。

4.变量在使用前应初始化,防止未经初始化的变量被引用。5.指针类型变量必须初始化为NULL。 6.指针不要进行复杂的逻辑或算术操作。

7.如果指针类型明确不会改变,应该强制为const类型的指针,以加强编译器的检查。8.减少指针和数据类型的强制类型转化。 9.移位操作一定要确定类型。

10.对变量进行赋值时,必须对其值进行合法性检查,防止越界等现象发生。11.类中的属性应声明为private,用公有的函数访问。

12.在编写派生类的赋值函数时,注意不要忘记对基类的成员变量重新赋值。13.构造函数应完成简单有效的功能,不应完成复杂的运算和大量的内存管理。 14.不要在栈中分配类的实例,也不要生成全局类实例。

15.正确处理拷贝构造函数与赋值函数。

16.过程/函数中申请的(为打开文件而使用的)文件句柄,在过程/函数退出之前要关闭,除非要把这个句柄传递给其它函数使用。

九、可测试性

1.在同一项目组或产品组内,为准备集成测试和系统联调,要有一套统一的调测开关及相应信息输出函数,并且要有详细的说明。统一的调试接口和输出函数由模块设计和测试人员根据项目特性统一制订,由项目系统人员统一纳入系统设计中。

2.在同一个项目组或产品组内,调测打印出的信息串要有统一的格式。信息串中应当包含所在的模块名(或源文件名)及行号等信息。

3.在编写代码之前,应预先设计好程序调试与测试的方法和手段,并设计好各种调测开关及相应测试代码(如打印函数等)。

十、断言与错误处理

1.整个软件系统应该采用统一的断言。如果系统不提供断言,则应该自己构造一个统一的断言供编程时使用。

2.使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况之间的区别,后者是必然存在的并且是一定要作出处理的。

3.指向指针的指针及更多级的指针必须逐级检查。4.对较复杂的断言加上明确的注释。

5.用断言保证没有定义的特性或功能不被使用。

6.用调测开关来切换软件的DEBUG版和RELEASE版,而不要同时存在RELEASE版本和DEBUG版本的不同源文件,以减少维护的难度。

7.正式软件产品中应把断言及其它调测代码去掉(即把有关的调测开关关掉)。8.在软件系统中设置与取消有关测试手段,不能对软件实现的功能等产生影响。 9.用断言来检查程序正常运行时不应发生但在调测时有可能发生的非法情况。

推荐第9篇:一点Duilib编程总结

一点Duilib编程总结

1.duilib简介

duilib是一个开源的DirectUI界面库,简洁但是功能强大。而且还是BSD的license,所以即便是在商业上,大家也可以安心使用。

现在大家可以从这个网站获取到他们所有的源码:/p/duilib/ 为了让我们能更简单的了解其机制,我们按照如下顺序一步一步的来对他进行观察:

工具库:用于支撑整个项目的基础

控件库:这是dui最关键的部分之一,相信也是大家最关注的部分之一,另外这里也来看看它是如何管理这些控件的

消息流转:有了控件库,我们需要将Windows窗口的原生消息流转给这些控件,另外在这里也来看看Focus,Capture等等的实现

资源组织和皮肤加载:有了上面所有的这些,我们再来看看它是如何自动创建皮肤的

简单使用:最后,来看看到底要如何使用它

以下是duilib工程带的一副总体设计图,在看代码之前看看这幅图,对看代码会很有帮助。

duilib: 2.工具库

由于duilib没有对外部的任何库进行依赖,所以在其内部实现了很多用于支撑项目的基础类,这些类分布在Util文件夹中:

UI相关:CPoint/CSize/CDuiRect 简单容器:CStdPtrArray/CStdValArray/CStdString/CStdStringPtrMap 上面这些类看名字就基本能够理解其具体的含义了,当然除了基本的基础库,还有一些和窗口使用相关的工具的封装:

窗口工具:WindowImplBase,这个工具我们在这里不详述,后面会再次提到。 3.控件库

控件库在duilib的实现中被分为了两块:Core和Control:

Core中包含的是所有控件公用的部分,里面主要是一些基类和绘制的封装。 Control中包含的就是各个不同的控件的行为了。 Core部分和控件相关的类图非常简单: duilib-core: 3.1.控件基类:CControlUI CControlUI在整个控件体系中非常重要,它是所有控件的基类,也是组成控件树的基本元素,控件树中所有的节点都是一个CControlUI。

他基本包括了所有控件公共的属性,如:位置,大小,颜色,是否有焦点,是否被启用,等等等等。当然这个类中还提供了非常多的基础函数,用于重载来实现子控件,如获取控件名称和ClaName,是否显示,等等等等。

另外为了方便从XML中直接解析出控件的各个属性,这个类中还在提供了一个SetAttribute的方法,传入字符串的属性名称和值对特定的属性进行设置,内部其实就是挨个比较字符串去完成的,所以平时使用的时候就还是不要使用的比较好了,因为每个属性实际上都有特定的方法来获取和设置。

另外每个控件中还有几个事件管理的对象——CEventSource,这些对象会在特定的时机被触发,如OnInit,调用其中保存的各个回调函数。 3.1.1.控件类型转换

这里我们就碰到一个问题,控件树中的每一个节点都是CControlUI,但是其实这些节点可能是文字,可能是图像,也有可能是列表,那么他怎么在这些控件指针之间进行转换呢?

强制转型不是一个好的选择,duilib中使用的是CControlUI::GetInterface,传入一个字符串,传出指向控件的指针。类似于COM的QueryInterface。

LPVOIDCControlUI::GetInterface(LPCTSTRpstrName) { if(_tcscmp(pstrName,_T(\"Control\"))==0)returnthis; returnNULL; } 3.2.容器基类:CContainerUI 有了基本的控件基类之后,我们就需要容器来将他管理起来,这个容器就是CContainerUI,其内部用一个数组来保存所有的CControlUI的对象,后续的所有工作,就都是基于这个对象来进行的了。

这样在CContainerUI里面,主要实现了一下几个功能: 子控件的查找:CContainerUI::FindControl 子控件的生命周期管理:是否销毁(在Remove的时候自动销毁)/是否延迟销毁(交给CPaintMangerUI去一起销毁)。

滚动条:所有的容器都支持滚动条,在其内部会对键盘和鼠标滚轮事件进行处理(CContainerUI::DoEvent),对其内部所有的元素调整位置,最后在绘制的时候实现滚动的效果

绘制:由于容器中有很多元素,所以为了加快容器的绘制,绘制的时候会获取其真正需要绘制的区域,如果子控件不在此区域中,那么就不予绘制了

3.3.控件实现

有了普通的基类和容器的基类之后,我们就可以在其之上搭建控件了。其类图大致如下:

duilib-control: 3.3.1.基本控件

duilib实现了非常多的基本控件,他们分布在Control文件夹下,每一个头文件就是一个控件,主要有:

CLabelUI/CTextUI/CEditUI/CRichEditUI CButtonUI/CCheckBoxUI/COptionUI(RadioButton) CScrollBarUI/CProgreUI/CSliderUI CListUI CDateTimeUI/CActiveXUI/CWebBrowserUI 3.3.2.Layout 除了基本控件之外,duilib为了辅助大家对界面元素进行布局,还在中间实现了专门用于Layout的元素:

CChildLayoutUI CHorizontalLayoutUI/CVerticalLayoutUI/CTileLayoutUI:纵向排列,横向排列格子排列

CTabLayoutUI:Tab 3.3.3.控件绘制

绘制控件实际上有很多代码都是可以抽取出来的,比如:九宫格拉伸图片,平铺图片等等工作,我们实际上都不需要每次都去重写。所以这部分代码被抽取出来,形成了CRenderEngine,这个类在Core/UIRender下。在这个里面,我们可以看到很多的用于绘制方法。

claUILIB_APICRenderEngine { public: //......staticvoidDrawLine(HDChDC,constRECT&rc,intnSize,DWORDdwPenColor); staticvoidDrawRect(HDChDC,constRECT&rc,intnSize,DWORDdwPenColor); staticvoidDrawRoundRect(HDChDC,constRECT&rc,intwidth,intheight,intnSize,DWORDdwPenColor); staticvoidDrawText(HDChDC,CPaintManagerUI*pManager,RECT&rc,LPCTSTRpstrText,\\ DWORDdwTextColor,intiFont,UINTuStyle); staticvoidDrawHtmlText(HDChDC,CPaintManagerUI*pManager,RECT&rc,LPCTSTRpstrText, DWORDdwTextColor,RECT*pLinks,CDuiString*sLinks,int&nLinkRects,UINTuStyle); //......}; 3.4.控件管理:CPaintManagerUI 当所有这些基本的控件都准备好了之后,我们就只要将这些控件管理起来,这样一个基本的控件库就完成了,而这个管理就是CPaintManagerUI来负责的。

在duilib中,一个Windows的原生窗口和一个CPaintManagerUI一一对应。其主要负责如下几个内容,后面会分开来细说,现在先了解一个概念就行:

控件管理 资源管理

转化并分发Windows原生的窗口消息 为了实现上面这些功能,其中有几个用于管理控件和资源的关键的数据结构: m_pRoot:保存根控件的节点

m_mNameHash:保存控件名称Hash和控件对象指针的关系

m_mOptionGroup:保存控件相关的Group,这个Group并不是TabOrder,他用于实现Option控件

m_aCustomFonts:用来管理字体资源 m_mImageHash:用来管理图片资源

这些结构基本都可以看作是一堆列表和Map,这样可以用其来实现控件和资源的管理了。

4.消息流转 有了控件,现在我们的问题是,如何将原生的窗口消息分发给界面中所有的控件,使其行为和原生的一样呢?

4.1.窗口基础类:CWindowWnd 在duilib中,用来表示窗口的最基础的类是CWindowWnd,在这个类中实现了如下基本的内容:

原生窗口的创建(CWindowWnd::Create) Subcla(CWindowWnd::Subcla)

最基本的消息处理函数(CWindowWnd::__WndProc)和消息分发(CWindowWnd::HandleMeage)

模态窗口(CWindowWnd::ShowModal)

duilib通过这个类,将原生窗口的消息分发给其派生类,最后传给整个控件体系。另外在duilib中,需要进行消息处理的基本控件,都是从这个类继承出来的。

4.2.消息分发

一旦我们使用CWindowWnd类创建了窗口之后,消息就会通过CWindowWnd::HandleMeage进行分发,我们可以和WTL等其他的库一样,在此对原始的窗口消息进行处理。

LRESULTCWindowWnd::HandleMeage(UINTuMsg,WPARAMwParam,LPARAMlParam) { return�0�2::CallWindowProc(m_OldWndProc,m_hWnd,uMsg,wParam,lParam); } 当然如果我们觉得这样麻烦,我们也可以使用CPaintManagerUI来对其进行默认处理。我们上面提到CPaintManagerUI还会对所有的控件进行管理,这样,消息就传递给了窗口内部特定的控件了。

这些默认处理集中在CPaintManagerUI::MeageHandler()中,其内部会对很多窗口消息进行处理,并将其分发到对应的控件上去,比如对WM_LBUTTONDOWN的处理。

caseWM_LBUTTONDOWN: { //......POINTpt={GET_X_LPARAM(lParam),GET_Y_LPARAM(lParam)}; m_ptLastMousePos=pt; CControlUI*pControl=FindControl(pt); //......TEventUIevent={0}; event.Type=UIEVENT_BUTTONDOWN; //......pControl->Event(event); } break; 4.2.1.Focus&Capture 通过上面这个最简单的例子,我们基本可以猜到duilib对Focus和Capture的处理方法了:用一个成员变量保存对应的控件,在消息到达时直接转发消息。

在CPaintMainagerUI中,大家可以找到一个成员变量:m_pFocus,这个就是用来保存焦点控件的。在WM_KEYDOWN等键盘消息发生时,duilib就会模拟Windows行为,将消息直接转给当前Focus的控件。 caseWM_KEYDOWN: { if(m_pFocus==NULL)break; TEventUIevent={0}; event.Type=UIEVENT_KEYDOWN; //...m_pFocus->Event(event); //...} break; 但是很奇怪的是,duilib里面并没有对Capture做处理,分发鼠标消息到对应的子控件上,可能是还没有完善的原因。

4.2.2.其他消息分发方式 除了Event以外,CPaintManagerUI还提供了其他几种用于处理消息的方法: Notifier:在窗口上处理一些控件的逻辑,可以将其看成和WM_NOTIFY差不多的功能

PreMeageFilter:消息预处理,这个大家肯定不陌生了。 PostPaint:绘制后的回调

TranslateAccelerator:快捷键的处理

这里需要注意的是:PreMeageFilter和TranslateAccelerator是通过全局数组来实现的,这并不符合多线程的窗口编程要求,所以duilib对多线程的支持并不是很好!

4.3.WindowImplBase 为了简化duilib的使用,库中提供了一个非常方便的工具:WindowImplBase。 这个类将常用的功能封装在其内部,比如Notifier和PreMeageFilter,并在其中提供了各种默认的虚回调函数,供派生类重载。通过这个类,我们可以非常方便的来实现一个简单的界面。

claUILIB_APIWindowImplBase :publicCWindowWnd ,publicCNotifyPump ,publicINotifyUI ,publicIMeageFilterUI ,publicIDialogBuilderCallback { //...... virtualUINTGetClaStyle()const; //...... virtualLRESULTOnClose(UINT/*uMsg*/,WPARAM/*wParam*/,LPARAM/*lParam*/,BOOL&bHandled); virtualLRESULTOnDestroy(UINT/*uMsg*/,WPARAM/*wParam*/,LPARAM/*lParam*/,BOOL&bHandled); //......5.资源组织和皮肤加载

好了,现在我们已经有了控件管理和控件库,现在我们需要让UI框架来帮忙组织这些资源,并且自动的来帮我们创建皮肤,减少我们的开发量。

duilib中的皮肤文件主要有几个部分组成: xml描述文件:描述窗口中控件的布局和样式 各种资源如图片

我们把这些资源放在一个文件夹中,这样就形成了基础的皮肤包。当然我们还可以将其组合成一个zip包,从而加快IO访问,但是修改起来就会相对麻烦。所以我们可以在debug中使用前者,而在release中使用后者。

我们可以在bin\\skin下面找到duilib中自带demo的所有的皮肤包。 皮肤中,最关键的部分就是这个xml描述文件了,一个xml描述文件对应着一个窗口的信息,如:控件的类型和样式等等。为了有一个直观的印象,我截取了duilib中ListDemo的xml描述文件的一部分放在这里:

<?xmlversion=\"1.0\"encoding=\"utf-8\"?> ... ...

... 为了通过配置文件自动创建皮肤,duilib提供了一个类:CDialogBuilder(DuiLib\\Core\\UIDlgBuilder.h)。

这个类提供了从皮肤包(文件夹和zip格式)中的xml中创建皮肤的方法:CDialogBuilder::Create。内部实际上就是一个xml的解析,依次创建各式控件。 除了创建控件,这个类还将一些可以复用的资源提取出来放入CPaintManagerUI中统一管理,如字体和图片等等。

6.简单使用

由于项目里面实在是带了太多太多的demo,而且在duilib的工程中,还有一个doc的目录,里面也非常详细的描述了要如何使用duilib来创建一个简单的工程。

所以关于duilib的简单使用,这里就不再详述了,这里就只列出GameDemo的main函数,这个函数非常的简单,但是已经基本可以表达了。

intAPIENTRYWinMain(HINSTANCEhInstance,HINSTANCE/*hPrevInstance*/,LPSTR/*lpCmdLine*/,intnCmdShow) { CPaintManagerUI::SetInstance(hInstance); CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath()+_T(\"skin\")); CPaintManagerUI::SetResourceZip(_T(\"GameRes.zip\")); HRESULTHr=�0�2::CoInitialize(NULL); if(FAILED(Hr))return0; CGameFrameWnd*pFrame=newCGameFrameWnd(); if(pFrame==NULL)return0; pFrame->Create(NULL,_T(\"\"),UI_WNDSTYLE_FRAME,0L,0,0,1024,738); pFrame->CenterWindow(); �0�2::ShowWindow(*pFrame,SW_SHOWMAXIMIZED); CPaintManagerUI::MeageLoop(); �0�2::CoUninitialize(); return0; } 7.总结

总的来说,duilib还是一个很小巧好用的皮肤引擎的,但是他仍然有其不好的地方:对多线程的支持不好,不支持动画。但是无论如何,它还是不错的,所以如果你已经看到了这里,那么接下来跑到vs里面建一个工程,玩一把才是正经事~

推荐第10篇:智能车编程总结

智能车编程总结

智能车核心是飞思卡尔xs128芯片,尽可能利用单片机里的硬件资源是程序的核心。程序理应要有漂亮的算法,但由于智能车任务不复杂,合理管理和配置硬件资源才是最重要的。  编程步骤(关键找到程序框架)

I.程序第一步:通过配置寄存器来编写单片机资源的底层程序。

A.配置总线时钟频率(通过锁相环电路)

B.配置输出PWM(脉宽调制波)功能(占空比)

C.配置定时中断功能(PIT定时中断)

D.配置输入捕捉功能(脉冲累加器)

E.配置基本输入输出端口的电平

II.程序第二步:利用底层程序编写各种其他硬件的驱动程序

A.驱动电机、舵机(通过PWM波)

B.驱动传感器发射和接收(通过IO端口和PWM波)。

C.驱动码盘测速装置并接收。(通过输入捕捉功能)。

III.程序第三步:连接各种硬件,顺序完成巡线任务。

IV.程序第四位:利用控制思想,不断调试和优化程序。

 编程思想(程序关键要清楚)

I.尽量使各种功能都封装成函数。

II.程序分层次,不同层次尽量写在不同文档中(函数层层调用)。

III.主函数中简单明了,思路、层次分明。

IV.各种工具函数同一管理。(延时,绝对值,取最大最小值等)

V.重点参数使用全局变量方便调试。

 控制方法:使用PID控制方法(关键在调试)

I.电机调试PID

(以预设速度与实际检测的速度的差值为偏差值error)

II.摇头舵机PID

(以传感器偏离中心距离为偏差值error)

III.转向舵机PID

(以摇头舵机偏离中心的角度为偏差值error)

其他一些都是根据实际情况的一些细节处理,比如过十字交叉线,出道检测,起点检测等。

第11篇:网络编程常见问题总结

网络编程常见问题总结

网络编程常见问题总结 串讲(一)

网络编程常见问题总结

在网络程序中遇到的一些问题进行了总结, 这里主要针对的是我们常用的TCP socket相关的总结, 可能会存在错误, 有任何问题欢迎大家提出.

对于网络编程的更多详细说明建议参考下面的书籍

《UNIX网络编程》 《TCP/IP 详解》 《Unix环境高级编程》

非阻塞IO和阻塞IO:

在网络编程中对于一个网络句柄会遇到阻塞IO和非阻塞IO的概念, 这里对于这两种socket先做一下说明

基本概念:

socket的阻塞模式意味着必须要做完IO操作(包括错误)才会返回。 非阻塞模式下无论操作是否完成都会立刻返回,需要通过其他方式来判断具体操作是否成功。

设置:

一般对于一个socket是阻塞模式还是非阻塞模式有两种方式 fcntl设置和recv,send系列的参数.

fcntl函数可以将一个socket句柄设置成非阻塞模式:

flags = fcntl(sockfd, F_GETFL, 0); fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); 设置之后每次的对于sockfd的操作都是非阻塞的

recv, send函数的最后有一个flag参数可以设置成MSG_DONTWAIT临时将sockfd设置为非阻塞模式,而无论原有是阻塞还是非阻塞。 recv(sockfd, buff, buff_size, MSG_DONTWAIT); send(scokfd, buff, buff_size, MSG_DONTWAIT); 区别:

读:

读本质来说其实不能是读,在实际中, 具体的接收数据不是由这些调用来进行,是由于系统底层自动完成的,read也好,recv也好只负责把数据从底层缓冲copy到我们指定的位置.对于读来说(read, 或者 recv) ,在阻塞条件下如果没有发现数据在网络缓冲中会一直等待,当发现有数据的时候会把数据读到用户指定的缓冲区,但是如果这个时候读到的数据量比较少,比参数中指定的长度要小,read并不会一直等待下去,而是立刻返回。read的原则是数据在不超过指定的长度的时候有多少读多少,没有数据就会一直等待。所以一般情况下我们读取数据都需要采用循环读的方式读取数据,一次read完毕不能保证读到我们需要长度的数据,read完一次需要判断读到的数据长度再决定是否还需要再次读取。在非阻塞的情况下,read的行为是如果发现没有数据就直接返回,如果发现有数据那么也是采用有多少读多少的进行处理.对于读而言, 阻塞和非阻塞的区别在于没有数据到达的时候是否立刻返回.

recv中有一个 MSG_WAITALL的参数 recv(sockfd, buff, buff_size, MSG_WAITALL), 在正常情况下 recv是会等待直到读取到buff_size长度的数据,但是这里的WAITALL也只是尽量读全,在有中断的情况下recv还是可能会 被打断,造成没有读完指定的buff_size的长度。所以即使是采用recv + WAITALL参数还是要考虑是否需要循环读取的问题,在实验中对于多数情况下recv还是可以读完buff_size,所以相应的性能会比直接read 进行循环读要好一些。不过要注意的是这个时候的sockfd必须是处于阻塞模式下,否则WAITALL不能起作用。

写:

写的本质也不是进行发送操作,而是把用户态的数据copy到系统底层去,然后再由系统进行发送操作,返回成功只表示数据已经copy到底层缓冲,而不表示数据以及发出,更不能表示对端已经接收到数据. 对于write(或 者send)而言,在阻塞的情况是会一直等待直到write完全部的数据再返回.这点行为上与读操作有 所不同,究其原因主要是读数据的时候我们并不知道对端到底有没有数据,数据是在什么时候结束发送的,如果一直等待就可能会造成死循环,所以并没有去进行这方面的处理;而对于write, 由于需要写的长度是已知的,所以可以一直再写,直到写完.不过问题是write是可能被打断造成write一次只write一部分数据, 所以write的过程还是需要考虑循环write, 只不过多数情况下一次write调用就可能成功. 非阻塞写的情况下,是采用可以写多少就写多少的策略.与读不一样的地方在于,有多少读多少是由网络发送的那一端是否有数据传输到为标准,但是对于可以写多少是由本地的网络堵塞情况为标准的,在网络阻塞严重的时候,网络层没有足够的内存来进行写操作,这时候就会出现写不成功的情况,阻塞情况下会尽可能(有可能被中断)等待到数据全部发送完毕,对于非阻塞的情况就是一次写多少算多少,没有中断的情况下也还是会出现write到一部分的情况.

网络编程常见问题总结 串讲(二)

超时控制:

对于网络IO,我们一般情况下都需要超时机制来避免进行操作的线程被handle住,经典的做法就是采用select+非阻塞IO进行判断,select在超时时间内判断是否可以读写操作,然后采用非堵塞读写,不过一般实现的时候读操作不需要设置为非堵塞,上面已经说过读操作只有在没有数据的 时候才会阻塞,select的判断成功说明存在数据,所以即使是阻塞读在这种情况下也是可以做到非阻塞的效果,就没有必要设置成非阻塞的情况了.

这部分的代码可以参考ullib中ul_sreado_ms_ex和ul_swriteo_ms_ex.

采用ul_sreado_ms_ex读数据也是不能保证返回大于0就一定读到指定的数据长度, 对于读写操作, 都是需要判断返回的读长度或者写长度是否是需要的长度, 不能简单的判断一下返回值是否小于0.对于ul_sreado_ms_ex的情况如果出现了发送端数据发送一半就被close掉的情况就有可能导致接收端读不到完整的数据包. errno 只有在函数返回值为负的时候才有效,如果返回0或者大于0的数, errno 的结果是无意义的.有些时候 会出现read到0, 但是我们认为是错误的情况然后输出errno造成误解,一般建议在这种情况要同时输出返回值和errno的结果,有些情况由于只有errno造成了对于问 题的判断失误。

长连接和短连接的各种可能的问题及相应的处理

这里主要是发起连接的客户端的问题,这里列出的问题主要是在采用同步模型的情况下才会存在的问题.

短连接:

采用短连接的情况一般是考虑到下面的一些问题: 后端服务的问题, 考虑最简单的情况下一个线程一个连接, 如果这个连接采用了长连接那么就需要我们处理连接的线程和后端保持一一对应,然后按照某些原则进行处理(n对n的关系), 但由于一方面服务器可能增加,这样导致需要前后端保持一致,带来了更多的麻烦,另一方面线程数上不去对应处理能力也会产生影响,而短连接每次连接的时候只 需要关注当前的机器,问题相对会少一些.其实这个问题可以采用连接池的方式来解决,后面会提到.不需要考虑由于异常带来的脏数据。负载均衡方面可以简单考虑, 无论线程数是多少还是后端服务器的数量是多少都没有关系, 每次考虑单个连接就可以了.当然如果负载逻辑简单,并且机器相对固定,一个线程一个长连接问题也不大.

规避一些问题, 在过去有些情况下出现长连接大延时,数据没响应等问题, 测试的时候发现换短连接问题就解决了,由于时间关系就没有再继续追查, 事实上这些问题现在基本上都已经定位并且有相关的解决方案了.

不足:

效率不足, 由于连接操作一般会有50ns~200ns的时间消耗,导致短连接需要消耗更多的时间会产生TIME_WAIT问题,需要做更多的守护

长连接:

长连接相比短连接减少了连接的时间消耗, 可以承受更高的负载.但在使用的时候需要考虑一些问题脏数据, 在一些特殊情况(特别是逻辑错误的情况下) 会存在一些我们并不需要的数据.这个时候的处理比较安全的方式是一旦检测到就关闭连接, 检测的方式在在发起请求前用前面为什么socket写错误,但用recv检查依然成功? 介绍的方式进行检查.不过有些程序会采用继续读把所有不需要的数据读完毕(读到 EAEGIN), 不过这种方式过分依赖逻辑了,存在了一定的风险.不如直接断开来的简单 后端连接, 前面也提到了 在这种情况我们一般会采用连接池的方式来解决问题比如(public/connectpool中就可以维护不同的连接,使每个线程都可以均匀的获取到句 柄) 服务端的处理这个时候需要考虑连接的数量,简单的方式就是一个长连接一个线程, 但是线程也不能无限增加( 增加了,可能造成大量的上下文切换使的性能下降).我们一般在长连接的情况采用pendingpool的模型, 通过一个异步队列来缓冲, 这样不需要考虑客户端和服务端的线程数问题,可以任意配置(可以通过线下测试选择合适的线程数)

一些特殊的问题, 主要是长连接的延时 在后面的FAQ中会有详细的说明.

一般来说,对于我们多数的内部业务逻辑都是可以采用长连接模式,不会产生太多的问题.

网络编程常见问题总结 串讲(三)

主要线程模型优缺点和注意事项

这里所列出的线程模型,目前在我们的public/ub下都有相关的实现,在 ubFAQ中也有相关的说明,这里主要针对这些模 型的使用做相关的说明

最简单的线程模型 同时启动多个线程,

每个线程都采用accept的方式进行阻塞获取连接(具体实现上一般是先select在accept, 一方面规避低内核的惊群效应,另一方面可以做到优雅退出).多个线程竞争一个连接, 拿到连接的线程就进行自己的逻辑处理, 包括读写IO全部都在一个线程中进行.短连接每次重新accept, 长连接,第一次的时候accept然后反复使用.一般来说在总连接数很少的情况下效果会比较好,相对适用于少量短连接(可以允许比线程数多一些)和不超过线程总数的长连接(超过的那些连接,除非 accept的连接断开,否则不可能会有线程对它进行accept). 但如果同一时候连接数过多会造成没有工作线程与客户端进行连接,客户端会出现大量的连接失败, 因为这个时候线程可能存在不能及时accept造成超时问题, 在有重试机制的情况下可能导致问题更糟糕.有些程序在出现几次超时之后会长时间一直有连接超时往往就是在这种情况下发生的. 这种模型的最大优点在于编写简单, 在正常情况下工作效果不错.在public/ub中的xpool就是属于这种模型,建议针对连接数少的服务进行使用,比如一些一对一的业务逻辑.

生产者消费者模型

普通线程模型在长连接方面存在使用限制(需要对于线程数进行变化, 而线程又不是无限的), 短连接在处理同时大量连接(比如流量高峰期)的时候存在问题.

生产者消费者模型是可以把这种影响减少. 对于有数据的活动连接放到异步队列中, 其他线程竞争这个队列获取句柄然后进行相关的操作.由于accept是专门的线程进行处理, 出现被handle的情况比较少,不容易出现连接失败的情况.在大流量的情况下有一定的缓冲,虽然有些请求会出现延时,但只要在可以接受的范围内,服务还 是可以正常进行.一般来说队列的长度主要是考虑可以接受的延时程度. 这种模式也是我们现在许多服务比较常用的模型.可以不用关心客户端和服务的线程数对应关系,业务逻辑上也是比较简单的。

但这种模式在编程的 时候,对于长连接有一个陷阱,判断句柄是否可读写以前一般采用的是select, 如果长连接的连接数比工作线程还少,当所有的连接都被处理了,有连接需要放回pool中,而这个时候如果正常建立连接的监听线程正好处于select状 态,这个时候必须要等到 select超时才能重新将连接放入select中进行监听,因为这之前被放入select进行监听的处理socket为空,不会有响应,这个时候由于时 间的浪费造成l长连接的性能下降。一般来说某个连接数少,某个连接特别活跃就可能造成问题.过去的一些做法是控制连接数和服务端的工作线程数以及通过监听一个管道fd,在工作线程结束每次都激活这个fd跳出这次select来控制。现在的2.6 内核中的epoll在判断可读写的时候不会存在这个问题(epoll在进行监听的时候,其它线程放入或者更改, 在epoll_wait的时候是可以马上激活的), 我们现在的服务多采用epoll代替select来解决这个, 但是主要的逻辑没有变化.ub_server中epool和public/ependingpool都是采用种模式

异步模型

这里只做一些简单的介绍。上 面两者模型本质都是同步的处理业务逻辑,在一个线程中处理了读请求,业务逻辑和写回响应三个过程(很多业务更复杂,但是都是可以做相应的拆封的), 但是读和写这两个IO的处理往往需要阻塞等待, 这样造成了线程被阻塞, 如果要应付慢连接(比如外围抓取等待的时间是秒级的甚至更多), 在等待的时候其实CPU没有干多少事情, 这个时候就造成了浪费.一种考虑是增加线程数,通过提高并发来解决这个问题, 但是我们目前的线程数还是有限的,不可能无限增加.而且线程的增加会带来cpu对于上下文切换的代价,另一方面多个线程从一个队列中获取可用连接, 这里存在互斥线程多的时候会导致性能下降,当然这里可以通过把一个队列改多队列减少互斥来实现. 引入异步化的处理, 就是把对于IO的等待采用IO复用的方式,专门放入到一个或者若干个线程中去, 处理主逻辑的程序可以被释放出来, 只有在IO处理完毕才进行处理, 这样可以提高CPU的使用率,减少等待的时间.一般情况下几个线程(一般和CPU的核数相当)可以应付很大的流量请求 public/kylin , ub/ub(ub事件模型)都是基于纯异步思想的异步框架。而ub中的appool是简化版本将原本ub框架中网络IO处理进行了异步化,不过目前只支持 采用nshead头的模式。 网络编程常见问题总结 串讲(四)

为什么网络程序会没有任何预兆的就退出了

一般情况都是没有设置忽略PIPE信号 ,在我们的环境中当网络触发broken pipe (一般情况是write的时候,没有write完毕, 接受端异常断开了), 系统默认的行为是直接退出。在我们的程序中一般都要在启动的时候加上 signal(SIGPIPE, SIG_IGN); 来强制忽略这种错误

write出去的数据, read的时候知道长度吗?

严格来说, 交互的两端, 一端write调用write出去的长度, 接收端是不知道具体要读多长的.这里有几个方面的问题

write 长度为n的数据, 一次write不一定能成功(虽然小数据绝大多数都会成功), 需要循环多次write ,write虽然成功,但是在网络中还是可能需要拆包和组包, write出来的一块数据, 在接收端底层接收的时候可能早就拆成一片一片的多个数据包.TCP层中对于接收到的数据都是把它们放到缓冲中, 然后read的时候一次性copy, 这个时候是不区分一次write还是多次write的。所以对于网络传输中 我们不能通过简单的read调用知道发送端在这次交互中实际传了多少数据.一般来说对于具体的交互我们一般采取下面的方式来保证交互的正确,事先约定好长度, 双方都采用固定长度的数据进行交互, read, write的时候都是读取固定的长度.但是这样的话升级就必须考虑两端同时升级的问题。特殊的结束符或者约定结束方式, 比如http头中采用连续的/r/n来做头部的结束标志.也有一些采用的是短连接的方式, 在read到0的时候,传输变长数据的时候一般采用定长头部+变长数据的方式, 这个时候在定长的头部会有一个字段来表示后面的变长数据的长度, 这种模式下一般需要读取两次确定长度的数据.我们现在内部用的很多都是这样的模式.比如public/nshead就是这样处理, 不过nshead作为通用库另外考虑了采用 通用定长头+用户自定义头+变长数据的接口。

总的来说read读数 据的时候不能只通过read的返回值来判断到底需要读多少数据, 我们需要额外的约定来支持, 当这种约定存在错误的时候我们就可以认为已经出现了问题.另外对于write数据来说, 如果相应的数据都是已经准备好了那这个时候也是可以把数据一次性发送出去,不需要调用了多次write.一般来说write次数过多也会对性能产生影响,另一个问题就是多次连续可能会产生延时问题,这个参看下面有关长连接延时的部分问题.

小提示

上面提到的都是TCP的情况, 不一定适合其他网络协议.比如在UDP中 接收到连续2个UDP包, 需要分别读来次才读的出来, 不能像TCP那样,一个read可能就可以成功(假设buff长度都是足够的)。 如何查看和观察句柄泄露问题 一般情况句柄只有1024个可以使用,所以一般情况下比较容易出现, 也可以通过观察/proc/进程号/fd来观察。

另外可以采用valgrind来检查, valgrind参数中加上 --track-fds = yes 就可以看到最后退出的时候没有被关闭的句柄,以及打开句柄的位置

为什么socket写错误,但用recv检查依然成功?

首先采用recv检查连接的是基于我们目前的一个请求一个应答的情况对于客户端的请求,逻辑一般是这样 建立连接->发起请求->接受应答->长连接继续发请求

recv检查一般是这样采用下面的方式: ret = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); 通过判断ret 是否为-1并且errno是EAGAIN 在非堵塞方式下如果这个时候网络没有收到数据, 这个时候认为网络是正常的。这是由于在网络交换模式下 我们作为一个客户端在发起请求前, 网络中是不应该存在上一次请求留下来的脏数据或者被服务端主动断开(服务端主动断开会收到FIN包,这个时候是recv返回值为0), 异常断开会返回错误.当然这种方式来判断连接是否存在并不是非常完善,在特殊的交互模式(比如异步全双工模式)或者延时比较大的网络中都是存在问题的,不过对于我们目前内网中的交互模式还是基本适用的.这种方式和socket写错误并不矛盾, 写数据超时可能是由于网慢或者数据量太大等问题, 这时候并不能说明socket有错误, recv检查完全可能会是正确的.一般来说遇到socket错误,无论是写错误还读错误都是需要关闭重连.

为什么接收端失败,但客户端仍然是write成功

这个是正常现象,write数据成功不能表示数据已经被接收端接收导致,只能表示数据已经被复制到系统底层的缓冲(不一定发出), 这个时候的网络异常都是会造成接收端接收失败的.长连接的情况下出现了不同程度的 延时 在一些长连接的条件下, 发送一个小的数据包,结果会发现从数据write成功到接收端需要等待一定的时间后才能接收到, 而改成短连接这个现象就消失了(如果没有消失,那么可能网络本身确实存在延时的问题,特别是跨机房的情况下) 在长连接的处理中出现了延时,而且时间固定,基本都是40ms, 出现40ms延时最大的可能就是由于没有设置TCP_NODELAY 在长连接的交互中,有些时候一个发送的数据包非常的小,加上一个数据包的头部就会导致浪费,而且由于传输的数据多了,就可能会造成网络拥塞的情况, 在系统底层默认采用了Nagle算法,可以把连续发送的多个小包组装为一个更大的数据包然后再进行发送.但是对于我们交互性的应用程序意义就不大了,在这种情况下我们发送一个小数据包的请求,就会立刻进行等待,不会还有后面的数据包一起发送, 这个时候Nagle算法就会产生负作用,在我们的环境下会产生40ms的延时,这样就会导致客户端的处理等待时间过长, 导致程序压力无法上去.在代码中无论是服务端还是客户端都是建议设置这个选项,避免某一端造成延时。所以对于长连接的情况我们建议都需要设置TCP_NODELAY, 在我们的ub框架下这个选项是默认设置的.

小提示:

对于服务端程序而言, 采用的模式一般是

bind->listen ->accept, 这个时候accept出来的句柄的各项属性其实是从listen的句柄中继承, 所以对于多数服务端程序只需要对于listen进行监听的句柄设置一次TCP_NODELAY就可以了,不需要每次都accept一次.

设置了NODELAY选项但还是时不时出现10ms(或者某个固定值)的延时 这种情况最有可能的就是服务端程序存在长连接处理的缺陷.这种情况一般会发生在使用我们的pendingpool模型(ub中的cpool)情况下,在 模型的说明中有提到.由于select没有及时跳出导致一直在浪费时间进行等待. 上面的2个问题都处理了,还是发现了40ms延时?

协议栈在发送包的时候,其实不仅受到TCP_NODELAY的影响,还受到协议栈里面拥塞窗口大小的影响.在连接发送多个小数据包的时候会导致数据没有及时发送出去. 这里的40ms延时其实是两方面的问题: 对于发送端, 由于拥塞窗口的存在,在TCP_NODELAY的情况,如果存在多个数据包,后面的数据包可能会有延时发出的问题.这个时候可以采用 TCP_CORK参数, TCP_CORK 需要在数据write前设置,并且在write完之后取消,这样可以把write的数据发送出去( 要注意设置TCP_CORK的时候不能与TCP_NODELAY混用,要么不设置TCP_NODELAY要么就先取消TCP_NODELAY) 但是在做了上 面的设置后可能还是会导致40ms的延时, 这个时候如果采用tcpdump查看可以注意是发送端在发送了数据包后,需要等待服务端的一个ack后才会再次发送下一个数据包,这个时候服务端出现了延 时返回的问题.对于这个问题可以通过设置server端TCP_QUICKACK选项来解决.TCP_QUICKACK可以让服务端尽快的响应这个ack包. 这个问题的主要原因比较复杂,主要有下面几个方面

当TCP协议栈收到数据的时候, 是否进行ACK响应(没有响应是不会发下一个包的),在我们linux上返回ack包是下面这些条件中的一个接收的数据足够多

处于快速回复模式(TCP_QUICKACK)

存在乱序的包

,如果有数据马上返回给发送端,ACK也会一起跟着发送

如果都不满足上面的条件,接收方会延时40ms再发送ACK, 这个时候就造成了延时。

但是对于上面的情况即使是采用TCP_QUICKACK,服务端也不能保证可以及时返回ack包,因为快速回复模式在一些情况下是会失效(只能通过修改内核来实现) 目前的解决方案只能是通过修改内核来解决这个问题,STL的同学在 内核中增加了参数可以控制这个问题。

会出现这种情况的主要是连接发送多个小数据包或者采用了一些异步双工的编程模式,主要的解决方案有下面几种对于连续的多个小数据包, 尽量把他们打到一个buffer中间, 不过会有内存复制的问题 ( 采用writev方式发送多个小数据包, 不过writev也存在一个问题就是发送的数据包个数有限制,如果超过了IOV_MAX(我们的限制一般是1024), 依然可能会出现问题,因为writev只能保证在IOV_MAX范围内的数据是按照连续发送的。

writev或者大buffer的方式在异步双工模式下是无法工作,这个时候只能通过系统方式来解决。 客户端 不设置TCP_NODELAY选项, 发送数据前先打开TCP_CORK选项,发送完后再关闭TCP_CORK,服务端开启TCP_QUICKACK选项

采用STL修改的内核5-6-0-0,打开相关参数。

网络编程常见问题总结 串讲(五)

TIME_WAIT有什么样的影响?

对于TIME_WAIT的出现具体可以参考>中的章节,总的来说对于一个已经建立的连接如果是主动 close, 那么这个连接的端口(注意:不是socket)就会进入到TIME_WAIT状态,在我们的机器上需要等待60s的时间(有些书上可能提到的是 2MSL,1MSL为1分钟,但我们的linux实现是按照1分钟的).在这一段时间内,这个端口将不会被释放,新建立的连接就无法使用这个端口(连接的时候会报Cannot aign requested addre的错误).可以通过/proc/sys/net/ipv4/ip_local_port_range看到可用端口的范围,我们的机器上一般是32768--61000,不足3W个,这样的结果就是导致如果出现500/s的短连接请求,就会导致端口不够用连接不上。 这种情况一般修改系统参数tcp_tw_reuse或者在句柄关闭前设置SO_LINGER选项来解决,也可以通过增大 ip_local_port_range来缓解, 设置SO_LINGER后句柄会被系统立刻关闭,不会进入TIME_WAIT状态,不过在一些大压力的情况还是有可能出现连接的替身,导致数据包丢失。 系统参数/proc/sys/net/ipv4/tcp_tw_reuse设为1 会复用TIME_WAIT状态socket,如果开启,客户端在调用connect调用时,会自动复用TIME_WAIT状态的端口,相比 SO_LINGER选项更加安全。

对于服务器端如果出现TIME_WAIT状态,是不会产生端口不够用的情况,但是TIME_WAIT过多在服务器端还是会占用一定的内存资源,在/proc/sys/net/ipv4/tcp_max_xxx 中我们可以系统默认情况下的所允许的最大TIME_WAIT的个数,一般机器上都是180000, 这个对于应付一般程序已经足够了.但对于一些压力非常大的程序而言,这个时候系统会不主动进入TIME_WAIT状态而且是直接跳过, 这个时候如果去看 dmsg中的信息会看到 \"TCP: time wait bucket table overflow\" , 一般来说这种情况是不会产生太多的负面影响, 这种情况下后来的socket在关闭时不会进入TIME_WAIT状态,而是直接发RST包, 并且关闭socket.不过还是需要关注为什么会短时间内出现这么大量的请求。

小提示: 如果需要设置SO_LINGER选项, 需要在FD连接上之后设置才有效果

: O% B: j3 o/ A

什么情况下会出现CLOSE_WAIT状态?

一般来说,连接的一端在被动关闭的情况下,已经接收到FIN包(对端调用close)后,这个时候如果接收到FIN包的一端没有主动close就会出 现CLOSE_WAIT的情况。 一般来说,对于普通正常的交互,处于CLOSE_WAIT的时间很短,一般的逻辑是检测到网络出错,马上关闭。 但是在一些情况下会出现大量的CLOS_WAIT, 有的甚至维持很长的时间, 这个主要有几个原因:

没有正确处理网络异常, 特别是read 0的情况, 一般来说被动关闭的时候会出现read 返回0的情况。一般的处理的方式在网络异常的情况下就主动关闭连接句柄泄露了,句柄泄露需要关闭的连接没有关闭而对端又主动断开的情况下也会出现这样的问 题。连接端采用了连接池技术,同时维护了较多的长连接(比如ub_client, public/connectpool),同时服务端对于空闲的连接在一定的时间内会主动断开(比如ub_server, ependingpool都有这样的机制).如果服务端由于超时或者异常主动断开, 客户端如果没有连接检查的机制,不会主动关闭这个连接, 比如ub_client的机制就是长连接建立后除非到使用的时候进行连接检查,否则不会主动断开连接。 这个时候在建立连接的一端就会出现CLOSE_WAIT状态。这个时候的状态一般来说是安全(可控的,不会超过最大连接数).在com 的connectpool 2中这种情况下可以通过打开健康检查线程进行主动检查,发现断开后主动close.

网络编程常见问题总结 串讲(六) 顺序发送数据,接收端出现乱序接收到的情况:

网络压力大的情况下,有时候会出现,发送端是按照顺序发送, 但是接收端接收的时候顺序不对.

一般来说在正常情况下是不会出现数据顺序错误的情况, 但某些异常情况还是有可能导致的.

在我们的协议栈中,服务端每次建立连接其实都是从accpet所在的队列中取出一个已经建立的fd, 但是在一些异常情况下,可能会出现短时间内建立大量连接的情况, accept的队列长度是有限制, 这里其实有两个队列,一个完成队列另一个是未完成队列,只有完成了三次握手的连接会放到完成队列中。如果在短时间内accept中的fd没有被取出导致队 列变满,但未完成队列未满, 这个时候连接会在未完成队列中,对于发起连接的一端来说表现的情况是连接已经成功,但实际上连接本身并没有完成,但这个时候我们依然可以发起写操作并且成 功, 只是在进行读操作的时候,由于对端没有响应会造成读超时。对于超时的情况我们一般就把连接直接close关闭了, 但是句柄虽然被关闭了,但是由于TIME_WAIT状态的存在, TCP还是会进行重传。在重传的时候,如果完成队列有句柄被处理,那么此时会完成三次握手建立连接,这个时候服务端照样会进行正常的处理(不过在写响应的 时候可能会发生错误)。从接收上看,由于重传成功的情况我们不能控制,对于接收端来说就可能出现乱序的情况。 完成队列的长度和未完成队列的长度由listen时候的baklog决定((ullib库中ul_tcplisten的最后一个参数),在我们的 linux环境中baklog是完成队列的长度,baklog * 1.5是两个队列的总长度(与一些书上所说的两个队列长度不超过baklog有出入).两个队列的总长度最大值限制是128, 既使设置的结果超过了128也会被自动改为128。128这个限制可以通过 系统参数 /proc/sys/net/core/somaxconn 来更改, 在我们 5-6-0-0 内核版本以后,STL将其提高到2048.另外客户端也可以考虑使用SO_LINGER参数通过强制关闭连接来处理这个问题,这样在close以后就不启用重传机制。另外的考虑就是对重试机制根据 业务逻辑进行改进。

连接偶尔出现超时有哪些可能?

主要几个方面的可能

服务端确实处理能力有限, cpu idel太低, 无法承受这样的压力, 或者 是更后端产生问题

accept队列设置过小,而连接又特别多, 需要增大baklog,建议设置为128这是我们linux系统默认的最大值 由/proc/sys/net/core/somaxconn决定,可以通过修改这个值来增大(由于很多书上这个地方设置为5,那个其实是4.2BSD支 持的最大值, 而不是现在的系统, 不少程序中都直接写5了,其实可以更大, 不过超过128还是按照128来算)

程序逻辑问题导致accept处理不过来, 导致连接队列中的连接不断增多直到把accept队列撑爆, 像简单的线程模型(每个线程一个accept), 线程被其他IO一类耗时操作handle,导致accept队列被撑爆, 这个时候默认的逻辑是服务端丢弃数据包,导致client端出现超时, 但是可以通过打开/proc/sys/net/ipv4/tcp_abort_on_overflow开关让服务端立刻返回失败

当读超时的时候(或者其他异常), 我们都会把连接关闭,进行重新连接,这样的行为如果很多,也可能造成accept处理不过来

异常情况下,设置了SO_LINGER造成连接的ack包被丢失, 虽然情况极少,但大压力下还是有存在的.

当然还是有可能是由于网络异常或者跨机房耗时特别多产生的, 这些就不是用户态程序可以控制的。

另外还有发现有些程序采用epoll的单线模式, 但是IO并没有异步化,而是阻塞IO,导致了处理不及时.

网络编程常见问题总结 串讲(七)

listen的时候的backlog有什么影响?

backlog代表连接的队列, 这里对于内核中其实会维护2个队列

未完成队列, 这个是服务器端接收到连接请求后会先放到这里(第一次握手)这个时候端口会处于SYN_RCVD状态

已完成队列,完成三次握手的连接会放到这里,这个时候才是连接建立

在我们 的linux环境中backlog 一般是被定义为已完成队列的长度, 为完成队列一般是按照以完成队列长度的一半来取, backlog为5, 那么已完成队列为5,未完成队列为3, 总共是8个。 如果这里的8个都被占满了,那么后面的连接就会失败,这里的行为可以由 /proc/sys/net/ipv4/tcp_abort_on_overflow 参数控制, 这个参数打开后队列满了会发送RST包给client端,client端会看到Connection reset by peer的错误(线上部分内核打开了这个参数), 如果是关闭的话, 服务端会丢弃这次握手, 需要等待TCP的自动重连, 这个时间一般比较长, 默认情况下第一次需要3秒钟, 由于我们的连接超时一般都是很小的, client采用ullib库中的超时连接函数, 那么会发现这个时候连接超时了。

长连接和短连接混用是否会有问题?

虽然这种方式并不合适,但严格来说如果程序中做好相关的守护操作(包括一些情况下系统参数的调整) 是不会出现问 题,基本来说在长短连接混用情况下出现的问题都是由于我们的程序存在不同程度上的缺陷造成的.

可能出现的问题:

只要有一端采用了短连接,那么就可以认为总体是短连接模式。

服务端长连接, 客户端短连接

客户端主动关 闭, 服务端需要接收到close的FIN包, read返回0 后才知道客户端已经被关闭。在这一段时间内其实服务端多维护了一个没有必要连接的状态。在同步模式(pendingpool,ub-xpool, ub-cpool, ub-epool)中由于read是在工作线程中,这个连接相当于线程多做了一次处理,浪费了系统资源。如果是IO异步模式(ub/apool或者使用 ependingpool读回调)则可以马上发现,不需要再让工作线程进行处理

服务端如果采用普通线程模型(ub-xpool)那么在异常情况下FIN包如果没有及时到达,在这一小段时间内这个处理线程不能处理业务逻辑。如果出现问题的地方比较多这个时候可能会有连锁反应短时间内不能相应。

服务端为长连接,对于服务提 供者来说可能早期测试也是采用长连接来进行测试,这个时候accept的baklog可能设置的很小,也不会出现问题。 但是一旦被大量短连接服务访问就可能出现问题。所以建议listen的时候baklog都设置为128, 我们现在的系统支持这么大的baklog没有什么问题。

每次总是客户端主动断开,这导致客户端出现了TIME_WIAT的状态,在没有设置SO_LINGER或者改变系统参数的情况下,比较容易出现客户端端口不够用的情况。

服务端短连接,客户端长连接这个时候的问 题相对比较少, 但是如果客户端在发送数据前(或者收完数据后)没有对脏数据进行检查,在写的时候都会出现大量写错误或者读错误,做一次无用的操作,浪费系统资源 一般的建议是采用长连接还是短连接,两端保持一致, 但采用配置的方式并不合适,这个需要在上线的时候检查这些问题。比较好的方式是把采用长连接还是短连接放到数据包头部中。客户端发送的时候标记自己是采用 短连接还是长连接,服务端接收到后按照客户端的情况采取相应的措施,并且告知客户端。特别的如果服务端不支持长连接,也可以告知客户端,服务采用了短连 接

要注意的是,如果采用了一些框架或者库, 在read到0的情况下可能会多打日志,这个对性能的影响可能会比较大。

网络编程常见问题总结 串讲(八)

select, epoll使用上的注意

select, epoll实现上的区别可以参考, 本质上来说 select, poll的实现是一样的,epoll由于内部采用了树的结构来维护句柄数,并且使用了通知机制,省去了轮询的过程,在对于需要大量连接的情况下在CPU上会有一定的优势.

select默认情况下可以支持句柄数 是1024, 这个可以看/usr/include/bits/typesizes.h 中的__FD_SETSIZE, 在我们的编译机(不是开发机,是SCMPF平台的机器)这个值已经被修改为51200, 如果select在处理fd超过1024的情况下出现问题可用检查一下编译程序的机器上__FD_SETSIZE是否正确.

epoll在句柄数的限制没有像select那样需要通过改变系统环境中的宏来实现对更多句柄的支持

另外我们发现有些程序在使用epoll的时候打开了边缘触发模式(EPOLLET), 采用边缘触发其实是存在风险的,在代码中需要很小心,避免由于连接两次数据到达,而被只读出一部分的数据.EPOLLET的本意是在数据情况发生变化的时候激活(比如不可读进入可读状态), 但问题是这个时候如果在一次处理完毕后不能保证fd已经进入了不可读状态(一般来说是读到EAGIN的情况), 后续可能就一直不会被激活.一般情况下建议使用EPOLLET模式.一个最典型的问题就是监听的句柄被设置为EPOLLET, 当同时多个连接建立的时候, 我们只accept出一个连接进行处理, 这样就可能导致后来的连接不能被及时处理,要等到下一次连接才会被激活.

小提示: ullib 中常用的ul_sreado_ms_ex,ul_swriteo_ms_ex内部是采用select的机制,即使是在scmpf平台上编译出来也还是受到 51200的限制,可用ul_sreado_ms_ex2,和ul_swriteo_ms_ex2这个两个接口来规避这个问题,他们内部不是采用 select的方式来实现超时控制的(需要ullib 3.1.22以后版本)

一个进程的socket句柄数只能是1024吗?

答案是否定的, 一台机器上可以使用的socket句柄数是由系统参数 /proc/sys/fs/file-max 来决定的.这里的1024只不 过是系统对于一个进程socket的限制,我们完全可以采用ulimit的参数把这个值增大,不过增大需要采用root权限,这个不是每个工程师都可以采 用的.所以 在公司内采用了一个limit的程序,我们的所有的机器上都有预装这个程序,这个程序已经通过了提权可以以root的身份设置ulimit的 结果.使用的时候 limit ./myprogram 进行启动即可, 默认是可以支持51200个句柄,采用limit -n num 可以设置实际的句柄数.如果还需要更多的连接就需要用ulimit进行专门的操作. 另外就是对于内核中还有一个宏NR_OPEN会限制fd的做大个数,目前这个值是1024*1024

小提示: linux系统中socket句柄和文件句柄是不区分的,如果文件句柄+socket句柄的个数超过1024同样也会出问题,这个时候也需要limit提高句柄数.

ulimit对于非root权限的帐户而言只能往小的值去设置, 在终端上的设置的结果一般是针对本次shell的, 要还原退出终端重新进入就可以了。

用limit方式启动,程序读写的时候出core?

这个又是另外一个问题,前面已经提到了在网络程序中对于超时的控制是往往会采用select或者poll的方式.select的时候对于支持的FD其 实是有上限的,可以看/usr/inclue/sys/select.h中对于fd_set的声明,其实一个__FD_SETSIZE /(8*sizeof(long))的long数组,在默认情况下__FD_SETSIZE的定义是1024,这个可以看 /usr/include/bits/typesizes.h 中的声明,如果这个时候这个宏还是1024,那么对于采用select方式实现的读写超时控制程序在处理超过1024个句柄的时候就会导致内存越界出 core .我们的程序如果是线下编译,由于许多开发机和测试这个参数都没有修改,这个时候就会造成出core,其实不一定出core甚至有些情况下会出现有数据但 还是超时的情况. 但对于我们的SCMPF平台上编译出来的程序是正常的,SCMPF平台上这个参数已经进行了修改,所以有时会出现QA测试没问题,RD 自测有问题的情况。

一台机器最多可以建立多少连接?

理论上来说这个是可以非常多的, 取决于可以使用多少的内存.我们的系统一般采用一个四元组来表示一个唯一的连接{客户端ip, 客户端端口, 服务端ip, 服务端端口} (有些地方算上TCP, UDP表示成5元组), 在网络连接中对于服务端采用的一般是bind一个固定的端口, 然后监听这个端口,在有连接建立的时候进行accept操作,这个时候所有建立的连接都只 用到服务端的一个端口.对于一个唯一的连接在服务端ip和 服务端端口都确定的情况下,同一个ip上的客户端如果要建立一个连接就需要分别采用不同的端,一台机器上的端口是有限,最多65535(一个 unsigned char)个,在系统文件/proc/sys/net/ipv4/ip_local_port_range 中我们一般可以看到32768 61000 的结果,这里表示这台机器可以使用的端口范围是32768到61000, 也就是说事实上对于客户端机器而言可以使用的连接数还不足3W个,当然我们可以调整这个数值把可用端口数增加到6W.但是这个时候对于服务端的程序完全不受这个限制因为它都是用一个端口,这个时候服务端受到是连接句柄数的限制,在上面对于句柄数的说明已经介绍过了,一个 进程可以建立的句柄数是由/proc/sys/fs/file-max决定上限和ulimit来控制的.所以这个时候服务端完全可以建立更多的连接,这个 时候的主要问题在于如何维护和管理这么多的连接,经典的一个连接对应一个线程的处理方式这个时候已经不适用了,需要考虑采用一些异步处理的方式来解决, 毕竟线程数的影响放在那边

小提示: 一般的服务模式都是服务端一个端口,客户端使用不同的端口进行连接,但是其实我们也是可以把这个过程倒过来,我们客户端只用一个端但是服务端确是不同的端 口,客户端做下面的修改原有的方式 socket分配句柄->connect 分配的句柄 改为 socket分配句柄 ->对socket设置SO_REUSEADDR选项->像服务端一样bind某个端口->connect 就可以实现

不过这种应用相对比较少,对于像网络爬虫这种情况可能相对会比较适用,只不过6w连接已经够多了,继续增加的意义不一定那么大就是了.

对于一个不存在的ip建立连接是超时还是马上返回?

这个要根据情况来看, 一般情况connect一个不存在的ip地址,发起连接的服务需要等待ack的返回,由于ip地址不存在,不会有返回,这个时候会一直等到超时才返回。如 果连接的是一个存在的ip,但是相应的端口没有服务,这个时候会马上得到返回,收到一个ECONNREFUSED(Connection refused)的结果。

但是在我们的网络会存在一些有限制的路由器,比如我们一些机器不允许访问外网,这个时候如果访问的ip是一个外网ip(无论是否存在),这个时候也会马上返回得到一个Network is unreachable的错误,不需要等待。

第12篇:NAND FLASH 编程总结

NAND FLASH操作总结

目前NAND FLASH主要是SAMSUNG、TOSHIBA两家公司生产。本文我们主要讨论这两家的产品型号。另外我们还会讨论Hitachi的AND Flash,

为了内容条理起见,我们将分别讨论SAMSUNG、TOSHIBA的Binary Flash,详细说明:

1、各个厂家各个型号Flash的操作时序、以及这些操作在“USB-闪存盘控制器”中的影响;

2、同一厂家不同型号间的区别、不同厂家之间的区别;

然后讨论TOSHIBA的MLC Flash;

最后我们要考虑一下AND Flash的情况,并给出一个初步的结论:我们是否需要支持AND Flash。

通过这些比较,给出一个较明确的结论:我们的“USB-闪存盘控制器”需要支持的Flash操作有那些,时序图如何!

SAMSUNG:

SAMSUNG推出的NAND Flash主要有以下容量:

32Mbit、64Mbit、128Mbit、256Mbit、512Mbit、1Gbit、2Gbit、4Gbit通常,我们把其中的1Gbit、2Gbit、4Gbit叫做“大容量”,其余的则不加强调。

32Mbit、64Mbit、128Mbit、256Mbit、512Mbit的Flash 的特性基本相似: Organization

- Data Register : (512 + 16) Byte Automatic Program and EraseBlock Erase : (8K + 256)Byte/(16K + 512)Byte 528-Byte Page Read OperationSerial Page Acce : 50ns(Min.) Fast Write Cycle TimeBlock Erase time : 2ms(typ.) Flash操作包括基本的七种操作:

Read

1、Read

2、Read ID、Reset、Page Program、Block Erase、Read Status 512Mbit的Flash引入了“Plane”和“Copy-Back”的概念,并为此增加了四种新的操作,但却放弃了128Mbit、256Mbit中“Sequential Data Input”操作,这四种操作是:针对“Plane”的Page Program(Dummy)、Multi_Plane Block Erase、Read Multi_Plane Status、针对“Copy-Back”的Copy_Back Program 1Gbit、2Gbit、4Gbit(大容量)的操作基本相同,但他们比一般Flash多了Copy-Back、Cache Program的功能:

Organization

- Data Register : (2K + 64) BytePage Program : (2K + 64)ByteRandom Acce : 25s(Max.)Program time : 300~400s(typ.)Page Program : (512 + 16)ByteRandom Acce : 25s(Max.)Program time : 200s(typ.) - Block Erase time : 2ms(typ.) Flash操作包括基本的八种操作:

Read

1、Read

2、Read

3、Read ID、Reset、Page Program、Block Erase、Read Status 这里的Read

1、Read

2、Read3命令与SAMSUNG的Read

1、Read2命令功能是相同的。但她的随机读周期要稍微长一些:25s(Max.),其他时间则基本相同。下面我们就每一个操作做详细的介绍!

时序示意图:

第15页

共29页

Latch Timing Diagram for Command/Addre/Data(example.64Mbit)

Command Input Cycle Timing Diagram(example.64Mbit)

Addre Input Cycle Timing Diagram(example.64Mbit)

第16页

共29页

Data Input Cycle Timing Diagram(example.64Mbit)

Serial Read Cycle Timing Diagram(example.64Mbit)

第17页

共29页

Status Read Cycle Timing Diagram(example.64Mbit)

上面我们给出了各个操作的时序示意图,下面就各个参数进行说明!

第18页

共29页

AC CHARACTERISTICS AND RECOMMENDED OPERATING CONDITIONS

(Ta _ 0° to 70°C, VCC _ 2.7 V to 3.6 V)

下面就各个操作的具体时序进行讨论:

第19页

共29页

Read Cycle (1) Timing Diagram

Read Cycle (1) Timing Diagram: When Interrupted by CE

第20页

共29页

Read Cycle (2) Timing Diagram

Read Cycle (3) Timing Diagram

第21页

共29页

Sequential Read (1) Timing Diagram

Sequential Read (2) Timing Diagram

第22页

共29页

Sequential Read (3) Timing Diagram

Auto-Program Operation Timing Diagram

第23页

共29页

Auto Block Erase Timing Diagram

ID Read Operation Timing Diagram 同样,TOSHIBA的512Mbit Flash也是“大容量”的一种过渡类型。她也有她自身的一些特殊操作:“Multi Block Programming”、“ Multi Block Erase”。这些类似于SAMSUNG的“Multi Plane”的操作。其操作时序如下:

第24页

共29页

Multi Block Programming Timing(to be continued)

第25页

共29页

(continuation 1) Multi Block Programming Timing

第26页

共29页

(continuation 2) Multi Block Programming Timing

第27页

共29页

(continuation 3) Multi Block Programming Timing

第28页

共29页

Multi Block Erase Timing Diagram

由上面的介绍,我们可以看出所有NAND Flash的特性基本相似:操作时序、操作特点还有相关的性能参数都相似。

下面我们要介绍的两个类型的Flash都是MLC(Multi Level Cell)结构的Flash。不过一种是NAND型,另一种是AND型。我们先来看TOSHIBA的MLC NAND Flash。

第29页

共29页

第13篇:VC编程资料总结

CString类的完美总结

2012-05-04 15:50:50 我来说两句

收藏

我要投稿

①、CString 类对象的初始化: CString str;

CString str1(_T(\"abc\")); CString str2 = _T(\"defg\");

TCHAR szBuf[] = _T(\"kkk\"); CString str3(szBuf); CString str4 = szBuf;

TCHAR *p = _T(\"1k2\"); //TCHAR * 转换为 CString CString str5(p); CString str6 = p;

CString str7(str1); CString str8 = str7;

②、字符串基本操作: ● 长度:GetLength(); CString str(_T(\"abc\"));

int len = str.GetLength(); //len == 3

● 是否为空,即不含字符:IsEmpty(); ● 清空字符串:Empty(); CString str(_T(\"abc\"));

BOOL mEmpty = str.IsEmpty(); //mEmpty == FALSE str.Empty();

mEmpty = str.IsEmpty(); //mEmpty == TRUE

● 转换大小写:MakeUpper、MakeLower ● 转换顺序:MakeReverse CString str(_T(\"Abc\")); str.MakeUpper(); //str == ABC str.MakeLower(); //str == abc str.MakeReverse(); //str == cba

● 字符串的连接:+、+= CString str(_T(\"abc\"));

str = _T(\"de\") + str + _T(\"kp\"); //str == deabckp str += _T(\"123\"); //str == deabckp123 TCHAR szBuf[] = _T(\"789\");

str += szBuf; //str == deabckp123789

● 字符串的比较:==、!=、(、= 不常用)、Compare(区分大小写)、CompareNoCase(不区分大小写)

CString str1(_T(\"abc\")); CString str2 = _T(\"aBc\"); if (str1 == str2){

MeageBox(_T(\"str1 等于 str2\")); }else{

MeageBox(_T(\"str1 不等于 str2\")); }

③、字符串的查找:

Find、ReverseFind、FindOneOf 三个函数可以实现字符串的查找操作

Find 从指定位置开始查找指定的字符或者字符串,返回其位置,找不到返回 -1;

举例:

CString str(_T(\"abcdefg\"));

int idx = str.Find(_T(\"cde\"), 0); //idx 的值为2;

ReverseFind 从字符串末尾开始查找指定的字符,返回其位置,找不到返回 -1,虽然是从后向前查找,但是位置为从开始算起;

CString str(_T(\"abcdefg\"));

int idx = str.ReverseFind(\'e\'); //idx 的值为4;

FindOneOf 查找参数中给定字符串中的任意字符,返回第一次出现的位置,找不到返回 -1;

CString str(_T(\"abcabcd\"));

int idx = str.FindOneOf(_T(\"cbd\")); //idx 的值为1;

④、字符串的替换与删除:

Replace 替换 CString 对象中的指定的字符或者字符串,返回替换的个数,无匹配字符返回 0;

CString str(_T(\"abcdabc\")); int num = str.Replace(\'b\', \'k\'); //str == akcdakc, num == 2

CString str(_T(\"abcdabc\"));

int num = str.Replace(_T(\"bc\"), _T(\"kw\")); //str == akwdakw, num == 2

Remove 删除 CString 对象中的指定字符,返回删除字符的个数,有多个时都会删除;

CString str(_T(\"abcdabcb\"));

int num = str.Remove(\'b\'); //str == acdac, num == 3

Delete 删除 CString 对象中的指定位置的字符,返回处理后的字符串长度;

CString str(_T(\"abcd\"));

int num = str.Delete(1, 3); //str == a, num == 1

⑤、字符串的提取:

Left、Mid、Right 三个函数分别实现从 CString 对象的 左、中、右 进行字符串的提取操作;

CString str(_T(\"abcd\"));

CString strResult = str.Left(2); //strResult == ab strResult = str.Mid(1); //strResult == bcd strResult = str.Mid(0, 2); //strResult == ab strResult = str.Right(2); //strResult == cd

⑥、单个字符的修改: GetAt、SetAt 可以获取与修改 CString 对象中的单个 TCHAR 类型字符;

操作符也可以获取 CString 对象中的单个字符,但为只读的,不能进行修改;

CString str(_T(\"abcd\"));

str.SetAt(0, \'k\'); //str == kbck TCHAR ch = str.GetAt(2); //ch == c

⑦、其他类型与 CString 对象类型的转换:

● 格式化字符串:Format 方法,实现从 int、long 等数值类型、TCHAR、TCHAR * 等类型向 CString 类型的转换;

int num = 6; CString str;

str.Format(_T(\"%d\"), num);

● CString 类型向 int 等数值类型、TCHAR * 类型的转换: TCHAR *pszBuf = str.GetBuffer(); str.ReleaseBuffer();

TCHAR *p = (LPTSTR)(LPCTSTR)str;

CString str1(_T(\"123\")); int num = _ttoi(str1);

⑧、CString 对象的 Ansi 与 Unicode 转换: 大家可以直接使用上节课给大家讲解的方法,此外这里给大家介绍一种从 Ansi 转换到 Unicode 的隐含方法:

//当前工程环境为Unicode CString str; str = \"abc\"; char *p = \"defg\"; str = p;

⑨、CString 对象字符串所占用的字节数: CString str = _T(\"abc\");

错误的求法:sizeof(CString)、sizeof(str)

正确的求法:str.GetLength()*sizeof(TCHAR)

⑩、当作为 TCHAR * 类型传参时,确保申请了足够用的空间,比如使用 GetModuleFileName 函数

转载]CString、TCHAR*、char*转换

[转载]CString、TCHAR*、char*转换 char*、TCHAR*转换CString CString str(****) 下面详细写一下其它转换

////////////////////////////// /* *********************************************************************** * 函数: TransCStringToTCHAR * 描述:将CString 转换为 TCHAR* * 日期:

*********************************************************************** */ TCHAR* CPublic::CString2TCHAR(CString &str) { int iLen = str.GetLength(); TCHAR* szRs = new TCHAR[iLen]; lstrcpy(szRs, str.GetBuffer(iLen)); str.ReleaseBuffer(); return szRs; } /* *********************************************************************** * 函数: TCHAR2Char * 描述:将TCHAR* 转换为 char* * 日期:

*********************************************************************** */ char* TCHAR2char(TCHAR* tchStr) { int iLen = 2*wcslen(tchStr);//CString,TCHAR汉字算一个字符,因此不用普通计算长度

char* chRtn = new char[iLen+1] wcstombs(chRtn,tchStr,iLen+1);//转换成功返回为非负值 return chRtn; } /* *********************************************************************** * 函数: char2tchar * 描述:将 char* 转换为 TCHAR* * 日期:

*********************************************************************** */ TCHAR *char2tchar(char *str) { int iLen = strlen(str); TCHAR *chRtn = new TCHAR[iLen+1]; mbstowcs(chRtn, str, iLen+1); return chRtn; } /* *********************************************************************** * 函数: CString2char * 描述:将CString转换为 char* * 日期:

*********************************************************************** */ char* CPublic::CString2char(CString &str) { int len = str.GetLength(); char* chRtn = (char*)malloc((len*2+1)*sizeof(char));//CString的长度中汉字算一个长度

memset(chRtn, 0, 2*len+1); USES_CONVERSION; strcpy((LPSTR)chRtn,OLE2A(str.LockBuffer())); return chRtn; } //参考

/////////////////////// //Pocket PC上的UNICODE和ANSI字符串

//By Vaili Philippov, September 26, 2001.

//杨方思歧 译

//////////////////////// /* *********************************************************************** * 函 数 名:GetAnsiString * 描 述:将CString(unicode)转换为char*(ANSI) * 参 数:CString &s 要转换的CString * 返 回 值:返回转换结果 * 创建日期:

* 最后修改:

*********************************************************************** */ char* GetAnsiString(const CString &s) { int nSize = 2*s.GetLength(); char *pAnsiString = new char[nSize+1]; wcstombs(pAnsiString, s, nSize+1); return pAnsiString; }

////////////////////////////////////////////////////////////////////////////////////////////// WideCharToMultiByte和MultiByteToWideChar函数的用法

支持Unicode编码,需要多字节与宽字节之间的相互转换

WideCharToMultiByte的代码页用来标记与新转换的字符串相关的代码页。

MultiByteToWideChar的代码页用来标记与一个多字节字符串相关的代码页。

常用的代码页由CP_ACP和CP_UTF8两个。

使用CP_ACP代码页就实现了ANSI与Unicode之间的转换。

使用CP_UTF8代码页就实现了UTF-8与Unicode之间的转换。

wstring AnsiToUnicode(( const string& str ) { int len = 0;

len = str.length();

int unicodeLen = ::MultiByteToWideChar( CP_ACP, 0, str.c_str(),-1,NULL,0 );

wchar_t * pUnicode;

pUnicode = new wchar_t[unicodeLen+1];

memset(pUnicode,0,(unicodeLen+1)*sizeof(wchar_t));

::MultiByteToWideChar( CP_ACP,0, str.c_str(),-1, (LPWSTR)pUnicode, unicodeLen );

wstring rt; rt = ( wchar_t* )pUnicode; delete pUnicode; return rt; }

string UnicodeToAnsi( const wstring& str ) {

char* pElementText; int iTextLen;

// wide char to multi char

iTextLen = WideCharToMultiByte( CP_ACP, 0, str.c_str(), -1, NULL, 0, NULL, NULL );

pElementText = new char[iTextLen + 1];

memset( ( void* )pElementText, 0, sizeof( char ) * ( iTextLen + 1 ) );

::WideCharToMultiByte( CP_ACP, 0, str.c_str(), -1, pElementText,iTextLen,NULL,NULL );

string strText;

strText = pElementText;

delete[] pElementText;

return strText; }

wstring UTF8ToUnicode(( const string& str ) { int len = 0;

len = str.length();

int unicodeLen = ::MultiByteToWideChar( CP_UTF8, 0, str.c_str(),-1,NULL,0 );

wchar_t * pUnicode;

pUnicode = new wchar_t[unicodeLen+1];

memset(pUnicode,0,(unicodeLen+1)*sizeof(wchar_t));

::MultiByteToWideChar( CP_UTF8,0, str.c_str(),-1, (LPWSTR)pUnicode, unicodeLen );

wstring rt; rt = ( wchar_t* )pUnicode; delete pUnicode; return rt; }

string UnicodeToUTF8( const wstring& str ) {

char* pElementText; int iTextLen;

// wide char to multi char

iTextLen = WideCharToMultiByte( CP_UTF8, 0, str.c_str(), -1, NULL, 0, NULL, NULL );

pElementText = new char[iTextLen + 1];

memset( ( void* )pElementText, 0, sizeof( char ) * ( iTextLen + 1 ) );

::WideCharToMultiByte( CP_UTF8, 0, str.c_str(), -1, pElementText,iTextLen,NULL,NULL );

string strText;

strText = pElementText;

delete[] pElementText;

return strText; }

第14篇:Mudos编程总结[推荐]

Mudos编程总结

1,Mudos系统调用系统 MudLib系统文件 的过程和一些特点

Mudos启动以后先要寻找一个配置文件,用来配置MudLib文件系统的一些信息(这里时config.cfg),找不到就无法启动。找到以后根据配置文件里面的参数进行初始化Mudos,然后调用配置文件里面的两个入口文件,即simul_efun.c和master.c 。首先载入simul_efun文件并生成一个特殊的全局对象,这个对象用于定义一些全局使用的函数,也可以重载Mudos中的Efun函数,这些新定义的文件使用起来和Efun是一样的。 然后系统会调用master.c文件并生成一个全局对象(主控对象),主控对象用于系统的全局控制,包括全局对象的加载,错误信息的跟踪处理等。接下来,系统会根据主控对象中的定义,载入一些系统需要用到的全局对象。到此,Mudos系统就启动完成了。

2、用户连接系统后的处理过程。。。。

当用户通过客户端进行连接以后,Mudos系统会调用master对象特有的connect()函数,通过这个函数编程人员需要创建并返回一个用户对象,系统会将用户连接到这个用户对象上,即是说这个用户对象就代表了这个用户。到此,就算完成了用户的连接过程。

3、用户的登陆后的处理过程

当Mudos系统调用master的connect函数创建并返回一个用户对象以后,用户对象会调用特有的logon()函数,这个函数用来把用户的设置都设置好并进行游戏。注:最好在这里重新建立一个用户对象,然后通过exec函数把用户的连接转移到新的对象上,然后删除这个对象。

4、Mudos系统中的系统中 对象

simul_efun对象、master主控对象和用户对象这三种对象都是系统提供的特殊对象,其 中simul_efun对象和master主控对象在系统中只有一个实例,即不能被clone也不能new创建,而用户对象主要的作用就是用来创建用户并完成初始化的对象,用户每次登陆都会自动生成一个,用户登陆完成后最好删除它。这里需要指出主控对象和用户对象都有特有的一些函数,这些函数提供给Mudos系统来调用的,用来完成一些系统需要处理的事情。

5、Master主控对象

object connect()程序连接后调用的函数,创建并返回一个用户对象。 string *epilog(int load_empty) 返回一个包含物件文件名称的数组, 其中所有的文件为启动游戏之前必须预先载入的对象.void preload(string file) 系统按照epilog函数返回的数组载入全局对象后调用次函数,用来判断对象是否成功创建 static void crash(string error, object command_giver, object current_object) 当系统异常终止 (crash) 时, 就调用主控物件中的此函数,用来记录一些系统crash的log信息。 void log_error(string file, string meage) 编译程序发生任何错误系统都会调用此函数,用于发现是哪个对象出了什么错误。用于记录编译程序时出现的错误信息。

string error_handler( mapping error, int caught ) 主控物件处理错误的函数,此函数让 mudlib 代替系统处理错误情形。用来处理系统运行时出现的错误信息。 string get_root_uid() 取得

root 使用者识别名称需要获取系统的uid时调用此函数。 string get_bb_uid() 取得骨架使用者识别名称。 string creator_file(string str) 系统创建任何对象时都会调用此函数,用来获得系统初始化对象的uid值。 mixed compile_object(string file) 提供虚拟对象,当系统无法按照给定的路径载入对象时调用此函数。如果返回值是0,系统将不会载入这个对象,如果返回值是一个对象,系统会把这个对象当作是系统要载入的对象。一般来说,这里会返回一个void对象。 string object_name(object ob) 系统调用此函数以知晓一个物件的名称。 string domain_file(string str) 返回一个指定对象所属的区域,系统调用此函数来获得对象的区域。 string author_file(string str) 返回一个指定对象所属的作者,系统调用此函数来获得对象的作者。 int save_ed_setup(object who, int code) ed()函数储存一个使用者的编辑程序设定或组态设定时调用 int retrieve_ed_setup(object who) ed()函数取得使用者的编辑程序设定或组态设定 string make_path_absolute(string file) ed()函数调用此函数以转换文件的相对路径名称为绝对路径名称 string get_save_file_name(string fname) ed()函数使用者不正常断线时, 此函数应返回与原来文件不同的名称, 以免覆写原来的文件 string privs_file(string file) 为新创造的对象指定一个私有字符串。创建任何对象时系统都会调用此函数.对象的文件名称为其参数,返回的字符串就用作此对象的私有字符串。 void slow_shutdown(int minutes) 告知 mud 目前正处于缓慢关闭的过程中.当系统无法从堆中配置更多的内存, 而只能使用保留的内存区块时, 主控物件会调用此函数.此函数只有在组态文件中设定了「内存区块保留大小」才会被调用.距离关闭时间还剩下几分钟则为此函数的参数.void flag( string ) 当系统启动时, 处理 mudlib 所指定的特定标志.这个函数暂时还不是很了解。 int valid_override( string file, string name ) 是否允许使用 efun:: 的情形,即是否允许运行efun重载前的函数。 int valid_seteuid( object ob, string str ) 是否允许设定一个对象的euid。 int valid_socket( object eff_user, string fun, mixed *info ) 是否允许调用socket 外部函数 int valid_asm( string file )

是否能使用asm{}来控制 LPC->C 编译的物件 int valid_compile_to_c( string file ) 是否允许由 LPC->C 的方式编译 int valid_hide( object who ) 是否允许一个对象具有匿踪和看到匿踪对象的能力 int valid_object( object ob ) 是否允许载入某个对象。 int valid_save_binary( string filename )是否允许一个对象储存它的源代码 int valid_bind(object binder, object old_owner, object new_owner) 是否允许bind() 函数 即把某个对象的函数指针指向其他对象。 int valid_write( string file, mixed user, string func ) 是否允许一个人有权限写入一个文件 int valid_read( string file, mixed user, string func ) 是否允许一个人有权限读取一个文件 int valid_shadow( object ob ) 控制哪些对象可以作为投影。 int valid_link( string original, string reference ) 是否允许link() 函数 int view_errors( object user ) 是否允许一个使用者看到错误消息 int valid_compile( string file )是否允许预先编译文件,这是22.2b14新加的设置

6、、、、用户对象

void logon() 主控对象conncet函数返回的用户对象会直接调用次函数,用以对用户对象的初始化处理。 string proce_input(string str) 用户输入信息后调用此函数,字符串处理后再传递给本用户对象,用来处理用户输入信息。 void net_dead() 当用户对象断线时, 系统调用此函数。用来处理用户断线后的事情。 void terminal_type(string term_type) 用户对象登陆后调用,获得用户登陆的终端型号。 void receive_meage(string type, string str) 用户对象的消息处理函数,系统的meage()函数会把消息分配给各个符合条件的用户对象,用户对象接收到消息后调用此函数来接收并处理消息。 void catch_tell( string meage ); 无论系统对

一个对象送出任何消息都会调用此函数来处理这些消息.消息可以依照需要显示、丢弃、修改.此函数可以当作耳罩挡住某些消息, 也可以用于消息处理程序.void receive_snoop( string meage) ; 一个用户对象窥视另一个用户对象时, 所有窥视的文字会传递给用户对象中的这个函数.在此函数中, 您可以处理这些文字.次函数的内容, 通常会再传递给 receive() 函数.void telnet_suboption( string buffer );

获得用户终端的一些设置。 void write_prompt( void ); 如果在用户对象中有定义了次函数,则预设的提示符号该显示时, 系统将调用此函数.当用户正处于 input_to (输入文字) 和 ed (编辑程序)状态时, 系统不会调用此函数

7、、、、所有对象

void create( void ); 对象的构造函数,对象被系统载入后调用此函数来初始化对象。复制的对象也会调用。 需要说明的是,系统第一次载入对象后会自动运行对象和其所有的父类中的create,复制的对象只运行对象自己的create。 int id( string an_id ); 此函数被系统的present() 函数调用, 以判断一个指定的对象是否以字符串an_id为识别名称id.如果an_id是此对象的识别名称之一, 就返回 1, 不是则返回 0。 void init( void ); 当系统把对象A放入对象B时(即调用move_object() 函数), 如果A是人物对象,让A在B里调用A的init函数,同时调用B里面所有对象的init函数。不管A是不是人物对象,让B里所有人物对象调用A的init函数。 int move_or_destruct( object dest ); 如果一个对象的环境对象被摧毁了, 就调用此函数, 并用于它的内容对象.dest是正要被摧毁的对象.如果 dest 不存在, 则 dest 为 0.如果 dest 中的对象没有把自己移出被摧毁的对象, 则也会被一起摧毁.int clean_up( int inherited ); 系统每过一段时间,对非激活对象调用 clean_up() 函数.inherited指出此对象是否有别的对象继承.如果返回 0,此对象将永远不再调用 clean_up().如果返回 1, 则继续判断调用。通常一个对象在 clean_up() 中所作的事, 是摧毁自己以节省内存.void reset( void ); 系统在每次reset之后(时间由系统设定),所有游戏中现存的对象都会调用reset()函数.reset() 常用于检查宝物或怪物是否还在某个房间, 以判断要不要重新产生一份.

8、、、、一些概念的说明

用户对象用户对象用户对象用户对象((((interactive()):):):):用户连接的对象就是用户对象,系统会调用用户对象的一些方法来实现用户的输入输出和消息处理等。如果用户对象断开连接,用户对象就变成为普通对象。但是系统会记录曾经是用户对象的对象,通过userp()函数可以判断一个对象是否曾经是用户对象。 人物人物人物人物对象对象对象对象(living())::::人物对象是曾经呼叫过 enable_commands()的对象,当然用户对象也是人物对象。 环境对象环境对象环境对象环境对象(environment())::::环境对象是通过move_object()函数激活的对象。 激活激活激活激活对象对象对象对象::::在系统规定的(clear_up)时间内,曾经调用过init()函数的对象。 复制复制复制复制对象对象对象对象(clonep())::::系统在第一次载入某个对象后会建立一个初始对象并存于系统内存中,以后每次重新建立对象包括new()、clone_object()、call_other()等都会通过拷贝这个初始对象来建立新的对象,每个拷贝出来的对象,系统都会指定一个递增的数字标识此对象。这里需要说明的是,new()等函数建立对象时,如果初始对象不存在的话,系统会自动载入并初始化此对象,并把此对象定为初始对象,然后再拷贝一个对象作为new()等函数的返回值。系统建立初始对象的时候会由父到子的调用对象所有的create()函数,而拷贝出来的对象只调用对象本身的create()函数。游戏中存在的对象都是复制出来的对象。初始对象只有系统才能调用。

对象对象对象对象uid值值值值::::这个值只能通过主控对象中的creator_file()函数赋值或者export_uid()函数传递。指明当前对象的使用者名称。 对象对象对象对象euid值值值值::::对象有效的使用者名称,可以通过seteuid和geteuid函数设置和读取。 对象的对象的对象的对象的继承继承继承继承、、、、构造及初始化构造及初始化构造及初始化构造及初始化::::Mudos启动以后,可以自动或者通过函数调用来创建和复制各种对象,Mudos系统的作用就是用来管理这些对象。Mudos在生成对象的时候有两种方式,一种是载入对象,一种是复制已经载入的对象。首先,Mudos是通过对象的文件名来寻找和定位对象的,当需要载入对象的时候,先检查对象是否已经载入,如果没有载入会检查对象是否有需要继承别的对象,如果需要就先载入需要继承的对象,然后再载入并生成需要载入的对象。对象的继承实际上是子对象先浅拷贝一份父对象(但不初始化),然后再构造自己。任何一个对象被载入内存或被复制出来都会执行create函数来初始化自己。

9、、、、EFUN函数说明

This_object():这个函数返回由当前文件所建立或拷贝的对象。如果这个文件继承了另一个文件,那么另一个文件中的这个函数也返回由这个文件所建立或拷贝的对象。 previous_object(n):返回调用此函数的第前n个对象,previous_object(0)= previous_object(),表示返回调用此函数的对象。 This_interactive():返回调用此函数的用户对象。 This_player():返回调用此函数的人物对象。This_player(1)返回This_interactive() Load_object():如果已经载入则返回已经载入的对象,否则载入对象并返回它。 New():如果对象没载入则载入对象,并复制一个对象返回,否则复制已经载入的对象返回。这里需要指出的是复制对象的过程并不会载入被复制对象所要继承的对象。 Clone_object():和new一样,目前不知道有什么区别。 replace_program():这个函数是用当前对象的继承对象来替换当前对象,但是保留当前对象的全局变量。就是说如果被替换以后,当前对象就只有继承对象的方法,而当前对象所定义的方法都不存在了。

第15篇:数据库编程总结(推荐)

数据库编程总结

当前各种主流数据库有很多,包括Oracle, MS SQL Server, Sybase, Informix, MySQL, DB2, Interbase / Firebird, PostgreSQL, SQLite, SAP/DB, TimesTen, MS ACCESS等等。数据库编程是对数据库的创建、读写等一列的操作。数据库编程分为数据库客户端编程与数据库服务器端编程。数据库客户端编程主要使用ODBC API、ADO、ADO.NET、OCI、OTL等方法;数据库服务端编程主要使用OLE DB等方法。数据库编程需要掌握一些访问数据库技术方法,还需要注意怎么设计高效的数据库、数据库管理与运行的优化、数据库语句的优化。

一、访问数据库技术方法

数据库编程分为数据库客户端编程与数据库服务器端编程。数据库客户端编程主要使用ODBC API、ADO、ADO.NET、OCI、OTL等方法;数据库服务端编程主要使用OLE DB等方法。

1、几种是数据库访问方法比较

ODBC

API是一种适合数据库底层开发的编程方法,ODBC

API提供大量对数据源的操作,ODBC

API能够灵活地操作游标,支持各种帮定选项,在所有ODBC相关编程中,API编程具有最高的执行速度。 DAO提供了很好的数据库编程的对象模型.但是,对数据库的所有调用以及输出的数据都必须通过Acce/Jet数据库引擎,这对于使用数据库应用程序,是严重的瓶颈。

OLE

DB提供了COM接口,与传统的数据库接口相比,有更好的健壮性和灵活性,具有很强的错误处理能力,能够同非关系数据源进行通信。

ADO最主要的优点在于易于使用、速度快、内存支出少和磁盘遗迹小。

ADO.NET 是利用数据集的概念将数据库数据读入内存中,然后在内存中对数据进行操作,最后将数据集数据回写到源数据库中。

OTL 是 Oracle, Odbc and DB2-CLI Template Library 的缩写,是一个C++编译中操控关系数据库的模板库, OTL中直接操作Oracle主要是通过Oracle提供的OCI接口进行,进行操作DB2数据库则是通过CLI接口来进行,至于MS的数据库和其它一些数据库,则OTL只提供了ODBC来操作的方式。当然Oracle和DB2也可以由OTL间接使用ODBC的方式来进行操纵。具有以下优点:跨平台;运行效率高,与C语言直接调用API相当;开发效率高,起码比ADO.net使用起来更简单,更简洁;部署容易,不需要ADO组件,不需要.net framework 等。

2、VC数据库编程几种方法

VC数据库编程几种方法,包括ODBC连接、MFC

ODBC连接、DAO连接、OLE

DB、OLE

DB

Templates连接、ADO、Oracle专用方法(OCI(Oracle

Call

Interface)访问、Oracle

Object

OLE

C++

Cla

Library )。

通用方法

1.ODBC连接

ODBC(Open

DataBase

Connectivity)是MSOA的一部分,是一个标准数据库接口。它提供对关系数据库访问的统一接口,实现对异构数据源的一致访问。 ODBC数据访问由以下部分组成:

句柄(Handles):ODBC使用句柄来标识ODBC环境、连接、语句和描述器.

缓存区(Buffers):

数据类型(Data

types)

一致性级别(Conformance

levels)

用ODBC设计客户端的一般步骤:

分配ODBC环境

分配连接句柄

连接数据源

构造和执行SQL语句

获得查询结果

断开数据源的连接

释放ODBC环境

ODBC

API是一种适合数据库底层开发的编程方法,ODBC

API提供大量对数据源的操作,ODBC

API能够灵活地操作游标,支持各种帮定选项,在所有ODBC相关编程中,API编程具有最高的执行速度.因此,ODBC

API编程属于底层编程。

2.MFC

ODBC连接

MFC

ODBC是MFC对ODBC进行的封装,以简化对ODBC

API的 调用,从而实现面向对象的数据库编程接口.

MFC

ODBC的封装主要开发了CDatabase类和CRecordSet类

(1) CDatabase类

CDatabase类用于应用程序建立同数据源的连接。CDatabase类中包含一个m_hdbc变量,它代表了数据源的连接句柄。如果要建立CDatabase类的实例,应先调用该类的构造函数,再调用Open函数,通过调用,初始化环境变量,并执行与数据源的连接。在通过Close函数关闭数据源。

CDatabase类提供了对数据库进行操作的函数及事务操作。

(2) CRecordSet类

CRecordSet类定义了从数据库接收或者发送数据到数据库的成员变量,以实现对数据集的数据操作。

CRecordSet类的成员变量m_hstmt代表了定义该记录集的SQL语句句柄,m_nFields为记录集中字段的个数,m_nParams为记录集所使用的参数个数。

CRecordSet的记录集通过CDatabase实例的指针实现同数据源的连接,即CRecordSet的成员变量m_pDatabase.

MFC

ODBC编程更适合于界面型数据库应用程序的开发,但由于CDatabase类和CRecordSet类提供的数据库操作函数有限,支持的游标类型也有限,限制了高效的数据库开发。在编程层次上属于高级编程。

应用实例: 1.打开数据库

CDatabase database;

database.OpenEx( _T( \"DSN=zhuxue\" ),CDatabase::noOdbcDialog);//zhuxue为数据源名称

2.关联记录集

CRecordset recset(&database);

3.查询记录

CString sSql1=\"\";

sSql1 = \"SELECT * FROM tablename\" ;

recset.Open(CRecordset::forwardOnly, sSql1, CRecordset::readOnly);

int ti=0;

CDBVariant var;//var可以转换为其他类型的值

while (!recset.IsEOF())

{

//读取Excel内部数值

recset.GetFieldValue(\"id\",var);

jiangxiang[ti].id=var.m_iVal;

recset.GetFieldValue(\"name\", jiangxiang[ti].name);

ti++;

recset.MoveNext();

}

recset.Close();//关闭记录集

4.执行sql语句

CString sSql=\"\";

sSql+=\"delete * from 院系审核\";//清空表

database.ExecuteSQL(sSql);

sSql也可以为Insert ,Update等语句

5.读取字段名

sSql = \"SELECT * FROM Sheet1\" ;

//读取的文件有Sheet1表的定义,或为本程序生成的表.

// 执行查询语句

recset.Open(CRecordset::forwardOnly, sSql, CRecordset::readOnly);

int excelColCount=recset.GetODBCFieldCount();//列数

CString excelfield[30];

//得到记录集的字段集合中的字段的总个数

for( i=0;i

{

CODBCFieldInfo fieldinfo;

recset.GetODBCFieldInfo(i,fieldinfo);

excelfield[i].name =fieldinfo.m_strName;//字段名

}

6.打开excel文件

CString sDriver = \"MICROSOFT EXCEL DRIVER (*.XLS)\"; // Excel安装驱动

CString sSql,sExcelFile; //sExcelFile为excel的文件路径

TRY

{

// 创建进行存取的字符串

sSql.Format(\"DRIVER={%s};DSN=\'\';FIRSTROWHASNAMES=1;READONLY=FALSE;CREATE_DB=/\"%s/\";DBQ=%s\",sDriver, sExcelFile, sExcelFile);

// 创建数据库 (既Excel表格文件)

if( database.OpenEx(sSql,CDatabase::noOdbcDialog) )

{

//可以把excel作为一个数据库操作

}

}

catch(e)

{

TRACE1(\"Excel驱动没有安装: %s\",sDriver);

AfxMeageBox(\"读取失败,请检查是否定义数据区Sheet1\");

}

3.DAO连接

DAO(Data

Acce

Object)是一组Microsoft

Acce/Jet数据库引擎的COM自动化接口.DAO直接与Acce/Jet数据库通信.通过Jet数据库引擎,DAO也可以同其他数据库进行通信。DAO还封装了Acce数据库的结构单元,通过DAO可以直接修改Acce数据库的结构,而不必使用SQL的数据定义语言(DDL)。

DAO的体系结构如下:

DAO封装的类:

(1)CdaoWorkspace:对DAO工作区(数据库处理事务管理器)的封装

(2)CdaoDatabase:对DAO数据库对象的封装,负责数据库连接.

(3)CdaoRecordset:对DAO记录集对象的封装,代表所选的一组记录.

(4)CdaoTableDef:对表定义对象的封装,代表基本表或附加表定义.

(5)CdaoQueryDef:对查询对象的封装,包含所有查询的定义.

(6)CdaoException:DAO用于接收数据库操作异常的类.

(7)CDaoFieldExchange

DAO提供了很好的数据库编程的对象模型.但是,对数据库的所有调用以及输出的数据都必须通过Acce/Jet数据库引擎,这对于使用数据库应用程序,是严重的瓶颈。

DAO相对于ODBC来说,属于高层的数据库接口.

4.OLE

DB连接

OLE

DB对ODBC进行了两方面的扩展:一是提供了数据库编程的OLE接口即COM,二是提供了一个可用于关系型和非关系型数据源的接口。

OLE

DB提供了COM接口,与传统的数据库接口相比,有更好的健壮性和灵活性,具有很强的错误处理能力,能够同非关系数据源进行通信。

与ODBC

API一样,OLE

DB也属于底层的数据库编程接口,OLE

DB结合了ODBC对关系数据库的操作功能,并进行扩展,可以访问非关系数据库。

OLE

DB访问数据库的原理如下:

OLE

DB程序结构:

OLE

DB由客户(Consumer)和服务器(Provider)。客户是使用数据的应用程序,它通过OLE

DB接口对数据提供者的数据进行访问和控制。OLE

DB服务器是提供OLE

DB接口的软件组件。根据提供的内容可以分为数据提供程序(Data

Provider)和服务提供程序(Service

Provider)。

程序结构原理图如下:

数据提供程序

数据提供程序拥有自己的数据并把数据以表格的形式呈现给使用者使用.

服务提供程序

服务提供程序是数据提供程序和使用者的结合。它是OLE

DB体系结构中的中间件,它是OLE

DB数据源的使用者和数据使用程序的提供者

数据使用程序

数据使用程序对存储在数据提供程序中的数据进行使用和控制.

OLE

DB开发程序的一般步骤:

初始化COM环境

连接数据源

打开对话

执行命令

处理结果

清除对象

应用实例:

使用OLEDB编写数据库应用程序 1

概述

OLE DB的存在为用户提供了一种统一的方法来访问所有不同种类的数据源。OLE DB可以在不同的数据源中进行转换。利用OLE DB,客户端的开发人员在进行数据访问时只需把精力集中在很少的一些细节上,而不必弄懂大量不同数据库的访问协议。 OLE DB是一套通过COM接口访问数据的ActiveX接口。这个OLE DB接口相当通用,足以提供一种访问数据的统一手段,而不管存储数据所使用的方法如何。同时,OLE DB还允许开发人员继续利用基础数据库技术的优点,而不必为了利用这些优点而把数据移出来。

使用ATL使用OLE DB数据使用程序

由于直接使用OLE DB的对象和接口设计数据库应用程序需要书写大量的代码。为了简化程序设计,Visual C++提供了ATL模板用于设计OLE DB数据应用程序和数据提供程序。 利用ATL模板可以很容易地将OLE DB与MFC结合起来,使数据库的参数查询等复杂的编程得到简化。MFC提供的数据库类使OLE DB的编程更具有面向对象的特性。Viual C++所提供用于OLE DB的ATL模板可分为数据提供程序的模板和数据使用程序的模板。

使用ATL模板创建数据应用程序一般有以下几步骤: 1)、创建应用框架

2)、加入ATL产生的模板类

3)、在应用中使用产生的数据访问对象3 不用ATL使用OLE DB数据使用程序

利用ATL模板产生数据使用程序较为简单,但适用性不广,不能动态适应数据库的变化。下面我们介绍直接使用MFC OLE DB类来生成数据使用程序。 模板的使用

OLE DB数据使用者模板是由一些模板组成的,包括如下一些模板,下面对一些常用类作一些介绍。 1)、会话类 CDataSource类

CDataSource类与OLE DB的数据源对象相对应。这个类代表了OLE DB数据提供程序和数据源之间的连接。只有当数据源的连接被建立之后,才能产生会话对象,可以调用Open来打开数据源的连接。 CSeion类

CSeion所创建的对象代表了一个单独的数据库访问的会话。一个用CDataSource类产生的数据源对象可以创建一个或者多个会话,要在数据源对象上产生一个会话对象,需要调用函数Open()来打开。同时,会话对象还可用于创建事务操作。

CEnumeratorAcceor类

CEnumeratorAcceor类是用来访问枚举器查询后所产生的行集中可用数据提供程序的信息的访问器,可提供当前可用的数据提供程序和可见的访问器。 2)、访问器类 CAceor类

CAcceor类代表与访问器的类型。当用户知道数据库的类型和结构时,可以使用此类。它支持对一个行集采用多个访问器,并且,存放数据的缓冲区是由用户分配的。 CDynamicAcceor类

CDynamicAcceor类用来在程序运行时动态的创建访问器。当系统运行时,可以动态地从行集中获得列的信息,可根据此信息动态地创建访问器。 CManualAcceor类

CManualAcceor类中以在程序运行时将列与变量绑定或者是将参数与变量捆定。 3)、行集类 CRowSet类

CRowSet类封装了行集对象和相应的接口,并且提供了一些方法用于查询、设置数据等。可以用Move()等函数进行记录移动,用GetData()函数读取数据,用Insert()、Delete()、SetData()来更新数据。 CBulkRowset类

CBulkRowset类用于在一次调用中取回多个行句柄或者对多个行进行操作。 CArrayRowset类

CArrayRowset类提供用数组下标进行数据访问。 4)、命令类 CTable类 CTable类用于对数据库的简单访问,用数据源的名称得到行集,从而得到数据。 CCommand类

CCommand类用于支持命令的数据源。可以用Open()函数来执行SQL命令,也可以Prepare()函数先对命令进行准备,对于支持命令的数据源,可以提高程序的灵活性和健壮性。

在stdafx.h头文件里,加入如下代码。 #include extern CComModule _Module; #include #include #include // if you are using schema templates 在stdafx.cpp文件里,加入如下代码。 #include CComModule _Module; 决定使用何种类型的存取程序和行集。 获取数据

在打开数据源,会话,行集对象后就可以获取数据了。所获取的数据类型取决于所用的存取程序,可能需要绑定列。按以下步骤。

1、用正确的命令打开行集对象。

2、如果使用CManualAcceor,在使用之前与相应列进行绑定。要绑定列,可以用函数GetColumnInfo,如下所示: // Get the column information ULONG ulColumns

= 0; DBCOLUMNINFO* pColumnInfo = NULL; LPOLESTR pStrings

= NULL; if (rs.GetColumnInfo(&ulColumns, &pColumnInfo, &pStrings) != S_OK) AfxThrowOLEDBException(rs.m_pRowset, IID_IColumnsInfo); struct MYBIND* pBind = new MYBIND[ulColumns]; rs.CreateAcceor(ulColumns, &pBind[0], sizeof(MYBIND)*ulColumns); for (ULONG l=0; l

3、用while循环来取数据。在循环中,调用MoveNext来测试光标的返回值是否为S_OK,如下所示: while (rs.MoveNext() == S_OK) {

// Add code to fetch data here

// If you are not using an auto acceor, call rs.GetData() }

4、在while循环内,可以通过不同的存取程序获取数据。1) 如果使用的是CAcceor类,可以通过使用它们的数据成员进行直接访问。如下所示:

2) 如果使用的是CDynamicAcceor 或CDynamicParameterAcceor 类,可以通过GetValue或GetColumn函数来获取数据。可以用GetType来获取所用数据类型。如下所示: while (rs.MoveNext() == S_OK) {

// Use the dynamic acceor functions to retrieve your

// data

ULONG ulColumns = rs.GetColumnCount();

for (ULONG i=0; i

{

rs.GetValue(i);

} } 3) 如果使用的是CManualAcceor,可以指定自己的数据成员,绑定它们。就可以直接存取。如下所示: while (rs.MoveNext() == S_OK) {

// Use the data members you specified in the calls to

// AddBindEntry.

wsprintf(\"%s\", szFoo); } 决定行集的数据类型

在运行时决定数据类型,要用动态或手工的存取程序。如果用的是手工存取程序,可以用GetColumnInfo函数得到行集的列信息。从这里可以得到数据类型。 4

总结

由于现在有多种数据源,,想要对这些数据进行访问管理的唯一途径就是通过一些同类机制来实现,如OLE DB。高级OLE DB结构分成两部分:客户和提供者。客户使用由提供者生成的数据。

就像其它基于COM的多数结构一样,OLE DB的开发人员需要实现很多的接口,其中大部分是模板文件。

当生成一个客户对象时,可以通过ATL对象向导指向一个数据源而创建一个简单的客户。ATL对象向导将会检查数据源并创建数据库的客户端代理。从那里,可以通过OLE DB客户模板使用标准的浏览函数。

当生成一个提供者时,向导提供了一个很好的开端,它们仅仅是生成了一个简单的提供者来列举某一目录下的文件。然后,提供者模板包含了OLE DB支持的完全补充内容。在这种支持下,用户可以创建OLE DB提供者,来实现行集定位策略、数据的读写以及建立书签。 应用案例:

Visual C++中使用OLE DB读写SQL Server 在需要对数据库进行操作时,OLE DB总是被认为是一种效率最高但最难的方法。但是以我最近使用OLE DB的经验看来,OLE DB的效率高则高矣,但却一点都不难。说它难恐怕主要是因为可参考的中文资料太少,为了帮助以后需要接触OLE DB的同行,我撰写了这篇文章。本文包含如下内容:

1.OLE DB写数据库; 2.OLE DB读数据库;

3.OLE DB对二进制数据(text、ntext、image等)的处理。

首先来看看对SQL Server进行写操作的代码,有一定VC基础的读者应该可以很顺利地看懂。OLE DB写数据库,就是这么简单!

注:

1.以下代码中使用的模板类EAutoReleasePtr与ATL中的CComPtr类似,是一个在析构时自动调用Release的类。CComPtr的代码在ATLBASE.H中定义。

2.以下代码均在UNICODE环境下编译,因为执行的SQL语句必须是UNICODE的。设置工程为UNICODE的方法是:首先在project->settings->C/C++的属性页中的Preproceor中,删除_MBCS写入UNICODE,_UNICODE。然后在link属性页中Category中选择output,在Entry-Point symbol 中添加wWinMainCRTStartup。

EAutoReleasePtr pIDBInitialize; HRESULT hResult = ConnectDatabase( &pIDBInitialize, _T(\"127.0.0.1\"), _T(“sa”), _T(\"paword\") ); if( FAILED( hResult ) ) {

//失败,可能是因为数据库没有启动、用户名密码错等等

return; }EAutoReleasePtr pIOpenRowset; hResult = CreateSeion( pIDBInitialize, &pIOpenRowset ); if( FAILED( hResult ) ) {

//出错

return; }EAutoReleasePtr pICommand; EAutoReleasePtr pICommandText; hResult = CreateCommand( pIOpenRowset, &pICommand, &pICommandText ); if( FAILED( hResult ) ) {

//出错

return; }hResult = ExecuteSQL( pICommand, pICommandText, _T(\"USE PBDATA\") ); if( FAILED( hResult ) ) {

//如果这里失败,那就是SQL语句执行失败。在此处,就是PBDATA还未创建

return; }

// 创建表 ExecuteSQL( pICommand, pICommandText, _T(\"CREATE TABLE 2005_1(Volume real NOT NULL,ID int NOT NULL IDENTITY)\") );

// 添加记录

ExecuteSQL( pICommand, pICommandText, _T(\"INSERT INTO 2005_1 VALUES(100.0)\") ); //...

其中几个函数的代码如下:

HRESULT ConnectDatabase( IDBInitialize** ppIDBInitialize, LPCTSTR pszDataSource, LPCTSTR pszUserID, LPCTSTR pszPaword ) {

ASSERT( ppIDBInitialize != NULL && pszDataSource != NULL && pszUserID != NULL && pszPaword != NULL );

UINT uTimeout = 15U; // 连接数据库超时(秒)

TCHAR szInitStr[1024];

VERIFY( 1023 >= wsprintf( szInitStr, _T(\"Provider=SQLOLEDB;Data Source=%s;Initial Catalog=master;User Id=%s;Paword=%s;Connect Timeout=%u\"), pszDataSource, pszUserID, pszPaword, uTimeout ) );

//Initial Catalog=master指明连接成功后,\"USE master\"。

EAutoReleasePtr pIDataInitialize;

HRESULT hResult = ::CoCreateInstance( CLSID_MSDAINITIALIZE, NULL, CLSCTX_INPROC_SERVER,

IID_IDataInitialize, ( void** )&pIDataInitialize );

if( FAILED( hResult ) )

{

return hResult;

}

EAutoReleasePtr pIDBInitialize;

hResult = pIDataInitialize->GetDataSource( NULL, CLSCTX_INPROC_SERVER, ( LPCOLESTR )szInitStr,

IID_IDBInitialize, ( IUnknown** )&pIDBInitialize );

if( FAILED( hResult ) )

{

return hResult;

}

hResult = pIDBInitialize->Initialize( );

if( FAILED( hResult ) )

{

return hResult;

}

* ppIDBInitialize = pIDBInitialize.Detach( );

return S_OK; }

HRESULT CreateSeion( IDBInitialize* pIDBInitialize, IOpenRowset** ppIOpenRowset ) {

ASSERT( pIDBInitialize != NULL && ppIOpenRowset != NULL );

EAutoReleasePtr pSeion;

HRESULT hResult = pIDBInitialize->QueryInterface( IID_IDBCreateSeion, ( void** )&pSeion );

if( FAILED( hResult ) )

{

return hResult;

}

EAutoReleasePtr pIOpenRowset;

hResult = pSeion->CreateSeion( NULL, IID_IOpenRowset, ( IUnknown** )&pIOpenRowset );

if( FAILED( hResult ) )

{

return hResult;

}

* ppIOpenRowset = pIOpenRowset.Detach( );

return S_OK; }

HRESULT CreateCommand( IOpenRowset* pIOpenRowset, ICommand** ppICommand, ICommandText** ppICommandText ) {

ASSERT( pIOpenRowset != NULL && ppICommand != NULL && ppICommandText != NULL );

HRESULT hResult;

EAutoReleasePtr pICommand;

{

EAutoReleasePtr pICreateCommand;

hResult = pIOpenRowset->QueryInterface( IID_IDBCreateCommand, ( void** )&pICreateCommand );

if( FAILED( hResult ) )

{

return hResult;

}

hResult = pICreateCommand->CreateCommand( NULL, IID_ICommand, (IUnknown**)&pICommand );

if( FAILED( hResult ) )

{

return hResult;

}

}

EAutoReleasePtr pICommandText;

hResult = pICommand->QueryInterface( &pICommandText );

if( FAILED( hResult ) )

{

return hResult;

}

* ppICommand = pICommand.Detach( );

* ppICommandText = pICommandText.Detach( );

return S_OK; }

HRESULT ExecuteSQL( ICommand* pICommand, ICommandText* pICommandText, LPCTSTR pszCommand, LONG* plRowsAffected ) {

ASSERT( pICommand != NULL && pICommandText != NULL && pszCommand != NULL && pszCommand[0] != 0 );

HRESULT hResult = pICommandText->SetCommandText( DBGUID_DBSQL, ( LPCOLESTR )pszCommand );

if( FAILED( hResult ) )

{

return hResult;

}

LONG lAffected;

hResult = pICommand->Execute( NULL, IID_NULL, NULL, plRowsAffected == NULL ? &lAffected : plRowsAffected, ( IUnknown** )NULL );

return hResult; }

以上就是写数据库的全部代码了,是不是很简单呢?下面再来读的。

// 先用与上面代码中一样的步骤获取pICommand,pICommandText。此处省略

HRESULT hResult = pICommandText->SetCommandText( DBGUID_DBSQL, ( LPCOLESTR )_T(\"SELECT Volume FROM 2005_1 WHERE ID = @@IDENTITY\") ); //取我们刚刚添加的那一条记录

if( FAILED( hResult ) ) {

return; }

LONG lAffected; EAutoReleasePtr pIRowset; hResult = pICommand->Execute( NULL, IID_IRowset, NULL, &lAffected, ( IUnknown** )&pIRowset ); if( FAILED( hResult ) ) {

return; }

EAutoReleasePtr pIAcceor; hResult = pIRowset->QueryInterface( IID_IAcceor, ( void** )&pIAcceor ); if( FAILED( hResult ) ) {

return; }

// 一个根据表中各字段的数值类型而定义的结构,用于存储返回的各字段的值

struct CLoadLastFromDB {

DBSTATUS dwdsVolume;

DWORD

dwLenVolume;

float

fVolume; };

// 此处我们只查询了一个字段。如果要查询多个字段,CLoadLastFromDB中要添加相应的字段定义,下面的dbBinding也要相应扩充。dbBinding[].iOrdinal要分别指向各个字段,dbBinding[].wType要根据字段类型赋合适的值。

DBBINDING dbBinding[1]; dbBinding[0].iOrdinal

= 1;

// Volume 字段的位置,从 1 开始

dbBinding[0].obValue

= offsetof( CLoadLastFromDB, fVolume ); dbBinding[0].obLength

= offsetof( CLoadLastFromDB, dwLenVolume ); dbBinding[0].obStatus

= offsetof( CLoadLastFromDB, dwdsVolume ); dbBinding[0].pTypeInfo

= NULL; dbBinding[0].pObject

= NULL; dbBinding[0].pBindExt

= NULL; dbBinding[0].dwPart

= DBPART_VALUE | DBPART_STATUS | DBPART_LENGTH; dbBinding[0].dwMemOwner = DBMEMOWNER_CLIENTOWNED; dbBinding[0].eParamIO

= DBPARAMIO_NOTPARAM; dbBinding[0].cbMaxLen

= 0; dbBinding[0].dwFlags

= 0;

第16篇:S7300梯形图编程总结

位逻辑指令

比较指令

转换指令

计数指令

6 跳转指令

7整形数学运算

浮点运算

传送指令

程序控制指令

第17篇:Java线程编程总结

线程编程方面

60、java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?

答:有两种实现方法,分别是继承Thread类与实现Runnable接口 用synchronized关键字修饰同步方法

反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被\"挂起\"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。 6

1、sleep() 和 wait() 有什么区别? 答:sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。

wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

62、同步和异步有何异同,在什么情况下分别使用他们?举例说明。

答:如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。

当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。 6

3、启动一个线程是用run()还是start()? 答:启动一个线程是调用start()方法,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行。这并不意味着线程就会立即运行。run()方法可以产生必须退出的标志来停止一个线程。

64、当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法? 答:不能,一个对象的一个synchronized方法只能由一个线程访问。

我认为:其他线程可以进入非synchronized方法,但不能进入这个对象的synchronized方法。 6

5、请说出你所知道的线程同步的方法。

答:wait():使一个线程处于等待状态,并且释放所持有的对象的lock。

sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。

notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

66、多线程有几种实现方法,都是什么?同步有几种实现方法,都是什么? 答:多线程有两种实现方法,分别是继承Thread类与实现Runnable接口 同步的实现方面有两种,分别是synchronized,wait与notify 6

7、线程的基本概念、线程的基本状态以及状态之间的关系

答:线程指在程序执行过程中,能够执行程序代码的一个执行单位,每个程序至少都有一个线程,也就是程序本身。

Java中的线程有四种状态分别是:运行、就绪、挂起、结束

68、简述synchronized和java.util.concurrent.locks.Lock的异同 ? 答:主要相同点:Lock能完成synchronized所实现的所有功能

主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。

Jsp方面

69、forward 和redirect的区别

答:forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器,浏览器根本不知道服务器发送的内容是从哪儿来的,所以它的地址栏中还是原来的地址。

redirect就是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址,一般来说浏览器会用刚才请求的所有参数重新请求,所以seion,request参数都可以获取。 70、jsp有哪些内置对象?作用分别是什么?

答:JSP共有以下9种基本内置组件(可与ASP的6种内部组件相对应):

request 用户端请求,此请求会包含来自GET/POST请求的参数

response 网页传回用户端的回应

pageContext 网页的属性是在这里管理

seion 与请求有关的会话期

application servlet 正在执行的内容

out 用来传送回应的输出 config servlet的构架部件

page JSP网页本身

exception 针对错误网页,未捕捉的例外

71、jsp有哪些动作?作用分别是什么? 答:JSP共有以下6种基本动作

jsp:include:在页面被请求的时候引入一个文件。

jsp:useBean:寻找或者实例化一个JavaBean。

jsp:setProperty:设置JavaBean的属性。

jsp:getProperty:输出某个JavaBean的属性。

jsp:forward:把请求转到一个新的页面。

jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记 7

2、JSP中动态INCLUDE与静态INCLUDE的区别?

答:动态INCLUDE用jsp:include动作实现

它总是会检查所含文件中的变化,适合用于包含动态页面,并且可以带参数

静态INCLUDE用include伪码实现,定不会检查所含文件的变化,适用于包含静态页面

<%@ include file=\"included.htm\" %>7

3、两种跳转方式分别是什么?有什么区别? 答:有两种,分别为:

前者页面不会转向include所指的页面,只是显示该页的结果,主页面还是原来的页面。执行完后还会回来,相当于函数调用。并且可以带参数.后者完全转向新页面,不会再回来。相当于go to 语句。

74、JSP的内置对象及方法。

答:request表示HttpServletRequest对象。它包含了有关浏览器请求的信息,并且提供了几个用于获取cookie, header, 和seion数据的有用的方法。

response表示HttpServletResponse对象,并提供了几个用于设置送回 浏览器的响应的方法(如cookies,头信息等)

out对象是javax.jsp.JspWriter的一个实例,并提供了几个方法使你能用于向浏览器回送输出结果。

pageContext表示一个javax.servlet.jsp.PageContext对象。它是用于方便存取各种范围的名字空间、servlet相关的对象的API,并且包装了通用的servlet相关功能的方法。

seion表示一个请求的javax.servlet.http.HttpSeion对象。Seion可以存贮用户的状态信息

applicaton 表示一个javax.servle.ServletContext对象。这有助于查找有关servlet引擎和servlet环境的信息

config表示一个javax.servlet.ServletConfig对象。该对象用于存取servlet实例的初始化参数。 page表示从该页面产生的一个servlet实例

Servlet方面

75、说一说Servlet的生命周期?

答:servlet有良好的生存期的定义,包括加载和实例化、初始化、处理请求以及服务结束。这个生存期由javax.servlet.Servlet接口的init,service和destroy方法表达。 Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy方法。

与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。

76、JAVA SERVLET API中forward() 与redirect()的区别?

答:前者仅是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;后者则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。所以,前者更加高效,在前者可以满足需要时,尽量使用forward()方法,并且,这样也有助于隐藏实际的链接。在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用sendRedirect()方法。 7

7、Servlet的基本架构 答:

public cla ServletName extends HttpServlet { public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } }

78、什么情况下调用doGet()和doPost()?

答:Jsp页面中的form标签里的method属性为get时调用doGet(),为post时调用doPost()。 7

9、servlet的生命周期

答:web容器加载servlet,生命周期开始。通过调用servlet的init()方法进行servlet的初始化。通过调用service()方法实现,根据请求的不同调用不同的do***()方法。结束服务,web容器调用servlet的destroy()方法。

80、如何现实servlet的单线程模式 答:<%@ page isThreadSafe=\"false\"%>8

1、页面间对象传递的方法

答:request,seion,application,cookie等

82、JSP和Servlet有哪些相同点和不同点,他们之间的联系是什么?

答:JSP是Servlet技术的扩展,本质上是Servlet的简易方式,更强调应用的外表表达。JSP编译后是\"类servlet\"。Servlet和JSP最主要的不同点在于,Servlet的应用逻辑是在Java文件中,并且完全从表示层中的HTML里分离开来。而JSP的情况是Java和HTML可以组合成一个扩展名为.jsp的文件。JSP侧重于视图,Servlet主要用于控制逻辑。 8

3、四种会话跟踪技术

答:会话作用域ServletsJSP 页面描述

page否是代表与一个页面相关的对象和属性。一个页面由一个编译好的 Java servlet 类(可以带有任何的 include 指令,但是没有 include 动作)表示。这既包括 servlet 又包括被编译成 servlet 的 JSP 页面

request是是代表与 Web 客户机发出的一个请求相关的对象和属性。一个请求可能跨越多个页面,涉及多个 Web 组件(由于 forward 指令和 include 动作的关系)

seion是是代表与用于某个 Web 客户机的一个用户体验相关的对象和属性。一个 Web 会话可以也经常会跨越多个客户机请求

application是是代表与整个 Web 应用程序相关的对象和属性。这实质上是跨越整个 Web 应用程序,包括多个页面、请求和会话的一个全局作用域 8

4、Request对象的主要方法 答:

setAttribute(String name,Object):设置名字为name的request的参数值 getAttribute(String name):返回由name指定的属性值

getAttributeNames():返回request对象所有属性的名字集合,结果是一个枚举的实例 getCookies():返回客户端的所有Cookie对象,结果是一个Cookie数组 getCharacterEncoding():返回请求中的字符编码方式 getContentLength():返回请求的Body的长度

getHeader(String name):获得HTTP协议定义的文件头信息 getHeaders(String name):返回指定名字的request Header的所有值,结果是一个枚举的实例 getHeaderNames():返回所以request Header的名字,结果是一个枚举的实例 getInputStream():返回请求的输入流,用于获得请求中的数据 getMethod():获得客户端向服务器端传送数据的方法

getParameter(String name):获得客户端传送给服务器端的有name指定的参数值

getParameterNames():获得客户端传送给服务器端的所有参数的名字,结果是一个枚举的实例 getParameterValues(String name):获得有name指定的参数的所有值 getProtocol():获取客户端向服务器端传送数据所依据的协议名称 getQueryString():获得查询字符串

getRequestURI():获取发出请求字符串的客户端地址 getRemoteAddr():获取客户端的IP地址 getRemoteHost():获取客户端的名字

getSeion([Boolean create]):返回和请求相关Seion getServerName():获取服务器的名字

getServletPath():获取客户端所请求的脚本文件的路径 getServerPort():获取服务器的端口号

removeAttribute(String name):删除请求中的一个属性

85、我们在web应用开发过程中经常遇到输出某种编码的字符,如iso8859-1等,如何输出一个某种编码的字符串? 答:

Public String translate (String str) { String tempStr = \"\"; try { tempStr = new String(str.getBytes(\"ISO-8859-1\"), \"GBK\"); tempStr = tempStr.trim(); } catch (Exception e) { System.err.println(e.getMeage()); } return tempStr; } 8

6、Servlet执行时一般实现哪几个方法? 答:

public void init(ServletConfig config) public ServletConfig getServletConfig() public String getServletInfo() public void service(ServletRequest request,ServletResponse response) public void destroy()

Jdbc、Jdo方面8

8、Jdo是什么?

87、Cla.forName的作用?为什么要用?

答:调用该访问返回一个以字符串指定类名的类的对象。 答:JDO是Java对象持久化的新的规范,为java data object的简称,也是一个用于存取某种数据仓库中的对象的标准化API。JDO提供了透明的对象存储,因此对开发人员来说,存储数据对象完全不需要额外的代码(如JDBC API的使用)。这些繁琐的例行工作已经转移到JDO产品提供商身上,使开发人员解脱出来,从而集中时间和精力在业务逻辑上。另外,JDO很灵活,因为它可以在任何数据底层上运行。JDBC只是面向关系数据库(RDBMS)JDO更通用,提供到任何数据底层的存储功能,比如关系数据库、文件、XML以及对象数据库(ODBMS)等等,使得应用可移植性更强。 8

9、说出数据连接池的工作机制是什么? 答:J2EE服务器启动时会建立一定数量的池连接,并一直维持不少于此数目的池连接。客户端程序需要连接时,池驱动程序会返回一个未使用的池连接并将其表记为忙。如果当前没有空闲连接,池驱动程序就新建一定数量的连接,新建连接的数量有配置参数决定。当使用的池连接调用完成后,池驱动程序将此连接表记为空闲,其他调用就可以使用这个连接。 90、Jdo是什么? 答:JDO是Java对象持久化的新的规范,为java data object的简称,也是一个用于存取某种数据仓库中的对象的标准化API。JDO提供了透明的对象存储,因此对开发人员来说,存储数据对象完全不需要额外的代码(如JDBC API的使用)。这些繁琐的例行工作已经转移到JDO产品提供商身上,使开发人员解脱出来,从而集中时间和精力在业务逻辑上。另外,JDO很灵活,因为它可以在任何数据底层上运行。JDBC只是面向关系数据库(RDBMS)JDO更通用,提供到任何数据底层的存储功能,比如关系数据库、文件、XML以及对象数据库(ODBMS)等等,使得应用可移植性更强。

Xml方面

91、xml有哪些解析技术?区别是什么? 答:有DOM,SAX,STAX等

DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问。

SAX:不现于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问 STAX:Streaming API for XML (StAX) 9

2、你在项目中用到了xml技术的哪些方面?如何实现的?

答:用到了数据存贮,信息配置两方面。在做数据交换平台时,将不能数据源的数据组装成XML文件,然后将XML文件压缩打包加密后通过网络传送给接收者,接收解密与解压缩后再同XML文件中还原相关信息进行处理。在做软件配置时,利用XML可以很方便的进行,软件的各种配置参数都存贮在XML文件中。

93、XML文档定义有几种形式?它们之间有何本质区别?解析XML文档有哪几种方式? 答:a: 两种形式 dtd schema,b: 本质区别:schema本身是xml的,可以被XML解析器解析(这也是从DTD上发展schema的根本目的),c:有DOM,SAX,STAX等

DOM:处理大型文件时其性能下降的非常厉害。这个问题是由DOM的树结构所造成的,这种结构占用的内存较多,而且DOM必须在解析文件之前把整个文档装入内存,适合对XML的随机访问

SAX:不现于DOM,SAX是事件驱动型的XML解析方式。它顺序读取XML文件,不需要一次全部装载整个文件。当遇到像文件开头,文档结束,或者标签开头与标签结束时,它会触发一个事件,用户通过在其回调事件中写入处理代码来处理XML文件,适合对XML的顺序访问 STAX:Streaming API for XML (StAX)

第18篇:Windows编程知识点总结

1、基于MFC对话框程序的框架代码主要由一下几个部分组成:

A.应用程序类

B.对话框类

C.资源文件

D.预编译文件

2、Windows是建立在消息驱动机制上的。

3、模态对话框运行机制?

当模态对话框产生后,应用程序没有处理消息,唯一的解释就是模态对话框产生后会屏蔽其他窗体消息。事实也正是如此,创建模态对话框后,应用程序只会响应该对话框的消息,知道应用收到结束模态对话框窗体的消息后,才会把控制权交还给应用程序。

4、非模态对话框

与模态对话框不同,非模态对话框不会垄断用户的输入,用户任然可以使用其他窗体。

5、属性对话框主要分为两个部分:

A.属性页

B.属性对话框架

6、属性页只是对话框的一种形式。

7、控件的逻辑顺序决定着软件中需要使用TAB进行焦点切换时的顺序排列。

8、控件按钮有四种基本类型:

A.下压按钮

B.复选按钮

C.单选按钮

D.自绘按钮

9、按钮控件会向父窗口发出控件通知消息

A.BN_CLICKED:鼠标单击消息

B.BN_DOUBLECLICKED:鼠标双击击消息

C.BN_SETFOCUS:获取用户输入焦点

D.BN_KILLFOCUS:失去用户输入焦点

10、在MFC环境下,除了使用CButton类本身的成员函数来操作按钮控件,还可以使用窗体相关类的成员函数,使用这些函数可以根据按钮ID直接进行操作。

11、MFC的CEdit类封装了编辑框控件的基本操作。

12、

UPPERCASES设置大写,

lowerCase设置小写,

Number设置只能接受数字,

Paword设置显示为*。

13、MFC的CListBox类封装了列表框控件的基本操作。

14、

AddString:添加字符串列表项

DeleteString:删除字符串列表项

Dir:从当前目录向列表框添加文件名

FindString:在列表框中查找包含指定前缀的第一个列表项

FindStringExact:在列表框中查找与指定字符串匹配的列表项

InsertString:向列表框中插入一个列表项

ResetContentp:清空编辑框和列表框的所有内容

Selectstring:在列表框中查找字符串

15、组合框有三种模式:

简易式

下拉式

下拉列表式

16、组合框有两种形式:

Dropdown样式

Drop List样式

17、MFC的CProgreCtrl类封装了进度条控件的基本操作。

18、tist->SetTimer(1,100,NULL)

设置计时器ID为1,每100ms触发一次

19、tist->KillTimer(1)

关闭计时器1

20、列表控件有四种呈现方式:

图标视图

小图标视图

列表视图

报表视图

21、文档视图结构是使用MFC开发基于文档的应用程序的基本框架,最主要的思想是数据的管理与显示分离。

22视图类CView

23、使用AppWizard创建SDI和MDI的过程相似,主要区别是创建SDI时不生成CChildFrame类,CMainFrame的基类为CMainWnd;而创建MDI时会生成两个框架类,一个是CMainFrame类,由CMDIFrameWnd类派生而来,另一个是CChildFrame类,由CMDIChildWnd类派生而来

24、文档与视图体系

一个文档可以对应多个视图,而一个视图只能对应一个文档

文档与视图的结构的优势在于数据的管理与显示分离,

在开发文档/视图体系开发应用过程时,涉及到:文档模板、文档、视图、框架窗口

25、MFC的CView类是所有视图类的基类,主要有两大工能:

将与其相关联文档的数据呈现给用户

接受用户对数据的修改,并反馈给文档

26、建立MFC单文档应用程序,实例项目名称为“single”框架会自动生成4个类:

CSingleApp类:应用程序类

CMainFrame类:框架类

CSingleDoc文档类

CSingleView类:视图类

27、在MFC中使用GDI 进行绘画操作一般会涉及两类对象:

设备上下文对象

GDI对象

28、设备上下文,是一种windows数据结构,它包含与设备绘制属性相关的信息。

29、主要有一下几种GDI 对象:

CPen对象:用来绘制线条

CBrush对象:用来填充绘制对象的内部

CBitmap对象:用来操作位图对象

CFont对象:用来绘制文本

CPalette对象:用于应用程序和色彩输出设备之间的接口

30、使用GDI进行图形绘制的一般流程:

创建GDI对象

创建获取得设备上下文对象

使用SelectObject把GDI对象选入设备上下文

使用图形输出函数在指定上下文中绘制图形

31、画刷其实是一个像素大小为8*8的位图,用多个相同的位图对封图形的内部进行填充

32、位图

位图是描述图形最简单直观的一种形式,把图像横向分为等间距的W列,纵向分为等间距的H列,于是这个图形的大小被定义为分辨率W*H

33、键盘上的每一个健都对应一个唯一的扫描码,在windows系统中为实现设备无关的要求,需要使用虚拟键值

34、当有键盘事件发生时,设备驱动器首先获取健的扫描码,并转换为虚拟键值

35、在应用程序中响应键盘消息有两种方式:

响应本进程的键盘消息

响应系统的键盘消息

36、keybd_event只是用于产生键盘事件,至于由谁来处理他并不关心。系统捕捉到键盘事件后,会转化为键盘消息的形式派发给当前系统中拥有键盘输入焦点的应用程序。SendMeage和PostMaeage的区别在于,PostMaeage首先把消息发到指定句柄所在线程的消息队列再由线程派发。SendMeage是把消息直接发送的指定句柄的窗体或控件。而往往很多情况下。指定句柄是个控件。而对控件的消息消息处理一般都是定义在主对话框上的。除非对其进行子类化,如果使用SendMeage,消息就无法到达主对话框因而无法达到预期的效果。

37、TCP协议是一个面向连接的、可靠的协议,UDP协议是一个不可靠的、无连接的协议

38、因特网控制报文协议(Internet Control Meage Protocol ,ICMP)

39、套接字是一种网络编程接口,提供了一种网络数据发送和接受机制,套接字是网络通信的基础,一个套接字表示通信的一端,使用套接字可以实现数据包在网络上的传输。

40、soket函数,用来根据指定的地址协议簇、套接字类型和协议类型创建一个套接字。

listen函数,监听远程连接

accept函数,用来接受客户端连接。

sendto函数,用来向指定地址发送数据。

recv函数,用来在已建立连接的套接字上接收数据。

recvfrom函数,用来在指定套接字上接收数据。

setsockopt函数,用来设置套接字属性。

41、进程是一个正在运行的程序的实例,有两部分组成:

一个操作系统用来管理进程的内核对象。

创建时系统所分配的资源,主要是内存地址单元。

41、进程地址空间作为一个载体。包含进城的所有数据和代码以及堆和栈。为线程的运行提供保障。

42、进程是静态的

43、线程也有两个部分组成:

线程内核对象;线程堆栈。

44、线程是动态的、可执行的、它总是在某个进程的环境中创建的。

45、每个线程都是运行在其所属进程的地址空间,因此线程只能访问本进程的地址空间,而其他进程对其是不可见的

46、所谓‘页’,只是操作系统为了更好的管理地址空间所产生的一个逻辑上的概念,把一个固定长度的地址空间作为一个页,通常为4kb,系统在在对地址空间进行管理时,把每4kb的空间作为一个单位,最终形成内存管理的多级索引结构。

47、物理存储器和进程虚拟地址空间的映射是无序的,每一个进程虚拟地址空间也只能由一个物理存储器页来映射。

48、“地址转换机构”就是负责他们彼此之间的关联。

49、虚拟内存也有三种基本状态:

空闲:地址空间没有被占用,不能使用

保留:地址空间没有被占用,但还没有与物理存储器相关联,不能使用。

提交:且已经与物理存储器相关联,可以使用。

50、内存映射文件

内存映射文件可以在进程中保留一个地址空间区域,并把磁盘文件提交给该区域。一旦映射成功,就可以在内存中直接操作文件,而不再需要使用readfile、writefile等文件操作API 。

51、情况下会使用内存映射文件:

系统使用内存映射文件加载可执行模块(.exe)和动态链接(DDL)

操作数据文件

共享内存

52、消息传递

消息传递机制并不以进程为界限,处理消息的是窗体,而与是否在同一进程无关。因而在进程间使用消息传递作为通信手段有个前提,即都是窗体应用程序。

53、共享内存的原理

使用内存共享机制,在任何一个进程内创建内存映射,却能够在其他多个进程中使用。这些进程共享的是物理内存器的同一个页面,把这些物理内存映射到虚拟内存时各个进程的虚拟地址不一定相同。当一个进程将数据写入共享内存时,其他进程可以立即获取数据变更情况,显然这种共享内存的方式是完全可以满足在进程间进行大数据快速传输任务要求的。

54、与内存映射磁盘文件一样,共享内存的本质也是内存映射机制。

55、使用系统页文件支持的内存映射文件,及共享内存。

56、进程间通信的三种基本方法:匿名管道、命名管道、邮槽

57、管道是用于进程间通信的共享内存区域。创建管道的进程称为管道服务器,而连接这两个管道的进程称为管道客户端,一个进程向管道写入信息,另外一个进程从管道读取信息

58、匿名管道

匿名管道是基于字符和半双工的,一般用于程序输入输出的重定向。如果需要获取一个基于控制台窗口应用程序的输出,此时就可以是用匿名管道,首先使用CreatePipe函数创建匿名管道。

59、命名管道的特征:

命名管道是双向的,进程间可以使用同一管道进行交互

命名管道不但可以面向字节流,还可以面向消息。所以读取进行可以读取写进程发送的不同长度的消息

多个独立的管道实例可以用同一个名称来命名。

命名管道可以用于网络间两个进程的通信,而其实现过程与本地进程通信完全一致

60、创建命名管道使用CreateNamedPipe函数

61、邮槽是实现单通道的进程间通信的通信方式。创建邮槽的进程称为邮槽服务器,向邮槽发送消息的进程称为邮槽客户端

60、创建邮槽使用CreateMailslot函数

61、windows剪贴板是一种比较简单同时也是开销比较小的进程间通信方式。

62、剪贴板通信机制

windows系统支持剪贴板IPC的基本机制室友系统预留的一块全局共享内存,可用于被各个进程暂时存储数据。写入进程首先穿件一个全局内存块,并将数据写到该内存块;接受数据的进程通过剪贴板机制获取此内存块的句柄,并完成对该内存块数据的读取。

63、剪贴板通信有5种基本情况:

文本剪贴板

位图剪贴板

自定义格式

延迟提交

多项数据

63、进程同步可分为两大类:

访问共享资源,多个进程访问进程共享资源时,需要确保资源不受破坏。

事件通知,一个线程完成某项任务后通知其他线程。

64、原子访问,是指线程对共享资源的独占式访问。

65、关键代码段,是指在执行前首先去的对共享资源的访问权,然后让代码以原子操作方式执行来访问共享资源的一种方法。

66、内核对象等待函数,其所等待的也正是内核对象的受信状态。

67、内核对象本质上只是一个内存块。

68、内核对象是操作系统对资源进行管理的单位。

69、内核对象有两种状态:已通知状态、未通知状态

70、在所有内核对象中,事件内核对象是最基本的对象,事件内核对象有两种状态:已通知状态、未通知状态。在多线程环境先经常使用事件内核对象的这种特性实现线程同步。

71、事件内核对象有两种基本类型:手动重置事件、自动重置事件。

72、等待定时器内核对象是可以在某个指定时间或者以规定的间隔发出通知信号的内核对象,一般在某个线程需要定时执行某项功能时用这种内核对项

73、信标内核对项常用于在多线程环境下控制某类资源的使用,使用信标内核对象可以让系统自动地维护资源的数量,并合理的控制线程对资源的访问情况。

74、信标的出色之处在于它们能够以原子操作方式来执行测试和设置操作,当向信标申请一个资源时,操作系统就要检验是否有这个资源可供使用,同时将可用资源的数量递减,而不让另一个线程加以干扰。只有当资源数量递减后,系统才允许另一个线程申请对资源的访问权。

75、如果线程需要访问该资源就要先获取互斥对象,所有线程都应遵循这个规则。

76、动态链接库(DLL)是Windows操作系统的基础,所有API函数都包含在DLL中。应用程序并不是只有一个可执行程序,而是由一个可执行模块和若干个DLL模块组成。当执行应用程序时,系统会把与可执行模块相链接的DLL模块加载到当前进程地址空间。

77、动态链接库(DLL)的最大特性是支持动态载入。

78、要产生可执行文件有两个步骤:

1)编译过程。编译源码,为每个C/C++源文件生成一个.obj模块,由编译器完成。

2)链接过程。把应用程序的各个.obj模块链接起来产生.exe文件,有链接器完成。

79、DLL隐式链接,是指在链接过程中,把应用程序所需要DLL模块的名字和输入符号都记录下来,使得应用程序在运行时可以动态载入该DLL。

80、与隐式链接相比,显示加载DLL主要有以下几个特性:

1)显示加载的DLL并不会在应用程序初始化时就加载,而是在运行时根据需要调用LoadLibrary或LoadLibraryEx函数来加载。

2)在应用程序输入表中没有记录。

3)隐式链接的前提是需要有DLL所对应的lib文件,如果没有lib文件就无法进行隐式链接。显示加载就没有这个限制。

81、DllMain函数是DLL的可选入口。

82、线程本地存储器(TLS)的主要作用是可以避免多个线程同时访问同一全局或者静态变量时导致的冲突。

83、TLS有两种使用方法:静态TLS和动态TLS。

84、结构异常处理有三种基本的使用方法,分别是结束异常程序、异常处理程序和顶层异常处理。

85结构化异常处理(SEH)是被作为一种系统机制引入到操作系统中的。

86、可执行文件格式(PE),其格式中得分数据结构通常定义在winnt.h中。

第19篇:软件编程实习总结_工作总结

软件编程实习总结_工作总结

实习时间:3月1日———5月9日 实习地点:广州xx大学生实训基地 实训目标:

1.通过系统性的实训,熟练掌握java se、java ee知识体系,具备java开发经验;

2.了解企业软件开发流程规范,初步具备正确的软件开发编程思路,掌握软件开发的基本方法,养成规范化项目开发习惯;3.以项目案例带动实训,以企业开发为基础,对参加实训学生进行项目分组管理,学生需按要求完成每天布置的项目案例,实训结束时必须按要求完成多个项目开发工作,通过案例练习、企业项目开发、评审,使学生初步具备软件开发的实战能力; 实习单位概述:xx科技以中关村科技园区为依托,在中国软件业发达城市——北京、上海、广州、深圳、大连、南京、武汉、杭州、西安、苏州、成都、太原等20大城市,建立了近40家培训中心,占地2万平米,年培训1万多名软件人才。已累计为ibm、微软、摩托罗拉、华为、中软、用友、yahoo、阿里巴巴、tom、新浪、搜狐、百度、联想、神州数码、大唐电信、亚信等知名it企业培养输送了7万多名中高级软件人才,目前xx全国拥有600多名员工,年产值近2亿元,是中国领先的中高端软件人才培训机构。 xx科技目前提供的职业培训包括:外企软件开发工程师、软件测试工程师、oracle数据库管理员、unix系统

管理员、网络系统工程师。 实习概况:

1.java语言简介。包括java的发展、java语言的特点、java应用程序的运行机制和环境、java的垃圾回收机制、java开发环境的安装和配置和java源程序的编译和运行等。

其中,配置java开发环境时,需要修改环境变量(先在系统变量中新建一个java_home的变量,将其值设置为jdk1.6.0的安装路径;然后修改path变量,在该变量值类加入路:%java_home%\\bin;%java_home%\\jre\\bin; 接着再修改clapath变量,若没有手动加入,在其值类加入路径:.; 最后利用javac -version测试java的版本);在编译和运行java源程序时,先使用javac *.java进行编译,再利用java *进行运行。

2.java基本语法。包括java的命名规范、注释、标识符、数据类型、变量、运算符、表达式、控制语句等。其中,java的命名规范包括:以字母(unicode)﹑“$”﹑“—”﹑为开头。 第二个字符开始是字母(unicode)、数字、“$”﹑“—”,不限制长度,不能是 java的关键字,区分大小写和可以是中文;java的基本数据类型有八种:整型类型(byte,short,int,long)、浮点类型(float,double)、字符型(char)和布尔型(boolean);java的控制语句包括:选择语句(if-else,switch-case)、循环语句(for,do-while,while)和循环跳转语句(break结束整个

循,continue结束本次循环)。

3.mysql数据库基本指令。包括创建、查询、删除数据库和表,向表中添加、删除、更新数据和查询表,修改表的结构等。 其中,列出数据库实例和表: show databases; show tables; 使用数据库实例: use databasename; 显示表中列的属性: desc tablename; 创建一张表:

create table tablename( 属性及其数据类型 ); 查询数据:

select * from tablename(where id=); 添加数据:

insert into tablename(属性1,...)values(属性值1,...); 删除数据:

delete from tablename(where id=); 更新数据:

update tablename set 字段名=„„,字段名

=„„( where id=); 表结构的修改,如增加一个字段格式:

alter table tablename add column(字段名,字段类型); 4.数组的介绍。包括数组的申明,初始化(静态和动态初始化),数组的访问、遍历和复制等。

5.string中一些api方法。如找到指定位置的字符charat(int index),求字符串的长度length(),字符串的连接concat、判断字符串是否以指定的字符结束engswith(‘c’)、判断两个字符串是否相等equal等。

6.接口的介绍。包括collection、list、hashmap等,以及这些接口的一些api的使用。 7.类的封装和重载。

8.mysql数据库与myeclipse的连接,需要在工程中导

第20篇:《网络编程课程设计》实习总结

河北科技师范学院欧美学院

实习类型教学实习实习单位河北科技师范学院欧美学院实习起止时间年月 年日 指导教师刘正林所在院(系)信息技术系专业班级网专1101学生姓名崔珺珺学号

一、实习的基本概况

(一) 理论指导

根据实训内容自行确定

(二) 项目情况

项目名称:企业门户网站

开发环境:Windows XP

项目分工情况:

我的任务:制作企业门户网站框架

二、实习过程或步骤

新建一个记事本文件,开始填写代码,我所做的是一家广告公司的主页面,所以题目的代码是这么写的:XX广告公司欢迎您!一个公司的主页必须有其他的链接,因为成员比较少,所以我做的比较简单,我用了这个代码,

a {color:black;text-decoration:none}

a:hover{color:red;text-decoration:none}

--!>

这样,超链接的字体是黑色的,但鼠标指向它以后就是红色的。

从互联网下载一张名为1后缀为JPG的图片,“XX广告,行业的领头者”下一步编写代码,并且将该1.JPG放入主页所在的文件夹,代码部分如下:

但是由于知识浅薄,图片放上去不是非常协调。

对网页上半部分的框架表格进行处理,的对字体、字号进行设置,代码如下:

首页

公司概况

公司机构

职务设置

工作科研

活动动态

联系客服

图书资料

客户服务

了解了一些代码的含义,比如一个表格可以分成很多行,用表示,比如指的是每行又可以分成许多单元格。

为了使网页有简单的交互性在主页上设置了一个登录界面和按钮,代码如下

用户名:

密码:

我连接了一个新建名为1.HTML的网页,主要是自己弄不太懂有关数据库的知识,所以只能简单的编写,这样不论在用户名、密码输入什么,都能跳入第二个页面。

进行了左侧表格框架的设计编写,代码如下:

服务导航

管理层

财务室

设计部

后续处理

服务接待

后勤供给

客户服务

订单档案

人事公告 最后。进行了简单的后续处理修改。

三、实习感受

(一) 成绩与收获

通过这次网页制作实习课程,我更加深入的了解了各种代码语言的作用,从互联网上学习了更多的知识。学习制作网页,必须先从学习HTML语言做起,弄清楚各种语言在制作网页中的重要作用,通过这次综合性实验,我已经可以灵活运用所学知识和技巧制作简单的网页。实验过程中,我尽量充分运用老师教过的知识,对所学知识有一定的巩固作用,为了做出更好的效果我也翻阅参考了其他的资料,学习到了更多网页处理的技巧,制作网页过程中我遇到了不少的问题,通过询问同学和查找资料都获得了 解决的办法,这次实践对我而言是一个不小的提升,在实践过程中学习巩固对知识能有更深的记忆。网页制作是一门很实用的学科,值得我们以后更深入的去学习。现在我掌握的制作知识还是太少,需要以后多与老师、同学们交流,自己在网络上也需要更加深入的挖掘有关知识。

(二) 问题与不足

对一些代码的作用不了解,乱加运用导致页面混乱,而且缺少独自处理错误与难题的能力,对于有些知识仅是一看就过,其实自己根本没有掌握,而且掌握了的也是一知半解,没有将知识加以用工,总是想做些什么但是真到了做的时候不会,还得去翻书。

对编程的总结范文
《对编程的总结范文.doc》
将本文的Word文档下载到电脑,方便编辑。
推荐度:
点击下载文档
相关专题
点击下载本文文档