1 、 const 有什么用途?(请至少说明两种)
答:( 1 )可以定义 const 常量
( 2 ) const 可以修饰函数的参数、返回值,甚至函数的定义体。被 const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
2 、在 C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C” ?
答: C++ 语言支持函数重载, C 语言不支持函数重载。函数被 C++ 编译后在库中的名字与 C 语言的不同。假设某个函数的原型为: void foo(int x, int y);
该函数被 C 编译器编译后在库中的名字为 _foo , 而 C++ 编译器则会产生像 _foo_int_int 之类的名字。
C++ 提供了 C 连接交换指定符号 extern “ C ”来解决名字匹配问题。
3 、请简述以下两个 for 循环的优缺点( 5 分)
for (i=0; i<N; i++)
{
if (condition)
DoSomething();
else
DoOtherthing();
}
if (condition)
{for (i=0; i<N; i++)
DoSomething();
}
else
{
for (i=0; i<N; i++)
DoOtherthing();
}
优点:程序简洁
缺点:多执行了N-1 次逻辑判断,并且打断了循环“流水线”作业,使得编译器不能对循环进行优化处理,降低了效率。
优点:循环的效率高
缺点:程序不简洁
4 、有关内存的思考题
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) 语句不起作用。
5 、编写 strcpy 函数( 10 分)
已知 strcpy 函数的原型是
char *strcpy(char *strDest, const char *strSrc);
其中 strDest 是目的字符串, strSrc 是源字符串。
( 1 )不调用 C++/C 的字符串库函数,请编写函数 strcpy
char *strcpy(char *strDest, const char *strSrc);
{
assert(( str Dest!=NULL) && ( strSrc !=NULL)); // 2 分
char *address = str Dest; // 2 分
while( (* str Dest++ = * strSrc ++) != ‘ /0 ’ ) // 2 分
NULL ;
return address ; // 2 分
}
5.1 strcpy 能把 strSrc 的内容复制到 strDest ,为什么还要 char * 类型的返回值?
答:为了实现链式表达式。 // 2 分
例如 int length = strlen( strcpy( strDest, “hello world”) );
6 、编写类 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 = ‘ /0 ’ ;
}
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) 释放原有的内存资源 // 3 分
delete [] m_data;
// (3 )分配新的内存资源,并复制内容 // 3 分
int length = strlen(other.m_data);
m_data = new char[length+1]; // 若能加 NULL 判断则更好
strcpy(m_data, other.m_data);
// (4 )返回本对象的引用 // 3 分
return *this;
}
7、 实现双向链表删除一个节点 P ,在节点 P 后插入一个节点,写出这两个函数。
void DeleteNode(DuNode *p)
{
p->prior->next=p->next;
p->next->prior=p->prior;
}
void InsertNode(DuNode *p, DuNode *s)//Node "s" is inserted after "p"
{
s->next=p->next;
p->next->prior=s;
p->next=s;
s->prior=p;
}
8 、 Windows 程序的入口是哪里?写出 Windows 消息机制的流程。
WINDOWS 入口是 WinMain 函数
消息机制的流程:
系统中发生了某个事件
Windows 把这个事件翻译为消息,然后把它放到消息队列中
l 应用程序从消息队列中接收到这个消息,把它存放在 TMsg 记录中
l 应用程序把消息传递给一个适当的窗口的窗口过程
l 窗口过程响应这个消息并进行处理
9 .写一个函数,将其中的 /t 都转换成 4 个空格。
#include<iostream>
using namespace std;
char* Convert_t(char *des,char *src)
{
char *temp;
des=new char[100];
temp=des;
while(*src!='/0')
{
if(*src=='/t')
{
src++;
*des++=' ';
*des++=' ';
*des++=' ';
*des++=' ';
continue;
}
*des++=*src++;
}
*des='/0';
des=temp;
return des;
}
int main()
{
char *t="asdf/tasd/tasasddas//tdfasdf",*d;
cout<<t<<endl;
cout<<Convert_t(d,t);
getchar();
}
10 .如何定义和实现一个类的成员函数为回调函数?
如果类的成员函数是一个 callback 函数, 必须宣告它为 "static" ,才能把 C++ 编译器加诸于函数的一个隐藏参数 this 去掉。
11 . C++ 里面是不是所有的动作都是 main() 引起的?如果不是,请举例。
不是的, C ++里面有些动作不是引起的,比如,全局对象的实例化、全局变量的动态空间申请,等等
下面是一个例子:
#include<iostream>
using namespace std;
char *des=new char[100];
// 全局变量的动态空间申请在程序运行之后, main 运行之前完成。所以不是所有的动作都是 main 引起的。
int main()
{
char *des="abc";
cout<<des<<endl;
getchar();
}
12 . C++ 里面如何声明 const void f(void) 函数为 C 程序中的库函数?
extern "C" const void f(void);
这样声明之后,相当于告诉 C , 函数 const void f(void) 是在 C ++语言的文件中声明或者实现的, c 程序可以使用这个 C ++中的函数了,从而实现 C ++和 c 的混合编程。
13 、编写一个函数,作用是把一个char 组成的字符串循环右移n 个。比如原来是“abcdefghi” 如果n=2 ,移位后应该是 “hiabcdefgh”
正确解答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 ) ) = '/0';
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 );
}
14 、写出输出结果
void fun(char s[10])
{
char a[10];
cout<<"a:"<<sizeof(a)<<endl;
cout<<"s:"<<sizeof(s)<<endl;
}
输出: a:10
s:4
15 、内存的分配方式的分配方式有几种?
答: 一、从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量。
二、在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
三、从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。
16. 是不是一个父类写了一个 virtual 函数,如果子类覆盖它的函数不加 virtual , 也能实现多态 ?
答: virtual 修饰符会被隐形继承的。 private 也被集成,只事派生类没有访问权限而已。 virtual 可加可不加。子类的空间里有父类的所有变量 (static 除外 ) 。同一个函数只存在一个实体 (inline 除外 ) 。子类覆盖它的函数不加 virtual , 也能实现多态。在子类的空间里,有父类的私有变量。私有变量不能直接访问。
17. 进程间通信的方式有 ?
进程间通信的方式有 共享内存, 管道 , Socket ,消息队列 , DDE 等
18.C++ 中什么数据分配在栈或堆中, New 分配数据是在近堆还是远堆中?
答:栈 : 存放局部变量,函数调用参数 , 函数返回值,函数返回地址。由系统管理
堆 : 程序运行时动态申请, new 和 malloc 申请的内存就在堆上
(Google 搜 ) : DOS 下程序是独占方式,堆分为近堆和远堆,近堆和栈是在数据段开辟的同一块内存地址,栈从下往上增长,堆从上向下分配,中间没有规定分界线,所以程序控制不当,如深层次的递归,大量的动态地址分配很容易造成堆栈冲突,即堆栈地址重叠,从而造成死机和程序运行异常。堆和栈连在一起说的原因就是如此。至于远堆则是指在数据段和代码段以外计算机所有没有使用的剩余基本内存。 Windows 采用的是虚拟地址,内存分配方式就不一样了
补充: DOS 下堆栈的分配是由程序而不是操作系统自己控制的,具体分配大小、方式随编译系统、程序模式不同而异。 C 语言的堆栈分配代码在启动代码中 .
19. 全局变量和局部变量在内存中是否有区别?如果有,是什么区别?
全局变量储存在静态数据库,局部变量在堆栈。
20.TCP/IP 建立连接的过程 ?(3-way shake)
答:在 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 状态,完成三次握手。
21.winsock 建立连接的主要实现步骤 ?
答:服务器端: socker() 建立套接字,绑定( bind )并监听( listen ),用 accept ()
等待客户端连接。
客户端: socker() 建立套接字,连接( connect )服务器,连接上后使用 send() 和 recv (
),在套接字上写读数据,直至数据交换完毕, closesocket() 关闭套接字。
服务器端: accept ()发现有客户端连接,建立一个新的套接字,自身重新开始等待连
接。该新产生的套接字使用 send() 和 recv ()写读数据,直至数据交换完毕, closesock
et() 关闭套接字。
22.static 有什么用途?(请至少说明两种)
1) 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用
23. 引用与指针有什么区别?
1) 引用必须被初始化,指针不必。
2) 引用初始化以后不能被改变,指针可以改变所指的对象。
3) 不存在指向空值的引用,但是存在指向空值的指针。
24 、请写出下列代码的输出内容
#include<stdio.h>
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
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]
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/candygun1122/archive/2009/06/10/4256790.aspx