第一篇:c语言笔试题总结
一、选择题(1)~(10)每小题2分,(11)~(50)每小题1分,共60分)
下列各题A)、B)、C)、D)四个选项中,只有一个选项是正确的,请将正确的选项涂写在答题卡相应位置上,答在试卷上不得分。
(1)在数据结构中,从逻辑上可以把数据结构分为_______。
A)动态结构和静态结构 B)紧凑结构和非紧凑结构
C)线性结构和非线性结构 D)内部结构和外部结构
答案:C
评析:逻辑结构反映数据元素之间的逻辑关系,线性结构表示数据元素之间一对一的关系,非线性结构表示数据元素之间一对多或多对一的关系。
(2)若进栈序列为l,2,3,4,进栈过程中可以出栈,则下列不可能的一个出栈序列是_______。
A)1,4,3,2 B)2,3,4,l
C)3,1,4,2 D)3,4, 2,1
答案:C
评析:栈是一种后进先出表,选项c中,先出栈的是3,说明此时栈内必然有1,2,由于l先于2进栈,所以l不可能在2之前出栈,故选项C这种出栈序列是不可能的。
(3)排序方法中,将整个无序序列分割成若干小的子序列并分别进行插入排序的方法,称为_______。
A)希尔排序 B)冒泡排序 C)插入排序 D)选择排序
答案:A
评析:希尔排序法的基本思想是:将整个无序序列分割成若干小的子序列分别进行插入排序。
(4)在顺序表(3,6,8,10,12,15,16,18,21,25,30)中,用二分法查找关键码值11,所需的关键码比较次数为_______。
A)2 B)3 C)4 D)5
答案:C
评析:二分法查找是用关键码与线性表的中间元素比较,然后根据比较结果来判断是结束查找,还是在左边或者右边子表按相同的方法继续查找。本题中,与ll比较的关键码分别为15,8,10,12四个。
(5)对于n个结点的单向链表(无表头结点),需要指针单元的个数至少为_______。
A)n-1 B)n C)n+l D)2n
答案:C
评析:在n个结点的单向链表(无表头结点)中,每个结点都有一个指针单元(即指针域),加上头指针,至少需要n+1个指针单元。
(6)在软件开发过程中,软件结构设计是描述_______。
A)数据存储结构 B)软件体系结构 C)软件结构测试 D)软件控制过程
答案:B
评析:从工程管理角度来看,软件设计分两步完成:概要设计和详细设计。概要设计(又称结构设计)将软件需求转化为软件体系结构、确定系统级接口、全局数据结构或数据库模式。
(7)模块本身的内聚是模块独立性的重要性度量因素之一。在7类内聚中,具有最强内聚 的一类是_______。
A)顺序性内聚 B)过程性内聚 C)逻辑性内聚 D)功能性内聚
答案:D
评析:内聚性是一个模块内部各元素间彼此结合的紧密程度的度量。内聚共有7类,它们之间的内聚性由弱到强排列顺序为:偶然内聚、逻辑内聚、时间内聚、过程内聚、通信内聚、顺序内聚和功能内聚。
(8)数据存储和数据流都是_______,仅仅是所处的状态不同。
A)分析结果 B)事件 C)动作 D)数据
答案:D
评析:数据流图有4种成分:源点或终点、处理、数据存储和数据流。数据存储是处于静止状态的数据,数据流是处于运动中的数据。
(9)数据的完整性是指数据的正确性、有效性和_______。
A)可维护性 B)独立性 C)安全性 D)相容性
答案:D
评析:数据模型的完整性规则是给定的数据模型中数据及其联系所具有的制约和依存规则,用以限定符合数据模型的数据库状态及其状态的变化,以保证数据的正确性、有效性和相容性。
(10)关系代数运算是以_______为基础的运算。
A)关系运算 B)谓词运算 C)集合运算 D)代数运算
答案:C
评析:关系代数运算是以关系代数作为运算对象的一组高级运算的集合。它的基本操作是并、交、差、笛卡尔积,另外还包垂直分割(投影)、水平分割(选择)、关系的结合(连接)等。
(11)能将高级语言程序转换成目标语言程序的是_______。
A)调试程序 B)解释程序 C)编译程序 D)编辑程序
答案:C
评析:用高级语言编写的程序称为“源程序”,而计算机只能识别和执行由0和l组成的二进制指令,所以高级语言必须先用一种称为“编译程序”的软件,把源程序翻译成二进制形式的“目标程序”。
(12)_______是构成c语言程序的基本单位。
A)函数 B)过程 C)子程序 D)子例程
答案:A
评析:c程序是由函数构成的。一个c源程序至少包含一个main函数,也可以包含一个main函数和若干个其他函数,因此,函数是c程序的基本单位。
(13)可以在C语言中用做用户标识符的是_______。
A)void B)as_b3 C)for D)2c
define _123-abc Do
WORD If cas SIG
答案:B
评析:c语言规定,标识符只能由字母、数字和下划线三种符号组成,而且第一个字符必须是字母或下划线。另外还需要注意的是关键字不能作标识符。选项A中void,C中for都为关键字,D中2c以字母开头。
(14)若有以下类型说明语句:
char w;int x;float y,z;
则表达式w*x+z-y的结果为________类型。
A)float B)char C)int D)double
答案:A
评析:在进行运算时,不同类型的数据参加运算,需要先将其转换成同一类型的数据,然后再进行运算。转换的顺序由低到高为:char,short→int→unsigned→long→double→float,故结果为float型。
(15)main(()
{ float x=123A56;
printf(“%-5.2fn”,x); }
以上程序输出的结果是________。
A)123.4 B)123.5 C)123.45 D)123.46
答案:D
评析:f格式符,用来输出实数,以小数形式输出。“%-m.nf”的含义是:输出数据共占m列,其中n位小数,如果输出位数小于m。则右端补空格。如果总长度大于列数,则按实际情况四舍五入输出。
(16)下面语句的输出结果是________。
Printf(“%d\n”,strlen(“\t\”\065\xff\n”));
A)14 B)8
C)5 D)输出项不合法,无正常输出
答案:C
评析:在c语言中,以“\”开头的字符均为转义字符,其中“\”后可跟l~3位八进制数或在“\”后跟字母x及l~2位十六进制数,以此来代表一个特定的字符。
(17)下列程序的输出结果是________。
main()
{ int a=0,b=0,c=0; if(++a>0lI++b>0)++c;
printf(“\na=%d,b=%d,c=%d”,a,b,C); }
A)a=0,b=0,c=0 B)a=l,b=l,c=1
C)a=l,b=O, c=I D)a=0, b=1.c=1
答案:C
评析:
“︱︱”是或运算,它有个“短路”的特点需特别注意,当“︱︱”运算符左边的表达式的值为真时,则程序就不再对“︱︱”右边的表达式的值进行运算,而是使得整个表达式的值直接为真。
(18)下列程序的输出结果是_________。
Main()
{ int i;
for(i=1;i+l;i++)
{ if(i>4){printlf(”%d”,i++);break;} }
printf(“%d”,i++);
}
A)55 B)56
C)程序错误,没有输出 D)循环条件永远为真,死循环
答案:B
评析:本程序中有个for循环,但注意到for循环的条件是“i+l”,也就是只要i+l的值为真(非零值均为真),就执行循环。当i=l的时,i+l的值为真,判断if条件不成立,执行i++,输出i的值为5。
(19)下列程序的输出结果是_________。
#define A 100
main()
{ int i=O,sum=O;
do{ if(I==(i/2)*2)continue;
sum+=i;
}while(++i printf(“%d\n”,sum); } A)2500 B)2050 C)4 D)O 答案:A 评析:本题程序的功能是求1到_99之问(包括1和99)所有奇数之和。程序中的while循环的终止条件为++i=100,在while循环体内,如果i是偶数,则执行continue,跳过这一次循环,执行下一次循环,否则求和。最后输出的值是1到99之间(包括l和99)所有奇数之和(1+99)*50/2=2500。 (20)下列程序的输出结果是_________。 main() { int I=3; switch(i) { case 1: case 2:printf(”%d”,i); case 3: case 4:break; default:printf(”OK”); } } A)0 B)3 C)OK D)没有任何输出 答案:D 评析:在题中,i的值为3,由于“case 3:”后面没有break语句,所以继续向下执行“case 4:”后面的语句,由于“case 4:”后面的语句为break强行退出switch语句,所以,本题没有任何输出。 (21)下列程序执行后的输出结果是________。 main() { int m[][3]={1,4,7,2,5,8,3,6,9}; int i,k=2: for(I=0;i<3;i++) {printf(”%d”,m[k][i]);} } A)456 B)258 C)369 D)789 答案:C 评析:根据二维数组的定义得出:m[O][O]=1,m[O][1]=4,m[O][2]=7,m[1][0]=2,rail][1]=5,m[1][2]=8,m[2][0]=3,m[2][l]=6,m[2][2]=9,所以本题的输出是第3行的值m[2][0],m[2][1],m[2][2],即369。 (22)设已定义洱口k为int类型变量,则以下for循环语句_________。 for(i=0;k=-1,k=1;i++,k++) printf(”****\n”); A)判断循环结束的条件不合法 B)是无限循环 C)循环一次也不执行 D)循环只执行一次 答案:B 评析:本题定义了一个for循环,循环变量是i,但由于本题并没有设置循环条件,所以循环的条件永远默认为真,即无限次执行循环。 (23)下面程序的输出结果是___________。 unsigned fun(unsigned num) { unsigned k=1; do{ k*=num%lO; num/=lO; }while(num); return(k); } main() { unsigned n。26; printf(”%d\n”,fun(n)); } A)0 B)4 C)12 D)无限次循环 答案:C 评析:本题定义了一个fun函数,用于num求和,具体执行过程如下: num=26:k=k*(num%10)=1*(26%10),所以k=6,num=num/10=2; num=2:k=k*(num%10)=6*(2%10),所以k=12,num=num/lO=O; num=O:while条件不成立,所以返回k的值12. (24)已知字母A的ASCII码值是65,字母a的ASCII码值是97,以下程序_______。 main() { char a=‘A’; int b=20; printf(“%d,%o”,(a=a+a,a+b,b),a+‘a’-‘A’,b); } A)表达式非法,输出零或不确定值 B)因输出项过多,无输出或输出不确定值 C)输出结果为20,141 D)输出结果为20,141,20 答案:C 评析:本题中首先输出逗号表达式“a=a+a,a+b,b”的值,即20。然后以八进制的形式输出a+‘a’-‘A’的值为97对应的八进制数141,由于最后一个表达式b没有对应输出格式的输出项表列就不会输出。 (25)C语言函数返回值的类型是由__________决定的。 A)return语句中的表达式类型 B)调用函数的主调函数类型 C)调用函数时临时 D)定义函数时所指定的函数类型 答案:D 评析:函数值的类型应当是在定义函数时指定的。在定义函数时对函数值说明的类型一般应该和return语句中的表达式类型一致,如果不_致,则以函数类型为准,即函数类型决定返回值的类型。 (26)下列程序执行后输出的结果是___________。 int d=l: fun(int p) { int d。5; d+=p++; printf(”%d,”,d); } main() { int a=3; fun(a); d+=a++: printf(”%d\n”,d); } A)8,12 B)9,13 C)8,4 D)9,5 答案:C 评析:本题执行过程如下:首先调用fun函数,使得实参a的值3传递给形参p,得到局部变量d=8,打印出局部变量d的值8;返回主函数执行“d+=a++”,此处的d为全局变量,所以d=1+3=4(由于本题是值传递,所以在函数fun中对p值的改变并不能引起a的改变),故本题的输出是8,4。 (27)已知下面的程序段,正确的判断是_________。 #define A 3 #define B(A)((_A+1)‘a) int a=3: …… X=3*(A+B(7)); A)程序错误,不允许嵌套定义 B)X=93 C)X=8l D)程序错误,宏定义不允许有参数 答案:C 评析:本题的宏定义是合法的,宏定义展开为3*(3+((A+1)*a))=3*(3+((7+1)*3))=81。 (28)定义int*swap()指的是_______。 A)一个返回整型值的函数swap() B)一个返回指向整型值指针的函数swap() C)一个指向函数swap()的指针,函数返回一个整型值 D)以上说法均错 答案:B 评析:一个函数可以带回一个整型值、字符值、实型值等,但也可以带回指针型数据,即地址。本题的定义中,包括括号和·号,由于f)优先级高于t。故它是一个返回整型指针的函数。 (29)以下程序段的输出结果是__________。 main() { char s1[10],s2[10],s3[10]; scanf(”%s”,s1);gets(s2);gets(s3); puts(s 1);puts(s2);puts(s3); } 输入数据如下:(此处代表回车符)aaa bbb A)aaa B)aaa C)aaa\0bbb\0 D)aaabbb bbb bbb 答案:B 评析:scanf是标准输入函数,在输入字符串aaa时,实际的内容为“aaa ”,“\0”是由系统自动加入的;gets的功能是从终端读入一行字符,即一直读到换行符为止,并由系统自动以“\0”代替换行符。 (30)下述函数功能是________。 Int fun(char*x) { char*y=x; while(*y++); return y-x-l; } A)求字符串的长度 B)求字符串存放的位置 C)比较两个字符串的大小 D)将字符串x连接到字符串y后面 答案:A 评析:在函数体内定义一字符型指针并指向形参,然后遍历其中各字符直到NULL,最后返回字符串首尾地址的差值,即字符串的长度。 (31)以下程序的输出结果是_________。 main() { char str[12]={‘s’,‘t’,‘r’,‘i’,‘n’,‘ g’}; printf(”%d\n”,strlen(str)); } A)6 B)7 C)ll D)12 答案:A 评析:在c语言中,字符串的长度是其首字符到NULL(不含)字符的总字符个数。本题定义字符数组str的同时,对第7个元素,由系统自动添加上“\0”,故字符串的长度为6。 (32)请读程序段: char str[]=”ABCD”,*p=str; printf(”%d\n”,*(p+4)); 程序段的输出结果是_________。 A)68 B)0 C)字符‘D’的地址 D)不确定的值 答案:B 评析:在对字符数组赋字符串值时,系统会自动在字符串的末尾加上一个字符串结束标志“\0”,故指向字符数组的指针p的+(p+4)的值为“\0”。由于“\0”的编码值就是0,所以本题输出为0。 (33)若有定义:int a[4][10];,则以下选项中对数组元素a[i][j]引用错误的是________。 (0<=i<4,0<=j<10) A)*(&a[O][O]+10*i+j)B)*(a+i)+j C)*(*(a+i)+j)D)*(a[i]+j) 答案:B 评析:本题中选项B是错误的引用,*(a+i)+j只代表了a[i][i]的地址。 (34)设有以下语句: char strl[]=”string”,str2[8]。str3。str4=”strin∥; 则__________不是对库函数的正确调用。 A)strcpy(strl,”HELLOl”); B)strcpy(str2,”HELL02”); C)strcpy(str3,”HELL03”); D)strcpy(str4,”HELL04”); 答案:C 评析:c语言中:sgcpy(stl,st2);,其两个参数均为字符指针或字符数组,选项c中的目的串指针str3没有指向具体有效的存储单元,故是错误的调用。 (35)请读程序: #include #include main(){ char*sl=”AbCdEf”,*s2=”aB”; s1++;s2++; printf(”%d\n”,strcmp(s 1,s2)); } 上面程序的输出结果是___________。 A)正数 B)负数 C)零 D)不确定的值 答案:A 评析:函数strcmp的功能是比较字符串s1和s2,如果sl>s2,则返回个正数;如果sls2,所以函数的值为正数。 (36)下面程序的输出是_________。 char s[]=”ABcD”; main() { char*p; for(p=s;p printf(”%s\n”,p); } A)ABCD B)A C)D D)ABCD BCD B C ABC CD C B AB D D A A 答案:A 评析:在第一次执行for循环时,字符数组的首地址赋给了指针变量p,使得指针变量p指向了s的首地址,输出p所指向的字符串;第二次执行for循环时,p值增加1,p指向了s的第二个元素输出BCD;第三次输出CD;第四次输出D;直到p指向字符串的结束字符“\0”,for循环终止执行。 (37)以下程序输出的结果为__________。 main() { char* alpha[6]={“ABCD”,EFGH”,”IJKL”,”MNOP”,”QRST”,”UVwX”}; char**p; int i: p=alpha; for(I=0;i<4;i++) printf(”%s”,p[I]); } A)ABCDEFGHIJKL B)ABCD C)ABCDEFGHIJKLMNOP D)AEIM 答案:C 评析:alpha[O]指向“ABCD”的首地址;alpha[1]指向“EFGH”的首地址;alpha[2]指向“IJKL”的首地址,依此类推。当执行p=alpha后,p指向指针数组alpha的首地址。for循环中输出了4个字符串。 (38)下面程序的输出结果是_________。 #include main() { char*p[]={”B00L”,”0PK”,”H”,”SP”}; int i: for(i=3;i>=0;i–,i–) printf(“%c”,*p[i]); printf(”\n”); } A)SO B)SP C)SPOPK D)SHOB 答案:A 评析:p[0]存放的是“BOOL\0”的首地址;p[1]存放的是“OPK\0”的首址等。 在printf语句中输出的+p[I]表示p[i]字符串的第一个字符。在for循环中,i的初值为3,那么输出的第一个字符为“s”,接着两次i–,则输出的值为+p[1],即字符“0”,所以本题的输出为SO。 (39)以下程序的输出结果是_________。 #include void prt(int*x,int*y,int*z) { printf(”%d,%d,%d\n”,++*x,++*y*(z++));} int a=10,b=40,c=20; main() { prt(&a,&b&C); prt(&a,&b,&C); } A)ll,42,3l B)ll,41,20 C)1l,21,40 D)11,41,2l 12,22,41 12,42,20 11,2l,41 12,42,22 答案:B 评析:由于实参传送的是变量的地址,所以对形参指针所指向的单元内容的改变,即对实参内容的改变。 (40)若一个外部变量的定义形式为static int x;,那么,其中static的作用应该是_______。 A)将变量存储在静态存储区 B)使变量x可以由系统自动初始化 C)使x只能在本文件内引用 D)使x的值可以永久保留 答案:C 评析:事实上,无论有无static修饰,外部变量都具有A、B和c三种特性。作为一种修饰,static仅是限制此类型外部变量的引用范围:只能在定义它的文件范围内使用。 (41)以下程序的输出结果是________。 #include #define SQR(x)x*x main() { int a,k=3; a=++SQR(k+1); printf(”%d\n”,a); } A)8 B)9 C)17 D)20 答案:B 评析:本题宏替换中遇到形参x以实参k+l代替,其它字符不变。sQR(k+1)展开后应为字符串k+l*k+l。 (42)下面是对宏定义的描述,不正确的是_______。 A)宏不存在类型问题,宏名无类型,它的参数也无类型 B)宏替换不占用运行时间 C)宏替换时先求出实参表达式的值,然后代入形参运算求值 D)宏替换只不过是字符替代而已 答案:C 评析:宏替换实质上就是字符替代,它不可能进行计算,故c错误。带参数的宏与函数相比,宏在程序编译之前已经将代码替换到程序内,执行时不会产生类似于函数调用的问题,可以说不占用运行时间。 (43)以下程序(程序左边的数字为附加的行号)________。 1#include 2#include 3main() 4{char s[]=”string”; puts(s); strcpy(s,”hello”); printf(”%3s\n”,s);} A)没有错 B)第l行有错 C)第6行有错 D)第7行有错 答案:B 评析:字符串复制函数strcpy包含在头文件string.h中,因此,程序中的第l行文件包含命令是错误的。 (44)若有如下说明,则__________的叙述是正确的。 struct st { int a; int b[2l; }a; A)结构体变量a与结构体成员a同名,定义是非法的 B)程序只在执行到该定义时才为结构体st分配存储单元 C)程序运行时为结构体st分配6个字节存储单元 D)类型名struct st可以通过extern关键字提前引用(即引用在前,说明在后) 答案:D 评析:结构体变量a与结构体成员a同名是合法的定义,引用成员a的方法是a.a,变量a处于不同的“层次”上,系统完全能够分清。st是一个结构体名,不会为结构体名分配存储空间,应该是在运行时为结构体变量a分配6个字节的存储单元,故选项B和选项C错误。 (45)若有以下结构体定义,则________是正确的引用或定义。 struct example { int x; int y; }v1; A)example.x=10 B)example v2.x=10 C)struct v2;v2.x=lO D)struct example v2={10}; 答案:D 评析:在定义结构体变量时,不能只用结构体名example或关键字strum进行定义,必需要用结构体类型名struct example定义,在引用结构体成员变量时,需要用结构体变量名进行引用,所以选D。 (46)下列程序的执行结果是_________。 #include union un { int i; char c[21; }; void main() { union un x; x.c[0]=10: x.c[1]=1: printf(“\n%d”,x.i); } A)266 B)ll C)265 D)138 答案:A 评析:由于本题定义的是共用体,所以成员表列中的整型变量x与字符数组c共占用同一个存储单元,且此存储单元为2个字节,通常c[O]位于低字节,c[1]位于高字节,所以x.i的值为266。 (47)已知形成链表的存储结构如下图所示,则下述类型描述中的空白处应填_______。 struct link Datanext{ char data; ___________ }node; A)struct link next B)link*next C)struct next link D)struct link*next 答案:D 评析:在单向链表中,由于每个结点需要存储下一个结点的地址,且下一个结点的数据类型与前一个结点的数据类型完全相同,故应为struct link*next。 (48)已知小写字母a的ASCII码为97,大写字母A的ASCII.码为65,以下程序的结果是__________。 main() { unsigned int a=32,b=66; printf(“%c\n”,atb); } A)66 B)98 C)b D)B 答案:C 评析:位运算符“l”的作用是按位或,即两个二进制数的相应位中只要有一个为1,该位的结果值为l。最后以字符型输出,98对应的字符“b”。 (49)C语言库函数龟ets(str,n,fp)的功能是_________。 A)从fp指向的文件中读取长度n的字符串存入str指向的内存 B)从fp指向的文件中读取长度不超过n-l的字符串存入str指向的内存 C)从fp指向的文件中读取n个字符串存/Xstr指向的内存 D)从str读取至多n个字符到文件fp 答案:B 评析:fgets函数的作用是从指定的文件读入一个字符串。fgets(str,n,fp);中的n为要求得到的字符的个数,但只从fb指向的文件输入n-1个字符,然后在最后加一个‘\O’字符,因此得到的字符串共有n个字符。 (50)下述程序向文件输出的结果是__________。 #include void main() { FILE*fp=fopen(“TEST”,”wb”); fprintf(fp,”%d%5.0f%c%d”,58,76273.0,’-',2278); fclose(fp); } A)58 76273-2278 B)5876273。.000000-2278 C)5876273-2278 D)因文件为二进制文件而不可读 答案:C 评析:fprintf函数工作时,多个数据间不会自动加分隔符,选项A错误;浮点数的输出格式是“%5.0f”表明其小数部分输出O位,即没有输出,所以选项B也是错误的。 二、填空题(每空2分,共40分) 请将每空的正确答案写在【l】至【20】序号的横线上,答在试卷上不得分。(1)对于长度为n的顺序存储的线性表,当随机插入和删除一个元素时,需平均移动元素 的个数为 【l】。 答案:【1】n/2 评析:删除一个元素,平均移动的元素个数为(n-l+n-2+……+0)n=(n-1)/2;插入一个元素,平均移动元素个数为(n+n-l+n-2+……+1)n=(n+1)/2;所以总体平均移动元素个数为n/2。 (2)注释说明了程序的功能,它分为 【2】 注释和功能性注释。 答案:【2】序言性 评析:注释一般分为序言性注释和功能性注释。 (3)软件测试中路径覆盖测试是整个测试的基础,它是对软件 【3】 进行测试。 答案:【3】结构 评析:路径测试是白盒测试方法中的一种,它要求对程序中的每条路径最少检查一次,目的是对软件的结构进行测试。 (4)数据库技术的主要特点为数据的集成性、数据的高 【4】 和低冗余性、数据独立性和数据统一管理与控制。 答案:【4】共享性 评析:数据库技术的主要特点有以下几个方面:数据的集成性,数据的高共享性与低冗余性,数据韵独立性,数据统一管理与控制。 (5)数据元素之间 【5】 的整体称为逻辑结构。 答案:【5】逻辑关系 评析:数据元素之间逻辑关系的整体称为逻辑结构。数据的逻辑结构就是数据的组织形式。 (6)若有定义int m=5,y=2,则执行表达式y+=y-=m*=y后,y的值为 【6】。 答案:【6】.16 评析:将赋值运算符右侧的“表达式”的值赋给左侧的变量,并且赋值运算符按照“白右而左”的结合顺序,本题表达式应先算m的值为10,再运算y的值为8,最后计算y=y+(-8)=-8+(-8)=-16。 (7)若x和y都是double型变量,]ix的初值为3.0,y的初值为2.0,则表达式pow(y,fabs(x))的值为 【7】。 答案:【7】8.000000 评析: fabs函数功能是求x的绝对值,计算结果为double型。pow功能是计算x的y次方的值,计算结果同样为double型。所以本题表达式相当于2.0的3.0次方,结果为8.000000。 (8)设有char a,b;,若要通过a&b运算屏蔽掉a中的其它位,只保留第2位和第8位(右起为第1位),则b的二进制是 【8】。 答案:【8】10000010 评析:运算“&”的规则是只有当两个相应的二进制位都为1时,该位的结果才为1。要保留第2、8位,只要将其与二进制数10000010相与。 (9)下列程序执行后输出的结果是 【9】。 f(int a) { static c=0; C=a+C++: return(C); } main() { int a=2,i,k; for(i=O;i<2;i++) k=f(a++); printf(”%d\n”,k); } 答案:【9】7 评析:在程序执行时,static变量仅初始化一次,下次使用时将使用上次保存的值。 (10)下面程序执行后输出的结果是 【10】。 int m=13: int fun(int x,int y) { int m=3; return(x*y-m); } main() { int a=7,b=5; printf(”%d\n”,fun(a,b)/m); } 答案:【10】2 评析:本题变量m既是外部变量(值是13),又是fun函数的局部变量(值为3)。函数fun(x*y-m)的值为7*5-3=32,在main函数中,ftm(a,b)/m中的m应取外部变量的值13,因此输出2。 (11)下列程序执行后输出的结果是 【11】。 main() { nt arr[10],i,k=0; for(I=0;i for(I=1;i<4;i++)k+=arr[i]+i; printf(”%d\n”,k); } 答案:【11】12 评析:本题的第一个fur循环用于给数组arr赋初值,第二个for循环用于求和运算。由于第二个fur循环初始值为1,而循环条件为i<4,所以求的是art[1]到arr[3]及i的和,所以输出结果为12。 (12)下列程序执行后输出的结果是 【12】。 struct s {int x,y;}data[2]={10,100,20,200}; main(){ struct s*p=data; printf(”%dn”,++(p->x)); } 答案:【12】11 评析:程序中结构体数组data首地址(即&data[0])赋值给结构体指针变量p,++(p->x)表示先将p所指向的结构体变量的成员x值加1,然后将此x(即data[01.x]输出。 (13)下面程序有两个printf语句,如果第一个printf语句输出的是194,则第二个print语句的输出结果是 【13】。 main() { int a [10]={1,2,3,4,5,6,7,8,9,0},*p; p=a; printf(”%X\n”,p); printf(”%x\n”,p+9); } 答案:【13】la6 评析:对于指针变量的运算,就是对地址的运算。本题中由于指针指向的是整型变量,所以,使指针变量移动9个位置也就是移动18个字节。注意,本题是以16进制输出的。 (14)以下函数的功能是计算s=l+l/2!+l/3!+…+l/n!,请填空.double fun(int n) { double s=O.O,fac=1.O; int i,k=1; for(i=l;i<=n;i++) { 【14】 ; fat=fat/k; s=s+fac; } } 答案:【14】k=k*i 评析:本题中通过for循环求s表达式中每一项的和,表达式“fac=fac/k;”求的是每一项的值,所以k的值应为n!,在求n!的时候,可以用上次循环阶乘的值乘i,就可以直接得此次n!,故本题填k=k*i。 (15)下面程序的运行结果是 【15】。 main() { unsigned a=0112,x; x=a>>3: printf(”x=%o”,x); } 答案:【15】x=11 评析:在对无符号数的右移是高位补0。 (16)函数delete(s,i,n)是作用是从字符串s中删除从第i个字符开始的n个字符,请填空。 void delete(char s[],int i,int n) { int j,k,length=O; while(s[1ength]) 【16】 ; –i: j=i; } if(【17】) { k=i+n; if(i+n<=length) while(k s[j++]=s[k++]; s[j]=‘\0’;} 答案:【16】length++ 【17】i 评析:第一个循环极有可能是计算串的长度,在i<=length时字符才被删除,被删除的是从第i个到第i+n或最后一个间的所有字符。删除前,应判断i<=length。由于已经进行了一i运算,故实际应填入i (17)下述函数统计一个字符串中的单词个数,单词是指处在空格之间的字符序列,请填空。 int word(char*s) { int num=O,flag=O; while(*s) { if(【18】 ==”)flag=O; else if(【19】){flag=1;num++} } return 【20】 ; } 答案:【18】*s++ 【19】flag==O或*(s-1)==” 【20】num 评析:在统计字符串单词个数的算法中,本题的flag是为了记录一个单词是否结束。第18空应填*s++;如果某个字符不是空格,则必需判断它是否是单词,如是,则使得flag的标志为1,num的值加1。本题判断方法是:先判断s所指向的字符是否为空格,如果是则使得flag=O,否则判断前一个字符是否是空格,如果是则说明这个字符是一个单词的开始,将flag标志为1,num的值加1,如果不是,则不必记录。故第19空应flag==O或*(s-1)==”;最后一个空格需填写的是返回的单词的个数,即num。 网络安全应具有以下五个方面的特征: 保密性:信息不泄露给非授权用户、实体或过程,或供其利用的特性。 完整性:数据未经授权不能进行改变的特性。即信息在存储或传输过程中保持不被修改、不被破坏和丢失的特性。 可用性:可被授权实体访问并按需求使用的特性。即当需要时能否存取所需 的信息。例如网络环境下拒绝服务、破坏网络和有关系统的正常运行等都属于对可用性的攻击; 可控性:对信息的传播及内容具有控制能力。 可审查性:出现安全问题时提供依据与手段第一部分:基本概念及其它问答题 1、关键字static的作用是什么? 这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用: 1).在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 2).在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。 3).在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。(本地化数据和代码范围的好处和重要性)。 2、“引用”与指针的区别是什么? 答、1)引用必须被初始化,指针不必。 2)引用初始化以后不能被改变,指针可以改变所指的对象。3)不存在指向空值的引用,但是存在指向空值的指针。 指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。 流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。 3、.h头文件中的ifndef/define/endif 的作用? 答:防止该头文件被重复引用。 4、#include 与 #include “file.h”的区别? 答:前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。 5、描述实时系统的基本特性 答 :在特定时间内完成特定的任务,实时性与可靠性。 6、全局变量和局部变量在内存中是否有区别?如果有,是什么区别? 答 :全局变量储存在静态数据区,局部变量在堆栈中。 7、什么是平衡二叉树? 答 :左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1。 8、堆栈溢出一般是由什么原因导致的? 答 :1.没有回收垃圾资源2.层次太深的递归调用 9、冒泡排序算法的时间复杂度是什么?答 :O(n^2) 10、什么函数不能声明为虚函数?答:constructor 11、队列和栈有什么区别?答:队列先进先出,栈后进先出 12、不能做switch()的参数类型答 :switch的参数不能为实型。 13、局部变量能否和全局变量重名?答:能,局部会屏蔽全局。要用全局变量,需要使用”::” 局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内 14、如何引用一个已经定义过的全局变量? 答、可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变量,假定你将那个变量写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。 15、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么? 答、可以,在不同的C文件中以static形式来声明同名全局变量。可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错。 16、语句for(;1 ;)有什么问题?它是什么意思?答、和while(1)相同,无限循环。 17、do„„while和while„„do有什么区别?答、前一个循环一遍再判断,后一个判断以后再循环。 18、statac 全局变量、局部变量、函数与普通全局变量、局部变量、函数 答、全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。 static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件 static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值; static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝 19、程序的内存分配 答:一个由c/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。 4、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。 5、程序代码区—存放函数体的二进制代码 例子程序 这是一个前辈写的,非常详细 //main.cpp int a=0;//全局初始化区 char *p1;//全局未初始化区 main(){ intb;栈 char s[]=”abc”;//栈 char *p2;//栈 char *p3=”123456″;//123456 在常量区,p3在栈上。static int c=0; //全局(静态)初始化区 p1 =(char*)malloc(10);p2 =(char*)malloc(20);//分配得来得10和20字节的区域就在堆区。 strcpy(p1,”123456″);//123456 放在常量区,编译器可能会将它与p3所向”123456″优化成一个地方。} 20、解释堆和栈的区别 答:堆(heap)和栈(stack)的区别(1)申请方式 stack:由系统自动分配。例如,声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间 heap:需要程序员自己申请,并指明大小,在c中malloc函数如p1=(char*)malloc(10);在C++中用new运算符,如p2=(char*)malloc(10);但是注意p1、p2本身是在栈中的。(2)申请后系统的响应 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 (3)申请大小的限制 栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。 堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。(4)申请效率的比较: 栈:由系统自动分配,速度较快。但程序员是无法控制的。 堆:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.另外,在WINDOWS下,最好的方式是用Virtual Alloc分配内存,他不是在堆,也不是在栈,而是直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活。(5)堆和栈中的存储内容 栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。 当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。 堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。(6)存取效率的比较 char s1[]=”aaaaaaaaaaaaaaa”;aaaaaaaaaaa是在运行时刻赋值的; char *s2=”bbbbbbbbbbbbbbbbb”;而bbbbbbbbbbb是在编译时就确定的; 但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。比如: #include voidmain(){ char a=1;char c[]=”1234567890″;char *p=”1234567890″;a = c[1];a = p[1];return;} 对应的汇编代码 10:a=c[1];004010678A4DF1movcl,byteptr[ebp-0Fh] 0040106A884DFCmovbyteptr[ebp-4],cl 11:a=p[1];0040106D8B55ECmovedx,dwordptr[ebp-14h] 004010708A4201moval,byteptr[edx+1] 004010738845FCmovbyteptr[ebp-4],al 第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。 21、什么是预编译,何时需要预编译? 答:预编译又称为预处理,是做些代码文本的替换工作。处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等,就是为编译做的预备工作的阶段,主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。c编译系统在对程序进行通常的编译之前,先进行预处理。 c提供的预处理功能主要有以下三种:1)宏定义 2)文件包含 3)条件编译 22、关键字const是什么含意? 答: “const意味着常数”业余者。“只读”正确的答案。const int a;a是一个常整型数 int const a;a是一个常整型数 const int *a;a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。int * const a;a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)int const * a const;a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的) 1).关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。) 2).通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 3).合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现 23、关键字volatile有什么含意 并给出三个不同的例子。 答:一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 1).并行设备的硬件寄存器(如:状态寄存器) 2).一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)3).多线程应用中被几个任务共享的变量 1).一个参数既可以是const还可以是volatile吗?解释为什么。是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 2).一个指针可以是volatile 吗?解释为什么。是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。3).下面的函数有什么错误: int square(volatile int *ptr){return *ptr * *ptr;} 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码 int square(volatile int *ptr){int a,b;a = *ptr;b = *ptr;return a * b;} 由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下: long square(volatile int *ptr){ int a;a = *ptr;return a * a;} 24、三种基本的数据模型 答:按照数据结构类型的不同,将数据模型划分为层次模型、网状模型和关系模型。 25、结构与联合有和区别? 答:(1).结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。 (2)对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的 26、描述内存分配方式以及它们的区别? 答:1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。 2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。 3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多 27、请说出const与#define 相比,有何优点? 答:Const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。 1)const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。 2)有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。 28、简述数组与指针的区别? 答:数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。(1)修改内容上的差别 char a[] = “hello”;a[0] = ‘X’; char *p = “world”;// 注意p 指向常量字符串 p[0] = ‘X’;// 编译器不能发现该错误,运行时错误 (2)用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个 指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。char a[] = “hello world”;char *p = a;cout<< sizeof(a)<< endl;// 12 字节 cout<< sizeof(p)<< endl;// 4 字节 计算数组和指针的内存容量 void Func(char a[100]){ cout<< sizeof(a)<< endl;// 4 字节而不是100 字节 } 29、分别写出BOOL,int,float,指针类型的变量a 与“零”的比较语句。答:BOOL : if(!a)or if(a)int : if(a == 0)float : const EXPRESSION EXP = 0.000001 if(a < EXP && a >-EXP)pointer : if(a!= NULL)or if(a == NULL) 30、如何判断一段程序是由C 编译程序还是由C++编译程序编译的? 答:#ifdef __cplusplus cout<<“c++”;#else cout<<“c”;#endif 31、论述含参数的宏与函数的优缺点 答: 带参宏 函数 处理时间 编译时 程序运行时 参数类型 没有参数类型问题 定义实参、形参类型 处理过程 不分配内存 分配内存 程序长度 变长 不变 运行速度 不占运行时间 调用和返回占用时间 32、用两个栈实现一个队列的功能?要求给出算法和思路!答:设2个栈为A,B, 一开始均为空.入队:将新元素push入栈A;出队:(1)判断栈B是否为空;(2)如果不为空,则将栈A中所有元素依次pop出并push到栈B;(3)将栈B的栈顶元素pop出; 这样实现的队列入队和出队的平摊复杂度都还是O(1), 比上面的几种方法要好 33、嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢? 答:这个问题用几个解决方案。我首选的方案是: while(1){ } 一些程序员更喜欢如下方案: for(;;){ } 第三个方案是用 goto Loop:...goto Loop;应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。 34、位操作(Bit manipulation) 答: 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。对这个问题有三种基本的反应 1)不知道如何下手。该被面者从没做过任何嵌入式系统的工作。 2)用bit fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。我最近不幸看到 Infineon为其较复杂的通信芯片写的驱动程序,它用到了bit fields因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。从道德讲:永远不要让一个非嵌入式的家伙粘实际硬件的边。 3)用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下: #define BIT3(0x1 << 3)static int a;void set_bit3(void){ a |= BIT3;} void clear_bit3(void){ a &= ~BIT3;} 一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作。 35、访问固定的内存位置(Accessing fixed memory locations) 答:嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。 这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下: int *ptr;ptr =(int *)0x67a9;*ptr = 0xaa66;A more obscure approach is: 一个较晦涩的方法是: *(int * const)(0x67a9)= 0xaa55;即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。 36、中断(Interrupts) 答: 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。 __interrupt double compute_area(double radius){ double area = PI * radius * radius;printf(“nArea = %f”, area);return area;} 这个函数有太多的错误了,以至让人不知从何说起了: 1)ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。 2)ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。 3)在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。4)与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。 37、动态内存分配(Dynamic memory allocation) 答:尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中,动态分配内存可能发生的问题是什么? 这里,我期望应试者能提到内存碎片,碎片收集的问题,变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了(主要是 P.J.Plauger, 他的解释远远超过我这里能提到的任何解释),所有回过头看一下这些杂志吧!让应试者进入一种虚假的安全感觉后,我拿出这么一个小节目: 下面的代码片段的输出是什么,为什么? char *ptr;if((ptr =(char *)malloc(0))== NULL)puts(“Got a null pointer”);else puts(“Got a valid pointer”);//get this 这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc,得到了一个合法的指针之后,我才想到这个问题。这就是上面的代码,该代码的输出是“Got a valid pointer”。我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。 38、Typedef 答:Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子: #define dPS struct s * typedef struct s * tPS;以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢?(如果有的话)为什么? 这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:typedef更好。思考下面的例子: dPS p1,p2;tPS p3,p4;第一个扩展为 struct s * p1, p2;上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。 39、用变量a给出下面的定义 答:a)一个整型数(An integer) b)一个指向整型数的指针(A pointer to an integer) c)一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer)d)一个有10个整型数的数组(An array of 10 integers) e)一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)f)一个指向有10个整型数数组的指针(A pointer to an array of 10 integers) g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer) h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(An array of ten pointers to functions that take an integer argument and return an integer)答案是: a)int a;// An integer b)int *a;// A pointer to an integer c)int **a;// A pointer to a pointer to an integer d)int a[10];// An array of 10 integers e)int *a[10];// An array of 10 pointers to integers f)int(*a)[10];// A pointer to an array of 10 integers g)int(*a)(int);// A pointer to a function a that takes an integer argument and returns an integer h)int(*a[10])(int);// An array of 10 pointers to functions that take an integer argument and return an integer 40、解释局部变量、全局变量和静态变量的含义。答: 41、写一个“标准”宏 答:交换两个参数值的宏定义为:.#define SWAP(a,b)(a)=(a)+(b);(b)=(a)-(b);(a)=(a)-(b);输入两个参数,输出较小的一个:#define MIN(A,B)((A)<(B))?(A):(B))表明1年中有多少秒(忽略闰年问题):#define SECONDS_PER_YEAR(60 * 60 * 24 * 365)UL #define DOUBLE(x)x+x 与 #define DOUBLE(x)((x)+(x))i = 5*DOUBLE(5); i为30 i = 5*DOUBLE(5); i为50 已知一个数组table,用一个宏定义,求出数据的元素个数 #define NTBL #define NTBL(sizeof(table)/sizeof(table[0])) 42、A.c 和B.c两个c文件中使用了两个相同名字的static变量,编译的时候会不会有问题?这两个static变量会保存到哪里(栈还是堆或者其他的)? 答:static的全局变量,表明这个变量仅在本模块中有意义,不会影响其他模块。他们都放在数据区,但是编译器对他们的命名是不同的。 如果要使变量在其他模块也有意义的话,需要使用extern关键字。 43、一个单向链表,不知道头节点,一个指针指向其中的一个节点,问如何删除这个指针指向的节点? 答:将这个指针指向的next节点值copy到本节点,将next指向next->next,并随后删除原next指向的节点。将这个节点复制成下一个节点的值,然后删除下一个节点 node *p;// 当前节点 node *q;q = p-> next;p.data = q.data;// 复制q节点到p p-> next = q-> next;// 删除q free(q); 第二部分:程序代码评价或者找错 1、下面的代码输出是什么,为什么? void foo(void){ unsigned int a = 6;int b =-20;(a+b > 6)? puts(“> 6″): puts(“<= 6“);} 这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 ”>6″。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。 2、评价下面的代码片断: unsigned int zero = 0;unsigned int compzero = 0xFFFF;/*1′s complement of zero */ 对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下: unsigned int compzero = ~0;这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。 3、C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么? int a = 5, b = 7, c;c = a+++b;这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成: c = a++ + b;因此, 这段代码持行后a = 6, b = 7, c = 12。 如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。 4、设有以下说明和定义: typedef union {long i;int k[5];char c;} DATE;struct data { int cat;DATE cow;double dog;} too;DATE max;则语句 printf(“%d”,sizeof(struct date)+sizeof(max));的执行结果是? 答、结果是:52。DATE是一个union, 变量公用空间.里面最大的变量类型是int[5], 占用20个字节.所以它的大小是20 data是一个struct, 每个变量分开占用空间.依次为int4 + DATE20 + double8 = 32.所以结果是 20 + 32 = 52.当然„在某些16位编辑器下, int可能是2字节,那么结果是 int2 + DATE10 + double8 = 20 5、请写出下列代码的输出内容 #include main(){ int a,b,c,d;a=10;b=a++;c=++a;d=10*a++;printf(“b,c,d:%d,%d,%d”,b,c,d);return 0;} 答:10,12,120 6、写出下列代码的输出内容 #include int inc(int a){ return(++a);} int multi(int*a,int*b,int*c){ return(*c=*a**b);} typedef int(FUNC1)(int in);typedef int(FUNC2)(int*,int*,int*);void show(FUNC2 fun,int arg1, int*arg2){ INCp=&inc;//代码不完整 int temp =p(arg1);fun(&temp,&arg1, arg2);printf(“%dn”,*arg2);} main(){ int a;show(multi,10,&a);return 0;} 答:110 7、请找出下面代码中的所有错误 说明:以下代码是把一个字符串倒序,如“abcd”倒序后变为“dcba” 1、#include”string.h” 2、main() 3、{ 4、char*src=”hello,world”; 5、char* dest=NULL; 6、int len=strlen(src); 7、dest=(char*)malloc(len); 8、char* d=dest; 9、char* s=src[len]; 10、while(len–!=0) 11、d++=s–; 12、printf(“%s”,dest); 13、return 0; 14、} 答: 方法1: int main(){ char* src = “hello,world”;int len = strlen(src);char* dest =(char*)malloc(len+1);//要为 分配一个空间 char* d = dest;char* s = &src[len-1];//指向最后一个字符 while(len–!= 0)*d++=*s–;*d = 0;//尾部要加 printf(“%sn”,dest);free(dest);// 使用完,应当释放空间,以免造成内存汇泄露 return 0;} 方法2: #include #include main(){ char str[]=”hello,world”;int len=strlen(str);char t;for(int i=0;i { t=str[i];str[i]=str[len-i-1];str[len-i-1]=t;} printf(“%s”,str);return 0;} 8、请问下面程序有什么错误? int a[60][250][1000],i,j,k;for(k=0;k<=1000;k++)for(j=0;j<250;j++)for(i=0;i<60;i++)a[i][j][k]=0;答案:把循环语句内外换一下 9、请问下面程序会出现什么情况?.#define Max_CB 500 void LmiQueryCSmd(Struct MSgCB * pmsg){ unsigned char ucCmdNum;......for(ucCmdNum=0;ucCmdNum 10、以下3个有什么区别 char * const p;//常量指针,p的值不可以修改 char const * p;//指向常量的指针,指向的常量值不可以改 const char *p; //和char const *p 11、写出下面的结果 char str1[] = “abc”;char str2[] = “abc”;const char str3[] = “abc”;const char str4[] = “abc”;const char *str5 = “abc”;const char *str6 = “abc”;char *str7 = “abc”;char *str8 = “abc”;cout <<(str1 == str2)<< endl;cout <<(str3 == str4)<< endl;cout <<(str5 == str6)<< endl;cout <<(str7 == str8)<< endl;结果是:0 0 1 1 解答:str1,str2,str3,str4是数组变量,它们有各自的内存空间; 而str5,str6,str7,str8是指针,它们指向相同的常量区域。 12、以下代码中的两个sizeof用法有问题吗? void UpperCase(char str[])// 将 str 中的小写字母转换成大写字母//这里这个str 是指针 { for(size_t i=0;i 度: ” << sizeof(str)/sizeof(str[0])<< endl;UpperCase(str);' cout << str << endl;答:函数内的sizeof有问题。根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的str是一个静态定义的数组,因此其大小为6,函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。 13、写出输出结果 main(){ int a[5]={1,2,3,4,5};int *ptr=(int *)(&a+1); printf(“%d,%d”,*(a+1),*(ptr-1));} 输出:2,5 *(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5 &a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int) int *ptr=(int *)(&a+1);则ptr实际是&(a[5]),也就是a+5 原因如下:&a是数组指针,其类型为 int(*)[5];而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同,a是长度为5的int数组指针,所以要加 5*sizeof(int),所以ptr实际是a[5],但是prt与(&a+1)类型是不一样的(这点很重要),所以prt-1只会减去sizeof(int*)。a,&a的地址是一样的,但意思不一样,a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].14、请问以下代码有什么问题: int main(){ char a;char *str=&a;strcpy(str,“hello”);printf(str);return 0;} 没有为str分配内存空间,将会发生异常 问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。char* s=“AAA”;printf(“%s”,s);s[0]='B';printf(“%s”,s);有什么错? “AAA”是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。cosnt char* s=“AAA”;然后又因为是常量,所以对是s[0]的赋值操作是不合法的。 15、有以下表达式: int a=248;b=4;int const c=21;const int *d=&a;int *const e=&b;int const *f const =&a;请问下列表达式哪些会被编译器禁止?为什么? *c=32;d=&b;*d=43;e=34;e=&a;f=0x321f; *c 这是个什么东东,禁止 *d 说了是const,禁止 e = &a 说了是const 禁止 const *f const =&a;禁止 16、交换两个变量的值,不使用第三个变量。即a=3,b=5,交换之后a=5,b=3;有两种解法, 一种用算术算法, 一种用^(异或)a = a + b;b = ab;or a = a^b;b = a^b;a = a^b;// 只能对int,char..or a ^= b ^= a; 17、下面的程序会出现什么结果.#include #include void getmemory(char *p){p=(char *)malloc(100);strcpy(p,”hello world”);} int main() {char *str=NULL;getmemory(str); printf(“%s/n”,str);free(str);return 0;} 程序崩溃,getmemory中的malloc 不能返回动态内存,free()对str操作很危险 18、下面的语句会出现什么结果? char szstr[10]; strcpy(szstr,”0123456789″); 答案:长度不一样,会造成非法的OS,应该改为char szstr[11]; 19、(void *)ptr 和(*(void**))ptr的结果是否相同? 答:其中ptr为同一个指针,(void *)ptr 和(*(void**))ptr值是相同的20、问函数既然不会被其它函数调用,为什么要返回1? int main(){ int x=3; printf(“%d”,x);return 1;} 答:mian中,c标准认为0表示成功,非0表示错误。具体的值是某中具体出错信息 21、对绝对地址0×100000赋值且想让程序跳转到绝对地址是0×100000去执行 (unsigned int*)0×100000 = 1234; 首先要将0×100000强制转换成函数指针,即:(void(*)())0×100000 然后再调用它: *((void(*)())0×100000)();用typedef可以看得更直观些: typedef void(*)()voidFuncPtr;*((voidFuncPtr)0×100000)(); 22、输出多少?并分析过程 unsigned short A = 10;printf(“~A = %un”, ~A);char c=128; printf(“c=%dn”,c); 第一题,~A =0xfffffff5,int值 为-11,但输出的是uint。所以输出4294967285 第二题,c=0×10,输出的是int,最高位为1,是负数,所以它的值就是0×00的补码就是128,所以输出-128。这两道题都是在考察二进制向int或uint转换时的最高位处理。 23、分析下面的程序: void GetMemory(char **p,int num){ *p=(char *)malloc(num);} int main(){ char *str=NULL;GetMemory(&str,100);strcpy(str,”hello”);free(str);if(str!=NULL){ strcpy(str,”world”);} printf(“n str is %s”,str);getchar();} 问输出结果是什么?希望大家能说说原因,先谢谢了 输出str is world。 free 只是释放的str指向的内存空间,它本身的值还是存在的.所以free之后,有一个好的习惯就是将str=NULL.此时str指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的, 尽管这段程序确实是存在大大的问题(上面各位已经说得很清楚了),但是通常会打印出world来。 这是因为,进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的。 当你malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的,只不过。。。。楼上都说过了,最好别这么干。 sizeof()和初不初始化,没有关系; strlen()和初始化有关。 char(*str)[20];/*str是一个数组指针,即指向数组的指针.*/ char *str[20];/*str是一个指针数组,其元素为指针型数据.*/ 25、long a=0×801010;a+5=? 答:0×801010用二进制表示为:“1000 0000 0001 0000 0001 0000”,十进制的值为8392720,再加上5就是 8392725 27、下面的函数实现在一个数上加一个数,有什么错误?请改正。 int add_n(int n){ static int i = 100;i += n;return i;} 当你第二次调用时得不到正确的结果,难道你写个函数就是为了调用一次?问题就出在 static上 28、给出下面程序的答案 typedef struct AA { int b1:5;int b2:2;}AA; void main(){ AA aa; char cc[100]; strcpy(cc,”0123456789abcdefghijklmnopqrstuvwxyz”); memcpy(&aa,cc,sizeof(AA));cout << aa.b1 < 答案是-16和1 首先sizeof(AA)的大小为4,b1和b2分别占5bit和2bit.经过strcpy和memcpy后,aa的4个字节所存放的值是: 0,1,2,3的ASC码,即 00110000,00110001,00110010,00110011 所以,最后一步:显示的是这4个字节的前5位,和之后的2位 分别为:10000,和01 因为int是有正负之分 所以:答案是-16和1 29、求函数返回值,输入x=9999;int func(x){ int countx = 0;while(x){ countx ++;x = x&(x-1);} return countx;} 结果?这是统计9999的二进制数值中有多少个1的函数,且有9999=9×1024+512+256+159×1024中含1的个数为2;512中含有1的个数为1;256中含1的个数为1;15中含1的个数为4;故共有1的个数为8,结果为8。 1000k);return 0;} #include void main(){ unsigned long int a,i=1;scanf(“%d”,&a);if(a%2==0){ for(i=1;i printf(“%d”,a,a-i);} else for(i=1;i<=a/2;i++) printf(“ %d, %d”,i,a-i);} 3、递规反向输出字符串的例子,可谓是反序的经典例程.void inverse(char *p){ if(*p = = ' ')return; inverse(p+1);printf(“%c”, *p);} int main(int argc, char *argv[]){ inverse(“abc ”);return 0;} 对1的另一种做法: #include void test(FILE *fread, FILE *fwrite){ char buf[1024] = {0}; if(!fgets(buf, sizeof(buf), fread))return; test(fread, fwrite);fputs(buf, fwrite);} int main(int argc, char *argv[]){ FILE *fr = NULL;FILE *fw = NULL; fr = fopen(“data”, “rb”);fw = fopen(“dataout”, “wb”);test(fr, fw);fclose(fr);fclose(fw); return 0;} 4、写一段程序,找出数组中第k大小的数,输出数所在的位置。例如{2,4,3,4,7}中,第一大的数是7,位置在4。第二大、第三大的数都是4,位置在1、3随便输出哪一个均可。函数接口为:int find_orderk(const int* narry,const int n,const int k)要求算法复杂度不能是O(n^2)谢谢! 可以先用快速排序进行排序,其中用另外一个进行地址查找 //快速排序 /* 快速排序,*/ #include using namespace std; void Qsort(int a[], int low, int high){ if(low >= high){ return;} int first = low;int last = high;int key = a[first];/*用字表的第一个记录作为枢轴*/ while(first < last){ while(first < last && a[last] >= key) { --last;} a[first] = a[last];/*将比第一个小的移到低端*/ while(first < last && a[first] <= key) { ++first;} a[last] = a[first]; /*将比第一个大的移到高端*/ } a[first] = key;/*枢轴记录到位*/ Qsort(a, low, first1] = t;//记录第几个位置用 sum++; cin >> t; } sum-= 1; Qsort(narry, 1, sum); for(int i = 1;i <= sum;i++) cout << narry[i] << 't';cout << endl;int k; cout << “Please input place you want:” << endl;cin >> k;int aa = 1;int kk = sum;for(;;){ if(aa == k) break; if(narry[kk]!= narry[kk1, k); result.pop_back(); } } else { copy(result.begin(), result.end(), ostream_iterator cout << endl;} } int main(int argc, char *argv[]){ int n, k;scanf(“%d%d”, &n, &k);solve(n, k);return 0;} 12、用指针的方法,将字符串“ABCD1234efgh”前后对调显示 /* 用指针的方法,将字符串“ABCD1234efgh”前后对调显示 */ #include using namespace std; int main(){ char str[] = “ABCD1234efgh”;int length = strlen(str); char *p1, *p2; p1 = str; p2 = str + length*str2; } int main(){ char *a = “333”;char*b = “55555”;cout < 16、实现子串定位int FindSubStr(const char *MainStr, const char *SubStr) /* 实现子串定位int FindSubStr(const char *MainStr, const char *SubStr)*/ #include using namespace std; int FindSubStr(const char* MainStr, const char* SubStr){ assert(MainStr!= NULL&&SubStr!= NULL);//检查 const char *p;const char *q;const char *s= MainStr;while(*MainStr){ p = MainStr; q = SubStr; while(*p && *q && *p++ == *q++);//对子串进行全部的检查 if(!*q) { return MainStr1]; a[a[i]1;i>0;i--)//否则,开始循环计算 { for(j = 0;j <= strlen(shortstring)1] = 0;printf(“第%4d个出局的人是:%4d号n”, i, j);} free(arr);return j;} int main(){ int n, m; scanf(“%d%d”, &n, &m); printf(“最后胜利的是%d号!n”, Josephu(n, m));system(“pause”);return 0;} 链表实现: #include #include typedef struct Node { int index; struct Node *next;}JosephuNode; int Josephu(int n, int m){ int i, j; JosephuNode *head, *tail;head = tail =(JosephuNode *)malloc(sizeof(JosephuNode));for(i = 1;i < n;++i){ tail->index = i; tail->next =(JosephuNode *)malloc(sizeof(JosephuNode));tail = tail->next;} tail->index = i;tail->next = head; for(i = 1;tail!= head;++i){ for(j = 1;j < m;++j){ tail = head; head = head->next;} tail->next = head->next; printf(“第%4d个出局的人是:%4d号n”, i, head->index);free(head); head = tail->next;} i = head->index;free(head);return i;} int main(){ int n, m;scanf(“%d%d”, &n, &m); printf(“最后胜利的是%d号!n”, Josephu(n, m));system(“pause”);return 0;} 第四部分:附加部分 1、位域 : 有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。 一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为: struct 位域结构名 { 位域列表 };其中位域列表的形式为: 类型说明符 位域名:位域长度 例如: struct bs { int a:8;int b:2;int c:6;};位域变量的说明与结构变量说明的方式相同。可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如: struct bs { int a:8;int b:2;int c:6;}data;说明data为bs变量,共占两个字节。其中位域a占8 位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明: 1.一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如: struct bs { unsigned a:4 unsigned :0 /*空域*/ unsigned b:4 /*从下一单元开始存放*/ unsigned c:4 } 在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。 2.由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。3.位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如: struct k { int a:1 int :2 /*该2位不能使用*/ int b:3 int c:2 }; 从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。 二、位域的使用位域的使用和结构成员的使用相同,其一般形式为: 位域变量名•位域名 位域允许用各种格式输出。main(){ struct bs { unsigned a:1;unsigned b:3;unsigned c:4;} bit,*pbit;bit.a=1;bit.b=7;bit.c=15; printf(“%d,%d,%dn”,bit.a,bit.b,bit.c);pbit=&bit; pbit->a=0;//此后三行是什么意思 pbit->b&=3;pbit->c|=1; printf(“%d,%d,%dn”,pbit->a,pbit->b,pbit->c);} 上例程序中定义了位域结构bs,三个位域为a,b,c。说明了bs类型的变量bit和指向bs类型的指针变量pbit。这表示位域也是可以使用指针的。 程序的9、10、11三行分别给三个位域赋值。(应注意赋值不能超过该位域的允许范围)程序第12行以整型量格式输出三个域的内容。第13行把位域变量bit的地址送给指针变量pbit。第14行用指针方式给位域a重新赋值,赋为0。第15行使用了复合的位运算符“&=”,该行相当于: pbit-b=pbit-b3位域b中原有值为7,与3作按位与运算的结果为3(111011=011,十进制值为3)。同样,程序第16行中使用了复合位运算“|=”,相当于: pbit-c=pbit-c|1其结果为15。程序第17行用指针方式输出了这三个域的值。 改错: #include int main(void){ int **p;int arr[100];p = &arr;return 0;} 解答:搞错了,是指针类型不同, int **p;//二级指针 &arr;//得到的是指向第一维为100的数组的指针 #include int main(void){ int **p, *q;int arr[100];q = arr;p = &q;return 0;} 一、请填写BOOL , float, 指针变量 与“零值”比较的 if 语句。(10分) 请写出 BOOL flag 与“零值”比较的 if 语句。(3分) 标准答案: if(flag)if(!flag)如下写法均属不良风格,不得分。if(flag == TRUE)if(flag == 1)if(flag == FALSE)if(flag == 0)请写出 float x 与“零值”比较的 if 语句。(4分)标准答案示例: const float EPSINON = 0.00001; if((x >= – EPSINON)&&(x <= EPSINON) 不可将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”此类形式。如下是错误的写法,不得分。if(x == 0.0)if(x!= 0.0) 请写出 char *p 与“零值”比较的 if 语句。(3分)标准答案: if(p == NULL) if(p!= NULL)如下写法均属不良风格,不得分。if(p == 0)if(p!= 0)if(p)if(!) 二、以下为Windows NT下的32位C++程序,请计算sizeof的值(10分) char str[] = “Hello”;char *p = str;int n = 10;请计算 sizeof(str)= 6(2分)sizeof(p)= 4(2分)sizeof(n)= 4(2分) void Func(char str[100]){请计算 sizeof(str)= 4(2分)} void *p = malloc(100);请计算 sizeof(p)= 4(2分) 三、简答题(25分) 1、头文件中的 ifndef/define/endif 干什么用?(5分) 答:防止该头文件被重复引用。 2、#include 和 #include “filename.h” 有什么区别?(5分) 答:对于#include,编译器从标准库路径开始搜索 filename.h 对于#include “filename.h”,编译器从用户的工作路径开始搜索 filename.h 3、const 有什么用途?(请至少说明两种)(5分)答:(1)可以定义 const 常量 (2)const可以修饰函数的参数、返回值,甚至函数的定义体。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。 4、在C++ 程序中调用被 C编译器编译后的函数,为什么要加 extern “C”?(5分) 答:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为: void foo(int x, int y);该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。 C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。 5、请简述以下两个for循环的优缺点(5分)第一个 for(i=0;i 第二个 if(condition){ for(i=0;i 缺点:多执行了N-1次逻辑判断,并且打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。 优点:循环的效率高 缺点:程序不简洁 四、有关内存的思考题(每小题5分,共20分) void GetMemory(char *p){p =(char *)malloc(100);} void Test(void){char *str = NULL;GetMemory(str);strcpy(str, “hello world”);printf(str);} 请问运行Test函数会有什么样的结果?答:程序崩溃。因为GetMemory并不能传递动态内存,Test函数中的 str一直都是 NULL。 strcpy(str, “hello world”);将使程序崩溃。 char *GetMemory(void){ char p[] = “hello world”;return p;} void Test(void){ char *str = NULL;str = GetMemory();printf(str);} 请问运行Test函数会有什么样的结果?答:可能是乱码。 因为GetMemory返回的是指向“栈内存”的指针,该指针的地址不是 NULL,但其原现的内容已经被清除,新内容不可知。 void GetMemory2(char **p, int num){ *p =(char *)malloc(num);} void Test(void){ char *str = NULL;GetMemory(&str, 100);strcpy(str, “hello”);printf(str);} 请问运行Test函数会有什么样的结果? 答:(1)能够输出hello(2)内存泄漏 void Test(void){ char *str =(char *)malloc(100);strcpy(str, “hello”);free(str); if(str!= NULL){ strcpy(str, “world”);printf(str);} } 请问运行Test函数会有什么样的结果?答:篡改动态内存区的内容,后果难以预料,非常危险。因为free(str);之后,str成为野指针,if(str!= NULL)语句不起作用。 五、编写strcpy函数(10分)已知strcpy函数的原型是char *strcpy(char *strDest, const char *strSrc); 其中strDest是目的字符串,strSrc是源字符串。(1)不调用C++/C的字符串库函数,请编写函数 strcpy char *strcpy(char *strDest, const char *strSrc);{assert((strDest!=NULL)&&(strSrc!=NULL));// 2分 char *address = strDest;// 2分 while((*strDest++ = * strSrc++)!= ‘ ’)// 2分 NULL;return address;// 2分 } 1.strcpy的实现代码 char * strcpy(char * strDest,const char * strSrc){if((strDest==NULL)||(strSrc==NULL))file://[/1] throw “Invalid argument(s)”;//[2] char * strDestCopy=strDest;file://[/3] while((*strDest++=*strSrc++)!=’ ′);file://[/4] return strDestCopy;}(2)strcpy能把strSrc的内容复制到strDest,为什么还要char * 类型的返回值? 答:为了实现链式表达式。// 2分 例如 int length = strlen(strcpy(strDest, “hello world”));错误的做法: [1](A)不检查指针的有效性,说明答题者不注重代码的健壮性。 (B)检查指针的有效性时使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),说明答题者对C语言中类型的隐式转换没有深刻认识。在本例中char *转换为bool即是类型隐式转换,这种功能虽然灵活,但更多的是导致出错概率增大和维护成本升高。所以C++专门增加了bool、true、false三个关键字以提供更安全的条件表达式。 (C)检查指针的有效性时使用((strDest==0)||(strSrc==0)),说明答题者不知道使用常量的好处。直接使用字面常量(如本例中的0)会减少程序的可维护性。0虽然简单,但程序中可能出现很多处对指针的检查,万一出现笔误,编译器不能发现,生成的程序内含逻辑错误,很难排除。而使用NULL代替0,如果出现拼写错误,编译器就会检查出来。[2](A)return new string(“Invalid argument(s)”);,说明答题者根本不知道返回值的用途,并且他对内存泄漏也没有警惕心。从函数中返回函数体内分配的内存是十分危险的做法,他把释放内存的义务抛给不知情的调用者,绝大多数情况下,调用者不会释放内存,这导致内存泄漏。 (B)return 0;,说明答题者没有掌握异常机制。调用者有可能忘记检查返回值,调用者还可能无法检查返回值(见后面的链式表达式)。妄想让返回值肩负返回正确值和异常值的双重功能,其结果往往是两种功能都失效。应该以抛出异常来代替返回值,这样可以减轻调用者的负担、使错误不会被忽略、增强程序的可维护性。[3] (A)忘记保存原始的strDest值,说明答题者逻辑思维不严密。[4] (A)循环写成while(*strDest++=*strSrc++);,同[1](B)。 (B)循环写成while(*strSrc!=’ ′) *strDest++=*strSrc++;,说明答题者对边界条件的检查不力。循环体结束后,strDest字符串的末尾没有正确地加上’ ′。 六、编写类String的构造函数、析构函数和赋值函数(25分) 已知类String的原型为: class String {public: String(const char *str = NULL);// 普通构造函数 String(const String &other);// 拷贝构造函数 ~ String(void);// 析构函数 String & operate =(const String &other);// 赋值函数 private: char *m_data;// 用于保存字符串 }; 请编写String的上述4个函数。标准答案: // String的析构函数 String::~String(void)// 3分 {delete [] m_data; // 由于m_data是内部数据类型,也可以写成 delete m_data;} // String的普通构造函数 String::String(const char *str)// 6分 {if(str==NULL) {m_data = new char[1];// 若能加 NULL 判断则更好 *m_data = ‘ ’;} else {int length = strlen(str); m_data = new char[length+1];// 若能加 NULL 判断则更好 strcpy(m_data, str);} } // 拷贝构造函数 String::String(const String &other)// 3分 {int length = strlen(other.m_data);m_data = new char[length+1];// 若能加 NULL 判断则更好 strcpy(m_data, other.m_data);} // 赋值函数 String & String::operate =(const String &other)// 13分 {//(1)检查自赋值 // 4分 if(this == &other)return *this;//(2)} 网络安全应具有以下五个方面的特征: 保密性:信息不泄露给非授权用户、实体或过程,或供其利用的特性。 完整性:数据未经授权不能进行改变的特性。即信息在存储或传输过程中保持不被修改、不被破坏和丢失的特性。 可用性:可被授权实体访问并按需求使用的特性。即当需要时能否存取所需 的信息。例如网络环境下拒绝服务、破坏网络和有关系统的正常运行等都属于对可用性的攻击; 可控性:对信息的传播及内容具有控制能力。 可审查性:出现安全问题时提供依据与手段第一部分:基本概念及其它问答题 1、关键字static的作用是什么? 这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用: 1).在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 2).在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。 3).在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。(本地化数据和代码范围的好处和重要性)。 2、“引用”与指针的区别是什么? 答、1)引用必须被初始化,指针不必。 2)引用初始化以后不能被改变,指针可以改变所指的对象。3)不存在指向空值的引用,但是存在指向空值的指针。 指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。 流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。 3、.h头文件中的ifndef/define/endif 的作用? 答:防止该头文件被重复引用。 4、#include 与 #include “file.h”的区别? 答:前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。 5、描述实时系统的基本特性 答 :在特定时间内完成特定的任务,实时性与可靠性。 6、全局变量和局部变量在内存中是否有区别?如果有,是什么区别? 答 :全局变量储存在静态数据区,局部变量在堆栈中。 7、什么是平衡二叉树? 答 :左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1。 8、堆栈溢出一般是由什么原因导致的? 答 :1.没有回收垃圾资源2.层次太深的递归调用 9、冒泡排序算法的时间复杂度是什么?答 :O(n^2) 10、什么函数不能声明为虚函数?答:constructor 11、队列和栈有什么区别?答:队列先进先出,栈后进先出 12、不能做switch()的参数类型答 :switch的参数不能为实型。 13、局部变量能否和全局变量重名?答:能,局部会屏蔽全局。要用全局变量,需要使用”::” 局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内 14、如何引用一个已经定义过的全局变量? 答、可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变量,假定你将那个变量写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。 15、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么? 答、可以,在不同的C文件中以static形式来声明同名全局变量。可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错。 16、语句for(;1 ;)有什么问题?它是什么意思?答、和while(1)相同,无限循环。 17、do„„while和while„„do有什么区别?答、前一个循环一遍再判断,后一个判断以后再循环。 18、statac 全局变量、局部变量、函数与普通全局变量、局部变量、函数 答、全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。 static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件 static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值; static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝 19、程序的内存分配 答:一个由c/C++编译的程序占用的内存分为以下几个部分 1、栈区(stack)—由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 2、堆区(heap)—一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表,呵呵。 3、全局区(静态区)(static)—全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。 4、文字常量区—常量字符串就是放在这里的。程序结束后由系统释放。 5、程序代码区—存放函数体的二进制代码 例子程序 这是一个前辈写的,非常详细 //main.cpp int a=0;//全局初始化区 char *p1;//全局未初始化区 main(){ intb;栈 char s[]=”abc”;//栈 char *p2;//栈 char *p3=”123456″;//123456 在常量区,p3在栈上。static int c=0; //全局(静态)初始化区 p1 =(char*)malloc(10);p2 =(char*)malloc(20);//分配得来得10和20字节的区域就在堆区。 strcpy(p1,”123456″);//123456 放在常量区,编译器可能会将它与p3所向”123456″优化成一个地方。} 20、解释堆和栈的区别 答:堆(heap)和栈(stack)的区别(1)申请方式 stack:由系统自动分配。例如,声明在函数中一个局部变量int b;系统自动在栈中为b开辟空间 heap:需要程序员自己申请,并指明大小,在c中malloc函数如p1=(char*)malloc(10);在C++中用new运算符,如p2=(char*)malloc(10);但是注意p1、p2本身是在栈中的。(2)申请后系统的响应 栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。 (3)申请大小的限制 栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。 堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。(4)申请效率的比较: 栈:由系统自动分配,速度较快。但程序员是无法控制的。 堆:是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.另外,在WINDOWS下,最好的方式是用Virtual Alloc分配内存,他不是在堆,也不是在栈,而是直接在进程的地址空间中保留一块内存,虽然用起来最不方便。但是速度快,也最灵活。(5)堆和栈中的存储内容 栈:在函数调用时,第一个进栈的是主函数中后的下一条指令(函数调用语句的下一条可执行语句)的地址,然后是函数的各个参数,在大多数的C编译器中,参数是由右往左入栈的,然后是函数中的局部变量。注意静态变量是不入栈的。 当本次函数调用结束后,局部变量先出栈,然后是参数,最后栈顶指针指向最开始存的地址,也就是主函数中的下一条指令,程序由该点继续运行。 堆:一般是在堆的头部用一个字节存放堆的大小。堆中的具体内容由程序员安排。(6)存取效率的比较 char s1[]=”aaaaaaaaaaaaaaa”;aaaaaaaaaaa是在运行时刻赋值的; char *s2=”bbbbbbbbbbbbbbbbb”;而bbbbbbbbbbb是在编译时就确定的; 但是,在以后的存取中,在栈上的数组比指针所指向的字符串(例如堆)快。比如: #include voidmain(){ char a=1;char c[]=”1234567890″;char *p=”1234567890″;a = c[1];a = p[1];return;} 对应的汇编代码 10:a=c[1];004010678A4DF1movcl,byteptr[ebp-0Fh] 0040106A884DFCmovbyteptr[ebp-4],cl 11:a=p[1];0040106D8B55ECmovedx,dwordptr[ebp-14h] 004010708A4201moval,byteptr[edx+1] 004010738845FCmovbyteptr[ebp-4],al 第一种在读取时直接就把字符串中的元素读到寄存器cl中,而第二种则要先把指针值读到edx中,在根据edx读取字符,显然慢了。 21、什么是预编译,何时需要预编译? 答:预编译又称为预处理,是做些代码文本的替换工作。处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等,就是为编译做的预备工作的阶段,主要处理#开始的预编译指令,预编译指令指示了在程序正式编译前就由编译器进行的操作,可以放在程序中的任何位置。c编译系统在对程序进行通常的编译之前,先进行预处理。 c提供的预处理功能主要有以下三种:1)宏定义 2)文件包含 3)条件编译 22、关键字const是什么含意? 答: “const意味着常数”业余者。“只读”正确的答案。const int a;a是一个常整型数 int const a;a是一个常整型数 const int *a;a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。int * const a;a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)int const * a const;a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的) 1).关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。) 2).通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。 3).合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现 23、关键字volatile有什么含意 并给出三个不同的例子。 答:一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子: 1).并行设备的硬件寄存器(如:状态寄存器) 2).一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)3).多线程应用中被几个任务共享的变量 1).一个参数既可以是const还可以是volatile吗?解释为什么。是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。 2).一个指针可以是volatile 吗?解释为什么。是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。3).下面的函数有什么错误: int square(volatile int *ptr){return *ptr * *ptr;} 这段代码的有个恶作剧。这段代码的目的是用来返指针*ptr指向值的平方,但是由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码 int square(volatile int *ptr){int a,b;a = *ptr;b = *ptr;return a * b;} 由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下: long square(volatile int *ptr){ int a;a = *ptr;return a * a;} 24、三种基本的数据模型 答:按照数据结构类型的不同,将数据模型划分为层次模型、网状模型和关系模型。 25、结构与联合有和区别? 答:(1).结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。 (2)对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的 26、描述内存分配方式以及它们的区别? 答:1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。 2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。 3)从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多 27、请说出const与#define 相比,有何优点? 答:Const作用:定义常量、修饰函数参数、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。 1)const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。 2)有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。 28、简述数组与指针的区别? 答:数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。(1)修改内容上的差别 char a[] = “hello”;a[0] = ‘X’; char *p = “world”;// 注意p 指向常量字符串 p[0] = ‘X’;// 编译器不能发现该错误,运行时错误 (2)用运算符sizeof 可以计算出数组的容量(字节数)。sizeof(p),p 为指针得到的是一个 指针变量的字节数,而不是p 所指的内存容量。C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它。注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。char a[] = “hello world”;char *p = a;cout<< sizeof(a)<< endl;// 12 字节 cout<< sizeof(p)<< endl;// 4 字节 计算数组和指针的内存容量 void Func(char a[100]){ cout<< sizeof(a)<< endl;// 4 字节而不是100 字节 } 29、分别写出BOOL,int,float,指针类型的变量a 与“零”的比较语句。答:BOOL : if(!a)or if(a)int : if(a == 0)float : const EXPRESSION EXP = 0.000001 if(a < EXP && a >-EXP)pointer : if(a!= NULL)or if(a == NULL)30、如何判断一段程序是由C 编译程序还是由C++编译程序编译的? 答:#ifdef __cplusplus cout<<“c++”;#else cout<<“c”;#endif 31、论述含参数的宏与函数的优缺点 答: 带参宏 函数 处理时间 编译时 程序运行时 参数类型 没有参数类型问题 定义实参、形参类型 处理过程 不分配内存 分配内存 程序长度 变长 不变 运行速度 不占运行时间 调用和返回占用时间 32、用两个栈实现一个队列的功能?要求给出算法和思路!答:设2个栈为A,B, 一开始均为空.入队:将新元素push入栈A;出队:(1)判断栈B是否为空;(2)如果不为空,则将栈A中所有元素依次pop出并push到栈B;(3)将栈B的栈顶元素pop出; 这样实现的队列入队和出队的平摊复杂度都还是O(1), 比上面的几种方法要好 33、嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢? 答:这个问题用几个解决方案。我首选的方案是: while(1){ } 一些程序员更喜欢如下方案: for(;;){ } 第三个方案是用 goto Loop:...goto Loop;应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。 34、位操作(Bit manipulation) 答: 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。对这个问题有三种基本的反应 1)不知道如何下手。该被面者从没做过任何嵌入式系统的工作。 2)用bit fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。我最近不幸看到 Infineon为其较复杂的通信芯片写的驱动程序,它用到了bit fields因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。从道德讲:永远不要让一个非嵌入式的家伙粘实际硬件的边。 3)用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下: #define BIT3(0x1 << 3)static int a;void set_bit3(void){ a |= BIT3;} void clear_bit3(void){ a &= ~BIT3;} 一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作。 35、访问固定的内存位置(Accessing fixed memory locations) 答:嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。 这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下: int *ptr;ptr =(int *)0x67a9;*ptr = 0xaa66;A more obscure approach is: 一个较晦涩的方法是: *(int * const)(0x67a9)= 0xaa55;即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。 36、中断(Interrupts) 答: 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字 __interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。 __interrupt double compute_area(double radius){ double area = PI * radius * radius;printf(“nArea = %f”, area);return area;} 这个函数有太多的错误了,以至让人不知从何说起了: 1)ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。 2)ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。 3)在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。4)与第三点一脉相承,printf()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。 37、动态内存分配(Dynamic memory allocation) 答:尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(heap)中动态分配内存的过程的。那么嵌入式系统中,动态分配内存可能发生的问题是什么? 这里,我期望应试者能提到内存碎片,碎片收集的问题,变量的持行时间等等。这个主题已经在ESP杂志中被广泛地讨论过了(主要是 P.J.Plauger, 他的解释远远超过我这里能提到的任何解释),所有回过头看一下这些杂志吧!让应试者进入一种虚假的安全感觉后,我拿出这么一个小节目: 下面的代码片段的输出是什么,为什么? char *ptr;if((ptr =(char *)malloc(0))== NULL)puts(“Got a null pointer”);else puts(“Got a valid pointer”);这是一个有趣的问题。最近在我的一个同事不经意把0值传给了函数malloc,得到了一个合法的指针之后,我才想到这个问题。这就是上面的代码,该代码的输出是“Got a valid pointer”。我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。 38、Typedef 答:Typedef 在C语言中频繁用以声明一个已经存在的数据类型的同义字。也可以用预处理器做类似的事。例如,思考一下下面的例子: #define dPS struct s * typedef struct s * tPS;以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。哪种方法更好呢?(如果有的话)为什么? 这是一个非常微妙的问题,任何人答对这个问题(正当的原因)是应当被恭喜的。答案是:typedef更好。思考下面的例子: dPS p1,p2;tPS p3,p4;第一个扩展为 struct s * p1, p2;上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二个例子正确地定义了p3 和p4 两个指针。 39、用变量a给出下面的定义 答:a)一个整型数(An integer) b)一个指向整型数的指针(A pointer to an integer) c)一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to an integer)d)一个有10个整型数的数组(An array of 10 integers) e)一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers to integers)f)一个指向有10个整型数数组的指针(A pointer to an array of 10 integers) g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer) h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(An array of ten pointers to functions that take an integer argument and return an integer)答案是: a)int a;// An integer b)int *a;// A pointer to an integer c)int **a;// A pointer to a pointer to an integer d)int a[10];// An array of 10 integers e)int *a[10];// An array of 10 pointers to integers f)int(*a)[10];// A pointer to an array of 10 integers g)int(*a)(int);// A pointer to a function a that takes an integer argument and returns an integer h)int(*a[10])(int);// An array of 10 pointers to functions that take an integer argument and return an integer 40、解释局部变量、全局变量和静态变量的含义。答: 41、写一个“标准”宏 答:交换两个参数值的宏定义为:.#define SWAP(a,b)(a)=(a)+(b);(b)=(a)-(b);(a)=(a)-(b);输入两个参数,输出较小的一个:#define MIN(A,B)((A)<(B))?(A):(B))表明1年中有多少秒(忽略闰年问题):#define SECONDS_PER_YEAR(60 * 60 * 24 * 365)UL #define DOUBLE(x)x+x 与 #define DOUBLE(x)((x)+(x))i = 5*DOUBLE(5); i为30 i = 5*DOUBLE(5); i为50 已知一个数组table,用一个宏定义,求出数据的元素个数 #define NTBL #define NTBL(sizeof(table)/sizeof(table[0])) 42、A.c 和B.c两个c文件中使用了两个相同名字的static变量,编译的时候会不会有问题?这两个static变量会保存到哪里(栈还是堆或者其他的)? 答:static的全局变量,表明这个变量仅在本模块中有意义,不会影响其他模块。他们都放在数据区,但是编译器对他们的命名是不同的。 如果要使变量在其他模块也有意义的话,需要使用extern关键字。 43、一个单向链表,不知道头节点,一个指针指向其中的一个节点,问如何删除这个指针指向的节点? 答:将这个指针指向的next节点值copy到本节点,将next指向next->next,并随后删除原next指向的节点。第二部分:程序代码评价或者找错 1、下面的代码输出是什么,为什么? void foo(void){ unsigned int a = 6;int b =-20;(a+b > 6)? puts(“> 6″): puts(“<= 6“);} 这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 ”>6″。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。 2、评价下面的代码片断: unsigned int zero = 0;unsigned int compzero = 0xFFFF;/*1′s complement of zero */ 对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下: unsigned int compzero = ~0;这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。 3、C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么? int a = 5, b = 7, c;c = a+++b;这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成: c = a++ + b;因此, 这段代码持行后a = 6, b = 7, c = 12。 如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。 4、设有以下说明和定义: typedef union {long i;int k[5];char c;} DATE;struct data { int cat;DATE cow;double dog;} too;DATE max;则语句 printf(“%d”,sizeof(struct date)+sizeof(max));的执行结果是? 答、结果是:52。DATE是一个union, 变量公用空间.里面最大的变量类型是int[5], 占用20个字节.所以它的大小是20 data是一个struct, 每个变量分开占用空间.依次为int4 + DATE20 + double8 = 32.所以结果是 20 + 32 = 52.当然„在某些16位编辑器下, int可能是2字节,那么结果是 int2 + DATE10 + double8 = 20 5、请写出下列代码的输出内容 b=a++;#include c=++a;main()d=10*a++;{ printf(“b,c,d:%d,%d,%d”,b,c,d);int a,b,c,d;return 0;a=10;} 答:10,12,120 6、写出下列代码的输出内容 #include int inc(int a){ return(++a);} int multi(int*a,int*b,int*c){ return(*c=*a**b);} typedef int(FUNC1)(int in);typedef int(FUNC2)(int*,int*,int*);void show(FUNC2 fun,int arg1, int*arg2){ INCp=&inc;int temp =p(arg1);fun(&temp,&arg1, arg2);printf(“%dn”,*arg2);} main(){ int a;show(multi,10,&a);return 0;} 答:110 7、请找出下面代码中的所以错误 说明:以下代码是把一个字符串倒序,如“abcd”倒序后变为“dcba” 1、#include”string.h” 2、main() 3、{ 4、char*src=”hello,world”; 5、char* dest=NULL; 6、int len=strlen(src); 7、dest=(char*)malloc(len); 8、char* d=dest; 9、char* s=src[len]; 10、while(len–!=0) 11、d++=s–; 12、printf(“%s”,dest); 13、return 0; 14、} 答: 方法1: int main(){ char* src = “hello,world”;int len = strlen(src); char* dest =(char*)malloc(len+1);//要为 分配一个空间 char* d = dest; char* s = &src[len-1];//指向最后一个字符 while(len–!= 0)*d++=*s–; *d = 0;//尾部要加 printf(“%sn”,dest); free(dest);// 使用完,应当释放空间,以免造成内存汇泄露 return 0;} 方法2: #include #include main(){ char str[]=”hello,world”;int len=strlen(str);char t; for(int i=0;i { t=str[i]; str[i]=str[len-i-1];str[len-i-1]=t;} printf(“%s”,str);return 0;} 8、请问下面程序有什么错误? int a[60][250][1000],i,j,k;for(k=0;k<=1000;k++)for(j=0;j<250;j++)for(i=0;i<60;i++)a[i][j][k]=0; 答案:把循环语句内外换一下 9、请问下面程序会出现什么情况?.#define Max_CB 500 void LmiQueryCSmd(Struct MSgCB * pmsg){ unsigned char ucCmdNum;......for(ucCmdNum=0;ucCmdNum ......;} 答案:死循环 10、以下3个有什么区别 char * const p;//常量指针,p的值不可以修改 char const * p;//指向常量的指针,指向的常量值不可以改 const char *p; //和char const *p 11、写出下面的结果 char str1[] = “abc”;char str2[] = “abc”;const char str3[] = “abc”;const char str4[] = “abc”;const char *str5 = “abc”;const char *str6 = “abc”;char *str7 = “abc”;char *str8 = “abc”;cout <<(str1 == str2)<< endl;cout <<(str3 == str4)<< endl;cout <<(str5 == str6)<< endl;cout <<(str7 == str8)<< endl;结果是:0 0 1 1 解答:str1,str2,str3,str4是数组变量,它们有各自的内存空间; 而str5,str6,str7,str8是指针,它们指向相同的常量区域。 12、以下代码中的两个sizeof用法有问题吗? void UpperCase(char str[])// 将 str 中的小写字母转换成大写字母 { for(size_t i=0;i if('a'<=str[i] && str[i]<='z')str[i]-=('a'-'A');} char str[] = “aBcDe”;cout << “str字符长度为: ” << sizeof(str)/sizeof(str[0])<< endl;UpperCase(str);cout << str << endl;答:函数内的sizeof有问题。根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的str是一个静态定义的数组,因此其大小为6,函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。 13、写出输出结果 main(){ int a[5]={1,2,3,4,5};int *ptr=(int *)(&a+1);printf(“%d,%d”,*(a+1),*(ptr-1));} 输出:2,5 *(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5 &a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int) int *ptr=(int *)(&a+1);则ptr实际是&(a[5]),也就是a+5 原因如下:&a是数组指针,其类型为 int(*)[5];而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同,a是长度为5的int数组指针,所以要加 5*sizeof(int),所以ptr实际是a[5],但是prt与(&a+1)类型是不一样的(这点很重要),所以prt-1只会减去sizeof(int*)。a,&a的地址是一样的,但意思不一样,a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地址,a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5].14、请问以下代码有什么问题: int main(){ char a; char *str=&a; strcpy(str,“hello”);printf(str);return 0;} 没有为str分配内存空间,将会发生异常 问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。char* s=“AAA”;printf(“%s”,s);s[0]='B'; printf(“%s”,s);有什么错? “AAA”是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。cosnt char* s=“AAA”;然后又因为是常量,所以对是s[0]的赋值操作是不合法的。 15、有以下表达式: int a=248;b=4;int const c=21;const int *d=&a;int *const e=&b;int const *f const =&a;请问下列表达式哪些会被编译器禁止?为什么? *c=32;d=&b;*d=43;e=34;e=&a;f=0x321f;*c 这是个什么东东,禁止 *d 说了是const,禁止 e = &a 说了是const 禁止 const *f const =&a;禁止 16、交换两个变量的值,不使用第三个变量。即a=3,b=5,交换之后a=5,b=3; 有两种解法, 一种用算术算法, 一种用^(异或)a = a + b;b = ab;or a = a^b;b = a^b;a = a^b;// 只能对int,char..or a ^= b ^= a; 17、下面的程序会出现什么结果.#include #include void getmemory(char *p){p=(char *)malloc(100);strcpy(p,”hello world”);} int main(){char *str=NULL;getmemory(str);printf(“%s/n”,str);free(str);return 0;} 程序崩溃,getmemory中的malloc 不能返回动态内存,free()对str操作很危险 18、下面的语句会出现什么结果? char szstr[10];strcpy(szstr,”0123456789″); 答案:长度不一样,会造成非法的OS,应该改为char szstr[11]; 19、(void *)ptr 和(*(void**))ptr的结果是否相同?答:其中ptr为同一个指针,(void *)ptr 和(*(void**))ptr值是相同的 20、问函数既然不会被其它函数调用,为什么要返回1?int main(){ int x=3;printf(“%d”,x);return 1;} 答:mian中,c标准认为0表示成功,非0表示错误。具体的值是某中具体出错信息 21、对绝对地址0×100000赋值且想让程序跳转到绝对地址是0×100000去执行 (unsigned int*)0×100000 = 1234;首先要将0×100000强制转换成函数指针,即:(void(*)())0×100000 然后再调用它: *((void(*)())0×100000)();用typedef可以看得更直观些: typedef void(*)()voidFuncPtr;*((voidFuncPtr)0×100000)(); 22、输出多少?并分析过程 unsigned short A = 10;printf(“~A = %un”, ~A);char c=128; printf(“c=%dn”,c); 第一题,~A =0xfffffff5,int值 为-11,但输出的是uint。所以输出4294967285 第二题,c=0×10,输出的是int,最高位为1,是负数,所以它的值就是0×00的补码就是128,所以输出-128。这两道题都是在考察二进制向int或uint转换时的最高位处理。 23、分析下面的程序: void GetMemory(char **p,int num){ *p=(char *)malloc(num);} int main(){ char *str=NULL; GetMemory(&str,100);strcpy(str,”hello”);free(str);if(str!=NULL){ strcpy(str,”world”);} printf(“n str is %s”,str);getchar();} 问输出结果是什么?希望大家能说说原因,先谢谢了 输出str is world。 free 只是释放的str指向的内存空间,它本身的值还是存在的.所以free之后,有一个好的习惯就是将str=NULL.此时str指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的,尽管这段程序确实是存在大大的问题(上面各位已经说得很清楚了),但是通常会打印出world来。 这是因为,进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的。 当你malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的,只不过。。。。楼上都说过了,最好别这么干。 sizeof()和初不初始化,没有关系; strlen()和初始化有关。 char(*str)[20];/*str是一个数组指针,即指向数组的指针.*/ char *str[20];/*str是一个指针数组,其元素为指针型数据.*/ 25、long a=0×801010;a+5=? 答:0×801010用二进制表示为:“1000 0000 0001 0000 0001 0000”,十进制的值为8392720,再加上5就是8392725 27、下面的函数实现在一个数上加一个数,有什么错误?请改正。 int add_n(int n){ static int i = 100;i += n;return i;} 当你第二次调用时得不到正确的结果,难道你写个函数就是为了调用一次?问题就出在 static上 28、给出下面程序的答案 typedef struct AA { int b1:5;int b2:2;}AA;void main(){ AA aa;char cc[100];strcpy(cc,”0123456789abcdefghijklmnopqrstuvwxyz”); memcpy(&aa,cc,sizeof(AA));cout << aa.b1 < 首先sizeof(AA)的大小为4,b1和b2分别占5bit和2bit.经过strcpy和memcpy后,aa的4个字节所存放的值是: 0,1,2,3的ASC码,即00110000,00110001,00110010,00110011 所以,最后一步:显示的是这4个字节的前5位,和之后的2位 分别为:10000,和01 因为int是有正负之分 所以:答案是-16和1 29、求函数返回值,输入x=9999;int func(x){ int countx = 0;while(x){ countx ++;x = x&(x-1);} return countx;} 结果?这是统计9999的二进制数值中有多少个1的函数,且有9999=9×1024+512+256+159×1024中含1的个数为2;512中含有1的个数为1;256中含1的个数为1;15中含1的个数为4;故共有1的个数为8,结果为8。 1000k);return 0;} #include void main(){ unsigned long int a,i=1;scanf(“%d”,&a);if(a%2==0){ for(i=1;i printf(“%d”,a,a-i);} else for(i=1;i<=a/2;i++) printf(“ %d, %d”,i,a-i);} 3、递规反向输出字符串的例子,可谓是反序的经典例程.void inverse(char *p){ if(*p = = ' ')return; inverse(p+1);printf(“%c”, *p);} int main(int argc, char *argv[]){ inverse(“abc ”); return 0;} 对1的另一种做法: #include void test(FILE *fread, FILE *fwrite){ char buf[1024] = {0};if(!fgets(buf, sizeof(buf), fread))return;test(fread, fwrite);fputs(buf, fwrite);} int main(int argc, char *argv[]){ FILE *fr = NULL;FILE *fw = NULL;fr = fopen(“data”, “rb”);fw = fopen(“dataout”, “wb”);test(fr, fw);fclose(fr);fclose(fw);return 0;} 4、写一段程序,找出数组中第k大小的数,输出数所在的位置。例如{2,4,3,4,7}中,第一大的数是7,位置在4。第二大、第三大的数都是4,位置在1、3随便输出哪一个均可。函数接口为:int find_orderk(const int* narry,const int n,const int k)要求算法复杂度不能是O(n^2)谢谢! 可以先用快速排序进行排序,其中用另外一个进行地址查找 代码如下,在VC++6.0运行通过。给分吧^-^ //快速排序 #include usingnamespacestd;intPartition(int*L,intlow,int high){ inttemp = L[low];intpt = L[low];while(low < high){ while(low < high && L[high] >= pt)–high;L[low] = L[high];while(low < high && L[low] <= pt)++low;L[low] = temp; } L[low] = temp;returnlow;} voidQSort(int*L,intlow,int high){ if(low < high){ intpl = Partition(L,low,high);QSort(L,low,pl1] = t;sum++;cin >> t;} sum-= 1; QSort(narry,1,sum); for(int i = 1;i <= sum;i++)cout << narry[i] << 't';cout << endl;intk; cout << “Please input place you want:” << endl;cin >> k;intaa = 1;intkk = 0;for(;;){ if(aa == k)break; if(narry[kk]!= narry[kk + 1]){ aa += 1;kk++;} } cout << “The NO.” << k << “number is:” << narry[sumkk])cout << i << 't';} return0;} 5、两路归并排序 Linklist *unio(Linklist *p,Linklist *q){ linklist *R,*pa,*qa,*ra;pa=p;qa=q;R=ra=p;while(pa->next!=NULL&&qa->next!=NULL){ if(pa->data>qa->data){ ra->next=qa;qa=qa->next;} else{ ra->next=pa;pa=pa->next;} } if(pa->next!=NULL)ra->next=pa;if(qa->next!=NULL)ra->next==qa;return R;} 6、用递归算法判断数组a[N]是否为一个递增数组。递归的方法,记录当前最大的,并且判断当前的是否比这个还大,大则继续,否则返回false结束: bool fun(int a[], int n){ if(n= =1)return true;if(n= =2)return a[n-1] >= a[n-2];return fun(a,n-1)&&(a[n-1] >= a[n-2]);} 7、单连表的建立,把’a'–’z’26个字母插入到连表中,并且倒叙,还要打印!方法1: typedef struct val { int date_1;struct val *next;}*p;void main(void){ char c; for(c=122;c>=97;c–){ p.date=c;p=p->next;} p.next=NULL;} } 方法2: node *p = NULL;node *q = NULL; node *head =(node*)malloc(sizeof(node));head->data = ‘ ‘;head->next=NULL; node *first =(node*)malloc(sizeof(node)); first->data = ‘a’;first->next=NULL;head->next = first;p = first; int longth = ‘z’ – ‘b’;int i=0; while(i<=longth){ node *temp =(node*)malloc(sizeof(node));temp->data = ‘b’+i;temp->next=NULL;q=temp;head->next = temp;temp->next=p;p=q;i++;} print(head); 8、请列举一个软件中时间换空间或者空间换时间的例子。 void swap(int a,int b){ int c;c=a;a=b;b=a;} —>空优 void swap(int a,int b){ a=a+b;b=a-b;a=a-b;} 9、outputstr所指的值为123456789 int continumax(char *outputstr, char *inputstr){ char *in = inputstr, *out = outputstr, *temp, *final; int count = 0, maxlen = 0;while(*in!= ‘ ′){ if(*in > 47 && *in < 58){ for(temp = in;*in > 47 && *in < 58;in++)count++;} else in++;if(maxlen < count){ maxlen = count;count = 0;final = temp;} } for(int i = 0;i < maxlen;i++){ *out = *final;out++;final++;} *out = ' ';return maxlen;} 10、不用库函数,用C语言实现将一整型数字转化为字符串 方法1: int getlen(char *s){ int n;for(n = 0;*s!= ' ';s++)n++;return n;} void reverse(char s[]){ int c,i,j;for(i = 0,j = getlen(s)u +1;//MainStr指向当前起始位,u指向 } MainStr ++;} return-1;} 17、已知一个单向链表的头,请写出删除其某一个结点的算法,要求,先找到此结点,然后删除。 slnodetype *Delete(slnodetype *Head,int key){}中if(Head->number==key){ Head=Pointer->next;free(Pointer);break;} Back = Pointer; Pointer=Pointer->next;if(Pointer->number==key){ Back->next=Pointer->next;free(Pointer);break;} void delete(Node* p){ if(Head = Node)while(p)} 18、有1,2,„.一直到n的无序数组,求排序算法,并且要求时间复杂度为O(n),空间复杂度O(1),使用交换,而且一次只能交换两个数.(华为)#include int main(){ int a[] = {10,6,9,5,2,8,4,7,1,3};int len = sizeof(a)/ sizeof(int);int temp; for(int i = 0;i < len;){ temp = a[a[i]1] = a[i];a[i] = temp; if(a[i] == i + 1)i++;} for(int j = 0;j < len;j++)cout< return 0;} 19、写出程序把一个链表中的接点顺序倒排 typedef struct linknode { int data;struct linknode *next;}node;//将一个链表逆置 node *reverse(node *head){ node *p,*q,*r;p=head;q=p->next;while(q!=NULL){ r=q->next;q->next=p;p=q;q=r;} head->next=NULL;head=p;return head;} 20、写出程序删除链表中的所有接点 void del_all(node *head){ node *p;while(head!=NULL){ p=head->next;free(head);head=p;} cout<<“释放空间成功!”< 21、两个字符串,s,t;把t字符串插入到s字符串中,s字符串有足够的空间存放t字符串 void insert(char *s, char *t, int i){ char *q = t;char *p =s;if(q == NULL)return;while(*p!=' '){ p++;} while(*q!=0){ *p=*q;p++;q++;} *p = ' ';} 23、公司考试这种题目主要考你编写的代码是否考虑到各种情况,是否安全(不会溢出)各种情况包括: 1、参数是指针,检查指针是否有效 2、检查复制的源目标和目的地是否为同一个,若为同一个,则直接跳出 3、读写权限检查 4、安全检查,是否会溢出 memcpy拷贝一块内存,内存的大小你告诉它 strcpy是字符串拷贝,遇到’ ′结束 /* memcpy ─── 拷贝不重叠的内存块 */ void memcpy(void* pvTo, void* pvFrom, size_t size){ void* pbTo =(byte*)pvTo;void* pbFrom =(byte*)pvFrom; ASSERT(pvTo!= NULL && pvFrom!= NULL);//检查输入指针的有效性 ASSERT(pbTo>=pbFrom+size || pbFrom>=pbTo+size);//检查两个指针指向的内存是否重叠 while(size–>0) *pbTo++ == *pbFrom++;return(pvTo);} 24、两个字符串,s,t;把t字符串插入到s字符串中,s字符串有足够的空间存放t字符串 void insert(char *s, char *t, int i){ memcpy(&s[strlen(t)+i],&s[i],strlen(s)-i);memcpy(&s[i],t,strlen(t));s[strlen(s)+strlen(t)]=’ ′;} 25、编写一个 C 函数,该函数在一个字符串中找到可能的最长的子字符串,且该字符串是由同一字符组成的。 char * search(char *cpSource, char ch){ char *cpTemp=NULL, *cpDest=NULL;int iTemp, iCount=0;while(*cpSource){ if(*cpSource == ch){ iTemp = 0; cpTemp = cpSource;while(*cpSource == ch)++iTemp, ++cpSource;if(iTemp > iCount) iCount = iTemp, cpDest = cpTemp;if(!*cpSource)break;} ++cpSource;} return cpDest;} 26、请编写一个 C 函数,该函数在给定的内存区域搜索给定的字符,并返回该字符所在位置索引值。int search(char *cpSource, int n, char ch){ int i;for(i=0;i return i;} 27、给定字符串A和B,输出A和B中的最大公共子串。比如A=“aocdfe” B=“pmcdfa” 则输出“cdf” */ //Author: azhen #include #include #include char *commanstring(char shortstring[], char longstring[]){ int i, j;char *substring=malloc(256);if(strstr(longstring, shortstring)!=NULL)//如果„„,那么返回shortstring return shortstring;for(i=strlen(shortstring)-1;i>0;i–)//否则,开始循环计算 { for(j=0;j<=strlen(shortstring)-i;j++){ memcpy(substring, &shortstring[j], i);substring[i]=' ';if(strstr(longstring, substring)!=NULL)return substring;} } return NULL;} main(){ char *str1=malloc(256);char *str2=malloc(256);char *comman=NULL;gets(str1);gets(str2);if(strlen(str1)>strlen(str2))//将短的字符串放前面 comman=commanstring(str2, str1);else comman=commanstring(str1, str2); printf(“the longest comman string is: %sn”, comman);} 28、写一个函数比较两个字符串str1和str2的大小,若相等返回0,若str1大于 str2返回1,若str1小于str2返回-1 int strcmp(const char * src,const char * dst){ int ret = 0;while(!(ret = *(unsigned char *)src – *(unsigned char *)dst)&& *dst){ ++src;++dst;} if(ret < 0)ret =-1; else if(ret > 0)ret = 1; return(ret);} 29、求1000!的未尾有几个0(用素数相乘的方法来做,如72=2*2*2*3*3); 求出1->1000里,能被5整除的数的个数n1,能被25整除的数的个数n2,能被125整除的数的个数n3, 能被625整除的数的个数n4.1000!末尾的零的个数=n1+n2+n3+n4;#include #define NUM 1000 int find5(int num){ int ret=0; while(num%5==0){ num/=5;ret++;} return ret;} int main(){ int result=0;int i; for(i=5;i<=NUM;i+=5){ result+=find5(i);} printf(“ the total zero number is %dn”,result);return 0;} 30、有双向循环链表结点定义为: struct node { int data;struct node *front,*next;};有两个双向循环链表A,B,知道其头指针为:pHeadA,pHeadB,请写一函数将两链表中data值相同的结点删除 BOOL DeteleNode(Node *pHeader, DataType Value){ if(pHeader == NULL)return;BOOL bRet = FALSE;Node *pNode = pHead;while(pNode!= NULL){ if(pNode->data == Value){ if(pNode->front == NULL){ pHeader = pNode->next;pHeader->front = NULL;} else { if(pNode->next!= NULL){ pNode->next->front = pNode->front;} pNode->front->next = pNode->next;} Node *pNextNode = pNode->next;delete pNode;pNode = pNextNode;bRet = TRUE;//不要break或return, 删除所有 } else { pNode = pNode->next;} } return bRet;} void DE(Node *pHeadA, Node *pHeadB){ if(pHeadA == NULL || pHeadB == NULL){ return;} Node *pNode = pHeadA;while(pNode!= NULL){ if(DeteleNode(pHeadB, pNode->data)){ if(pNode->front == NULL){ pHeadA = pNode->next;pHeadA->front = NULL;} else { pNode->front->next = pNode->next;if(pNode->next!= NULL){ pNode->next->front = pNode->front;} } Node *pNextNode = pNode->next;delete pNode; pNode = pNextNode;} else { pNode = pNode->next;} } } 31、编程实现:找出两个字符串中最大公共子字符串,如”abccade”,”dgcadde”的最大子串为”cad” int GetCommon(char *s1, char *s2, char **r1, char **r2){ int len1 = strlen(s1);int len2 = strlen(s2);int maxlen = 0; for(int i = 0;i < len1;i++){ for(int j = 0;j < len2;j++){ if(s1[i] == s2[j]){ int as = i, bs = j, count = 1; while(as + 1 < len1 && bs + 1 < len2 && s1[++as] == s2[++bs])count++; if(count > maxlen){ maxlen = count;*r1 = s1 + i;*r2 = s2 + j;} } } } 32、编程实现:把十进制数(long型)分别以二进制和十六进制形式输出,不能使用printf系列库函数 char* test3(long num){ char* buffer =(char*)malloc(11);buffer[0] = ’0′;buffer[1] = ‘x’;buffer[10] = ‘ ′;char* temp = buffer + 2;for(int i=0;i < 8;i++){ temp[i] =(char)(num<<4*i>>28);temp[i] = temp[i] >= 0 ? temp[i] : temp[i] + 16;temp[i] = temp[i] < 10 ? temp[i] + 48 : temp[i] + 55;} return buffer;} 33、输入N, 打印 N*N 矩阵 比如 N = 3,打印: 1 2 3 8 9 4 7 6 5 N = 4,打印: 1 2 3 4 12 13 14 5 11 16 15 6 10 9 8 7 解答: #define N 15 int s[N][N];void main(){ int k = 0, i = 0, j = 0;int a = 1;for(;k <(N+1)/2;k++){ while(j < N-k)s[i][j++] = a++;i++;j--;while(i < N-k)s[i++][j] = a++;i--;j--;while(j > k-1)s[i][j--] = a++;i–;j++;while(i > k)s[i--][j] = a++;i++;j++;} for(i = 0;i < N;i++){ for(j = 0;j < N;j++)cout << s[i][j] << 't';cout << endl;} } define MAX_N 100 int matrix[MAX_N][MAX_N];/* *(x,y):第一个元素的坐标 * start:第一个元素的值 * n:矩阵的大小 */ void SetMatrix(int x, int y, int start, int n){ int i, j; if(n <= 0)//递归结束条件 return; if(n == 1){ //矩阵大小为1时 matrix[x][y] = start;return;} for(i = x;i < x + n-1;i++)//矩阵上部 matrix[y][i] = start++; for(j = y;j < y + n-1;j++)//右部 matrix[j][x+n-1] = start++; for(i = x+n-1;i > x;i–)//底部 matrix[y+n-1][i] = start++; for(j = y+n-1;j > y;j–)//左部 matrix[j][x] = start++; SetMatrix(x+1, y+1, start, n-2);//递归 } void main(){ int i, j;int n; scanf(“%d”, &n);SetMatrix(0, 0, 1, n);//打印螺旋矩阵 for(i = 0;i < n;i++){ for(j = 0;j < n;j++) printf(“%4d”, matrix[i][j]);printf(“n”);} } 34、斐波拉契数列递归实现的方法如下: int Funct(int n){ if(n==0)return 1;if(n==1)return 1; retrurn Funct(n-1)+ Funct(n-2);} 请问,如何不使用递归,来实现上述函数? 请教各位高手! 解答:int Funct(int n)// n 为非负整数 { int a=0;int b=1;int c;if(n==0)c=1;else if(n==1)c=1;else for(int i=2;i<=n;i++)//应该n从2开始算起 { c=a+b;a=b;b=c;} return c;} 解答: 现在大多数系统都是将低字位放在前面,而结构体中位域的申明一般是先声明高位。100 的二进制是 001 100 100 低位在前 高位在后 001----s3 100----s2 100----s1 所以结果应该是 1 如果先申明的在低位则: 001----s1 100----s2 100----s3 结果是 4 1、原题跟little-endian,big-endian没有关系 2、原题跟位域的存储空间分配有关,到底是从低字节分配还是从高字节分配,从Dev C++和VC7.1上看,都是从低字节开始分配,并且连续分配,中间不空,不像谭的书那样会留空位 3、原题跟编译器有关,编译器在未用堆栈空间的默认值分配上有所不同,Dev C++未用空间分配为 01110111b,VC7.1下为11001100b,所以在Dev C++下的结果为5,在VC7.1下为1。 注:PC一般采用little-endian,即高高低低,但在网络传输上,一般采用big-endian,即高低低高,华为是做网络的,所以可能考虑big-endian模式,这样输出结果可能为4 35、判断一个字符串是不是回文 int IsReverseStr(char *aStr){ int i,j;int found=1;if(aStr==NULL)return-1;j=strlen(aStr);for(i=0;i if(*(aStr+i)!=*(aStr+j-i-1)){ found=0;break;} return found;} 36、Josephu 问题为:设编号为1,2,„ n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。数组实现: #include #include int Josephu(int n, int m){ int flag, i, j = 0; int *arr =(int *)malloc(n * sizeof(int));for(i = 0;i < n;++i)arr[i] = 1; for(i = 1;i < n;++i){ flag = 0; while(flag < m){ if(j == n)j = 0; if(arr[j])++flag;++j;} arr[j-1] = 0; printf(“第%4d个出局的人是:%4d号n”, i, j);} free(arr);return j;} int main(){ int n, m; scanf(“%d%d”, &n, &m); printf(“最后胜利的是%d号!n”, Josephu(n, m));system(“pause”);return 0;} 链表实现: #include #include typedef struct Node { int index;struct Node *next;}JosephuNode;int Josephu(int n, int m){ int i, j;JosephuNode *head, *tail;head = tail =(JosephuNode *)malloc(sizeof(JosephuNode));for(i = 1;i < n;++i){ tail->index = i;tail->next =(JosephuNode *)malloc(sizeof(JosephuNode));tail = tail->next;} tail->index = i;tail->next = head;for(i = 1;tail!= head;++i){ for(j = 1;j < m;++j){ tail = head;head = head->next;} tail->next = head->next;printf(“第%4d个出局的人是:%4d号n”, i, head->index);free(head);head = tail->next;} i = head->index;free(head);return i;} int main(){ int n, m;scanf(“%d%d”, &n, &m); printf(“最后胜利的是%d号!n”, Josephu(n, m));system(“pause”);return 0;} 第四部分:附加部分 1、位域 : 有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态,用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。 一、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为: struct 位域结构名 { 位域列表 }; 其中位域列表的形式为: 类型说明符 位域名:位域长度 例如: struct bs { int a:8;int b:2;int c:6;}; 位域变量的说明与结构变量说明的方式相同。可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如: struct bs { int a:8;int b:2;int c:6;}data; 说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。对于位域的定义尚有以下几点说明: 1.一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如: struct bs { unsigned a:4 unsigned :0 /*空域*/ unsigned b:4 /*从下一单元开始存放*/ unsigned c:4 } 在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。 2.由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。3.位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如: struct k { int a:1 int :2 /*该2位不能使用*/ int b:3 int c:2 };从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。 二、位域的使用位域的使用和结构成员的使用相同,其一般形式为: 位域变量名•位域名 位域允许用各种格式输出。main(){ struct bs { unsigned a:1;unsigned b:3;unsigned c:4;} bit,*pbit;bit.a=1;bit.b=7;bit.c=15;pri 改错: #include int main(void){ int **p;int arr[100];p = &arr;return 0;} 解答:搞错了,是指针类型不同, int **p;//二级指针 &arr;//得到的是指向第一维为100的数组的指针 #include int main(void){ int **p, *q;int arr[100];q = arr;p = &q;return 0;} 一、请填写BOOL , float, 指针变量 与“零值”比较的 if 语句。(10分) 请写出 BOOL flag 与“零值”比较的 if 语句。(3分) 标准答案: if(flag)if(!flag)如下写法均属不良风格,不得分。if(flag == TRUE)if(flag == 1)if(flag == FALSE)if(flag == 0)请写出 float x 与“零值”比较的 if 语句。(4分)标准答案示例: const float EPSINON = 0.00001; if((x >= – EPSINON)&&(x <= EPSINON) 不可将浮点变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”此类形式。如下是错误的写法,不得分。if(x == 0.0)if(x!= 0.0) 请写出 char *p 与“零值”比较的 if 语句。(3分)标准答案: if(p == NULL) if(p!= NULL)如下写法均属不良风格,不得分。if(p == 0)if(p!= 0)if(p)if(!) 二、以下为Windows NT下的32位C++程序,请计算sizeof的值(10分) char str[] = “Hello”;char *p = str;int n = 10;请计算 sizeof(str)= 6(2分)sizeof(p)= 4(2分) sizeof(n)= 4(2分)void Func(char str[100]){请计算 sizeof(str)= 4(2分)} void *p = malloc(100);请计算 sizeof(p)= 4(2分) 三、简答题(25分) 1、头文件中的 ifndef/define/endif 干什么用?(5分) 答:防止该头文件被重复引用。 2、#include 和 #include “filename.h” 有什么区别?(5分) 答:对于#include,编译器从标准库路径开始搜索 filename.h 对于#include “filename.h”,编译器从用户的工作路径开始搜索 filename.h 3、const 有什么用途?(请至少说明两种)(5分)答:(1)可以定义 const 常量 (2)const可以修饰函数的参数、返回值,甚至函数的定义体。被const修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。 4、在C++ 程序中调用被 C编译器编译后的函数,为什么要加 extern “C”?(5分) 答:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某 个函数的原型为: void foo(int x, int y);该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。 C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。 5、请简述以下两个for循环的优缺点(5分)for(i=0;i {if(condition)DoSomething();else DoOtherthing();} if(condition){for(i=0;i DoSomething();} else {for(i=0;i DoOtherthing();} 优点:程序简洁 缺点:多执行了N-1次逻辑判断,并且打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。优点:循环的效率高 缺点:程序不简洁 四、有关内存的思考题(每小题5分,共20分)void GetMemory(char *p){p =(char *)malloc(100);} void Test(void){char *str = NULL;GetMemory(str);strcpy(str, “hello world”);printf(str);} 请问运行Test函数会有什么样的结果?答:程序崩溃。因为GetMemory并不能传递动态内存,Test函数中的 str一直都是 NULL。 strcpy(str, “hello world”);将使程序崩溃。char *GetMemory(void){ char p[] = “hello world”;return p;} void Test(void){ char *str = NULL;str = GetMemory();printf(str);} 请问运行Test函数会有什么样的结果?答:可能是乱码。 因为GetMemory返回的是指向“栈内存”的指针,该指针的地址不是 NULL,但其原现的内容已经被清除,新内容不可知。 void GetMemory2(char **p, int num){ *p =(char *)malloc(num);} void Test(void){ char *str = NULL;GetMemory(&str, 100);strcpy(str, “hello”);printf(str);} 请问运行Test函数会有什么样的结果? 答:(1)能够输出hello(2)内存泄漏 void Test(void){ char *str =(char *)malloc(100);strcpy(str, “hello”);free(str); if(str!= NULL){ strcpy(str, “world”);printf(str);} } 请问运行Test函数会有什么样的结果?答:篡改动态内存区的内容,后果难以预料,非常危险。因为free(str);之后,str成为野指针,if(str!= NULL)语句不起作用。 五、编写strcpy函数(10分)已知strcpy函数的原型是char *strcpy(char *strDest, const char *strSrc); 其中strDest是目的字符串,strSrc是源字符串。(1)不调用C++/C的字符串库函数,请编写函数 strcpy char *strcpy(char *strDest, const char *strSrc);{assert((strDest!=NULL)&&(strSrc!=NULL));// 2分 char *address = strDest;// 2分 while((*strDest++ = * strSrc++)!= ‘ ’)// 2分 NULL; return address;// 2分 } 1.strcpy的实现代码 char * strcpy(char * strDest,const char * strSrc){if((strDest==NULL)||(strSrc==NULL))file://[/1] throw “Invalid argument(s)”;//[2] char * strDestCopy=strDest;file://[/3] while((*strDest++=*strSrc++)!=’ ′);file://[/4] return strDestCopy;}(2)strcpy能把strSrc的内容复制到strDest,为什么还要char * 类型的返回值? 答:为了实现链式表达式。// 2分 例如 int length = strlen(strcpy(strDest, “hello world”));错误的做法: [1](A)不检查指针的有效性,说明答题者不注重代码的健壮性。 (B)检查指针的有效性时使用((!strDest)||(!strSrc))或(!(strDest&&strSrc)),说明答题者对C语言中类型的隐式转换没有深刻认识。在本例中char *转换为bool即是类型隐式转换,这种功能虽然灵活,但更多的是导致出错概率增大和维护成本升高。所以C++专门增加了bool、true、false三个关键字以提供更安全的条件表达式。 (C)检查指针的有效性时使用((strDest==0)||(strSrc==0)),说明答题者不知道使用常量的好处。直接使用字面常量(如本例中的0)会减少程序的可维护性。0虽然简单,但程序中可能出现很多处对指针的检查,万一出现笔误,编译器不能发现,生成的程序内含逻辑错误,很难排除。而使用NULL代替0,如果出现拼写错误,编译器就会检查出来。[2](A)return new string(“Invalid argument(s)”);,说明答题者根本不知道返回值的用途,并且他对内存泄漏也没有警惕心。从函数中返回函数体内分配的内存是十分危险的做法,他把释放内存的义务抛给不知情的调用者,绝大多数情况下,调用者不会释放内存,这导致内存泄漏。 (B)return 0;,说明答题者没有掌握异常机制。调用者有可能忘记检查返回值,调用者还可能无法检查返回值(见后面的链式表达式)。妄想让返回值肩负返回正确值和异常值的双重功能,其结果往往是两种功能都失效。应该以抛出异常来代替返回值,这样可以减轻调用者的负担、使错误不会被忽略、增强程序的可维护性。[3](A)忘记保存原始的strDest值,说明答题者逻辑思维不严密。[4](A)循环写成while(*strDest++=*strSrc++);,同[1](B)。 (B)循环写成while(*strSrc!=’ ′)*strDest++=*strSrc++;,说明答题者对边界条件的检 查不力。循环体结束后,strDest字符串的末尾没有正确地加上’ ′。 六、编写类String的构造函数、析构函数和赋值函数(25分) 已知类String的原型为: class String {public: String(const char *str = NULL);// 普通构造函数 String(const String &other);// 拷贝构造函数 ~ String(void);// 析构函数 String & operate =(const String &other);// 赋值函数 private: char *m_data;// 用于保存字符串 }; 请编写String的上述4个函数。标准答案: // String的析构函数 String::~String(void)// 3分 {delete [] m_data; // 由于m_data是内部数据类型,也可以写成 delete m_data;} // String的普通构造函数 String::String(const char *str)// 6分 {if(str==NULL) {m_data = new char[1];// 若能加 NULL 判断则更好 *m_data = ‘ ’;} else {int length = strlen(str); m_data = new char[length+1];// 若能加 NULL 判断则更好 strcpy(m_data, str);} } // 拷贝构造函数 String::String(const String &other)// 3分 {int length = strlen(other.m_data); m_data = new char[length+1];// 若能加 NULL 判断则更好 strcpy(m_data, other.m_data);} // 赋值函数 String & String::operate =(const String &other)// 13分 {//(1)检查自赋值 // 4分 if(this == &other)return *this;//(2) 论述1.试述现代经济中间接融资与直接融资的关系。 答:直接融资是指公司、企业在金融市场上从资金所有者直接融通货币资金,其方式是发行股票或债券。间接融资是指公司、企业通过银行等金融机构融通资金,不与货币资金的所有者直接发生债权债务关系。 直接融资的优点在于:(1)资金供求双方直接联系,可以根据各自融资的条件,例如借款期限、数量和利率水平等方面的要求,实现资金的融通。(2)由于资金供求双方直接形成债权、债务关系,债权方自然十分关注债务人的经营活动;债务人面对直接债权人的监督,在经营上会有较大的压力,从而促进资金使用效益的提高。(3)通过发行长期债券和发行股票,有利于筹集具有稳定性的、可以长期使用的投资资金;由此筹集的资金具有可以长期使用的特点。在存在较发达的证券市场条件下,短期性资金也进入市场参与交易,支持这类长期融资的发展。 直接融资也有其局限性,主要表现在:(1)直接融资双方在资金数量、期限、利率等方面受到的限制比间接融资为多。(2)对资金供给者来说,直接融资风险,由于缺乏中介的缓冲,比间接融资为大。 间接融资的优点在于:(1)银行等金融机构以其网点多,吸收存款的起点低,能够广泛筹集社会各方面闲散资金,积少成多,形成巨额资金。(2)在直接融资中,融资的风险由债权人独自承担。而在间接融资中,由于金融机构的资产、负债是多样化的,融资风险便可由多样化的资产和负债结构分散承担,从而安全性较高。 间接融资的局限性,主要是由于资金供给者与需求者之间加入了金融机构为中介,隔断了资金供求双方的直接联系,在一定程度上减少了投资者对投资对象经营状况的关注和筹资者在资金使用方面的压力和约束。 由此可见直接融资和间接融资是金融市场上资金融通的两种方式,是调节盈余部门和赤字部门之间资金余缺的方式,两者均以信用为基础,相互补充,满足不同债权人和债务人的需要,实现了生产要素的重新优化组合、促进经济效益的提高,是金融市场不可缺少的因素。 2.阐明多种信用形式并存对我国银行经营改革的积极影响。 现代经济就是信用经济,信用关系无处不在,普遍于生活多方面及再生产的全过程,其规模和结构日趋扩张和复杂。 信用的形式包括商业信用和银行信用。商业信用是工商企业以赊销方式对购买商品的工商企业所提供的信用,商业票据是商业信用的重要手段。银行信用是指以银行和非银行金融机构为媒介、以处于货币形态的资金为借贷对象或载体所形成的信用。银行信用包括债权人债务人通过股票或债券在金融市场直接发生债权债务关系的直接融资和债权人债务人通过银行或非银行金融机构发生债权债务关系的间接融资两种方式。 此外信用形式还有国家信用和消费信用。国家信用是国家以发行国家债券为主要工具向货币所有者筹集资金用于弥补赤字、发展经济、调节经济生活。消费信用是指对消费者个人提供的,用以满足其消费方面的货币需求的信用,它能刺激生产、引导消费、调节经济、完善融资结构从而提高融资效率。 从1953年我国开始大规模有计划地发展国民经济以后,便开始了“大一统”的银行体系模式,一直延续到70年代末开始的改革。“大一统”的银行体系模式是以中国人民银行为唯一的银行,各分支银行按总行统一的指令和计划办事,严格监督和保证中央高度集中的计划任务的执行和实现,这是高度集中的计划经济管理体制的必然产物。改革开放以后我国首先是突破过去高度集中型的金融机构体系朝多元化体系方向改革,接着从1994年开始围绕“分业经营,分业管理”原则以及进一步加强监管、防范金融风险又进行了一系列的改革举措。随着我国经济建设的发展,信用的规模日趋扩张和形式日趋多样,对银行经营的模式和服务内容要求越来越高,也促进银行经营改革的快速推进。 3、如何理解国家信用在我国的开展? 答:国家信用是伴随着国家机器的形成所产生的一种古老的信用的形式,即国家举债。在现代社会中,从国内筹款是内债,从国外筹款是外债。不论内债或外债,在经济生活中都是不可忽视的重要因素。国家信用的主要工具是国家债券。 国家可以对内负债,也可以对外负债。国家国债有调节经济和弥补财政赤字等功能,但国家信用在我国的开展受多种因素的影响,一直未得到很好的利用。 要全面认识国家信用在我国的开展,先得正确分析观念问题。从内债在我国的开展过程看,国家信用开展经历了一个逐步打破观念束缚的过程。我国曾经是不允许举行国家信用的国家,并将即无内债,又无外债奉为社会主义财政制度优越性的体现,只有在54年—58年作为权宜之计发行国家信用,58年以后即停止,到68年结束还本付息,宣布即无内债,又无外债的政策并长期奉行这种政策。改革之初,我国开始打破传统的国家信用观念开始举债,81年开始恢复国内债务,82年开始恢复对居民个人的债券发行,但由于观念上的束缚和认识上的不足,并刻意划分与西方国家的国家信用的区别,国家不允许债券转让,到85年开放了二级市场,开始贴现,但奇高的贴现率实际上阻止了贴现,导致国库券在黑市流通,损坏了国家信用,损坏了老百姓的利益,扰乱了国家金融秩序。总体来说,80年代国债的开展极不科学,主要表现在国债的流动性、安全性、收益性均很高,国债的功能主要服务于国民收入的再分配,在当时条件下,发行国债只是权宜之计,没有想发展国家信用。90年代开始,国债的发行开始与优越性脱钩,开始比较科学看待国家信用的开展,90年代中期,开始大规模地开展国家信用。从正确看待新债补旧债的循环来说,新债补旧债,意味着一笔旧债可以永远不还或延长了旧债的使用期限,只要国民收入增长年复一年的增长,债务收入可以年复一年的提高。 要全面认识国家信用在我国的开展,必须结合中国的国情。国际上衡量一个国家负债比例的指标有两个:债务依存度,国际警戒线是20%,中国这个指标超过20%,应该控制国家信用;债务负担率,国际公认警戒线是60%,中国自开展国家信用以来,最高的债务负担率为18%,从这个指标看,中国的国家信用大有余地。因此,不能单纯地采用国际公认警戒线的标准来衡量,我国的国家信用开展不能简单等同于世界其他国家的状况,我国国家信用的开展完全有条件。国家信用问题在经济范畴讲,是合理存在的。 要全面认识国家信用在我国的开展,必须认识到在信用经济条件下,国家信用的开展,是以信用为前提,以偿还能力为前提,归根到底是以经济实力为前提。从我国外债开展的过程看,我国在80年代先运用外债,由于理论上的不统一和观念上的束缚,运用外债方式有问题,如80年代中期的提前偿还外债,对以后利用外资打下隐患,影响到80年代中期到94年近10年时间内,无法争取到低息贷款或优惠贷款,使得外债结构极不合理。而后,以信用经济理念,来看待国家信用问题,逐步摆脱了被动情况。现在我国外资利用情况非常好,是世界上第二大利用外资的国家,主要是由于中国有强大的黄金储备和外汇储备。 论述题4.我国在确定利率水平时应主要考虑哪些因素? 答: 马克思认为利息是部分剩余价值,利率变化应在社会平均利润率和零之间变化。西方经济学主要观点认为利率变化取决于供求对比。我国确定利率考虑的主要因素有: 1、社会平均利润率状况。平均利润率要按大多数企业能够承受水平来确定(即变通执行)。但如何确定一个绝大多数企业能够承受的水平是个难题,特别在企业数量越来越多的时候。因此,利率的市场化改革十分紧迫。 2、资金供求状况,这不是目前主要考虑因素,但将是未来改革取向,利率取决于民间借贷市场、同业拆借市场、外汇市场、债券市场因素,利率浮动范围将加大,逐步从官定利率占垄断地位走向以供求关系决定利率。 3、国家经济政策的要求,引导市场利率体现国家政策要求。 4、物价水平的变动,官定利率追随物价水平,也间接考察市场供求状况。 5、证券市场状况。利率和证券价格水平呈反向变动。 6、国际金融市场利率水平,主要是针对外汇和利用外资考虑。 7、考虑银行利益。 论述题5:如何理解金融市场实现其功能所应具备的理想条件? 就市场经济总结出来的理论,金融市场实现其功能归结为必须具备两个理想条件:完整的信息和完全由市场供求决定的价格。 完整的信息指金融交易的买卖双方均可以迅速获得所需要的信息,以便根据它们进行决策。但信息有其非主动性、随意性、不规范性、滞后性、不连续性及虚假性。即使加强了会计监管规范了市场在信息披露方面的管理,任何严格的信息披露法规和任何先进的信息传递组织和技术装备也不可能保证给市场提供真正“完整”的信息。同时,姑且不说实际难以避免的真伪信息杂陈,就算全是真的信息,买卖双方所获得的也往往是不对称的信息,从而使买卖双方不能处于同等的决策地位。虽然发达国家对金融市场参与者披露自身有关的理想状态,还有不小的距离。至少到目前为止,还难以证明,只要有了完整的信息,就可以消除金融市场波动以及不时爆发的动荡、紊乱和危机。 完全由市场供求决定的价格是指价格水平的形成除取决于资金供给方与需求方的竞争均衡外,不受任何力量干预。金融资产的市场价格需要由真实、及时、充分的市场所有相关的信息所决定。似乎这样就可以形成对价格有充分弹性的市场。但任何均衡价格的形成,包括作为资金“价格”的利率在内,通过交易场所资金供求变化而决定,都只能是一个趋向。是在均衡点周围摆动,并且也不能排除远离均衡点的可能。相应的,资金的分配也不会由于具有对价格的充分弹性而像设想的那样“优化”,那样“有效益”。同时,因为商行自我约束能力还不强,不能完全由市场供求决定,所以利率自由化也不是完全由市场决定,而是增加由市场决定的比重。 这两方面完整的信息是核心,充分准确的完全由市场决定的价格需要有完整的信息为基础,这是金融商品特殊的价值决定方式。但是金融市场要实现其功能,即使在发达的市场经济中也不可能具备那样理想的条件,而且还必须以社会难以避免的损失为代价,要以理论为指导,不断渐进式的改革。 论述题6.论我国现阶段实行金融分业经营及管理的必要性 理论界已经提出中国应发展“混业经营”,而且许多金融机构也已经出台了一些新举措,混业经营的大趋势已初见端倪。建议本题改换成,试分析在我国实行金融混业经营的可能性和必要性;或与论述题7合并。 答: 在金融运行中,银行业与证券业关系密切,两者之间呈现出十分复杂的互为影响的关系。如何促进银行业与证券业的协调运行,从宏观上看直接影响到资源配置效率和宏观金融体系的稳定,从微观上看则决定着银行业与证券业发展的方向。 世界各国的金融当局,依据各自不同的国情,立足于促进银行业与证券业的协调发展,分别采取了混业经营或分业经营的运行模式。分业经营是指银行业与证券业相互分离,分别由不同的金融机构进行经营,其特点是银行与证券两种业务严格分开。我国现阶段实行金融混业经营还缺乏许多必要条件,表现在: (1)以“产权清晰、职权明确、政企分开、管理科学”为标志的现代企业制度还远未建立。我国的企业目前还是国有经济占主体,长期在政企合一的计划管理体制下经营,造成产权不清晰、权职不明确等诸多体制上的问题,同时,企业经营方式落后,管理很不科学。 (2)我国的金融机构基本上全部属于国有企业,其存在及经营必须是以宏观效益为主导,即使是发生微观效益与宏观效益的矛盾,也应是微观效益服从于宏观效益。国内的银行制度还不是现代银行制度,如果让其混业经营,在缺乏自我约束、科学管理的条件下,其经营风险之大可想而知。特别是四大国有商业银行,因其在国内银行业的垄断地位,实行混业经营容易形成金融市场的垄断,产生不公平竞争,可能会招致新的更大的金融风险。 (3)发展极不成熟的证券业。我国证券市场起步不过十年,想一下子摆脱、打破中国四十年来的计划经济体制是困难而艰巨的,不可能一步而成。证券市场仍存在许多问题,混业经营就缺乏成熟条件。 (4)宏观金融监管极不完善。目前在分业监管中仍存在监管不善、风险控制机制不健全等的问题,证券欺诈等行为屡有发生,在这前提下谈对混业经营进行宏观监管不现实。 (5)法制极不健全。现有金融法规仍不够健全,在法制上仍然存在漏洞,并存在执法不严等等问题。 (6)我国证券业的起步虽然是由银行经营证券开始的,但由于我国证券市场尚处于起步阶段,发展不完善,风险较大,居于主体地位的我国四大国有商业银行还未真正成为自主经营、自负盈亏、自担风险、自我约束的经济主体,金融当局监管的监控能力也尚待提高,由此决定了我国实行银行业与证券业分业经营的模式。 另一方面,目前的分业经营与严格的分业监管同样存在许多问题,经营与监管极不规范,互相之间又恶性竞争,缺乏统一管理。 综上所述,我国现阶段仍然要坚持分业经营、分业管理的格局。 论述题7 论述我国商业银行经营向全能化发展的内外部条件 答:银行经营全能化,也有综合式银行之称。是指银行可以经营一切银行业务,包括各种期限和种类的存款贷款和全面的证券业务等。世界上采用全能式商业银行模式的国家以德国、奥地利和瑞士为代表。 银行经营全能化的优点有: 1.通过全面、多样化业务的开展,银行可以深入了解客户的情况,有助于做好存款贷款工作。2 借助于提供各种服务,有利于银行吸引更多的客户,增强银行的竞争地位。3 可以调剂银行各项业务盈亏,减少乃至避免风险,有助于银行稳定经营。 综观全球金融发展,商业银行经营向全能化发展的趋势势不可挡。即便是原先实施分业经营最为突出的美国和日本也已走上了全能化、综合化经营的道路。原因有以下几点: 1在金融业的竞争日益激烈的条件下,商业银行面对其他金融机构的挑战,利润率不断下降,迫使它们不得不从事各种更广泛的业务活动。 2吸收资金的负债活动的结构发生变化,可以获得大量长期资金来进行更多的业务活动,特别是长期信贷和投资活动。 3在上述情况下,国家金融管理当局也逐步放宽了对商业银行业务分工的限制。 实现商业银行经营全能化发展的途径则有: 利用金融创新绕开管制,向客户提供原先所不能经营的业务。2 通过收购、合并或成立附属机构形式渗入对方业务领域。3 通过直接开办其他金融机构所经营的业务实现综合经营。 尽管商业银行经营向全能化发展有着诸多优势,我国银行目前若要向全能化经营方向发展,还面临着种种条件的不成熟。归纳起来,需具备的外部条件有: 现代化企业制度的建立 企业是金融运行的微观基础,只有建立了真正的以“产权清晰,权责明确,政企分开,管理科学”为目标的现代企业制度,才能为银行经营向全能化发展提供一片健康的土壤。反之,如果企业仍然缺乏有效的自我激励机制,一旦银行实行混业经营,将承担巨大的货币风险。成熟的证券业 证券市场应作为国民经济运行的晴雨表,为银行全能化经营及时地提供市场动态信息,帮助银行作出正确的经营决策。同时证券市场也是全能化经营的银行的一个重要业务活动领域。一个成熟的证券市场,既包括了作为市场主体的券商、投资者、监管者和上市公司的成熟,也要求交易法则、投资思路、投资行为和监管活动的成熟。但我国的证券市场起步不过10年,正肩负着打破解放来运行了40多年的旧体制的重要任务。就目前来看,投机远大于投资,远未实现成熟化。完善的宏观金融监管 作为金融运行的监督管理机制,宏观金融监管应为银行经营提供一合理高效的运行环境,确保全能化经营活动的顺利开展。但目前我国的宏观金融监管还很不完善,无论是在措施方面还是手段方面。虽有中国人民银行、证监会、保监会分别对银行、证券业和保险业进行分业监管,但并没有实现预期的监管效果。我们可以看到的却是:大量银行违规资金流入股市,众多的上市公司造假帐,保险业则面临着偿还危机。如此种种,无不体现了宏观金融监管的薄弱与不力。健全的法制 健全的法律法规为金融运行起到了保驾护行的作用。只有真正实现了“有法可依,有法必依,执法必严,违法必究”,将我国建设成为社会主义法制国家,全能化的金融运行才能顺理成章。目前我国在金融领域里的立法还很不完善,仅有《中华人民共和国人民银行法》、《中华人民共和国商业银行法》等少量法规。其他多通过央行制定规章制度来指导银行运作。这种情况下,势必存在诸多的立法、执法方面的空白与不力。 除了上述外部条件外,我国银行业经营向全能化发展还有一重要的内部条件,即必须建立现代商业银行制度。 现代商业银行制度,以“自主经营,自负盈亏,自担风险,自我约束”为目标。要求银行真正具备内在的自我激励和自我表现约束机制,使商业银行成为真正的利益主体,而不是象目前这样的局面:四大国有银行的资金,人事均受制于国家,一旦银行的微观利益与国家的宏观利益发生冲突时,必然以牺牲银行利益换取国家利益。事实上,我们国家的四大国有银行已承担了相当的改革成本。 在讨论这个问题时,我们还应该看到,目前我国商业银行的分业经营模式是不利于商业银行经营向全能化发展的。所以只有打破这样的格局,才能真正实现我国商业银行经营向全能化发展。 论述8:试析我国银行业务多样化发展的必要性以及目前的约束条件 答: 我国在解放后, 改革开放前, 商业银行系统长期处于 “大一统” 的管理体制下,造成资产结构单调, 经营业务主要以贷款为主,而贷款又重要局限在短期和有偿性投放范围,从而导致银行业务形式单调.1.商业信用严重不规范性, 给我国银行业务多样化发展造成很大的约束.解放后银行信贷工作中曾长期讲计划性、物质保证性和按期归还性三个原则。其实,这是在吸取前苏联信贷原则的基础上形成的,除基于社会主义计划经济体制要求的计划性原则外,实际上与真实票据原则有一脉相承之处。正因为贷款局限于短期和有偿性投放范围,致使银行资产业务形式单调。改革开发以来,商业票据业务和证券业务随着金融体制的改革已逐步开展,资产业务单调的格局因而得到改善。但是,由于长期杜绝商业信用,到90年代初,我国商业信用的规模依然不够大,票据也不规范,当事各方完全不讲信用,这就在很大程度上制约了商业银行的贴现和中央银行的再贴现。由此看来,规范商业信用便成为我国银行业务多样化改革的前提。2.商业贷款的单一性, 我国商业作为从事金融业务的经济组织,在经营以效益性、安全性、流动性为经营原则。但由于长期处于“大一统”银行体制下,资产结构单调,主要以贷款为主,贷款对象大部分是国有经济的企事业单位,相当部分具有长期投资性质,而银行的负债又以存款为主,具有很强的被动性。在流动性、安全性上存在很大的危机,而且效益性较为低下。另外,银行在进行贷款时,由于与企业在长期往来关系,使贷款业务带有感情色彩,3.在我国开展信贷消费存在一些不理想的约束条件:①银行信贷工作者素质跟不上要求,消费信贷要求信贷懂技术、经济、经营的全面人才;②仓容不足,中国的银行基本没有自己的仓库,造成一批物质多头抵押,容易给银行造成损失;③理论准备不充分,认识不统一:a,国有银行与国有企业的关系是追求微观个体的利益最大化,还是国家体现宏观效益的工具。b,如何开展物资抵押贷款问题,在社会主义国家冻结封存适销对路的物资,是否是对财富的浪费。 论述9 商业银行之所以能够派生存款的基本条件是什么?制约其存款倍数扩张的因素是如何发挥作用的? 基本条件有3点: 1、部分准备金制度,不是100%的准备金制度; 2、转帐结算(活期存款吸收),若全是现金结算则不存在派生存款; 3、基础货币的获得,货币制造的主体在央行。 制约其存款倍数扩张的因素是通过以下方式发挥作用的: K=△D/△R=1/rd+c,+e,+rt*t,1、法定存款准备率(rd)发挥着重要作用,央行可通过调整rd等行为直接制约派生存款大小。 2、现金漏损率(c,)、超额存款准备金率(e,)和有关定期存款准备率的大小都制约着派生存款的大小。(1)存在着现金漏损现象,使银行系统的存款准备金会减少,也使银行由吸收存款而可扩大贷款的资金相应减少,由此减少了银行创造派生存款的能力。(2)银行并非一定会将超额准备金全部贷出。为安全或应付意外之需,银行实际持有的存款准备金总是高于法定准备金。(3)存款分活期存款和定期存款,准备金也应该分活期存款准备金和定期存款准备金,活期存款中还有rt*TD/D的部分不能成为创造存款货币D的基础。上述商业银行和个人行为都制约着派生存款的大小。 3、企业、个人等客户的行为也制约着派生存款的大小。并非银行任何时候总有机会将可能贷出的款项全部贷出,在经济停滞和预期利润率下降的情况下,即使银行愿意多贷,企业或个人也可能不要求贷款,从而可能的派生规模并不一定能够实现。 论述11.试结合派生存款中对乘数(K)的修正,谈谈银行吸收定期存款的意义。 首先谈一下派生存款。在支票存款转帐系统的条件下,当银行根据经验按存款的一定比例——假设是20%——保存铸币存款时,10000元铸币的存款,可使有关银行共发出40000元贷款,再把最初10000元存款包括在内,则共可以产生50000元存款(经过派生的存款总额)。从先后顺序来说,10000元是最初的存款,40000元是由于有了最初的存款才产生的。因此,通常把最初的存款称为“原始存款”,把在此基础上扩大的存款称为派生存款。 在讨论派生存款概念的基础表述中先假定存在3个假设条件:(1)银行不留存超额准备金,即超额准备金率为零;(2)全部交易以结算方式进行,不提现,即现金漏损率为零;(3)全部存款为活期存款。 在此基础上:(1)原始存款;(2)经过派生的存款总额;(3)必要的铸币库存对存款的比率,这三者的关系可以用如下公式表示: D=R 1/ r 式中,D为经过派生的存款总额;R为原始存款;r为必要的铸币库存对存款的比率。由此,我们引出乘数K的概念:K, 派生存款倍数,K=D/ R=1 / r , 所以 R=D r 为更加符合现实状况,我们对乘数K进行修正:(1)加上超额准备率 e R=D r +D e = D(r +e),所以 K=1/ r+e(2)加上备付金比率(防止提现)c R =D r + D e + D c=D(r+e+c)所以 K= 1/ r+e+c 由此可看出派生存款倍数K在不断减小,现实生活中由于还有定期存款,我们还要继续修正。 R = D r + D e + Dc +D Dt /D4 rt = D(r+e+c+Dt/ D4 rt) K =1/(r+e+c+Dt / D4 rt),其中rt为定期存款准备率,Dt/ D4为定期存款与活期存款的比率。K进一步变小,所以吸收定期存款越多,派生存款倍数越小。但要注意到这里“K”是指活期存款扩张倍数而非整个扩张倍数。 根据对乘数K的修正分析,我们可以看到银行吸收定期存款的意义在于: (1)吸收定期存款越多,虽然活期存款扩张倍数会下降,但银行存款总额会得到极大扩张。 (2)吸收定期存款越多,银行的贷款规模可以扩张,从而有利于银行效益的提高。(3)吸收定期存款越多,银行的超额准备金就可以少留,更有利于贷放。吸收定期存款越多,现金漏损将减少。因为针对同一“漏损率”,活期存款少则现金漏损少。 12.从中国国情出发,阐明我国实行间接融资占主导地位的必要性。 答:间接融资:在银行信用中,银行等金融机构是信用活动的中间环节,是媒介。从聚集资金,它们是货币资金所有者的债务人;从贷放资金角度,它们是货币资金需求者的债权人。至于货币资金的所有者同货币资金的需求者,两者之间并不发生直接的债权债务关系。这种通过向银行等金融机构借款来筹集资金方式为间接融资。与间接融资相对应的是直接融资,是指货币供给者和货币资金需求者之间直接发生信用关系,货币资金需求者自身直接发行融资凭证给货币资金供给者。 我国的融资结构处在金融主导型的总体格局下,直接融资的比重有所上升,但仍以间接融资占主导地位。 直接融资和间接融资各有优点,也各有其局限性: 直接融资的优点在于(1)资金供求双方直接联系,可以根据各自融资条件,实现资金融通;(2)资金供求双方直接形成债权债务关系,债务人面对直接债权人的监督,有利于促进资金使用效益的提高;(3)有利于筹集到长期使用的资金。直接融资的局限性在于:(1)直接融资双方在资金数量、期限、利率等方面所受到的限制比间接融资多;(2)由于缺乏中介的缓冲,融资风险较大。间接融资的优点在于:(1)银行等金融机构以其网点多,吸收存款的起点低,能够广泛筹集社会个方面闲散资金,积少成多,形成巨额资金;(2)由于金融机构的资产、负债多样化,融资风险便可由多样化的资产和负债结构分散承担,从而安全性较高。其主要局限性在于:资金供求双方之间加入了金融机构为中介,隔断了供求双方的直接关系,在一定程度上减少了投资者对投资对象经营状况的关注和筹资者在资金是使用方面的压力和约束。 我国目前资金供应的现实情况是,在长期“大一统”的金融体系下,银行在企业经营资金供应方面长期占据垄断地位。而且我国资金供应来源主要是储蓄业务,银行以其众多网点的优势,吸纳了大部分的储蓄资金,所以尽管随着改革开放的进行,我国的金融市场在逐渐发展、完善,金融工具在不断丰富、多样化,直接融资的比重有所上升,但间接融资仍占主导地位。建议补充我国间接融资的现状 论述题 13、试析居民储蓄分流之于我国经济金融的影响。 答: 一、居民储蓄分流及其原因: 1.居民储蓄分流是中国金融改革的一个特定范畴。它是相对于中国曾经的“大一统”银行信誉和现在仍然存在的银行垄断信用而言的,是指每年新增的居民储蓄,除了一部分继续流入银行形成储蓄存款,其他部分分流到股票、债券、保险单及外汇等其他信用形式工具上去。 2.储蓄存款分流的主要原因: (1)利率连续下调。从1996年5月以来,人民银行已连续7次下调利率(数据不含最近一次的调整),如1年期定期储蓄存款为例,已明显低于美元、欧元等国际通货的利率水平。连续和大幅度的利率下调,降低了其它因素的影响作用,使储蓄存款减少的实际效果逐步体现出来; (2)恢复征收利息税。国务院决定自1999年11月1日起恢复对储蓄存款利息征收个人所得税。这对于存款者来说,等于第八次降息。如1年期储蓄存款利率扣除20%的利息税后,仅为1.8%。从某种意义上讲,征收利息税比降息可能产生更大的分流储蓄存款的效果。降息给人的感觉是居民从银行得到的收益减少了,而征税的感觉是居民“应得的利益”被拿走了,因而后者比前者更能促使人们调整资产结构,寻求新的资金去向; (3)存款实名制的酝酿和实施。2000年4月1日起开始实施的个人存款实名制,使一些来历不明的存款大量转出,加速了储蓄存款的分流。 (4)储蓄存款收益率的下降,促使居民手中的资金寻找其它投资渠道,促进居民投资观念的转变,而这又反过来加速了储蓄存款资金的分流。此外,消费信贷的推出,促进了人们消费观念的转变,也加速了储蓄存款资金的分流。 3.随着中国金融改革的深化,居民在存款之外,获得了种类越来越多而且是越来越具有吸引力的金融投资机会,股市投资、国债投资和外币存款,就是其中最主要的3种。2000年,全国凭证式国债累计发行1900亿元,同比多增371亿元;居民外币储蓄存款全年增加176亿美元,同比多增35亿美元(折为人民币约合1500亿元);而股票市值在这一年已经接近5万亿,可流通部分也超过2万亿。 二、居民储蓄分流,它主要作用于对我国融资格局的影响及对银行业务经营的影响。1.对融资格局的影响 只有居民储蓄分流了,才能形成融资形式多样化的格局,能最大化地满足融资双方意愿,但要注意分流的度以间接融资占主导为主线。 (1)从宏观上讲,如新增的居民储蓄如果正常分流到我国股份市场上去,会使股份信用大大发展;如果正常分流到保险市场上去,会使保险信用大大发展;如果正常流到国债市场上去,会使债券信用大大发展,同样正常分流到外汇等等市场上去也使促进相关信用的发展。 (2)从微观角度看,储蓄存款收益率下降了,促使居民手中的资金寻找其它投资渠道,如去做外汇等投资,由于现在的社会保障体系尚未十分健全,将使一部分居民去买了商业保险,等等类似的情况一方面会促进居民储蓄分流,另一面丰富了居民投资与保障。 (3)总之,居民储蓄分流将大大改善中国的融资格局,形成融资方式多样化,最终提高融资效率,对我国经济金融日益成熟有深远的意义。 2.对银行业务经营的影响(主要从三方面分析)负债问题角度分析: (1)负债业务推动了直接融资的发展,有利于中国多样化格局的形成,有利于金融市场的形成,从而有利于银行主动负债业务的开展。 (2)主动负债业务的开展改变了单调的银行负债格局从而有利于分散风险,提高效益。 储蓄存款分流,改变了银行存款结构,但由于现在的社会保障体系尚未十分健全,各阶层、各年龄段的人士都因未来的生活保障首选储蓄,以及百姓投资金融通道不畅等各种因素影响,存款量的减少不会对银行的各种业务产生太大冲击。相反,银行则面临一个崭新的发展机遇:因为储蓄分流是建立在多种信用工具、多种信用形式并存基础上的。宏观总体来看,它将使银行的资产和负债多样化,同时分散风险,提高收益。 (3)但是应该看到分流的结果必然使银行稳定性资金来源的比例下降,所以根据中国银行业不良资产包袱(主要指不良贷款)很重的现状,因此要注意居民储蓄分流的力度,不能过快,必须循序渐进。 由于我国银行信贷资金来源中居民储蓄存款居第一位,虽然现金与存款都是银行的资金来源,但存款下降将会使银行资金运用下降,导致银行收益下降,储蓄分流将使银行经营面临严重考验。如果储蓄分流过快,必然使银行资金来源骤减,从而削弱其信贷支付能力,对经济发展产生不利影响。 资产问题角度分析: (1)同上一样,从资产业务角度,居民储蓄分流有利于银行开展再贴现业务,有利于银行开展投资业务,有利于资产业务多样化,规避风险,提高效益。 (2)但根据中国银行业现状,一定要注意银行的巨大不良资产,所以分流不能过快,必须循序渐进。 中间业务及表外业务角度分析: 居民储蓄分流推动了金融市场的发展,为银行中间业务的新业务的开拓提供了前提(如银行、证券结算问题),不仅可以提供原生而且可提供衍生工具,从而为银行规避风险,开辟了新的利润来源。 居民储蓄存款分流变为手中现金用于消费,则为银行开展私人理财和代收代付等中间业务及表外业务发展提供了更好的环境;银行还可通过多种业务的开展,建立多种安全保值、让老百姓喜爱的投资品种,使储蓄存款中的部分资金能适当转化为长期债券、各类保险等稳定性较强的金融资产形式。也就是说,银行可通过改变资产结构,替代公众货币分流,这样,一方面分散部分过于集中的银行储蓄资产,另一方面分散居民资产的风险,也可防止一些非法集资活动的出现。 在短缺经济时代,资金供不应求,资金规模越大,银行效益越好。现在不同了,随着存款分流,银行本应转向以发放贷款以及完善金融服务为立行之本,但由于在这方面缺少相应的激励制度,以及中央银行基准利率较高和银企关系发生的深刻变化等因素,使得银行尤其是国有商业银行大多以规避风险为主,面对存款分流,银行在这方面也应该是有潜可挖的。 值得一提的是:目前,中小企业和非国有经济在我国国民经济中已处于重要地位,而与之配套的中小金融机构却面临窘境,存款分流的另一方面是有限的存款正在向国有大银行集中。由于资金来源的减少,这些机构的市场占有率也会遇到阻力,这迫切需要管理部门在货币政策上有新对策。 总之,我国作为一个发展中国家,发挥储蓄分流作用,启动消费,拉动经济是极其重要的,从这一方面讲,储蓄分流应是一个渐进的过程,同时其于上面的分析也有利于银行拓展业务,最终提高经济效益。 论述14: 20世纪60年代以前,银行资金、来源大多是吸收活期存款。在银行看来,存否、存多少及存期长短,主动权都在客户手中。负债管理理论是在金融创新中发展起来的理论。银行面临资金不足,但为了维持与客户的良好关系,又必须满足客户对贷款的需求。所以,银行不得不以创新的方式去获取新的资金来源。负债管理的核心思想就是主张以借入资金的饿方法来保持银行的流动性,从而增加资产业务,增加银行收益。负债业务管理有效,则无须经常保有大量流动性资产,并可将资金投入到更有利可图的资产上,银行收益将更高。负债管理开创了保持银行流动性的新途径,可根据资产的需要调整或组织负债,让负债去适应或支持资产,为银行扩大业务规模和范围创造了条件。负债管理有利于银行风险的转嫁和分散,增加收益性。 因此我国银行在主动负债中应注意融资成本、市场风险,不要为负债业务形式的多样化而多样化当银行有充足的被动负债时无必要再吸收主动负债。有超额准备金,而无企业可放贷,资金充足时也无必要吸收主动负债。 答:随着各个历史时期经营条件的变化,商业银行经营管理理论经历了资产管理、负债管理与资产负债综合管理三个发展阶段。负债管理是在金融创新中发展起来的理论。一方面随着规模的扩张银行传统资金来源不足,另一方面银行资产业务规模也不断扩大。而金融创新为商业银行提供了新负债手段与新的资金来源。 负债管理的核心是以主动负债的方式来保持银行流动性,从而增加资产业务,增加银行收益性。 负债管理理论开创了存款货币银行经营管理理论的新篇章。 首先,开创了保持银行流动性的新途径。银行的流动性不仅可以通过资产管理获得,向外借款也可提供流动性,只要借款领域广大,流动性同样可以得到保证。 其次,由单靠吸收存款的被动型负债方式发展成向外借款的主动型负债方式。如果主动负债业务有效开展,则无须经常保有大量高流动性的资产,资产业务的开展余地更大,从而提高银行收益性。完全可以根据资产的需要调整或组织负债,让负债业务去支持资产业务。再次,负债管理为银行扩大业务规模和业务范围创造了条件。从历史上看,是金融创新催生了负责管理理论,而负债管理的实施又推动着金融创新向更深更广的领域发展。 虽然负债管理有诸多优点,但在开展负债管理时必须注意以下几点: 1.负债管理提高了银行的融资成本。通过借款融资的利息一般高于普通存款。而我国商业银行一般机构臃肿,人员编制庞大,成本高,盈利水平低。再加上投资渠道单一,政策约束多,银行资产业务盈利水平也不见得高。若主动负债业务开展不恰当,不但产生不了高额利润,还有可能弥补不了成本。 2.负债管理增加了经营风险。由于主动负债主要借助于金融市场,而市场的风险是很难控制的。特别在我国,金融市场的方方面面都不成熟,风险量化不宜,技术手段难以控制。一旦遭遇到市场变化,损失可能是巨大的。 3.负债管理注意力集中于对外借款,往往忽视银行自有资本的补充,不利银行的稳健经营。在我国一些商业银行近年来业务发展迅猛,但自有资本不足的隐患渐渐显露。 4.开展负债管理的目的不是负债形式的多样化。不要为负债形式的多样化而多样化。在我国,由于投资渠道单 一、传统思想束缚,居民的储蓄意识依然很强。因而,为商业银行提供了源源不断的资金来源,特别是其中的定期存款更是稳定的负债。银行是否有必要放弃低成本、稳定的负债,而开展成本更高的主动负债业务。所以,现阶段我国商业银行开展负债管理的动因与负债管理产生的动因是有区别的。 15. 试述深化我国中央银行体制改革面临的主要问题及对策。 目前改革20多年来已经进入关键时刻,对于我国的中央银行体制改革同样进入了关键时刻。主要面临的问题有: 1. 央行组织机构庞大臃肿,人浮于事,不可避免削弱它的宏观调节效率; 2. 央行行为扭曲,不利于宏观调节; 3. 独立执行货币政策受到三方面的干扰: (1)来自各级政府的干预:国务院及各级地方政府的干预政策独立执行;(2)政府各部门的同级的排挤,尤其是财政部;(3)来自被监管机构的干扰,形成“倒逼机制”。 从对策方面来看,主要是在央行本身特点的基础上,创造条件,削减分支机构,减臃增效;排除各种干扰,加强宏观控制,执行财政金融政策,运用货币政策工具,进行宏观调节。 那么从货币政策工具的运用来分析,也可以得出目前央行体制改革面临的主要问题及可采取的对策: 1. 准备金政策 总量政策中掺进结构性因素,定高准备金率,破坏了商业银行自身的平衡,最终原因是我国经济结构严重不平衡性。我国在准备金政策启动是严重扭曲的,直到1998年才得以一定程度解决,存在的几个问题: (1)准备金率仍然偏高,达到6%(2)法定存款准备金制度不分定期存款和活期存款,所有银行都采用同一比率;(3)准备金制度是付息制。理论界认为应取消付息制,以免造成银行惜贷,不愿冒风险。 因此要解决准备金政策方面的问题,也就是根据存在的问题,降低准备金率,同时要区分定期存款和活期存款的准备率。2. 再贴现政策 再贴现业务就前期的空白而言,我们已有了很大发展,成为很有前途的货币政策工具,潜力大,但远不理想,原因在于: (1)商业银行贴现规模不理想,关键是信用问题:商业信用规模不大;票据不规范;当事者各方不讲信用。中国在信用观念上一直存在问题,信用意识不强,因而造成银行不可能扩大票据贴现规模,从而替企业背上包袱,进而影响再贴现的开展; (2)我国是官定利率机制,不存在利用再贴现率的调整来间接影响市场利率走向的机制,通过基准利率的调整来影响市场利率的机制是不存在的,或者说很大程度上是有缺陷的,本身官定利率的制度说明了利率机制缺乏弹性; (3)由于准备金制度的曾经扭曲,使商业银行尤其是国有银行,如果能够轻易从中央银行获得再贷款融资的话,就无动力也无必要开展再贴现。 要改善这些问题,必须加强信用观念,改变原先的官定利率,向市场利率的机制转变,在改善准备金制度的基础上,来增加再贴现政策的货币工具。3. 公开市场业务 有了很大发展,99年以来已经成了我国每年基础货币增量供给的主要渠道,但仍有许多不理想的地方,原因在于: (1)国债规模不够大; (2)国债期限不合理,短期国债几乎为零:公开市场业务操作,应该是短期国债,便于吞吐,收益率很低,但我国没有这种国债;目前记帐式国债严重不足; (3)在国债占有的分布结构上不合理:大部分为个人所持有,而且是凭证式的,这种格局不好调整。但是从1999年以来,公开市场已经成为增量货币基础投放的主渠道,所以发展势头较好。我们更应该发行短期国债,以及记帐式国债业务,从而推进公开市场业务。 (4)国债市场不统一; (5)中央银行缺乏权威地位。 总的来说,央行的体制改革不仅应该在企业的机制改革上下大力气,同时在货币政策工具的实施上要有所改善,不仅解决体制上的问题和机制上的问题,同时要解决政策上和工具上的问题。 论述题 16、试述后凯恩斯学派对凯恩斯货币需求理论的发展。P254 答:凯恩斯的后继者们认为,凯恩斯的货币需求理论还在着缺陷,需要修正、补充和发展,从50年洼开始,他们从两方面取得了进展:一是关于交易性货币需求和预防性货币需求同样也是利率函数问题;二是关于人们多样化的资产选择对投机性货币需求的影响问题。 一、凯恩斯的货币供求理论 (一)货币供应理论 1.外生货币论。货币供应是由中央银行控制的外生变量。2.货币供应渠道。公开市场业务是增减货币供应的主渠道。3.货币供应变动的影响。 (二)货币需求理论 1.货币需求的动机与构成。交易动机构成货币的交易需求;预防动机构成货币的预防需求;投机动机构成货币的投机需求。 2.货币需求的特征 (1)货币交易需求和预防需求的特征:相对稳定可以预计;货币主要充当交换媒介;对利率不太敏感;是收入的递增函数。 (2)货币投机需求的特征:货币需求难以预测;货币主要作为财富贮藏;对利率极为敏感;是现行利率的递减函数。 3.货币总需求的构成和函数式。 货币需求的变动及影响。货币交易需求和预防需求主要受经济发展状况和收入水平现实变化的影响;货币投机需求主要受利率和心理因素的影响;极端情况下货币需求的不规则变动: (1)流动性陷阱;(2)通货恐慌;(3)清算恐慌。 现代凯恩斯主义学派对凯恩斯理论的发展: 新剑桥学派和新古典综合派。 二、新剑桥学派在货币供求问题上对凯恩斯理论的发展与修正,新剑桥学派的理论特点: (一)货币需求与供给 1.新剑桥学派的货币需求七动机说与凯恩斯三动机说的联系与区别 这两个理论的联系表现为两个方面,一是七动机说沿袭和采用了三动机说的分析方法;二是七动机说包括了三动机说。区别也体现为两点,一是二者论述的角度不同,二是七动机说提出了三动机说没有分析过的一个动机,即政府的需求动机,这个动机是新剑桥学派在货币需求理论方面的独特发展.2.货币需求七动机说 (1)产出流量动机;(2)货币-工资动机;(3)金融流量动机; (4)预防和投机动机;(5)还款和资本化融资动机;(6)弥补通货膨胀损失的动机;(7)政府需求扩张动机。3.货币需求的分类与特点 (1)商业性货币需求:与生产活动相联,与收入相关,形成货币的商业性流通,影响商品的价格。(2)投机性货币需求:与金融市场相联,与人们的未来预期相关,形成货币的金融性流通,影响的是利率。 (3)权力性货币需求:取决于政府的政策决策,其对经济的影响取决于如何分配这些额外货币。 (二)货币供应的决定与控制:“有限性”的货币供给理论 在货币供给理论方面,“有限性”这个词非常能够体现新剑桥学派货币供给理论的特色,无论是货币供应决定理论,还是货币供应的控制理论,新剑桥学派都强调中央银行能力的有限性,也就相应修正了凯恩斯典型的外生货币供应论。 1.现行的货币供应是中央银行被动地适应公众货币需求的结果。2.中央银行对货币供应量的控制能力是有限度的。 3.中央银行对货币供应的控制力在货币供应的增加和减少方面是不均匀的。 三、新古典综合派的理论特点 (一)货币供给理论 1.货币乘数论 (1)简单乘数模型:m=1/r(m为货币乘数、r为法定存款准备金率)(2)复杂乘数模型:m=1/(r+e+c)(e为超额准备金率、c为现金漏损率)2.货币供给理论中的“新观点”(1)非银行金融机构也具有创造信用,创造存款货币的功能。a.金融创新使替代活期存款和通货的生息资产迅速增加。b.定期存款与活期存款具有完全的替代性。c.非银行金融机构的业务活动也能创造出存款货币。d.银行与非银行金融机构在信用扩张上只具有程度上的差别。 (2)金融机构创造存款货币的能力实际上取决于经济运行状况和经济发展水平。a.金融机构的经营决策行为对创造货币能力的影响。b.的需求是创造存款货币的关键。 c.银行的法定存款准备金率不是控制货币扩张的唯一举措。d.融控制的目的和手段应该改变 1> 范围必须扩大,实行全面管理。 2> 方法从直接控制为主转向间接控制为主。3> 指标应该是金融机构的资产流动性比率。3.生货币供应论 (1)产决定银行的负债。(2)方面的创新作用。 (3)造的非银行形式支付扩大了信用规模。 (二)货币需求理论 1.“平方根定律”--鲍莫尔模型 (1)鲍莫尔模型的立论基础:经济行为以收益最大化为目标。 (2)货币交易需求与存货理论:人们的手持货币如同企业的存货一样,力图使其成本总额降到最低限度,由此可建立模型。 (3)模型的结论 a.最适当的现金持有额对利率的变化反应敏感,其利率弹性为(-0.5)。 b.现金持有额与交易总量、变现手续费之间存在着同方向但不同比例的变动关系。 2.“立方根定律”--惠伦模型(1)预防性货币需求的决定因素 a.手持货币的成本:转换现金的手续费和损失的利息。b.收入和支出的状况。 (2)预防性货币需求的最佳值公式:三个因素:①持币的机会成本;②转换现金的手续费;③转换现金的可能次数。 (3)惠伦模型的结论 a.最佳预防性货币需求与货币支出分布的方差、转换现金的手续费、利率及立方根关系。b.最佳预防性货币需求对收入和支出的弹性为1/3。c.最佳预防性货币需求对利率弹性为(-1/3)。 鲍莫尔模型、惠伦模型分别论证了交易性货币需求和预防性货币需求都受利率影响的观点,从而修正了凯恩斯关于交易性和预防性货币需求对利率不敏感的观点 “资产选择理论”--托宾模型 1.托宾模型的研究对象:人们怎样选择最优的金融资产组合。2.对两种资产保存形式的分析:风险与收益。3.托宾模型的含义与结论 (1)资产分散化原则:同时持有风险资产和安全性资产。(2)两种资产量上选择的依据:总效用最大。 (3)结论:第一,利率与货币投机需求反方向变动,与预期收益同方向变动;第二,货币投机需求的变动是通过人们调整资产组合实现的。 托宾的资产选择理论是对凯恩斯投机性货币需求理论的发展。通过这个理论,托宾论证了在未来不确定的情况下,人们依据总效用最大化原则在货币与债券之间进行组合,货币的投机需求与利率呈反方向变动 论述17.运用所学货币需求原理,结合中国实际谈谈对M=Y+P这一流行公式的看法。P261 货币需求理论的研究是货币理论中的重要内容之一,是货币政策选择的理论依据。该理论的发展,从马克思关于流通中货币量的理论,到费雪方程式、剑桥方程式,再到凯恩斯学派的货币需求分析,以及弗里德曼的货币需求函数,都从不同角度、多方面地分析了影响货币需求的各种原因。 在我国,自早期的“1:8”公式在改革开放后不再能准确反映货币流量状况和经济发展状况时,人们期望寻找一个简明而又易于度量的公式,其中影响较大的是:M=Y+P,即货币供给增长率M等于经济增长率Y加预期(或计划)物价上涨率P。 这个公式与“1:8”公式一样,旨在解决如何确定计划货币供给增长率的问题,但与“1:8”公式有明显的差异:(1)虽然在提出之初一些人将M定义为现金发行增长率,但在使用过程中,不少人已将M解释为M1或更大口径的货币增长率,这同“1:8”公式中“1”仅指现金有所不同。(2)经济增长率比传统“1:8”公式中社会零售商品总额指标所包括的范围大得多。(3)预期(或计划)物价变动率进入模型,实际上是承认了物价上涨对名义货币需求的不容忽视的影响作用。 而同时,对于这个流行公式在中国一直有着分歧。主要有以下几点:(1)经济增长率究竟如何计算。西方宏观经济计算体系通常是以GDP即国民生产总值来计算经济增长率,而在我国,GDP数据的准确性一直值得怀疑。其他经济指标,如社会零售商品总额、固定资产投资总额、进出口额等更不能全面客观地反映经济增长情况。(2)是否应把P作为货币需求量的重要决定因素之一。如果在计划现金发行或货币供给增长时考虑物价上涨因素,那就等于将货币政策当成了助长通货膨胀的工具,而目前经济发展中出现的持续通货紧缩现象则更增加了这一问题的复杂性。 我国改革开放二十多年以来,实际货币供应量都远远大于按M=Y+P公式计算得到的理论增长量,而这并未引起通货膨胀,原因是我国的现实经济社会中存在的客观情况,影响M=Y+P这个公式的适用性及其结论,必须考虑这些客观情况带来的影响: 1、物价水平波动。在中国目前的现实经济形式下,部分可控的产品价格上涨是非常必要的。所以应该考虑P仅代表政策要求涨价的部分要素,而不代表所有产品。 2、超额货币现象。即对于公式而言,现实的货币供应量中有相当一部分进入了未纳入公式计算统计的领域,或者该部分的货币并未形成商品而进入流通便被消耗掉,这反映了我们在市场的货币化进程中,部分非商品转化成了商品时,提出了新的货币需求,导致实际货币需求量增加。 3、强迫储蓄行为。我国在改革前,市场上的物资极度匮乏,居民即使持有货币也买不到需要的商品,货币便被沉淀下来,形不成现实购买力,即使货币供应量再大也不会导致通货膨胀,对计划价格体制下的市场无多大影响;改革后,市场的物资供应得到了空前的发展,但由于我国市场上的商品在一定程度上结构不合理,导致一定的消费断层的存在,并且在我国的改革进程中,居民的消费习惯、心理预期、风险防范等多种原因,导致部分货币不能进入消费而是进入了储蓄,相当量的货币再次被沉淀。储蓄的增加,使货币供给减少,为弥补缺口必需增加货币供应量。 4、地下经济影响。由于种种原因,导致中国存在大量的地下经济和隐形市场,他们吸收了部分货币,如大量违法的造假工厂和盗版生产线以及复杂的售假通道和假货消费群体。 5、金融和证券市场的影响。随着我国货币政策市场的发展,金融商品的品种不断增加,证券市场的规模不断壮大,其价值的上涨在一定程度上不会冲击物价水平和市场供求关系,但却需要现实货币的大量注入,从而吸收了部分货币,导致供给不足。这样一来,Y也就不能代表经济增长的实际情况,超额货币必然形成。 我们认为,公式M=Y+P 应修正为M=Y+P-V。但是修正后的公式仍不够理想。因为货币的流速正在减缓,而且同货币流通速度很难统计。国际上货币流通速度通常以归行速度统计,但在中国现金使用量很大,以此统计则不够科学。因此,公式M=Y+P虽较之以前的“1:8”公式有了较大改进,但仍存在一定的缺陷。对于宏观当局来说,微观个体的预期导致对货币量的需求变化将较难以客观的评价,在现实社会中,对M=Y+P公式的研究运用必须结合自身的国情、民情,将更多影响因素考虑其中,才能得到全面、客观的判断。 18、试论微观主体行为对货币供给的影响。答:货币供给可以区分为名义货币供给和实际货币供给,名义货币供给是指一定时间点上不考虑物价因素影响的货币存量;实际货币供给就是指剔除了物价影响之后的一定时点上的货币存量。 在传统计划体制下,我国货币供给的典型特点是以综合信贷计划制约货币供给规模,信贷资金管理体制是统存统贷,主要控制方式是分配信贷指标。在这种体制下,中国人民银行总行对货币供给的变动具有直接调控能力。 在典型、发达的市场经济条件下,货币供给的控制机制由两个环节构成:基础货币和乘数。金融当局只能通过调控这两者来间接地调控货币供给数量,而不能直接调控。当然,这些控制工具最终还是要通过微观基础即居民、企业及商业银行的反应才能起作用。 微观主体包括居民、企业、商业银行(存款货币银行)三个方面。微观主体行为对货币供给的影响主要表现在以下几个方面: 一、居民持币行为与货币供给: 当居民普遍增加现金保有数量时,通货对活期存款的比率会提高,反之,这个比率会下降,这个通常被简称作通货比率的比率与货币供给量是负相关关系。 根据市场经济国家的经验,对居民持币行为从而对通货比率产生影响的因素主要有四个: l、财富效应,当一个人的收人或财富大量增加时,通常,他持有现金的增长速度会相对降低;反之亦然,这说明,一般情况下,通货比率与财富和收入的变动成反方向变化。 2、预期报酬率变动效应。居民持有的现金是不产生利息的,因此它们的货币报酬率为零;储蓄存款有利息收益,那就是货币报酬率大于零。假若只存在现金和储蓄存款两种金融资产,显然,储蓄存款利率变动与通货比率是负相关关系,实际上在现金和储蓄存款之外还存在其他一些资产,如国库券、企业证券等,那么,其他资产价格或收益率的变动就会迂回地影响通货比率。例如,证券或债券的收益率提高了,如果人们的现金持有量不变,而储蓄存款由于相对收益水平下降而减少,那么通货比率就会相对提高。 3、对宏观形势的预期。假若出现了银行信用不稳定的苗头,居民就会大量提取存款,通货比率因而会增大。 4、非法经济活动,要逃避法律监督,倾向于用现金进行交易。所以非法经济活动的规模与通货比率为正相关关系。 上述有些因素的作用较大,有些暂时较小。未列出的因素,如对物价变动的预期在我们这里对持有现金的影响就比较突出。但任何因素影响的大小,都需结合条件的变化进行具体分析。 二、企业行为与货币供给: 在市场经济中,企业所有者或经营者在筹集运营资金和运用手中的货币资金进行投资或资产选择时,常常与居民遵循着共同的规则或规律。但是,表征企业行为对货币供给影响的特点的,是它们对资金的需求,从而对货币资金的需求,从而对贷款的需求。一般说来,这主要来自两方面: 1、经营的扩大或收缩。经营扩大要求补充资金,补充资金的投入一般要求从补充货币资金开始,如果企业对货币资金的投人靠的是自身的积累,这不需要补充贷款;反之,则需要追加贷款。追加贷款,就不能不影响货币供给。至于企业是否要扩大经营,在成熟的市场经济中是取决于对经济形势发展的预期,对未来收益的预期,等等。在我们这里,国有企业扩大经营,特别是扩大生产能力投资的倾向曾是十分强烈的,而所需资金则过分依赖银行贷款,这属于体制上的原因。但无论是什么原因,对贷款需求的压力加大,也就意味着对货币供给的压力加大。假设缺乏经营积极性成为某一时期企业行为的普遍特点,那时,再低的利息率也往往不能引起对贷款的需求,货币供给也就缺乏扩大的基础。 2、经营效益的高低。一般说来,不管是由于经营管理不善,还是整个经济比例、结构有问题,都会造成资金周转率降低;信贷资金占用时间延长,在相同的产出水准下会相对增加对贷款的需求并从而增加货币供应最;反之则会减小对于增加货币供应量的压力,经营效益不好以致出现亏损,企业不得不采取减缩经营乃至破产清理行为,那就会勾销一部分贷款,从而相应收缩一部分货币供给。如果企业行为并非如此,而是仍然亏损经营,并且还要保持职工的收入水平,那就要在产出状况不佳的状况下,继续要求得到贷款支持,其结果则是货币供给不得不被迫增加。 三、存款货币银行行为与货币供给: 一般情况下,银行对货币供给的影响主要靠两种行为:一是调节超额准备金的比率;二是调节向中央银行借款的规模。 银行的准备金是基础货币,其中超过中央银行法定准备率规定的金额,是银行没有据以发放贷款的部分,因而没有存款货币的创造。在准备金总额不变时,银行保有超额准备金越多,货币乘数就越小,相应地,货币供应量也越小;如果银行减少了超额准备金的保有数量,货币乘数会变大,货币供给也相应增加。 任何体制下的存款货币银行,在中央银行的准备金存款均有超法定比率部分,问题只是为什么有时要增加超额准备金,有时却要减少超额准备金。在市场经济中,主要动机有两个:一个是成本和收益动机;另一个是风险规避动机。 通常情况下,商业银行在中央银行的准备存款是没有利息的,而一笔资金用于贷款或证券投资则能获得收入。所以,保有超额准备金则等于放弃收入,这就是超额准备金的机会成本。从这点出发,商业银行总是力求把超额准备金压到最低限度。在发达的工业化国家中,银行通常把超额准备保持在1个百分点之下。如果银行保有的超额准备金也能得到一定的利息收入,那么银行要考虑的当然就是放弃运用的损失与不运用的收益这两者的对比状况,如我国准备存款不只有利息,而且很长期间利率相当高,从而存款货币银行就有可能保持大量超额准备金的选择。这些就属于成本收益动机的问题。 所谓风险规避动机,是指商业银行为了防止存款流出的不确定性所可能造成的损失而采取保有超额准备金的考虑,银行在经营过程中,如果出现存款大量流出的现象,若无超额准备金,就得采取诸如出售证券、催收贷款、向中央银行借款等行动,这会增大成本或减少收益;如果存款流出规模过大又无法采取措施补充资金,还可能面临倒闭的威胁。为了避免这些情况的出现,在出现存款流出苗头时,银行则必须采取增加超额准备金的行为。这说明,超额准备金保有数量与预期存款流出量为正相关关系预期总具有不确定性,不过不确定性有大有小,如果在某一特定时期,银行感到不确定性增大,就会增加超额准备金的保有数量;反之,也会相应地减少保有数量,不确定性系数同超额准备水平也是正相关关系。 超额准备保存行为,不论起于何种动机,均意味着对准备存款比例的制约,在一个典型的市场经济体系中,商业银行向中央银行借款,会增加准备金存款,也即基础货币的数量,从而能支持更多地创造存款货币。所以,在其他条件不变时,商业银行增加中央银行借款会扩大货币供应量,减少中央银行借款会减少货币供应量,决定商业银行向中央银行借款的行为动机也是成本收益动机,而决定成本和收益的因素则主要是市场利息率和中央银行贷款的贴现率。显然,市场利率的高低,正相关地影响从银行借款的多少;而中央银行贴现率的高低,对于商业银行借款数量的多少,则是负相关的。 商业银行向中央银行借款,影响基础货币,同时也就是中央银行向商此银行贷款影响基础货币的另一侧面,就像银行与企业在贷款增减的作用方面互为表里一样。 四、我国国有商业银行行为与货币供给: 近几年来,由于整个经济体制改革和存款货币银行改革具有实质意义的推进,银行行为与货币供给的联系也处于具有实质意义的转折点,直到1994年、1995年,国有商业银行行为对货币供给的影响具有这样一些特点: 1、国有银行面临强大的贷款需求压力。这一方面来自企业,特别是国有大中型企业。即使是经济效益不高甚至亏损的企业也要求追加贷款。另一方面则是来自各级政府。为了发展地方经济,地方政府经常为地方投资项目和企业提出贷款要求。在这两方面的压力下,银行贷款行为基本上是被动的;自主权极其微弱,而且就银行利益来说,在风险不足以威胁其命运时,扩大贷款就是扩大收益。国有商业银行的这一特点说明,它们自身并没有形成控制货币供给的机制。 2、在强大的贷款需求压力下,为了扩大贷款,银行首先是从中央银行争取较大的贷款规模,这种“规模”是直接计划管理模式的手段,它是核准银行可以扩大贷款的最高界限。然后是努力吸收存款,特别是储蓄存款,以支持扩大贷款。但更关键的是争取取得更多的中央银行贷款。在很大程度上依靠中央银行的支持扩大自己的业务是中国国有银行行为很突出的特点之一。 中央银行下达的贷款规模和其对国有银行的贷款支持,本是对货币供给的控制器。但在贷款需求的压力下,下级行对上级行,国有银行对中央银行有一个非常具有特征的行为,即申请追加。这种被称为“倒通机制”的追加申请对增加货币供给的作用力是极其巨大的。 3、利率、准备金之类的调节手段没有实质性的作用。从国有银行来说,利率对其从中央银行借款起不了多大调节作用,因为它发放贷款的利率与向中央银行借款的利率都是国家直接规定的,其间的利差已经考虑了它们的利益。准备金,以及类似的备付金、专项存款等,对于国有银行的具体行为有时其作用是决定性的。但对这些比率的调整行政性色彩极浓,并没有创造出使国有银行认真考虑准备率而自行安排资金营运的机制,而且在国有银行依靠中央银行大量贷款支持的情况下,如果提高准备率,往往通过从中央银行相应地多取得一些贷款即可抵消。 总的说来,在这样的情况下,国有商业银行并无内在的约束机制使其行为能对货币供给给予积极影响。 近期的改革实际上是针对上述问题逐步推进的,这包括: 1、改变国有商业银行对国有企业事实上存在的资金供给制关系,建造市场机制所要求的银行与企业关系。这不仅在于不断加强国有商业银行在银行业务上的自主决策权利,更重要的还在于国有企业建立现代企业制度的改革已开始了有实质意义的推进。 2、转变政府职能,实行政企分开,强调各级政府不得强令银行贷款。这在1997年已有明显的改变。 3、理顺中央银行与国有商业银行的关系,先后推行了多项有利于促使和保证商业银行独立自主经营的措施,这包括取消规模管理和其他一些措施。 如此等等,使自我约束机制在国有商业银行中开始明显发挥作用。这突出表现在贷款的自主决策权上,1997年在信贷领域有一个突出的问题,即“惜贷”问题。企业和地万政府抱怨银行,特别是国有商业银行“惜贷”----对于“应该”取得贷款的企业和项目不给予支持;银行则说“惜贷”问题并不存在----应该支持的均已贷款,得不到贷款的是不具备取得贷款的条件。不论谁是谁非,这一争论本身已说明国有商业银行的独立决策权已在经济生活中开始成为客观存在。也正是在这一背景下,货币供给的增长已由于国有商业银行行为而受到强有力的抑制。 改革的进程必然会有反复,但方向不会逆转,而且必将向纵深推进。可以肯定,实现 国有银行行为方式受市场规律和准则调节,并进而影响货币供给形成的格局,其前景是显而易见的。 论述题19:试评价“财政赤字若以发行债券(而不以向央行透支或借款)的方式来弥补,则割断了其与通货膨胀间的必然联系”的说法。 答:财政赤字=财政支出-(税收等经常性收入+政府负债),因此,财政赤字决定了现金发行量。财政通过征税或发行债券等其他方式把收入的货币加以分配,而收入的上缴、支出的解拨都是通过银行账户进行的。当收入的货币满足不了开支而又必须开支,在尽了借债努力的条件下,这些货币来源只有向中央银行借款。而当流通只有现钞一种形态的货币时,通货膨胀是必然的,但现代经济生活中已经发展有存款货币,这种必然性就自然消失了。国际通用的财政赤字概念是:当财政出现赤字时,政府就要采取措施弥补,办法不外有三种,增加税收、增发政府债券和变动基础货币。 增税,通常属于紧缩措施。但从短期效应来说,它并不会直接缩减货币供给财政可以动用节余弥补赤字,但必须与银行取得协商。必须看物资库存是否充分。因为增税会降低此新投资的积极性,降低对贷款的需求,从而可能成为控制货币供给增长的因素。如果加课税所收入的货币财政不再支出,那直接会缩减货币供给量。 用发行债券的方式来弥补预算赤字。而发行国债弥补赤字视情况不同对货币供给影响不同。当债券的购买者是公众或商业银行,并不产生增加货币供给的效应。当公众开出商业银行的支票或商业银行开出有自己支付的支票,这都使各银行在中央银行的准备金减少,但财政支出后,这些准备金又会恢复,准备金不变,创造货币的规模也不会变.如果公众用现钞和活期存款购买,会造成M1的相应缩减,一般不会给市场供求造成新的压力,没有带来新的购买力,就不会带来新的压力。如果从储蓄或定期存款购买,意味着M2中准货币(M2-M1)的减少,所以当财政支出形成M1时,虽然按M2货币供给总规模不便,但M1层次将被扩张,这会对市场供求造成新的压力。 透支或借款,增加基础货币。现钞是基础货币的一部分,它的增加也就是基础货币增加。而透支和借款使中央银行比较被动,容易造成货币量过多或通货膨胀,但不必然。如果透支或借款能够控制在中央银行基础货币应该扩张的范围内,则不会导致货币过多或通货膨胀,反之,则会导致货币过多或通货膨胀。而通常的情况是:财政部发行债券,中央银行直接收购。这样成为准备金增加的因素。如果国库直接向中央银行借款,也达到准备金增加的相同结果。准备金的增加是基础货币的增加,会产生倍数效应。因此能产生明显的增加货币供给的功能。所以“财政赤字发行债券而不想中央银行透支或借款的方式弥补,则割断了其与通货膨胀间的必然联系”这种说法是不正确的。 述题20:试从体制高度剖析“我国货币供给过程存在着‘倒逼机制’”这一流行见解。 首先我们必须肯定中国在现行体制下,货币供给往往是被动地去适应货币需求,中央银行很难实施各项既定的货币调节方案,即我国货币供给过程的确存在着“倒逼机制”。也就是说货币供给总是要被动地决定于客观经济过程,而我国的货币当局并不能有效地控制其变动。如:企业的贷款需求总是迫使商业银行被动地增加贷款供应,中央银行在企业和商业银行的贷款需求压力下又不得不实行松动的、迁就性的货币政策,使得货币供给被动地适应货币需求的现象发生。 虽然在理论上经济体系中的全部货币都是从银行流出,从本源上说,都是由中央银行资产负债业务决定的,只要控制每年的新增贷款数量就可以控制住总的货币供给;同时,中国的中央银行也有着控制货币供给增长的有效手段,只要能坚持按照稳定通货、稳定物价的政策严格执行信贷计划,货币供给就不会增长过快。但是,由于中国目前的国情,中央银行没有独立的决策地位,货币紧缩或松动大都由更高的决策层作出。所以,中央银行不过是拥有货币政策的执行权,而其决策权在高于中央银行的决策层。首先,在我国企业特别是国有大中型企业的利益主要表现为从国有银行取得贷款,维持和扩大其生产经营,以实现预期的任务和盈利目标。银行本可以选择效益好、信誉佳的企业作为发放或优先发放贷款的对象;对效益差甚至亏损或信誉不好的企业停止或限制发放贷款。但实际上在难以全面执行破产法的情况下,不论企业效益、信誉如何,银行都必须给予贷款。其次,对于各个地方政府,为尽快发展本地区经济,扩大生产规模,增加就业,甚至为维持落后、效益差的企业生存,因为这些关系着地区财政收入及社会安定、政治安定的因素,而使得银行也必然受到来自地方政府的强大压力。银行虽自成体系,但由于经营、生活在一个地区之内,所以也不得不考虑维护地方的利益。 再有就是个人的利益也在货币供给“倒逼机制”中起作用。职工工资收入本该是由企业经营状况决定的变量,但现在却存在追求职工收入尽可能最大化的倾向,即使企业经营不善甚至亏损,也要保证职工的工资奖金的发放。这就形成了个人利益通过国有企业对国有银行的资金依赖迫使货币供给出现倒逼现象。 从而,企业、地方政府、个人对各自利益的追求形成偏好经济和收入增长的合力,并直接影响货币的供给速度。综上所述,目前在中国的货币供给过程中存在着“倒逼机制”。虽然存在着“倒逼机制”,但是也并不是说央行就是其受害者。其实在中国,国有企业现行的并不是现代企业制度,企业中存在着:产权不清、权责不清、政企不分、管理不科学等种种问题。企业的生产经营甚至带有政策性的任务,为了维持就业、维持地方的社会、政治的安定,企业也不得不在经营不能自主的怪圈下经营,直至导致效益不佳甚至亏损。同样,商业银行对央行的倒逼也是因为其不能自主经营,扭曲的央行制度破坏了货币供求的平衡,迫使商业银行倒逼央行。在我国目前的这种扭曲了的全盘经济体制下,需要用“扭曲的”思路来解决问题,用适合我国国情的方法合理处理好从中央到地方、企业到个人的利益,以适应我国的政治、经济的建设。 论述题22:如何理解市场供求与货币供求的理论分析? 答:市场供求与货币供求之间存在着必然的内在联系。社会总供给决定货币需求,货币需求决定货币供给。而货币总供给决定社会总需求。社会总需求有影响着社会总供给。货币供给来源于银行体系的资产业务中主要是贷款活动创造出来的,因此银行贷款活动调节货币供给规模。货币供给规模由直接关系到社会总需求的扩张水平。同样的货币供给不引出同样的社会总需求,货币层次不同,流动性比重不同,机制扭曲,直接创造出来的货币并不决定现实购买力。 同样的社会供给会引出不一样的货币需求,总供给决定货币需求,但同等的总共即可有偏大或偏小的货币需求,经济体系中到底需要多少货币,从根本上取决于有多少实际资源需要货币实现其流转并完成生产、交换、分配和消费相互联系的再生产流程。这是社会总供给决定货币需求的基本理论出发点。而宏观的货币需求是通过微观主体对货币的需求体现出来的,只有微观主体的货币需求才能直接引出货币供给。货币需求引出货币供给,但也决非是等量的。微观主体的货币需求有其独立性,对任何一个微观主体来说,相对于自己的收入,对货币的需求可能偏大,也可能偏小,总供给决定收入,而收入是货币需求函数的确定性变量,因此,微观的货币需求其总和在实际上并不总是等于社会总供给决定的货币需求量。货币供给成为总需求的载体,同样,同等的货币供给可有偏大偏小的总需求。总需求的偏大、偏小,对总供给产生巨大的影响,不足,则总供给不能充分实现,过多,在一定条件下有可能推动总供给增加,但不可能由此消除差额。总需求的偏大、偏小也可以通过紧缩或扩张的政策予以调节。社会总需求大于总供给的矛盾,通过压缩货币供给,从而减少总需求的规模。但是当正常生产在需求扩张的支持下运作时,单纯的紧缩也不会使供求均衡。供需的差额不能由于紧缩需求而取得均衡。从长时间看,紧缩会抑制经济增长,供求矛盾仍难以解决。货币供求不均衡,市场供求也就难以实现均衡。 论述题23:结合我国改革实际,谈谈你对通货紧缩的看法。 答: 通货紧缩是相对于通货膨胀而言的,它是指物价疲软乃至下跌的态势,它不是偶然的,一时的,而是成为经济走向、趋势的物价疲软乃至下跌。 由于通货膨胀将造成物价持续上涨,一些经济学家认为,通货膨胀会损害经济成长。持续的通货膨胀,特别是恶性通货膨胀,会经由降低效率的效应阻碍经济成长。事实上,通货膨胀在历史上也的确曾经给各国经济,包括中国带来非常不利的影响,因此世界上不少人只记得通货膨胀和反通货膨胀这对矛盾,而认为通货紧缩是一件好事。 从新中国成立一直到改革开放20年来,我国曾经经历了数次恶性通货膨胀,确实给中国的经济成长带来非常不利的影响。特别是在1988年,通胀率接近20%,全国大中城市出现几次大的抢购风潮,1994年更是发生了中国改革开放以来最严重的通货膨胀,全国商品零售价格上涨了21.7%,消费价格上涨了24.1%;这两次较大的通货膨胀曾使许多人对改革以来的经济政策产生了怀疑,甚至有人认为,通货膨胀是经济改革的一个结果。 也就是在94-95年严重的通货膨胀之后,国家开始采取措施调控通货膨胀,其中运用手段最多的就是宏观紧缩政策,也就是实行紧缩性货币政策和紧缩性财政政策。这一政策持续至今,已经有数年时间。应该说,宏观紧缩政策确实有效的控制了通货膨胀,然而也使得中国经济陷入一个怪圈,那就是我们每年的国民经济增长速度平均保持在7.8%左右,而我们的物价指数却不断下滑。通货紧缩的情况越来越严重,90年代中后期中国经济发展的三个重要现象: (1)国民经济增长速度自1992年达到14.2%以后持续下降;(2)业趋势趋于严峻; (3)物价长时间连续走低,产生大量“以货求币”的现象。造成“以货求币”的原因是: (1)存量资产普遍缩水,资产随市场价格降低而不断降低,但债务并不缩水,负债率不断上升; (2)资本收益率不断下降,干什么都难赚钱,投资者理性选择套现保值; (3)增量资金找不到有利的投资领域:居民存款利率和贷款利率都大幅下降,人们的投资大幅下降,投资者转而持有现金资产,银行有钱贷不出。 造成通货紧缩的原因: 1. 国际性因素: (1)亚洲金融危机以后,世界经济呈现普遍的过剩与紧缩特征:随着国际社会经济生活的日益融合,应该说,通货紧缩的局面并不只是我国近年来所特有,包括美国在内的全球经济近年来其实也同样处于通货紧缩的局面。 (2)传统物质产品全面过剩的同时,高科技产品也出现明显过剩态势; (3)中国面对外部需求低迷,导致了出口不振和外部供给对国内市场价格巨大压抑,物价连续走低很大程度上是受强大外部竞争价格牵制的结果。 2. 供给领域的结构性因素:(1)原来需要的现在不需要了;(2)现在需要的生产不出来; (3)能生产的又竞争力不强:低度化技术构成是我国产业国际竞争力不强的原因;(4)已形成的技术竞争力又面临被淘汰的危险; (5)重复建设,规模不经济的小生产线遍地开花,导致成本居高难下,过剩形势下恶性竞争。3.体制性与政策性因素:是影响有效需求不足的原因 (1)各项改革不断深化过程中,社会保障制度体系未能及时到位,就业、医疗、教育、养老、住房改革等都导致对未来预期的恶化,老百姓有钱不敢花; (2)社会收入分配结构迫切需要调整,收入差距形成消费“断档”;(3)消费环境改善缓慢,如“乱收费”,过去形成的抑制消费规定严重制约新的消费热点形成。 以前由于大家一直受物价上涨之苦,因此不太能理解通货紧缩同样可能给一国经济带来致命伤害,但事实上,正是由于我国的通货紧缩现象越来越严重,已经和经济增长的大前提产生非常大的矛盾,使得我们的社会出现即使银行有钱,但也贷不出去款的现象。原本应该作为一国经济晴雨表的股市,也是连续三年下跌,根本无法反映这三年我国经济成长的现状,这些都和我国连续数年的通货紧缩有很大关系。然而,其他国家和我国不同的一点在于,以美国为代表的西方国家在经历近十年的经济增长之后,从2000年开始逐渐走向经济衰退并一直持续至今年,因此,他们国家的通货紧缩还是能够和经济衰退取得一致的。而我国的这种通货紧缩和经济增长并存的局面则显得有点不太正常,也是我国改革开放之后未能很好处理的一个经济关系。从以上方面可以看出,通货紧缩同样可能给我国的改革开放带来非常不利的影响。 面对通货紧缩的严峻考验我国政府作出很大努力,如1998年增发1000亿国债,加大对社会基础设施的财政投资力度,中国反通缩政策趋于完善和渐成的体系: 1. 从增加财政负债与加大财政支出转向运用货币手段与降低财政发债成本相结合,如连续多次降息; 2. 从非区别化刺激消费转向与调整收入分配结构相结合,如开征利息税; 3. 从单纯鼓励人们增加开支转向与促进收入增长相结合,如为公务员加薪。提高城镇低收入居民生活标准; 4. 从简单政府投资转向与结构调整和增强产品竞争力密切结合,如财政技改贴息; 5. 在强调扩大内需的同时,加强对出口的扶持,如提高多种产品的出口退税率; 6. 在努力调动居民金融资产存量同时,大力开拓信用资源,如发展消费信贷; 7. 在启动信贷消费热点上更密切推进资源化配置,与突破体制性障碍结合,如住房体制改革,开放住房二级市场。 近年来外部环境趋于改善,使得通缩压力有所减缓,世界与亚洲经济均呈现增长加速势头;部分重要物质产品的国际市场价格出现回升;随着亚洲经济好转国际资本重返亚洲。 从近一年来全国,乃至全球的物价指数来看,已经出现一些走出通缩的迹象。这主要是由于美国政府放弃强势美元政策,并连续十二次降息,使美国经济逐渐摆脱衰退阴影,重新出现恢复性增长苗头。美国以及其他西方国家经济的触底反弹,给全球经济带来较大利好,也使得同处全球一体化进程中的中国受益,物价开始全面上扬,其中最明显的莫过于全球商品价格连续暴涨。我们可以从商品期货市场中明显看到,去年以来,包括黄金在内的贵金属,大宗农产品以及有色金属全部上涨至96年以来高点,应该说这是各国通力治理通货紧缩所带来的成效,当然也包括中国政府所采取的种种刺激消费的措施。我们有理由相信,目前的中国经济已经开始逐渐走出通缩。由于中国的经济仍然处于强劲的发展势头中,我们1-3季度的经济增长达到9.1%,完全摆脱了非典的影响,(因此,未来中国的经济很可能向通货膨胀迈进。??) 要走出通缩政府必须采取一系列断然措施: 1. 政府有效的宏观调空政策,不能过松,也不能过紧,适度原则稳定经济发展; 2. 让人们的购买力真正用于购买“改善”; 3. 改善预期的根本是尽快完善社保制度体系,充实社保基金; 4. 增加赚钱机会,才会有人投资 5. 政府增支,如必要的职能预算和公共产品的必要补贴等。 当然,我们认为,无论是通货膨胀,还是通货紧缩,如果过度的话都会给经济生活带来极大不利影响,在经济学中,稳定的目标主要是寻求经济发展的稳定,物价水平的稳定。因此,一旦国家真的再度出现通货膨胀的话,政府仍应积极想办法应对,维持社会经济生活的稳定。 24。结合我国国情,阐明你对货币政策目标设置的看法? 一、货币政策指的是中央银行为实现既定的经济目标运用各种工具调节货币供给和利率,进而影响宏观经济的方针和措施的综合。一般包括三个方面的内容: 1、政策目标; 2、实现目标所运用的政策工具; 3、预期达到的政策效果。由于从确定目标到运用工具乃达到预期的政策效果,这中间存在着一些作用环节,因此货币政策目标实际也包含中介指标和政策传导机制等内容。 二、货币政策目标各国是不同的,就是对于一个国家而言,不同时期的货币政策目标也是不同的,但一般都围绕着“经济增长,充分就业,稳定物价和国际收支平衡作为货币政策目标而展开。 三、货币政策的四大目标之间的关系是比较复杂的,充分就业与经济增长这两者是正相关的:经济增长,就业增加;经济下滑,则失业增加。但除此之外,各个目标相互之间都有矛盾。 1、目标间的冲突首先表现在充分就业与稳定物价的矛盾上,在失业率和物价上涨率之间存在着此消彼长的置换关系,即“菲利普斯曲线”,按照“菲利普斯曲线”,作为中央银行的货币政策目标只有根据当时的社会经济条件,寻求物价上涨率和失业率之间的某一适当的组合点,而不可能有两全其美的办法。 2、物价稳定与经济增长的矛盾是四大目标之间的另一有待协调之处。就现代经济的实践而言,经济的增长大多伴随着物价的上涨。 3、目标之间的冲突还表现在经济增长与国际收支平衡的矛盾上。经济增长通常会增加对进口商品的需要,同时由于国民收入增加带来支付能力的增强,又可能提出对一部分本来可用于出口的商品的需求。两方面作用的结果使出口的增长慢于进口的增长,这就可能导致贸易差额的恶化。就资本项目而言,要促进经济增长,就要增加投资,在国内资金来源不足的情况下,必须借助于外资的流入。外资流入可能使国际收支中的资本项目出现顺差,一定程度上可弥补贸易逆差造成的国际收支失衡,但并不一定能使经济增长与国际收支平衡共存。 处理目标之间的冲突,无非是: 1、侧重于统筹兼顾,力求协调; 2、侧重于权衡或选择,视经济环境的需要而突出重点。近年来,我国理论界对于中国货币政策应有什么样的目标问题,一直存在着争论。主要观点有单一目标论,双重目标论和多重目标论。 四 结合我国国情,稳定货币是指物价保持稳定,这样经济才可能正常运行和发展,基本稳定是可以做到的,绝对稳定是不可能的。而经济增长是指国民经济的增长,经济增长才能对稳定货币打下坚定的基础;在中国,讨论充分就业和失业问题对目前中国有着特别现实的意义,稳定压倒一切。理论上说,中国的宏观经济管理部门没有那个权威部门有足够的单枪匹马的能力解决中国的就业问题,如果都没有权威和能力解决就业问题,谁来解决呢;实践上20年来,货币政策为充分就业做出了巨大的贡献;在平衡国际收支的问题上,同样也没有权威和能力的部门能单独解决国际收支问题。可是1984年以来,国家一直把平衡国际收支交给银行,实践上,银行为国际收支做出了巨大的贡献。 因此,目标要求各部门协调配合,齐心协力,把这些目标当成共同的目标,我们才能实现四大目标。 论述题 25、试阐明设置货币政策中介指标的必要性及标准。 答:从货币政策工具的运用到货币政策目标的实现之间有一个相当长的作用过程。在这个作用过程中有必要及时了解政策工具是否得力,估计政策目标能不能实现,这就需要借助于中介指标的设置。中介指标一般有利率、货币供应量、超额准备金和基础货币等。货币当局本身不能直接控制和实现诸如稳定、增长这些货币政策的目标,它只能借助于货币政策工具,并通过对这些中介指标的调节和影响最终实现政策目标。根据这些中介指标对货币政策工具反应的先后和作用于最终目标的过程,可分为两类:一类是近期指标,即中央银行对它的控制力较强,但离货币政策的最终目标较远;另一类是远期指标,即中央银行对它的控制力较弱,但离政策目标较近。因此,中介指标就成了货币政策作用过程中一个十分重要的中间环节,对它们的选择是否正确以及选定后能否达到预期调节效果,关系到货币政策最终目标能否实现。 设置货币政策中介指标的标准如下: 可控性。即是否易于为货币当局所控制。通常要求中介指标与所能适用的货币政策工具之间要有密切的、稳定的和统计数量上的联系。 可测性。其含义包括两个方面:一是中央银行能够迅速获取有关中介指标的准确数据;二是有较明确的定义并便于观察、分析和监测。 相关性。是指只要能达到中介指标,中央银行在实现或接近实现货币政策目标方面不会遇到障碍和困难。也就是说,要求中介指标与货币政策的最终目标之间要有密切的、稳定的和统计数量上的联系。 抗干扰性。货币政策在实施过程中常会受到许多外来因素或非政策因素的干扰。只要选取那些受干扰程度较低的中介指标,才能通过货币政策工具的操作达到最终目标。 与经济体制、金融体制有较好的适应性。经济及金融环境不同,中央银行为实现既定的货币政策目标而采用的政策工具不同,选择作为中介指标的金融变量也必然有区别。这是不言自明的。 论述第26题:谈谈改善我国中央银行宏观体调控手段的设想。 以下三方面着重从货币政策的角度来分析,而宏观调控手段并不仅仅包括货币政策调控,还包括如财政政策等。 我国中央银行宏观体调控主要包括准备金政策、再贴现政策和公开市场业务这三个手段。这些手段主要都是着眼于总量体调节的。 (一)、准备金政策: 准备金政策是指中央银行通过要求商业银行缴存存款准备金的方式来调节货币供给的政策。它规定了商业银行需按各类存款余额和由中央银行规定的法定存款准备率计算并上缴的存款准备金。准备金政策的运行机制通常是:当中央银行提高法定准备率时,商业银行一定比率的超额准备金就会转化为法定准备金,商业银行的放款能力降低,货币乘数变小,货币供应量就会相应收缩;当降低法定准备率时,则出现相反的调节效果,最终扩大货币供应量。法定存款准备率通常被认为是货币政策中最猛烈的工具之一,其政策效果表现在以下几个方面:(1)法定准备率由于是通过货币乘数影响货币供给,因此即时准备率调整的幅度很小,也会引起货币供应量的巨大波动。(2)即时存款准备率维持不变,它也在很大程度上限制了商业银行体系创造派生存款的能力。(3)即使商业银行等存款机构由于种种原因持有超额准备金,法定存款准备金的调整也会产生效果。如提高准备金率,实际上就是冻结了一部分超额准备金。 当然,准备金政策作为中央银行调控货币供应的政策也存在着明显的局限性:(1)由于准备率调整的效果较强,不宜作为中央银行日常调控货币供给的工具。(2)由于同样的原因,它的调整对整个经济和社会心理预期都会产生显著的影响,以致使得它有了固定化的倾向。(3)存款准备金对各类银行和不同种类的存款影响不一致,因而货币政策的效果可能因这些复杂情况的存在而不易把握。 中国的准备金政策于1984年起启动伊始就严重扭曲,直到15年后的1998年才真正开始解决这样的扭曲,但到今天仍然有缺陷。1984年在建立中央银行体制政策启动准备金制度时,由于中央银行的宏观调控政策中并没有再贴现政策和公开市场业务,所以准备金政策就成为最主要的调控手段。但在实践中,准备金政策作为间接控制杠杆,却变成了中央银行控制商业银行资金的直接工具。由于中央银行担心无法控制商业银行的经营行为,因此制定了很高的准备金率,严格控制了商业银行的自有资金,使商业银行在日常业务活动中无法做到资金的自求平衡。客观上,由于商业银行需要上缴很高的准备金,使得中央银行集中了70%的信贷资金来源。到1998年开始对准备金制度进行改革,改革后的准备金制度有了很大的改进,但仍然有着几个问题尚未解决:(1)准备金率仍然偏高。现行准备金率为6%,这与西方国家普遍1%到2%的准备金率相比依然偏高。而且中央银行还要求上商业银行保留一定的备付金。(2)定期存款和活期存款的准备金率不做区分。即对定期存款也按6%要求上缴准备金,与定期存款提前支取的实际情况不符。从另一个层面上讲,也说明了较高的总体准备金水平。(3)准备金政策还是支付利息的政策,需要进一步的改革。 (二)、再贴现政策: 再贴现政策是指中央银行通过规定和调整商业银行向中央银行的再贴现率而影响贷款的数量和基础货币的宏观货币调控政策。再贴现率变动影响商业银行贷款数量的机制是:贴现率提高,商业银行从中央银行的借款成本随之提高,它们会相应减少贷款数量;贴现率下降,意味着商业银行从中央银行的借款成本降低,则会产生鼓励商业银行扩大贷款的作用。再贴现政策一般包括两方面的内容:一是再贴现率的调整,二是规定向中央银行申请再贴现的资格。 再贴现的政策效果体现在两方面:(1)再贴现率的变动,在一定程度上反映了中央银行的政策意向,有一种告示效应。(2)通过影响商业银行的资金成本和超额准备来影响商业银行的融资决策。 但是,再贴现作为一种货币政策工具,也有不足之处:(1)不能使中央银行有足够的主动权。(2)再贴现率高低有限度。(3)随时调整再贴现率也会引起市场利率的经常波动。我国目前的再贴现政策工具的运用并不理想,主要反映在以下三个方面: (1)我国目前的再贴现政策工具的运用并不理想主要原因在于商业银行的贴现业务开展的不理想,而其根源又在于商业信用的不理想。 我国计划经济体制时期,取消了商业信用这种直接融资形式。改革开放以后,开始恢复,但恢复的二十多年来未取得长足的发展。主要表现为以下几个方面(1)商业信用规模远不够大。长期停滞后恢复,规模上无法马上发展起来。(2)票据不规范。早期的票据不规范表现为不使用专门的票据,出现了许多纠纷。而近期又出现许多不以真实商品交易为背景的融通票据,并向银行贴现后违规进入股市。(3)商业信用的当事各方不讲信用。虽然有真实商品交易和还款来源,但仍然有些企业无故不还。银行的贴现业务的良性发展必须是建立在健康的商业信用制度上的。由于存在上述约束条件,从而阻碍了商业银行票据业务的发展。而中央的再贴现业务也必须十以上商业银行可以健康开展贴现业务为前提的,因此,作为连锁效应,上述约束条件的存在也顺限制了中央银行运用再贴现工具进行货币调控的操作。 (2)我们国家那种通过基准利率的调节来影响市场利率的机制是不存在的,或者说很大程度上是有缺陷的。中国利率的总体格局使官定利率体制,不存在只调整一个基准利率即影响或迂回影响市场利率的体制。由于中央银行不但规定自己的再贴现利率水平,而且还规定商业银行各项存贷款业务的利率水平,因此这样的利率体系是缺乏弹性和回旋余地的。也既是由商业银行或金融机构自发决定市场利率或存贷款业务利率的利率机制是不存在的。(3)由于准备金制度的缺陷,即准备金制度使商业银行的资金无法做到自求平衡,商业银行往往依赖中央银行再贷款取得资金,如果商业银行再贷款可以轻易得到,那么商业银行就没有办理再贴现业务的动力和必要。 (三)、公开市场业务: 公开市场业务是指中央银行在金融市场上出售或购入财政部和政府机构的证券,特别是短期国库券,用以影响基础货币的宏观货币调控手段。它的运行机制如下:中央银行通过购买或出售债券可以增加或减少流通中现金或银行的准备金,即都是使基础货币增减。基础货币增加,货币供应量可随之增加;基础货币减少,货币供应量亦随之减少。 同样作为宏观货币调控手段,公开市场业务被认为是最有效的。它有着明细的优越性:(1)中央银行能够运用公开市场业务,影响商业银行准备金,从而直接影响货币供应量。(2)公开市场业务使中央银行能够随时根据金融市场的变化,进行经常性、连续性的操作。(3)通过公开市场业务,中央银行可以主动出击,不像再贴现政策那样处于被动地位。(4)由于公开市场业务的规模和方向可以灵活安排,中央银行可以运用它对货币供应量进行微调,而不会像存款准备金的变动那样,产生震动性影响。 然而,公开市场业务要有效地发挥其作用,必须具备一定地条件。(1)中央银行必须具有强大的、足以干预和控制整个金融市场的金融势力。(2)金融市场必须是全国性的,必须有相当的独立性,证券种类必须齐全并达到一定规模。(3)必须有其他政策工具的配合。如没有存款准备制度,这一工具也无法发挥作用。中国的公开市场业务操作是远不够理想的,主要原因有以下四点:(1)国债规模远不够大。国债资产规模在中央银行总资产比例远小于德国和日本的60%、英国的70%以及美国的80%。(2)国债期限结构不合理。都是中长期的国债,没有短期。但公开市场上操作的应该是短期国债,由于他们期限短,受益低,因此便于吞吐。(3)凭证式国债占国债相当部分,记账式国债不足。而公开市场上操作的主要是记账式国债。(4)从分布上,相当大部分国债为城乡居民或个人所持有的凭证式国债,中央银行无法进行吞吐。虽然,我国目前的公开业务操作仍存在上述问题,但从1999年起,公开市场操作已成为增量基础货币投放的主渠道,所以发展形势较好。 综上所述,目前我国中央银行的三大宏观调控的现状都存在一定的不足。但是,随着货币监管机构和社会各经济主体对这些宏观调控手段的认识逐步深刻,这些宏观调控手段必将规范化,从而真正产生各自应有的调控效应。 第27题:如何评价再贴现政策的功效及在我国的作用。 再贴现政策是指中央银行通过规定和调整商业银行向中央银行的再贴现率而影响贷款的数量和基础货币的宏观货币调控政策。再贴现率变动影响商业银行贷款数量的机制是:贴现率提高,商业银行从中央银行的借款成本随之提高,它们会相应减少贷款数量;贴现率下降,意味着商业银行从中央银行的借款成本降低,则会产生鼓励商业银行扩大贷款的作用。但如果同时存在更强劲的制约因素,如过高的利润预期或对经营前景毫无信心,这时利率的调节作用则是极为有限的。 再贴现政策一般包括两方面的内容:一是再贴现率的调整,二是规定向中央银行申请再贴现的资格。前者主要着眼于短期,即中央银行根据市场的资金供求状况,随时调低或调高再贴现率,以影响商业银行借入资金的成本,刺激或抑制资金需求,从而调节货币供应量。后者着眼于长期,对要再贴现的票据种类和申请机构加以规定,如区别对待,可起抑制或扶持的作用,改变资金流向。 再贴现的政策效果体现在两方面:(1)再贴现率的变动,在一定程度上反映了中央银行的政策意向,有一种告示效应。如再体现率的提高,意味着国家判断市场过热,有紧缩意向;反之,则意味着有扩张意向。这对短期市场利率常起导向作用。(2)通过影响商业银行的资金成本和超额准备来影响商业银行的融资决策。 但是,再贴现作为一种货币政策工具,也有不足之处:(1)不能使中央银行有足够的主动权,甚至市场的变化可能违背其政策意愿。商业银行是否愿意到中央银行申请再贴现,或再贴现多少,决定于商业银行的行为。如商业银行可通过其他途径筹措资金而不依赖于再贴现,则中央银行就不能有效地控制货币供应量。(2)再贴现率高低有限度。在经济高速增长时期,再贴现率无论多高,都难以遏止商业银行向中央银行再贴现或贷款。(3)对法定准备率来说,再贴现率比较易于调整,但随时调整再贴现率也会引起市场利率的经常波动,等等。我国目前的再贴现政策工具的运用并不理想,主要反映在以下三个方面: (4)我国目前的再贴现政策工具的运用并不理想主要原因在于商业银行的贴现业务开展的不理想,而其根源又在于商业信用的不理想。我国计划经济体制时期,取消了商业信用这种直接融资形式。改革开放以后,开始恢复,但恢复的二十多年来未取得长足的发展。主要表现为以下几个方面(1)商业信用规模远不够大。长期停滞后恢复,规模上无法马上发展起来。(2)票据不规范。早期的票据不规范表现为不使用专门的票据,出现了许多纠纷。而近期又出现许多不以真实商品交易为背景的融通票据,并向银行贴现后违规进入股市。(3)商业信用的当事各方不讲信用。虽然有真实商品交易和还款来源,但仍然有些企业无故不还。银行的贴现业务的良性发展必须是建立在健康的商业信用制度上的。由于存在上述约束条件,从而阻碍了商业银行票据业务的发展。而中央的再贴现业务也必须十以上商业银行可以健康开展贴现业务为前提的,因此,作为连锁效应,上述约束条件的存在也顺限制了中央银行运用再贴现工具进行货币调控的操作。 (5)我们国家那种通过基准利率的调节来影响市场利率的机制是不存在的,或者说很大程度上是有缺陷的。中国利率的总体格局使官定利率体制,不存在只调整一个基准利率即影响或迂回影响市场利率的体制。由于中央银行不但规定自己的再贴现利率水平,而且还规定商业银行各项存贷款业务的利率水平,因此这样的利率体系是缺乏弹性和回旋余地的。也既是由商业银行或金融机构自发决定市场利率或存贷款业务利率的利率机制是不存在的。由于准备金制度的缺陷,即准备金制度使商业银行的资金无法做到自求平衡,商业银行往往依赖中央银行再贷款取得资金,如果商业银行再贷款可以轻易得到,那么商业银行就没有办理再贴现业务的动力和必要。 综上所述,再贴现政策这一宏观货币调控手段的运用目前还存在着不少问题,但是,随着货币监管机构和社会各经济主体对这一宏观调控手段的认识逐步深刻,再贴现政策的执行必将规范化,从而真正产生应有的调控效应。 论述28:如何评价公开市场业务这一政策工具及在我国的作用。 公开市场业务是指中央银行在金融市场上出售或购入财政部和政府机构的证券,特别 是短期国库券,用以影响基础货币的宏观货币调控手段。它的运行机制如下:中央银 行通过购买或出售债券可以增加或减少流通中现金或银行的准备金,即都是使基础货 币增减。基础货币增加,货币供应量可随之增加;基础货币减少,货币供应量亦随之 减少。不过,是增减通货,还是增减准备金,还是两者在增减过程中的比例不同,会 导致不同的乘数效应,因而货币供应量增减的规模也有所不同。 同样作为宏观货币调控手段,公开市场业务被认为是最有效的。与准备金政策和再贴 现政策相比,它有着明细的优越性:(1)中央银行能够运用公开市场业务,影响商业 银行准备金,从而直接影响货币供应量。(2)公开市场业务使中央银行能够随时根据 金融市场的变化,进行经常性、连续性的操作。(3)通过公开市场业务,中央银行可 以主动出击,不像再贴现政策那样处于被动地位。(4)由于公开市场业务的规模和方 向可以灵活安排,中央银行可以运用它对货币供应量进行微调,而不会像存款准备金 的变动那样,产生震动性影响。 然而,公开市场业务要有效地发挥其作用,必须具备一定地条件。(1)中央银行必须 具有强大的、足以干预和控制整个金融市场的金融势力。(2)金融市场必须是全国性 的,必须有相当的独立性,证券种类必须齐全并达到一定规模。(3)必须有其他政策 工具的配合。如没有存款准备制度,这一工具也无法发挥作用。 中国的公开市场业务操作是远不够理想的,主要原因有以下四点:(1)国债规模远不 够大。国债资产规模在中央银行总资产比例远小于德国和日本的60%、英国的70%以及 美国的80%。(2)国债期限结构不合理。都是中长期的国债,没有短期。但公开市场 上操作的应该是短期国债,由于他们期限短,受益低,因此便于吞吐。(3)凭证式国 债占国债相当部分,记账式国债不足。而公开市场上操作的主要是记账式国债。(4)从分布上,相当大部分国债为城乡居民或个人所持有的凭证式国债,中央银行无法进 行吞吐。虽然,我国目前的公开业务操作仍存在上述问题,但从1999年起,公开市场 操作已成为增量基础货币投放的主渠道,所以发展形势较好。 2002中央银行进行了多次回购,以调节货币量,可增加此部分内容,使分析更加贴近实际。 综上所述,公开市场业务这一宏观货币调控手段的运用目前还存在着不少问题,但是,随着货币监管机构和社会各经济主体对这一宏观调控手段的认识逐步深刻,公 开市场业务的运用必将扩大,从而真正产生应有的调控效应。 论述29.结合国内外实际,谈谈发展中国家在金融自由化改革中应注意的问题。 金融自由化,是指政府放弃对金融市场和金融体系的过度干预,放松对利率和汇率的严格管制,使利率和汇率成为反映资金供求和外汇供求对比变化的信号,从而利于增加储蓄和投资,并促进经济增长。 对于发展中国家,金融自由化是克服以往金融压抑政策造成的负面影响,实现经济长期稳定成长的必由之路。但从许多发展中国家金融自由化改革的实践看,这条道路是不平坦的。金融自由化本身需要具备一定的条件,在具体的实施过程中必须措施配套、审慎推进。在条件尚不具备的情况下贸然推进,或者在改革的过程中次序错误、举措失当,都可能导致灾难性的后果。 总结二十世纪七十年代以来的经验教训,发展中国家金融自由化改革必须注意以下几个方面的问题: 对“金融自由化”必须有一个正确的认识 “金融自由化”不是完全取消政府对金融市场与金融体系的干预,而是对政府调控金融方式的改革。具体说就是要以法律和规章的干预取代人为的行政干预。政府仍然负有维护金融市场稳定、维持金融体系健康运行的责任。放松金融管制对市场机制的形成与市场调节作用的充分发挥是非常重要的。但若在放松管制之后,没有建立起一套适应新的市场环境、符合本国国情的审慎管理制度,就可能导致金融失控,危害经济安全与经济发展。 金融自由化改革必须具备一定的条件 ——要有稳定的宏观经济背景 在宏观经济不稳定的国家实行金融自由化政策,高的通货膨胀率容易导致高利率和实际汇率浮动,从而使资金出现不规则流动,进而引起许多企业和银行的破产。只有首先创造稳定的宏观经济背景,金融改革才能避免上述种种经济不安定状况。 宏观经济稳定是成功地实现金融自由化的前提条件。亚洲大多数成功的事例中,在实行金融改革之前大体上消除了宏观经济的失衡。国际收支平衡表和财政赤字得到管理,通货膨胀也相对较低。与上述情况不同的亚洲国家——菲律宾和斯里兰卡——在改革中扩展金融部门很不成功。例如,印度尼西亚的宏观经济条件理想,广义货币与GDP的比率从1983年金融自由化前9%的水平急剧上升,到1991年远远超过40%。与之相对照,斯里兰卡广义货币与GDP的比率在改革以后基本上没有变化。只有可靠的和持续的宏观经济稳定才能够带来货币需求的增加,从而有助于通过放松银行部门管制而形成的金融深化。 ——银行资产质量状况应该能够保证银行经得起金融自由化的冲击 发展中国家的银行因政府的行政干预形成大量呆账。但在政府的保护措施下,资产状况不佳尚不至于对银行生存和发展造成严重影响。金融自由化改革后,政府的保护取消,利率波动加大,竞争更加激烈,一旦资金流入的方向与规模发生变化,很容易使一些银行产生流动性危机,进而可能导致破产。这一问题在中国表现尤为突出。目前四大国有商业银行的不良资产比率均超过20%,已经严重资不抵债,之所以还能继续经营的唯一原因是源源不断流入的存款使其仍然保持了一定的支付能力。一旦国家取消对国有银行的保护,允许外资及私营银行平等竞争,存款必然被大量分流。存款分流以后,四大国有银行必然会陷入严重的支付危机,可能导致破产。如果政府利用扩张货币的政策避免其破产,则可能导致严重的通货膨胀,威胁宏观经济的稳定。 实施金融自由化之后,特别是继承了改革以前的劣质贷款后,利率的放开必须伴之以结构性改革,包括重组资产负债表以消除坏帐、将公有银行私有化,以及在银行部门采取措施以促进竞争。 当然,放松管制也会造成银行做出劣质贷款决策的情况。如果在改革之前,银行没有基于市场标准进行贷款,那么他们管理信贷评价和分配的能力萎缩了或是从来未被开发。此外,金融自由化过程本身将一些新的不确定性引入经济体制。因此,新近放松管制的银行很有可能作出劣质贷款决策来。在一个新的自由化的环境中,加强银行管理者的管理和风险评价能力将是重组进程中一个必要组成部分。这可能会要求政府采取行动。例如,政府可以放松对国内银行的外国所有权的限制,以便引进外国管理和信贷评估技术的“最佳做法”。 在一个放松管制的金融部门,也需要对系统风险进行谨慎的管理。耐人寻味的是,对金融机构有效的审慎监管的需求,在自由的环境中比在政府控制的金融约束机制下还要大。改革前存在的对银行行为的管制可以使金融部门保持稳定,尽管它是以牺牲相当大的经济效率为代价。放松管制将削弱这种基于管制的稳定,因而必须更加强调审慎的监管。如果银行选择使用蕴含在金融自由化中的自由,导致潜在的市场失灵,那么对宏观经济和金融稳定的影响可能是灾难性的。 金融自由化改革必须措施配套,稳步推进 ——金融自由化的改革必须与价格改革或自由定价机制相配合 假若一国的价格仍是保护价格或管制价格,在价格信号扭曲的条件下实行金融自由化,资金流动被错误的信号误导,结果会出现新的资源配置失效。 ——政府当局在推进金融自由化改革时,必须预先判断相对价格变动对不同集团利益的影响,并出于公平原则和政治均衡要求的考虑,适当采用经济补偿手段。 金融自由化措施实行后,利率和汇率的变动必然导致利益关系的调整,从而使改革措施面临部分利益集团的阻力。为顺利推进,政府基于公平合理的原则采取一定的经济补偿手段,以缓解压力、减轻社会震荡是必要的。 ——资本市场的开放要非常审慎 开放资本市场对促进发展中国家吸引外资具有重要作用。但资本市场的开放和资本的自由流动也加剧了一国金融体系的风险。国际游资的大规模进入与大规模撤离,可能给一国的金融市场造成严重冲击。因此发展中国家在实行金融自由化改革时,必须对资本市场的开放持格外的审慎态度。只有在各项条件均已具备且本国的经济实力足以承担的时候,资本市场的对外开放才能产生良好的效果。 1997年的东南亚金融危机迅速由泰国蔓延到其他东南亚国家及韩国,一个重要的原因就是这些国家的资本市场开放程度高。中国受到东南亚金融危机的影响相对比较小,最重要的原因就是中国的资本市场开放程度低。东南亚金融危机的原因很多,不能只用资本市场的开放度来衡量。 论30.如何评价微观主体预期之于货币政策的抵消作用 ? 一般来说货币政策是中央银行为实现既定的经济目标运用各种工具调节货币供给和利率,进而影响宏观经济的方针和措施的总和。货币政策的效应往往受到一些因素的影响,如政策的时滞、货币的流通速度、客观经济条件的变化、政治的变化等等。 微观主体的预期也是对货币政策有效性或效应高低构成挑战的另外一个因素。当一项货币政策提出时,各种微观主体,立即会根据可能获得的各种信息预测政策的后果,从而很快的作出对策,而且极少有时滞。货币当局推出的政策面对微观主体广采取的对消其作用的对策,政策可能归于无效。例如,政府拟采取长期的扩张政策,人们通过各种信息预期社会总需求会增加,物价会上涨,载这种情况下,工人辉通过工会与雇主谈判,要求提高工资,企业预期工资成本的增加而不愿意扩展经营。最后的结果是只有物价的上张而没有产出的增长。鉴于微观主体的预期,似乎只有在货币政策的取向和力度没有或没有完全为公众知晓的情况下才能生效或达到预期效果。但是这样的可能性不大。货币当局不可能长期不让社会知道它所要采取的政策;即使采取非常常规的货币政策,不久之后也会落在人们的预期之内。假如货币当局长期采取非常规的货币政策,则会导致微观经济主体作出错误判断,并会使经济陷入混乱之中。但实际的情况是,公众的预期即使是非常准确的,实施对策即使很快,其效应的发挥也要有个过程。这就是说,货币政策仍可奏效,但公众的预期行为会使其效应打很大的折扣。 建议增加理性预期概念,和卢卡斯批判进行进一步分析,使答案更加饱满。 1.引用与指针有什么区别? 1.引用必须被初始化,指针不必。 2.引用初始化以后不能被改变,指针可以改变所指的对象。3.不存在指向空值的引用,但是存在指向空值的指针。 2.堆栈溢出一般是由什么原因导致的? 没有回收垃圾资源。3.什么函数不能声明为虚函数? constructor函数不能声明为虚函数。4.写出float x 与“零值”比较的if语句。if(x>0.000001&&x<-0.000001) 5.不能做switch()的参数类型是: switch的参数不能为实型 6.头文件中的 ifndef/define/endif干什么用? 预处理 答:防止头文件被重复引用 7.#include 答: 对于#include 对于#include “filename.h”,编译器从用户的工作路径开始搜索filename.h 8.在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明? 答:函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern “C”修饰的变量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。 9.char str1[] = “abc”; char str2[] = “abc”; const char str3[] = “abc”; const char str4[] = “abc”; const char *str5 = “abc”; const char *str6 = “abc”; char *str7 = “abc”; char *str8 = “abc”; cout <<(str1 == str2)<< endl;cout <<(str3 == str4)<< endl; cout <<(str5 == str6)<< endl; cout <<(str7 == str8)<< endl; 结果是:0 0 1 1 str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域。 10.main() { int a[5]={1,2,3,4,5}; int *ptr=(int *)(&a+1); printf(“%d,%d”,*(a+1),*(ptr-1)); } 答:2,5 *(a+1)就是a[1],*(ptr-1)就是a[4], 执行结果是2, 5。&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)。int *ptr=(int *)(&a+1);则ptr实际是&(a[5]),也就是a+5 原因如下: &a 是数组指针,其类型为 int(*)[5];而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同;a是长度为5的int数组指针,所以要加 5*sizeof(int)。所以ptr实际是a[5]。但是prt与(&a+1)类型是不一样的(这点很重要),所以prt-1只会减去 sizeof(int*)。a,&a的地址是一样的,但意思不一样,a是数组首地址,也就是a[0]的地址,&a是对象(数组)首地 址,a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即a[5]。 11.交换两个变量的值,不使用第三个变量。即a=3,b=5,交换之后a=5,b=3; 答:有两种解法, 一种用算术算法, 一种用^(异或) a = a + b; b = a – b; a = a – b; or a = a^b;// 只能对int,char..b = a^b; a = a^b; or a ^= b ^= a; 12.列举几种进程的同步机制,并比较其优缺点。 答:原子操作、信号量机制、自旋锁、管程、会合、分布式系统 13.进程死锁的原因和4个必要条件 答:资源竞争及进程推进顺序非法;互斥、请求保持、不可剥夺、环路 14.要对绝对地址0×100000赋值,我们可以用(unsigned int*)0×100000 = 1234;那么要是想让程序跳转到绝对地址是0×100000去执行,应该怎么做? 答:*((void(*)())0×100000)(); 首先要将0×100000强制转换成函数指针,即:(void(*)())0×100000。然后再调用它: *((void(*)())0×100000)();用typedef可以看得更直观些: typedef void(*)()voidFuncPtr; *((voidFuncPtr)0×100000)(); 15.unsigned char *p1; unsigned long *p2; p1=(unsigned char *)0×801000; p2=(unsigned long *)0×810000; 请问 p1+5=______; p2+5=______; 答案:801005;810014。不要忘记了这个是16进制的数字,p2要加20变为16进制就是14 16、设有以下说明和定义: typedef union {long i;int k[5];char c;} DATE; struct data { int cat;DATE cow;double dog;} too;DATE max; 则语句 printf(“%d”,sizeof(too)+sizeof(max));的执行结果是:______ 答:DATE是一个union, 变量公用空间.里面最大的变量类型是int[5], 占用20个字节.所以它的大小是20 data是一个struct, 每个变量分开占用空间.依次为int4 + DATE20 + double8 = 32.所以结果是 20 + 32 = 52.当然„在某些16位编辑器下, int可能是2字节,那么结果是 int2 + DATE10 + double8 = 20 1.代码找错题(题目1) void test1(){ char string[10];char* str1=“0123456789”;strcpy(string, str1);} 代码找错题(题目2) void test2(){ char string[10], str1[10];for(I=0;I<10;I++){ str1[i] ='a';} strcpy(string, str1);} 代码找错题(题目3) Void test3(char* str1) { char string[10]; if(strlen(str1)<= 10) { strcpy(string, str1); } } 在swap函数中,p是一个“野”指针,有可能指向系统区,导致程序运行的崩溃。在VC++中DEBUG运行时提示错误“Access Violation”。该程序应该改为: 解答: test1: 字符串str1需要11个字节才能存放下(包括末尾的' '),而string只有10个字节的空间,strcpy会导致数组越界 test2: 如果面试者指出字符数组str1不能在数组内结束可以给3分;如果面试者指出strcpy(string, str1)调用使得从str1内存起复制到string内存起所复制的字节数具有不确定性可以给7分,在此基础上指出库函数strcpy工作方式的给10 分 test3: if(strlen(str1)<= 10)应改为if(strlen(str1)< 10),因为strlen的结果未统计' '所占用的1个字节 剖析: 考查对基本功的掌握: (1)字符串以' '结尾; (2)对数组越界把握的敏感度; (3)库函数strcpy的工作方式,如果编写一个标准strcpy函数的总分值为10,下面给出几个不同得分的答案: 2分 void strcpy(char *strDest, char *strSrc) { while((*strDest++ = * strSrc++)!= ' '); } 4分 void strcpy(char *strDest, const char *strSrc) //将源字符串加const,表明其为输入参数,加2分 { while((*strDest++ = * strSrc++)!= ' '); } 7分 void strcpy(char *strDest, const char *strSrc) { //对源地址和目的地址加非0断言,加3分 assert((strDest!= NULL)&&(strSrc!= NULL)); while((*strDest++ = * strSrc++)!= ' '); } 10分 //为了实现链式操作,将目的地址返回,加3分! char * strcpy(char *strDest, const char *strSrc) { assert((strDest!= NULL)&&(strSrc!= NULL)); char *address = strDest; while((*strDest++ = * strSrc++)!= ' '); return address; } (4)对strlen的掌握,它没有包括字符串末尾的' '。 读者看了不同分值的strcpy版本,应该也可以写出一个10分的strlen函数了,完美的版本为 int strlen(const char *str)//输入参数const { assert(strt!= NULL);//断言字符串地址非0 int len; while((*str++)!= ' ') { len++; } return len; } 试题4: void GetMemory(char *p) { p =(char *)malloc(100); } void Test(void) { char *str = NULL; GetMemory(str); strcpy(str, “hello world”); printf(str); } 试题5: char *GetMemory(void) { char p[] = “hello world”; return p; } void Test(void) { char *str = NULL; str = GetMemory(); printf(str); } 试题6: void GetMemory(char **p, int num) { *p =(char *)malloc(num); } void Test(void) { char *str = NULL; GetMemory(&str, 100); strcpy(str, “hello”); printf(str); } 试题7: void Test(void) { char *str =(char *)malloc(100); strcpy(str, “hello”); free(str); //省略的其它语句 } 解答: 试题4传入中GetMemory(char *p)函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完char *str = NULL;GetMemory(str);后的str仍然为NULL; 试题5中 char p[] = “hello world”;return p;的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。 试题6的GetMemory避免了试题4的问题,传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句 *p =(char *)malloc(num);后未判断内存是否申请成功,应加上: if(*p == NULL) { „//进行申请内存失败处理 } 试 题7存在与试题6同样的问题,在执行char *str =(char *)malloc(100);后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:str = NULL;试题6的Test函数中也未对malloc的内存进行释放。 剖析: 试题4~7考查面试者对内存操作的理解程度,基本功扎实的面试者一般都能正确的回答其中50~60的错误。但是要完全解答正确,却也绝非易事。 对内存操作的考查主要集中在: (1)指针的理解; (2)变量的生存期及作用范围; (3)良好的动态内存申请和释放习惯。 再看看下面的一段程序有什么错误: swap(int* p1,int* p2) { int *p; *p = *p1; *p1 = *p2; *p2 = *p; } swap(int* p1,int* p2) { int p; p = *p1; *p1 = *p2; *p2 = p; } 2.内功题 试题1:分别给出BOOL,int,float,指针变量 与“零值”比较的 if 语句(假设变量名为var) 解答: BOOL型变量:if(!var) int型变量: if(var==0) float型变量:const float EPSINON = 0.00001; if((x >= – EPSINON)&&(x <= EPSINON) 指针变量: if(var==NULL) 剖析: 考查对0值判断的“内功”,BOOL型变量的0判断完全可以写成if(var==0),而int型变量也可以写成if(!var),指针变量的判断也可以写成if(!var),上述写法虽然程序都能正确运行,但是未能清晰地表达程序的意思。 一 般的,如果想让if判断一个变量的“真”、“假”,应直接使用if(var)、if(!var),表明其为“逻辑”判断;如果用if判断一个数值型变 量(short、int、long等),应该用if(var==0),表明是与0进行“数值”上的比较;而判断指针则适宜用if(var==NULL),这是一种很好的编程习惯。 浮点型变量并不精确,所以不可将float变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。如果写成if(x == 0.0),则判为错,得0分。 试题2:以下为Windows NT下的32位C++程序,请计算sizeof的值 void Func(char str[100]) { sizeof(str)= ? } void *p = malloc(100); sizeof(p)= ? 解答: sizeof(str)= 4 sizeof(p)= 4 剖析: Func(char str[100])函数中数组名作为函数形参时,在函数体内,数组名失去了本身的内涵,仅仅只是一个指针;在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。 数组名的本质如下: (1)数组名指代一种数据结构,这种数据结构就是数组; 例如: char str[10]; cout << sizeof(str)<< endl; 输出结果为10,str指代数据结构char[10]。 (2)数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改; char str[10]; str++;//编译出错,提示str不是左值 (3)数组名作为函数形参时,沦为普通指针。 Windows NT 32位平台下,指针的长度(占用内存的大小)为4字节,故sizeof(str)、sizeof(p)都为4。 试题3:写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。另外,当你写下面的代码时会发生什么事? least = MIN(*p++, b);解答: #define MIN(A,B)((A)<=(B)?(A):(B)) MIN(*p++, b)会产生宏的副作用 剖析: 这个面试题主要考查面试者对宏定义的使用,宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对“参数”进行的是一对一的替换。程序员对宏定义的使用要非常小心,特别要注意两个问题: (1)谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。所以,严格地讲,下述解答: #define MIN(A,B)(A)<=(B)?(A):(B) #define MIN(A,B)(A <= B ? A : B)都应判0分; (2)防止宏的副作用。 宏定义#define MIN(A,B)((A)<=(B)?(A):(B))对MIN(*p++, b)的作用结果是:((*p++)<=(b)?(*p++):(*p++))这个表达式会产生副作用,指针p会作三次++自增操作。除此之外,另一个应该判0分的解答是:#define MIN(A,B)((A)<=(B)?(A):(B)); 这个解答在宏定义的后面加“;”,显示编写者对宏的概念模糊不清,只能被无情地判0分并被面试官淘汰。 试题4:为什么标准头文件都有类似以下的结构? #ifndef __INCvxWorksh #define __INCvxWorksh #ifdef __cplusplus extern “C” { #endif /* */ #ifdef __cplusplus } #endif #endif /* __INCvxWorksh */ 解答: 头文件中的编译宏 #ifndef __INCvxWorksh #define __INCvxWorksh #endif 的作用是防止被重复引用。 作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在symbol库中的名字与C语言的不同。例如,假设某个函数的原型为: void foo(int x, int y); 该函数被C编译器编译后在symbol库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。_foo_int_int这样的名字包含了函数名和函数参数数量及类型信息,C++就是考这种机制来实现函数重载的。 为了实现C和C++的混合编程,C++提供了C连接交换指定符号extern “C”来解决名字匹配问题,函数声明前加上extern “C”后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了。 试题5:编写一个函数,作用是把一个char组成的字符串循环右移n个。比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefgh” 函数头是这样的: //pStr是指向以' '结尾的字符串的指针 //steps是要求移动的n void LoopMove(char * pStr, int steps) { //请填充„ } 解答: 正确解答1: void LoopMove(char *pStr, int steps) { int n = strlen(pStr)– steps; char tmp[MAX_LEN]; strcpy(tmp, pStr + n); strcpy(tmp + steps, pStr); *(tmp + strlen(pStr))= ' '; strcpy(pStr, tmp); } 正确解答2: void LoopMove(char *pStr, int steps) { int n = strlen(pStr)– steps; char tmp[MAX_LEN]; memcpy(tmp, pStr + n, steps); memcpy(pStr + steps, pStr, n); memcpy(pStr, tmp, steps); } 剖析: 这试题主要考查面试者对标准库函数的熟练程度,在需要的时候引用库函数可以很大程度上简化程序编写的工作量。 最频繁被使用的库函数包括: (1)strcpy (2)memcpy (3)memset 试题6:已知WAV文件格式如下表,打开一个WAV文件,以适当的数据结构组织WAV文件头并解析WAV格式的各项信息。 WAVE文件格式说明表 偏移地址 字节数 数据类型 内 容 文件头 00H 4 Char “RIFF”标志 04H 4 int32 文件长度 08H 4 Char “WAVE”标志 0CH 4 Char “fmt”标志 10H 4 过渡字节(不定) 14H 2 int16 格式类别 16H 2 int16 通道数 18H 2 int16 采样率(每秒样本数),表示每个通道的播放速度 1CH 4 int32 波形音频数据传送速率 20H 2 int16 数据块的调整数(按字节算的) 22H 2 每样本的数据位数 24H 4 Char 数据标记符"data" 28H 4 int32 语音数据的长度 解答: 将WAV文件格式定义为结构体WAVEFORMAT: typedef struct tagWaveFormat { char cRiffFlag[4]; UIN32 nFileLen; char cWaveFlag[4]; char cFmtFlag[4]; char cTransition[4]; UIN16 nFormatTag; UIN16 nChannels; UIN16 nSamplesPerSec; UIN32 nAvgBytesperSec; UIN16 nBlockAlign; UIN16 nBitNumPerSample; char cDataFlag[4]; UIN16 nAudioLength; } WAVEFORMAT; 假设WAV文件内容读出后存放在指针buffer开始的内存单元内,则分析文件格式的代码很简单,为: WAVEFORMAT waveFormat; memcpy(&waveFormat, buffer,sizeof(WAVEFORMAT)); 直接通过访问waveFormat的成员,就可以获得特定WAV文件的各项格式信息。 剖析: 试题6考查面试者组织数据结构的能力,有经验的程序设计者将属于一个整体的数据成员组织为一个结构体,利用指针类型转换,可以将memcpy、memset等函数直接用于结构体地址,进行结构体的整体操作。透过这个题可以看出面试者的程序设计经验是否丰富。 试题7:编写类String的构造函数、析构函数和赋值函数,已知类String的原型为: class String { public: String(const char *str = NULL);// 普通构造函数 String(const String &other);// 拷贝构造函数 ~ String(void);// 析构函数 String & operate =(const String &other);// 赋值函数 private: char *m_data;// 用于保存字符串 }; 解答: //普通构造函数 String::String(const char *str) { if(str==NULL) { m_data = new char[1];// 得分点:对空字符串自动申请存放结束标志' '的空 //加分点:对m_data加NULL 判断 *m_data = ' '; } else { int length = strlen(str); m_data = new char[length+1];// 若能加 NULL 判断则更好 strcpy(m_data, str); } } // String的析构函数 String::~String(void) { delete [] m_data;// 或delete m_data; } //拷贝构造函数 String::String(const String &other) // 得分点:输入参数为const型 { int length = strlen(other.m_data); m_data = new char[length+1]; //加分点:对m_data加NULL 判断 strcpy(m_data, other.m_data); } //赋值函数 String & String::operate =(const String &other)// 得分点:输入参数为const型 { if(this == &other)//得分点:检查自赋值 return *this; delete [] m_data; //得分点:释放原有的内存资源 int length = strlen(other.m_data); m_data = new char[length+1];//加分点:对m_data加NULL 判断 strcpy(m_data, other.m_data); return *this; //得分点:返回本对象的引用 } 试题8:请说出static和const关键字尽可能多的作用 解答: static关键字至少有下列n个作用: (1)函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值; (2)在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问; (3)在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内; (4)在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝; (5)在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。 const关键字至少有下列n个作用: (1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了(2)对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const; (3)在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值; (4)对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量; (5)对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。例如: const classA operator*(const classA& a1,const classA& a2); operator*的返回结果必须是一个const对象。如果不是,这样的变态代码也不会编译出错: classA a, b, c; (a * b)= c;// 对a*b的结果赋值 操作(a * b)= c显然不符合编程者的初衷,也没有任何意义。 剖析:小 小的static和const居然有这么多功能,我们能回答几个?如果只能回答1~2个,那还真得闭关再好好修炼修炼。这个题可以考查面试者对程序设计知 识的掌握程度是初级、中级还是比较深入,没有一定的知识广度和深度,不可能对这个问题给出全面的解答。大多数人只能回答出static和const关键字 的部分功能。3.技巧题 试题1:请写一个C函数,若处理器是Big_endian的,则返回0;若是Little_endian的,则返回1 解答: int checkCPU(){ { union w { int a; char b; } c; c.a = 1; return(c.b == 1);} } 剖析: 嵌入式系统开发者应该对Little-endian和Big-endian模式 非常了解。采用Little-endian模式的CPU对操作数的存放方 式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。例如,16bit宽的数0×1234在Little-endian模式CPU内存中的存放方式(假设从地址0×4000开始存放)为: 内存地址 存放内容 0×4000 0×34 0×4001 0×12 而在Big-endian模式CPU内存中的存放方式则为: 内存地址 存放内容 0×4000 0×12 0×4001 0×34 32bit宽的数0×12345678在Little-endian模式CPU内存中的存放方式(假设从地址0×4000开始存放)为: 内存地址 存放内容 0×4000 0×78 0×4001 0×56 0×4002 0×34 0×4003 0×12 而在Big-endian模式CPU内存中的存放方式则为: 内存地址 存放内容 0×4000 0×12 0×4001 0×34 0×4002 0×56 0×4003 0×78 联合体union的存放顺序是所有成员都从低地址开始存放,面试者的解答利用该特性,轻松地获得了CPU对内存采用Little-endian还是Big-endian模式读写 试题2:写一个函数返回1+2+3+„+n的值(假定结果不会超过长整型变量的范围)解答: int Sum(int n){ return((long)1 + n)* n / 2;//或return(1l + n)* n / 2;} 剖析: 对于这个题,只能说,也许最简单的答案就是最好的答案。下面的解答,或者基于下面的解答思路去优化,不管怎么“折腾”,其效率也不可能与直接return(1 l + n)* n / 2相比!3.不用第三变量交换两个数 void swap(int a,int b){ a=a^b;b=b^a;a=a^b;} 或者 void swap(int a, int b){ a=a+b;b=a-b;a=a-b;} 不过这两种方法只是修改了函数的形参,如果要修改实参,可以采用如下的方法: void swap(int* a,int *b){ *a=*a^*b;*b=*b^*a;*a=*a^*b;printf(“In %s:a=%d,b=%dn”,__FUNCTION__,*a,*b);} 4.求大数的阶乘例如100!,使用通常的做法会溢出,这里要使用数组的方法。例如:123*20 相当于 100*20 + 20*20+3*20 #include i)–第二篇:C语言软件工程师笔试题精华
第三篇:C语言软件工程师笔试题精华
第四篇:银行笔试题大总结
第五篇:IT公司笔试题总结