自己总结的笔试题(2007.12)
研究生毕业时转战各个公司的笔试题总结,希望对后来者以及自己以后会有所帮助。
1、写一个函数,实现二进制文件的转存,要求转存后的文件的开头是文件的长度
#include "stdio.h"
main()
{
FILE *in,*out;
char ch,infile[],outfile[];
int length;
printf("Enter the infile name:/n");
scanf("%s",infile);
printf("Enter the outfile name:/n");
scanf("%s",outfile)
if((in=fopen(infile,"r"))==NULL)
{
printf("cannot open infile/n");
}
if((out=fopen(outfile,"w"))==NULL)
{
printf("cannot open outfile/n");
}
while(!feof(in)) fputc(fgetc(in),out);
putw(in.bsize,out);
fclose(in);
fclose(out);
}
2、线程和进程的关系:线程和进程都是操作系统中程序运行的基本单位,线程在于进程之下。一个程序至少有一个进程,一个进程至少有一个线程,从一个线程切换到另一线程所花费的代价比进程低。属于同一进程的线程可共享该进程的内存区和资源,多线程的操作可使多个执行部分可以同时执行,提高了系统的效率
数据对齐指数据所在的内存地址必须是该数据长度的整倍数
如何防止内存泄露:查malloc对应的free;看进程管理器;使用专用的工具
3、写一个函数实现傅利叶变换
4、字符串函数
(1).strcpy
char * strcpy(char * strDest,const char * strSrc)
{
if ((strDest==NULL)||(strSrc==NULL))
printf"Invalid arguments";
char * strDestCopy=strDest;
while (*strSrc!='"0')
*strDest++=*strSrc++;
return strDestCopy;
}
(2).strlen
int strlen(const char* src)
{
if (src==NULL)
printf"Invalid arguments";
int len = 0;
while(*src++ != '"0')
len++;
return len;
}
(3).递归实现字符反转函数reverse
#include<iostream>
#include<string>
void reverse(char *str, int len)
{
if(len==1) return;
else
{char temp;
temp=*str;*str=*(str+len);*(str+len)=temp;
reverse((str+1),(len-2));
return;
}
}
副:递归算法求最大公约数(两个数中最能被除尽的)
int zdgys(int a, int b)
{
for(int i=2;i<=min(a,b);i++) //从2开始到a,b中的较小数“试”
{
if(a%i==0&&b%i==0) //a,b都能被i整除,i是一个公约数
return i*zdgys(a/i,b/i); //递归调用
}
return 1;//如果没有大于等于2的公约数,则公约数为1
}
(4).实现英语单词反转函数
int main()
{
string strIn;
strIn="I am a student";
int end;
end=strlen(strIn)-1;
for(int i=strIn.length()-1;i>=0;i--)
{
if(strIn[i]==' ')
{
for(int j=i+1;j<=end;j++)
cout<<strIn[j];
cout<<' ';
end=i-1;
}
}
}
(5).实现strstr(): 找出字符串sub在字符串src的什么位置,如"34"在"12345"的3位置。
char *strstr(char *str,char *sub_str)
{
int i=0,j=0;
while(str[i]!='"0') {
if(str[i]==sub_str[j]) {
break;
}
else {
i++;
}
}
if(sub_str[i]!='"0')
return (char *)(str+i);
else return NULL;
}
(6).删空格函数
#include<string.h>
char *delspace(char *ptr)
{
if (*ptr=='"0')
return ptr;
if(*ptr ==' '){
do
strcpy(ptr,ptr+1);
while(*ptr==' ');
delspace(++ptr);
} else {
delspace(++ptr);
}
return --ptr;
}
5、函数效率
6、对数据库的一张表进行操作,同时要对另一张表进行操作,如何实现:首先确定两个表的主从关系,在一个表中建立主键,对应另外一个表建立外键,这样当主表修改或删除时即可以相应的对另外一个表进行操作
7、TCP/IP 建立连接的过程:在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
8、winsock建立连接的主要实现步骤:服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept()等待客户端连接。客户端:socker()建立套接字,连接(connect)服务器,连接上后使用send()和recv(),在套接字上写读数据,直至数据交换完毕,closesocket()关闭套接字。
9、触发器怎么工作的:触发器即一组写好的sql语句,当设定与触发器相关的表有变化时,即执行该语句。
10、在Windows中,线程同步机制可以通过临界区、互斥量、信号量、事件来实现,还可以使用消息循环。临界区:通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问;互斥量:为协调共同对一个共享资源的单独访问而设计的;信号量:为控制一个具有有限数量用户资源而设计;事件:用来通知线程有一些事件已发生,从而启动后继任务的开始。
主要实现的方法包括wait()等待并释放资源;sleep()进程睡眠;notify()唤醒等待进程;Allnotify()唤醒所有等待进程,让它们竞争
线程通信(?):同一进程的各线程可以直接读写进程数据段进行通信,同样需要同步和互斥手段的辅助
死锁:系统中两个或者多个进程都无限期的相互等待对方释放资源才能继续运行,否则就阻塞,此时系统处于停滞状态,这就是死锁。产生死锁的四个必要条件:
(1) 互斥条件:一个资源每次只能被一个进程使用。
(2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3) 不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4) 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
只要上四个条件一个不满足即可解开死锁
11、char *p="ABCDEF";
*p=A
char *ps=p+1;
(int &)ps=?
12、在编程中,null值指出一个变量中没有包含有效的数据,是一个宏定义,如指针变量声明为NULL说明其并未指向任何地址,可在后面给其灵活给与定义;而0是一个常量。
void类型的指针可以指向任何类型的变量,增加了程序的灵活性,使用前将其强制转化为其他类型
stdio.h:包含一些标准输入输出函数;
stdlib.h:包含一些宏定义(如NULL),及标准库函数,如malloc;
string.h:包括常用的字符串处理函数
char *p=(char *)malloc(sizeof(char)); //若不加(char *)会提示warning说将一个void *定义给char *;
int *p=new int[10]; //都是分配一个边续堆空间给指针p为首地址的
13、如何用数字的方式(只用int)实现一个抛物线绘制
求平方函数为sqrt(),加include <math.h>
RAND()可实现(0,255)之间产生一随机数,"/"为除结果取整。实现:1、生成(0,100)/(-255,255)之间随机数,尽量均匀分布;2、生成(20,30)之间随机数,说明其分布;3、(1,5)之间随机数,要求两端概率大,中间概率小
14、十进制数直接写,如“123,-456”;八进制数以0开头,如“0123,ASC码值”;十六进制数以ox开头,如“ox8000”;
八进制转十进制为每项值乘以8的位次幂,十进制转八进制为除8求余
与运算为求乘,或运算为求加(1+1=1不进位),异或(^或XOR)亦为求加,取反(~,1^1=0,也不进位),左/右移位(a>>=1,即a=a>>1向右移1位,无符号数下左边高位补0,有符号数可能补0或1;a<<=1,即a左移1,低位补0;注意移位并非自操作,相当于a+1,只使用a>>1,a本身并不变)
15、起泡算法,实现降序排列
#include "stdio.h"
void sort(int *array,int num);
main()
{
int a[10];
int i,length;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
length=sizeof(a);
sort(a,length);
return 0;
}
void sort(int *array,int num)
{
int j,k,temp;
for(k=0;k<num;k++)
for(j=0;j<(num-k);j++)
{
if(array[j]<array[j+1])
{temp=array[j];array[j]=array[j+1];array[j+1]=temp;}
}
for(j=0;j<num;j++)
printf("%d",array[j]);
}
16、链表:一个学生的信息是:姓名,学号,性别,年龄等信息,用一个链表,把这些学生信息连在一起, 给出一个age, 在些链表中删除学生年龄等于age的学生信息。
#include "stdio.h"
#include "conio.h"
struct stu{
char name[20];
char sex;
int no;
int age;
struct stu * next;
}*linklist;
(1)、创建链表定义:给出一个头结点(其值为空),指定创建的链表结点数,在该头结点后创建一个n结点的链表
struct stu *creatlist(int n)
{
int i;
//h为头结点,p为前一结点,s为当前结点
struct stu *h,*p,*s;
h = (struct stu *)malloc(sizeof(struct stu));
h->next = NULL;
p=h;
for(i=0;i<n;i++)
{
s = (struct stu *)malloc(sizeof(struct stu));
p->next = s; //注意,当p->next改变时,h->next也改变了,因为它们都是指针,指向的是同一地址
printf("Please input the information of the student: name sex no age "n");
scanf("%s %c %d %d",s->name,&s->sex,&s->no,&s->age);
s->next = NULL;
p = s;
}
printf("Create successful!");
return(h);
}
(2)、删除链表结点定义:给出头结点和要删除点的内容值,从头结点向后找,直到删除
void deletelist(struct stu *head,int a) //形参为头指针和需要删除点的参数(值)
{
struct stu *p,*s;
if(head==NULL){
printf("nlist null!");
return(head);
}
s=head;
while(s->age!=a&&s!=NULL)
{
p = s;
s = s->next;
}
if(s==NULL)
printf("The record is not exist.");
else
{
p->next = s->next;
printf("Delete successful!");
}
}
注:删除一个节点,必须知道其节点地址(如pNode)和其前一个节点的地址(如pPrev),则可使pPrev->Next=pNode->Next,并free(pNode);释放掉pNode的内存,完成了删除操作
在某节点后插入一个节点(pInsert),则需要知道该节点地址(如pNode),使pInsert->Next=pNode->next;pNode->Next=pInsert->Next;完成插入操作
(3)、显示链表定义:给定头结点,显示其链表内容
void display(struct stu *s)
{
s=s->next; //由于使用的头指针没有data,仅作为头指针使用,所以必须跳过
while(s!=NULL)
{
printf("%s %c %d %d"n",s->name,s->sex,s->no,s->age);
s=s->next;
}
}
(4)、插入链表定义:给定头指针,给定要插入的点,按插入点的值(stud->num)将其按升序插入链表中
struct stu * insert(struct stu *head, stuct stu *stud)
{struct stum *s,*temp;
s=head->next; //定义链表的头结点为一个空的头指针
if(s==NULL)
{s=stud;stud->next=NULL;}
else
{while((stud->num>s->num)&&(s->next!=NULL))
s=s->next;
if(s==NULL)
printf("The record is not exist.");
else
{if(stud->num<=s->num)
{if(s->next!=NULL)
{temp=s->next;
s->next=stud;
stud->next=temp;
}
else
{s->next=stud;
stud->next=NULL;
}
}
}
}
int main()
{
struct stu *s;
int n,age;
printf("Please input the length of seqlist:"n");
scanf("%d",&n);
s = creatlist(n);
display(s);
printf("Please input the age:"n");
scanf("%d",&age);
deletelist(s,age);
display(s);
return 0;
}
二叉树,排序算法
17、在C++中,使用#include<iostream.h>或#include <iostream> using namespace std;或#include <iostream>加上std:cout;
在C中,用#include<iostream.h>或#include <iostream>均可;
18、DLL是一种基于Windows的程序模块,它可以将多个程序所要访问的代码放入一个或多个DLL时,在运行时调用,让程序访问不属于自身的可执行代码。DLL有助于减小程序模块大小,并能共享数据和资源。
在动态库的情况下,有两个文件,一个是引入库(.LIB)文件,一个是DLL文件。lib文件一般是一些索引信息,包含被DLL导出的函数的名称和位置,DLL包含实际的函数和数据,应用程序使用LIB文件链接到所需要使用的DLL文件。如果只有lib文件(静态库),那么这个lib文件是静态编译出来的,索引和实现都在其中。
动态连接库的两种方式:调用一个DLL中的函数有两种方法:1.静态调用或隐式调用,即在编译时将DLL加载到应用程序中。使用比较简单,但DLL改变后,程序需要重新编译,占用的内存资源也比较多;2.动态调用或显式调用,指在应用程序中使用LoadLibrary调用DLL,使用FreeLibrary卸载DLL,优点是比较灵活。
19、使用初始化成员列表:
class point
{
point(int m=0,int n=0):x(m),y(n)
}
//相当于:
class point
{
point(int m=0,n=0)
{x=m;y=n;} //构造函数
}
在以下三种情况下使用初始化列表:需要初始化的成员是类对象、const型(由于const型不能被赋值)、引用(int &a 注:引用类型必须在定义时赋值)
20、COM:一种软件组件,不是接口或对象,可以实现组件间的互相通信,有操作系统与编写语言的通用性。封闭性好,重用性强,降低了软件成本与开发周期,缺点是当一个COM组件更新时,可能导致使用它的软件不可用。
COM提供了访问它的几个接口:1、lunknown:必须实现,管理COM对象生命周期,即释放内存用;2、QI:查询接口,从一个接口跳转到另一个;3、GUIDs:标识
21、编写构造函数、析构函数、拷贝构造函数和赋值函数(即类为空时默认生成的几个函数)
Class String
{
public:
String(const char *str=NULL);
String(const String &other); //拷贝构造函数,当使用引用作为函数的实参和形参时,可防止改动实参和形参
~String(void);
String & operate=(const String &other); //赋值函数,重载定义"="(operate),其变量和返回值均为String型引用变量
private:
char *m_data;
}
String::String(const char *str)
{
if(str==NULL)
{
m_data=new char[1];
*m_data='"0';
}
else
{
int length=strlen(str);
m_data=new char[length+1];
strcpy(m_data,str);
}
}
String::String(const String &other) //other是一个String类的其他对象,用另一个对象的值来初始化本对象的值,加&说明是对象的引用
{
int length=strlen(other.m_data);
m_data=new char[length+1];
strcpy(m_data,other.m_data);
}
String::~String(void)
{
delete [] m_data; //"[]":删除该指针后的连续空间
}
String & String::operate=(const String &other) //使用对象other代替当前对象
{
if(this==&other) //&此时为取对象other的地址,上面为引用变量声明
return *this;
else
{
delete [] m_data;
int length=strlen(other.m_data);
m_data=new char[length+1];
strcpy(m_data,other.m_data);
return *this;
}
}
注:注意这几个函数都是没有返回值的,而其对应操作的m_data是类的成员变量,可以直接进行调用
(1).拷贝构造函数和赋值函数的区别在于:拷贝构造函数只能在对象初建时由系统调用,而赋值函数可在任何时候由用户调用,用一个对象来定义另一个对象;
(2).const String &other在使用引用后再使用const,可保证被赋值的对象也是const型的
22、"n;'"0'
Class A: public B, public C
A::copy(char *p);
在使用指针为实形参时,可以对其指向的变量进行改变(*p=2),但不能对指针的值进行改变(p=(char *)malloc()对实参无影响)
不分配地址时,使用堆栈存储,有效期为函数结束,所以不能在函数中为主函数中的变量申请空间;使用malloc/new,分配的是堆,函数结束时不释放,需自行释放。全局变量区为系统定义
23、OSI七层结构:物理层,数据链路层(PPP协议),网络层(IP,ICMP:Internet控制报文协议),传输层(TCP,UDP),会话层,表示层,应用层(TELNET,HTTP,FTP,SNMP)
TCP/IP协议是一组包括TCP协议和IP协议,UDP(User Datagram Protocol)协议、ICMP(Internet Control Message Protocol)协议和其他一些协议的协议组。 TCP/IP协议并不完全符合OSI的七层参考模型,采用了4层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。TCP/IP是OSI的升级版本,osi只是参考,一般用到互联网就是用的tcp/ip,主要的区别在于网际互联与无连接传输。
简单网络管理协议(SNMP)是最早提出的网络管理协议之一,它为网络管理系统提供了底层网络管理的框架,易于实现,是一种无连接协议。SNMP被设计成与协议无关,所以它可以在IP,IPX,OSI以及其他用到的传输协议上被使用。SNMP是一系列协议组和规范,提供了一种从网络上的设备中收集网络管理信息的方法,SNMP也为设备向网络管理工作站报告问题和错误提供了一种方法。SNMP协议定义了数据包的格式,及网络管理员和管理代理之间的信息交换,它还控制着管理代理的MIB(管理信息库)数据对象,使用set,get,trap三种命令。
TCP与UDP的区别:简单的说TCP与UDP的区别是有无连接状态。TCP是有连接状态的,而UDP没有,所以TCP是一种比较安全可靠的通讯协议,而UDP则比较方便。
IP数据包头部是多长:20 bytes
TMN是提供一个有组织的网络结构,以取得各种类型的运行系统之间、运行系统与电信设备之间的互连,是采用商定的具有标准协议和信息的接口进行管理信息交换的体系结构。
24.瀑布模型:瀑布模型将软件生命周期划分为制定计划、需求分析、软件设计、程序编写、软件测试和运行维护等六个基本活动,并且规定了它们自上而下、相互衔接的固定次序,如同瀑布流水,逐级下落。
在瀑布模型中,软件开发的各项活动严格按照线性方式进行,当前活动接受上一项活动的工作结果,实施完成所需的工作内容。当前活动的工作结果需要进行验证,如果验证通过,则该结果作为下一项活动的输入,继续进行下一项活动,否则返回修改。
瀑布模型强调文档的作用,并要求每个阶段都要仔细验证。
但是,这种模型的线性过程太理想化,已不再适合现代的软件开发模式,几乎被业界抛弃,其主要问题在于:
(1) 各个阶段的划分完全固定,阶段之间产生大量的文档,极大地增加了工作量;
(2) 由于开发模型是线性的,用户只有等到整个过程的末期才能见到开发成果,从而增加了开发的风险;
(3) 早期的错误可能要等到开发后期的测试阶段才能发现,进而带来严重的后果。
25、使用assert的缺点是,频繁的调用会极大的影响程序的性能,增加额外的开销。
使用标准C++的类型转换符:static_cast:普通转换,从一个类型到另一个类型;
dynamic_cast:转换类的指针、引用或空指针
const_cast:用于修改const和volatile属性,将常量转为非常量
reinterpret_cast:用于指针、引用、算术类型(指int float)、函数指针或者成员指针的转换
STL:标准模板库(template),其代码从广义上分为三类:algorithm(算法)、container(容器)和iterator(迭代器),几乎所有的代码都采用了模板类和模版函数的方式,这相比于传统的由函数和类组成的库来说提供了更好的代码重用机会,可以能过少量的设置来实现我们所需的特定功能(如对参数类型进行定义)。通过13个头文件来实现,algorithm主要包含<algorithm>,<numeric>和<functional>,主要实现一些特写的算法操作模板;container包含<vector>,<list>等,主要实现经典数据结构外的一些常用的数据结构模板;iterator包含<utility>,<iterator>和<memory>,是algorithm和container之间的一个中间层,所有算法都是通过迭代器来存取元素序列工作的,每一个容器都定义了其本身所专有的迭代器,用以存取容器中的元素。
list是分散存贮的,vector是连续存贮的。如果你要进行插入、删除操作使用list相对较快,而如果访问比较频繁,vector就快些。
DCOM:DCOM(Distributed Component Object Model)是COM的扩展,它支持不同的两台机器上的组件间的通信,而且不论它们是运行在局域网、广域网、还是Internet上。借助DCOM你的应用程序将能够任意进行空间分布。
Subversion:一种开放源码的全新版本控制系统,支持可在本地访问或通过网络访问数据库和文件系统存储库。不但提供了常见的比较、修补、标记、提交、回复和分支功能性,Subversion 还增加了追踪移动和删除的能力。此外,它支持非 ASCII 文本和二进制数据,所有这一切都使 Subversion 不仅对传统的编程任务非常有用,同时也适于 Web 开发、图书创作和其他在传统方式下未采纳版本控制功能的领域。
OpenGL:“Open Graphics Library”,是个专业的3D程序接口,调用方便的专业底层3D图形库。与硬件无关,可以适用各种操作系统平台
Direct3D:基于COM的3D图形API。适合多媒体、娱乐、即时3D动画等广泛和实用的3D图形计算。但它也有缺陷,由于是基于COM接口而较为复杂,稳定性差,只在Windows平台上可用。
26、static作用:1、定义静态变量,其值在程序结束前都不会释放,并限制外部模块的访问(针对于函数和变量的默认声明extern)。用于局部变量中,成为静态局部变量. 静态局部变量有两个用法,记忆功能和全局生存期;2.用于全局变量,主要作用是限制此全局变量被其他的文件调用;3.用于类中的成员,表示这个成员是属于这个类但是不属于类中任意特定对象。分为定义成员变量,则是只有include后的文件可见;定义成员函数,则该函数属于类而不属于对象,屏蔽了this指针,相当于一个全局函数。
记忆功能:int fun() {
static int a = 1;
a++;
} //第一次调用fun后a为2,下一次调用a并不再初始化,而是2++为3。全局变量也有这种记忆功能,即不再被初始化
编译器会关闭static方法的动态绑定,这样就能生成效率稍微高一些的代码。
全局变量指在文件开头就定义的变量,如(static) int a; float f1(a); main(){}这种定义方式的;而在函数体内(不论是main函数还是功能函数),都是局部变量。全局变量(不论是staic还是extern)和静态局部变量都存于全局变量区,直到程序结束才释放。而将全局变量或局部变量声明为静态,则表明只能要本模块(文件)内访问。
在C文件中函数内的变量默认为局部变量,即在函数结束即释放,不能在其他函数内访问;而对于全局变量则可在其他函数中访问。
C中跨文件的调用及C++跨文件的调用:
27、Linux常用命令:
pwd: 当前工作目录
cd: 切换目录
ls: dir
cat: 从屏幕上写入文件或输出文件到屏幕上
touch:新建文件
cp: 复制
mv: 移动文件
rm: 删除文件
shutdown: 关机
reboot: 重启
fdsik -1:查磁盘使用
ps: 进程管理器,查看当前执行的进程的ID号与名称
top: 查看程序执行情况与内存使用,与ps不同的是它实时刷新
kill: 中止进程
data: 设定时间
call: 显示当前的月历或年历
su: 注销并更改用户
useradd: 增加用户
whatis: 查询某个命令的含义
28、如何在CDC对象上画一条线从(x1,y1)到(x2,y2)
Cpoint Point1,Point2;
Point1.x=x1;Point1.y=y1;
Point2.x=x2;Point2.y=y2;
CClientDC dc(this);
dc.MoveTo(Point1);
dc.LineTo(Point2);
数据对齐:访问特定变量时常按照特定的内存地址访问,这就需要各类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,即数据对齐。数据对齐可加快存取效率,如有些硬件平台是从偶数位开始读取,若把一个int型的数据存在奇数位,则要耗费两个周期才能访问到,而数据对齐后只需要一个周期。
对齐的实现:我们写程序的时并不需要考虑对齐问题,编译器会替我们选择适合目标平台的对齐策略。当然,我们也可以通知给编译器传递预编译指令而改变对指定数据的对齐方法。主要要估计struct,类等数据类型所占的内存大小。
char* s="0123567" sizeof(s)=4;
void* str=(void *)malloc(100) sizeof(str)=4; //这种方式定义的永远是指针的大小
char s[]="0123456789"n"; sizeof(s)=12;
char s[100]="abc"; sizeof(s)=100; //这种方式定义的是数组的大小
char s[]; sizeof(s)=4; //当未给数组分配大小(而不是未赋值!)时,又蜕化为指针(int s[]; sizeof(s)=4;)
int s[100]; sizeof(s)=400; //注意数组的类型!
注意:对使用指针定义和使用数组定义字符串的不同,char *str="abcde";sizeof(str)=4(指针大小不一);而char str[] ="abcde";sizeof(str)=7(数组长度)!
对类求sizeof的结果:注意数据是按顺序排列的,只要满足每个数据元素所需要的空间即可,可以组合。数组元素只涉及到它的数据类型。如结构体中有char int两种,则大小为4+4;如有char int[9] double,则大小为4*10+8(为8的整倍数,有组合,跟char与double的前后并无关系);static型的数据不计入sizeof,因为sizeof只计算栈中数据。而对char a[10]="abc",sizeof(a)=10;strlen(a)=3,因为二者的区别就在于strlen只适用于char *型,而且只计算到"0,并不计入"0;而sizeof可适用于多种数据类型。
29、宏定义的优缺点:宏定义用一个单一的指令来代替一组常用的执行语句或某个常用的常量,有利于简化代码书写和代码维护;但其容易产生二义性,如#define MUL(x) (x*x)时代入MUL(10+10)时为10+10*10+10;而且由于其为直接替换而没有类型检查,容易导致程序出错。
SendMessage()直接把消息发送给指定的窗体,等待程序处理消息完了之后才返回,是一个同步消息投放函数,返回值为程序处理消息后的返回值;PostMessage()把消息发给应用程序的消息队列,不管是否被处理即返回,是一个异步消息投放函数,返回值为PostMessage函数是否执行正确。
系统共有8个一类资源,针对该类资源有需求的进程共有3个,则一个进程最多可申请3个资源(8<=3*3<=3*4)。
一个由C/C++编译的程序占用的内存分为以下几个部分:
(1)、栈区(stack):由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。
(2)、堆区(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
(3)、全局区(静态区)(static),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域, 未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 - 程序结束后由系统释放。
(4)、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放 。
(5)、程序代码区—存放函数体的二进制代码。
30、拷贝构造函数中的浅拷贝问题:
struct A{
int x;
int y;
};
A a = {1,2};
A a2(a);
使用A a2(a);可以用对象a的值为对象a2赋值,此时没有显式的定义拷贝构造函数,由系统自动生成,会每一项的自动复制,称为浅拷贝,在没有指针时不会出现错误。使用浅拷贝会可能导致两个对象中某个成员指针指向同一个地址。如当a中有指针时,如char *p=(char *)malloc(256);时,拷贝构造b时也会拷贝一个char *p指针,但是指向一个地址的。从而当我们删除对象a时,对象b中对应指针就变成了悬浮指针。
程序中遇到的问题:最多的是书写中的问题,数据类型转换的问题,头文件包含;比较麻烦的是在移植中产生的问题,如lib库,还有删除ncb文件(无编译浏览文件),用于VC的自动完成功能,删除它则系统可以自动重新建立类图。
程序中的算法类,图像类,点类,光栅类。
31、C++的重中之重:虚函数
(1).函数重载:重新定义一个函数,用不同的参数来区分,在编译器中是通过自动的给两个相同的函数不同的命名,如_voidf;_intf对用户不可见。在不同参数的情况下,可以返回值不同。但是不可以能过返回值来识别重载,因为函数调用是可以忽略返回值的,如
int func() {return 100;}
double func(){return 300.444;} // 假如重载函数func成功,其实VC会报错说只通过返回值重载
int data = (int)func();
这样编辑器很难知道是调用的哪个函数。
(2).重载与覆盖:
父类与子类的同名函数:重载是一个类的多个同名不同参函数之间的操作。如果在A类中定义一个f(int,int);其子类B中定义一个f(int),定义一个B类的对象b,调用b.f(int,int)是会报错的,即子类与父类不存在重载,只可能有覆盖。而在子类中定义一个与父类同名的函数(成员函数覆盖),定义子类对象则调用的是子类的,定义父类的对象则是调用父类的。
父类A,子类B。对A a;B b;a=b;此时用a调用A、B中的同名函数,虚与非虚都是调用父类中的;而对A* a;B b;a=&b;此时用a调用A、B中的同名函数,非虚函数是调用父类中的,只有当虚函数时调用的才是子类B的。
但注意在上面第一种情况中,a中的成员变量会被b中的同名变量赋值。
重载与覆盖的区别:前者存在于一个类中,后者存在于有继承关系的不同类中;前者的参数列表必须不同,是多个函数之间的关系,后者必须相同,针对的是一个函数。
重载与覆盖的相同点:都是对一个同名函数的重新定义。
(3).虚函数的实现:
覆盖:分为成员函数覆盖和虚函数覆盖,后者反应的是多态性,前者则不行。
#include <iostream>
using namespace std;
class A{
public:
void f() {cout<<"A::f()"n";}
virtual void vf() {cout<<"A::vf()"n";}
};
class B: public A{
public:
void f() {cout<<"B::f()"n";}
virtual void vf(){cout<<"B::vf()"n";}
};
void main()
{
A a;
B b;
A* pA=&a;
A* pB=&b;
pA->f(); // 打印 "A::f()"
pA->vf(); // 打印 "A::vf()"
pB->f(); // 打印 "A::f()",成员函数覆盖,不反映多态性,调用的是父类方法
pB->vf(); // 打印 "B::vf()" 虚函数覆盖,反映多态性,调用的是子类的方法
(类具有虚函数列表,对象具有虚函数指针。父类指针pB调用虚函数,找到子类对象对应的虚函数指针,从而得到该类的虚函数列表,找到偏移量,得到要调用的函数地址。)
}
虚函数:用于实现多态性(将子类的指针赋给父类时,父类即可根据当前赋值给它的子对象的特性以不同的方式运作。而继承性是用于创建一个同类事物的家庭,封装性不用多说)。如上例所示,当定义一个A类型指针pB,将其指向子类B的对象(即用于调用子类的特性),不使用虚函数时其输出仍是父类的方法A::f(),而使用虚函数时输出的是子类的方法B::vf(),从而实现对一个父类功能的多态性。
非虚函数:a.f()输出A::f(); b.f()输出B::f()。
虚函数:a.vf()输出A::vf(); b.vf()输出B::vf()
虚函数的实现机制:在每个有虚函数的类中都存有一个虚函数列表,而每个类对象中都会有一个虚函数指针。虚函数列表中包含本类中的所有虚函数地址,当在父类中使用指针调用子类的虚函数时,调用对应的虚函数指针,从而得到对应的子类虚函数列表及对应函数在表中的偏移量,从而得到要调用的函数地址。
(4).使用虚函数要注意的问题:
调用虚函数时增加了额外的访问虚函数列表的开销,因此使用虚函数的效率较低。
若类中含有虚函数,其析构函数也必须为虚。而当父类的析构函数为虚时,子类的析构不论是否加有virtual,其自然为虚。
当父类中的虚函数的返回值为父类的指针或引用时,在子类中定义同名虚函数时必须重写返回值为子类的指针或引用。
31、消息循环与消息泵:消息循环的作用是从应用程序的消息队列中读取消息(::GetMessage,::PeekMessage),然后利用消息泵(CWinThread::PumpMessage)并把它派送出去(::DispatchMessage)。
DC:设备描述表,定义了一组图形对象的属性,如对设备界面的初始化函数,简单绘图函数等;如Moveto,Lineto;
CClientDC:用于客户区的输出,与特定窗口关联,可以让开发者访问目标窗口中客户区;
CWindowDC:定义该对象则设备环境的映射区域为整个窗口,而不仅仅是只可以在客户区域内绘图;
CPaintDC:其对象一般用在OnPaint内以响应Windows消息WM_PAINT,自动完成绘制,在整个窗口内进行重画,维持原有窗口完整性。
GDI:图形设备对象,对位图、刷子和字体等;
进程间的通信方式:信号通信机制; 共享存储区通信机制;共享文件通信机制;消息传递通信机制.
管道是连接读写进程的一个特殊文件,允许进程按先进先出方式传送数据,也能使进程同步执行操作。发送进程以字符流形式把大量数据送入管道,接收进程从管道中接收数据,所以叫管道通信.
管道的实质是一个共享文件,基本上可借助于文件系统的机制实现,包括(管道)文件的创建、打开、关闭和读写.进程对通信机构的使用应该互斥,一个进程正在使用某个管道写入或读出数据时,另一个进程就必须等待.发送者和接收者双方必须能够知道对方是否存在,如果对方已经不存在,就没有必要再发送信息.管道长度有限,发送信息和接收信息之间要实现正确的同步关系,当写进程把一定数量的数据写入管道,就去睡眠等待,直到读进程取走数据后,把它唤醒。
抽象类:至少含有一个纯虚函数的类,用于继承
抽象类与接口的区别:抽象类一般作为公共的父类为子类的扩展提供基础,具有普通的属性;而接口则是一种行为的具体方法。
32、i<j+1;是先算j+1,再算<。关系运算符的优先级低于算术运算符,但高于赋值运算符,如i<j=2,先算<;
cout<<a<<endl,cin>>a;
int a,b;a=126;b=a/10;则b为12,自动去除小数点后位;
float/double b=12/12.0;printf("%8f",b);都会输出12.000000小数点后6位,%8f中的8指的是输出列数,如果数字大于指定值,则突破该值输出,若小于则左空位;%ld是输出长整型数(long)或double;
cout和cin在输入输出时,系统自动匹配数据格式;
当把一个double赋给int时,如果该double位数较小,如12.0,则可在warning状态下赋给int;若本身为12,则无warning;但若是一个存储长度大于int的数,如12.456789e100,则会溢出,显示结果int型变量为0.
数组的长度:不能定义的:int a[]; int n=10,a[n]={1,2};可以定义的方式char a[]={'a','b'};
33、GSM系统(Global System for Mobile Communication)又称全球移动通信系统(全球通)。GSM通信系统主要由移动交换子系统(MSS)、基站子系统(BSS)和移动台(MS)三大部分组成。MSS完成信息交换、用户信息管理、呼叫接续、号码管理;BSS完成信道的分配、用户的接入和寻呼、信息的传送;MS是GSM系统的移动用户设备,它由两部分组成,移动终端和客户识别卡(SIM卡)。移动终端可完成话音编码、信道编码、信息加密、信息的调制和解调、信息发射和接收;SIM卡即智能卡,防止非法客户进入网路,并且存储与网路和客户有关的管理数据。
WCDMA:Wideband Code Division Multiple Access
TD-SCDMA——Time Division-Synchronous Code Division Multiple Access (时分同步的码分多址技术)
当使用char* ptr=(char *)malloc(0);系统为该指针在堆中分配了一个起始地址,但是其大小为0。所以可以使用*p='a';进行赋值,但是最好不要用,因为在使用free(ptr)时会出错。
定义指针后未对其分配地址的使用是错误的,如char *p;*p='a';但是char *str="abc";的操作是许可的。定义char a[]="abc"也是可以的
33、DNS:域名服务器(Domain Name Server),用于将用户输入的DNS 名称名转为IP地址;使用的端口号:53;
解析过程:当在浏览器中输入一个网址时,系统先查本地缓存中是否有相关记录,有则转到对应IP地址;否则转向上一级DNS服务器
TCP或UDP使用多种端口号
FTP:21;TELNET(远程登录):23;SMTP(Simple Mail Transfer Protocol):25;HTTP:80;SNMP:161
DHCP:Dynamic Host Configuration Protocol(动态主机分配协议),用于动态分配IP地址
ICMP:Internet Control Message Protocol(Internet控制报文协议),主要用于在主机与路由器之间传递控制信息,包括报告错误、交换 状态信息等。
IPV4中IP:32位;IPV6中IP;排序:O(n log n); 归并排序:O(n log2 n); 基数排序:O(n log n)
extern void *memcpy(void *dest, void *src, unsigned int count):由src所指内存区域复制count个字节到dest所指内存区域,返回 dest的地址指针;
extern void *memset(void *buffer, int c, int count):把buffer所指内存区域的前count个字节设置成字符c,返回buffer指针值。
高低位互换(2bytes即WORD情况下):b=(a << 8)&0xFF00 | (a >> 8)&0x00FF;
和1求&不变,和0求&均置0;和0求|不变,和1求|均置1。
浙公网安备 33010602011771号