网站备案公告/福建seo外包
1.//要声明一个deque?
deque<string> coll(istream_iterator<string>(cin), istream_iterator<string>());
以上代码中存在一个潜在的问题和一个实际问题。潜在的问题是cin将会被耗尽,因此后面代码不能再从其中读入输入,这很可能会是个逻辑问题。
更大的问题是,这里实际上并没有声明一个名为coll的deque<string>对象,而是声明了一个名为coll的函数,返回值为一个deque<string>,该函数有两个参数;一个是类型为istream_iterator<string>,名为cin的形参;另一个是(无名)参数则是一个:不接受任何参数,并返回一个istream_iterator<string>的函数。
为什么会这样?这是c++标准规定的:如果存在有二义性的声明语法,为了控制事态,编译器在解决这类二义性的时候统一遵循这样的规则:即如果有二义性的话,就当它是一个函数声明。
它相当于:
①、typedef istream_iterator<string> (Func)();
deque<string> coll3(istream_iterator<string> cin, Func);
②、typedef istream_iterator<string> (Func)();
deque<string> f(istream_iterator<string>, Func);
有两个方法可以避免让编译器将其识别为一个函数声明:
①、deque<string> coll((istream_iterator<string>(cin)),istream_iterator<string>());
这里提示编译器我们这里提供的是构造函数的参数而非参数声明,这跟void f((int i)) 不是函数声明是一回事,那对冗余的括号使得这种形式的代码不可能被解释成合法的声明语句。
②、istream_iterator<string> first(cin),last;
deque<string> coll(first,last);
准则:
优先使用具有变量作为构造函数的参数。这样可以避免与声明语句产生二义性。况且这样做也能令你的代码更清晰,更易于维护。
2.请解析下面程序的输出结果
#include <iostream>
#include <iomanip>
int main() {
int x = 1;
for(int i = 0; i < 100; ++i);
// What will the next line do? Increment???????????/
++x;
第一, 考虑for循环这一行:
for(int i= 0;i < 100; ++i);
这一行的结尾有个分号,这个“奇异递归拼写错误模式”。会让for循环的循环体变成空语句。
第二, 考虑注释行。你注意到这一行以一个奇怪的“/”字符结束了吗?
//下面这行代码会干些什么? 递增???????????/
三字符组(trigraph)是指三个字符组成的转义符,比如“??/”=“/”,“??!”= “~”。类似地,还有双字符组,比如“:>”=“]”
3.数组和指针的区别?
[数组和指针的分配]
数组是开辟一块连续的内存空间,数组本身的标示符代表整个数组,可以用sizeof取得真实的大小,指针则是只分配一个指针大小的内存,并可把它的值指向某个有效的内存空间
[空间的分配]
[全局的和静态的]
char *p= "hello ";
一个指针,指向只读数据块(section)里的 "hello ",可被编译器放入字符串池(也就是说,你在写一个char *q= "hello ",可能和p共享数据)
char a[]= "hello ";
一个数组,分配在可写数据块(section),不会被放到字符串池中
[局部]
char *p= "hello ";
一个指针,指向只读数据块(section)里的 "hello ",可被编译器放入字符串池(也就是说, 你在写一个char *q= "hello ",可能和p共享数据),另外,在函数中可以返回它的地址,也就是说,指针是局部变量,他指向的数据却是全局的.
char a[]= "hello ";
一个数组,分配在堆栈上,初始化由编译器进行(短的话直接用指令填充,长的就从全局字符串表拷贝),不会被放到字符串池中(但是却可能从字符串池中拷贝过来),也不应该返回它的地址.
[代码中的字面字符串]
printf( "%s/n ", "hello ");
这两个字面常量( "%s/n "和 "hello "),都在只读数据块里
[用途]
1.全局指针
用于不需要修改内容,却可能会修改指针的情况(当然,不修改也没人反对)
2.全局数组,用于不需要修改地址,却需要修改内容的场合
3.既需要修改指针,有需要修改内容怎么办呢?定义一个数组,在定义一个指针指向它就可以了
4.函数中如果不需要修改字符串的内容,应该尽量用char*p= "xxx "这种写法.初始化的局部字符数组影响效率,一般应该尽量避开(应该使用的情况下则不要犹豫)
以上说的是VC6的情况, 其他编译器也可以据此推测,
4.static有什么用途?(请至少说明两种)
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用
5.全局变量和局部变量在内存中是否有区别?如果有,是什么区别?
全局变量储存在静态数据库,局部变量在堆栈。
6.函数模板与类模板有什么区别?
答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化
必须由程序员在程序中显式地指定。
7.winsock建立连接的主要实现步骤?
答:服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept()
等待客户端连接。
客户端:socker()建立套接字,连接(connect)服务器,连接上后使用send()和recv(
),在套接字上写读数据,直至数据交换完毕,closesocket()关闭套接字。
服务器端:accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连
接。该新产生的套接字使用send()和recv()写读数据,直至数据交换完毕,closesock
et()关闭套接字。
8.动态连接库的两种方式?
答:调用一个DLL中的函数有两种方法:
1.载入时动态链接(load-time dynamic linking),模块非常明确调用某个导出函数
,使得他们就像本地函数一样。这需要链接时链接那些函数所在DLL的导入库,导入库向
系统提供了载入DLL时所需的信息及DLL函数定位。
2.运行时动态链接(run-time dynamic linking),运行时可以通过LoadLibrary或Loa
dLibraryEx函数载入DLL。DLL载入后,模块可以通过调用GetProcAddress获取DLL函数的
出口地址,然后就可以通过返回的函数指针调用DLL函数了。如此即可避免导入库文件了
9.类的sizeof问题
#include <iostream>
using namespace std;
class A
{
double a;
char b;
virtual p(){};
};
void main()
{
cout < <sizeof(A) < <endl;
}
打印出的结果是:24
对于这个问题:首先说明一下不带虚函数时候的情况,其分配规则为:
VC对一些变量的起始地址做了“对齐”处理。在默认情况下,VC规定1.各成员变量存放的起始地址相对于结构的起始地址的偏移量必须为该变量的类型所占用的字节数的倍数。下面列出常用类型的对齐方式(vc6.0,32位系统)。
类型 对齐方式(变量存放的起始地址相对于结构的起始地址的偏移量)
Char 偏移量必须为sizeof(char)即1的倍数
Short 偏移量必须为sizeof(short)即2的倍数
int 偏移量必须为sizeof(int)即4的倍数
float 偏移量必须为sizeof(float)即4的倍数
double 偏移量必须为sizeof(double)即8的倍数
各成员变量在存放的时候根据在结构中出现的顺序依次申请空间,同时按照上面的对齐方式调整位置,空缺的字节VC会自动填充。同时2.VC为了确保结构的大小为结构的字节边界数(即该结构中占用最大空间的类型所占用的字节数)的倍数,所以在为最后一个成员变量申请空间后,还会根据需要自动填充空缺的字节。
===========================
上面红色的部分是两条主要的规则。而绿色的部分是解决这个问题的关键。
代码实例1
#include <iostream>
using namespace std;
class A
{
double a;
char b;
int c;
};
void main()
{
cout< <sizeof(A)< <endl;
}
对于上面这个问题,我们把最后的虚函数改为int ;根据规则1,a占8个字节,b占1个字节,而后c要根据规则1对齐,所以b后面会有3个字节被填充掉,c从b后的第4个字节开始。它占4个,最后结果是:8+(1+3)+4=16。同时符合了规则2。所以为16。
用图表示为:
--
a 8个字节
---
b 一个字节
----
三个填充字节
-----
c 四个字节
---
那么现在请注意上面文字中的绿色部分。我们改变一下顺序。
代码实例2
#include <iostream>
using namespace std;
class A
{
int c;
double a;
char b;
};
void main()
{
cout< <sizeof(A)< <endl;
}
那么开始的c占4个字节,根据规则1,a在分配之前,c和a之间的距离应该为8,即c后面有4个空间需要被填充。对于b,其占一个字节。所以最后是(4+4)+8+1=17;根据规则2,整个类要是8的倍数,所以b后面会再填充7个,即17+7=24。
图示:
----
c 四个字节
----
4个填充
----
a 八个字节
---
b 一个字节
----
七个填充字节
当有虚函数的时候:
#include <iostream>
using namespace std;
class A
{
double a;
char b;
virtual p(){};
};
void main()
{
cout< <sizeof(A)< <endl;
}
比如楼主提供的这段代码。其存储形式类似表示为:(注明:vc6的编译器)
class A
{
[color=#FF0000]void [/color]*p;//虚函数表指针
class {
public:
char b;
double a;
};
};
你可以分析上面的这段代码,按照普通变量的规则 。里面的类为16个字节。最大对齐字节是8。所以p指针的存储空间按照最大的存储,为8。。所以总共为24。
10. #include <stdio.h>
int main(void)
{
int a[5]={1,2,3,4,5};
//假设a的地址为0x80000000,sizeof(int)=4
//假设是little-endian,则0x80000000-0x80000013的数据为:
// addr: 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13
// data: 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 05 00 00 00
int *ptr1=(int *)(&a+1); //ptr1 = 0x80000000+20=0x80000014
int *ptr2=(int *)((int )a+1); //ptr2 = 0x80000000+1 = 0x80000001
//&ptr1[-1] = 0x80000014-4 = 0x80000010,so ptr1[-1] = *(int *)0x80000010 = 5
//*ptr2 = *(int *)0x80000001 = 0x02000000
printf("%x,%x",ptr1[-1],*ptr2);
return 0;
}
所以在little-endian,32位机器上,输出是5,2000000
big-endian,32位机器上,输出是5,100
11.两端代码的区别









































































第一段代码会有问题
两段代码的 CLS 类中都有内存缓冲区引用,区别是第一段中该类实现了拷贝构造,而第二段中却是缺省的。这样在拷贝该类对象时,两段中的代码会有不同的行为:第一段的每个CLS对象会引用一个自己的内存缓冲,而第二段中的所有CLS对象将会引用同一个内存区,在foo()函数返回时该内存就被delete掉了。两段中foo()函数的功能一样,都是用来调用CLS类的拷贝构造函数和拆构函数的
、先说一下拷贝构造函数。拷贝构造函数就是声明为CLS & operator=(const CLS & c)那个函数。差别也就在这里产生,如上代码所示前一个类定义了拷贝构造函数,而后一个类没有定义。这时编译会怎么办,报错吗?不会的!编译会为没有声明拷贝构造函数的类自动生成一个拷贝构造函数。这个由编译器生的拷贝构造函数执行默认的“按位拷贝”动作,也就是前面有人提到的“浅拷贝”:它会把前一个对象的内容原封不动的拷贝第二个对象之中,即两个对象的内容完全相同。但是,问题就在产生了,因为,如果一个对象之中有指针成员的话,那么拷贝的是这个指针本身,而不是指针所指向的内容。这样,就会出现两个指针指向同一个内容(或内存地址)情况。如果,这个指针是动态分配的,而在类的析构函数中对其进行delete调用的话,就会出问题:指针所指的内容可能已经在一个对象的析构函数中已经被delete掉了,而另一个对象还在使用。至于会出现什么情况,那就没办法预料了。
解决方案就是一定要为含有指针成员的类定义拷贝构造函数,防止“浅拷贝”。就如同前一个类的定义那样。
2、从foo函数的声明可以看出,foo函数的参数c采用的是传值调用方式。
void foo(CLS c)
{
}
所谓传值调用就是说在你写下foo(parameter)时,编译器会将实参parameter拷贝一份给CLS c,也就是函数定义时的形式参数。这个拷贝的过程调用就是拷贝构造函数。
对于前一个类的定义来说:调用拷贝构造函数(自己定义的,正确的“深拷贝”函数)以后,c.p与parameter.p指向不同的内容(int变量)。在函数foo的执行过程中这个临时拷贝过来的变量一直有效,但是,在函数调用结束之前,这个临时对象的生命期也要结束了,系统会调用它的析构函数。在其析构函数中delete p;会释放p所指的内容(一个int型变量)。但是这时parameter的生命期还没有结束,只不是parameter.p指的int变量还是存在的。如果之后再对parameter进行操作就不会什么出问题了。
对于后一个类的定义来说:调用拷贝构造函数(编译器加上的,按位拷贝的“浅拷贝”函数)以后,c.p与parameter.p指向同一个内容(int变量)。在函数foo的执行过程中这个临时拷贝过来的变量一直有效,但是,在函数调用结束之前,这个临时对象的生命期也要结束了,系统会调用它的析构函数。在其析构函数中delete p;会释放p所指的内容(一个int型变量)。但是这时parameter的生命期还没有结束,只不过parameter.p指的int变量已经被释放了。如果之后再对parameter进行操作的话恐怕就要出问题了。