常见面试题

 

题目1-10

1.是不是一个父类写了一个virtual 函数,如果子类覆盖它的函数不加virtual ,也能实现多态?
virtual修饰符会被隐形继承的。private 也被集成,只事派生类没有访问权限而已。virtual可加可不加。子类的空间里有父类的所有变量(static除外)。同一个函数只存在一个实体 (inline除外)。子类覆盖它的函数不加virtual ,也能实现多态。在子类的空间里,有父类的私有变量。私有变量不能直接访问。

--------------------------------------------------------------------------
2.输入一个字符串,将其逆序后输出。(使用C++,不建议用伪码)
#include <iostream>
using namespace std;

void main()
{
char a[50];memset(a,0,sizeof(a));
int i=0,j;
char t;
cin.getline(a,50,'\n');
for(i=0,j=strlen(a)-1;i<strlen(a)/2;i++,j--)
{
   t=a[i];
      a[i]=a[j];
   a[j]=t;
}
cout<<a<<endl;
}
//第二种
string str;
cin>>str;
str.replace;
cout<<str;

--------------------------------------------------------------------------
3.请简单描述Windows内存管理的方法。
内存管理是操作系统中的重要部分,两三句话恐怕谁也说不清楚吧~~
我先说个大概,希望能够抛砖引玉吧
当程序运行时需要从内存中读出这段程序的代码。代码的位置必须在物理内存中才能被运行,由于现在的操作系统中有非常多的程序运行着,内存中不能够完全放下,所以引出了虚拟内存的概念。把哪些不常用的程序片断就放入虚拟内存,当需要用到它的时候在load入主存(物理内存)中。这个就是内存管理所要做的事。内存管理还有另外一件事需要做:计算程序片段在主存中的物理位置,以便CPU调度。
内存管理有块式管理,页式管理,段式和段页式管理。现在常用段页式管理
块式管理:把主存分为一大块、一大块的,当所需的程序片断不在主存时就分配一块主存空间,把程 序片断load入主存,就算所需的程序片度只有几个字节也只能把这一块分配给它。这样会造成很大的浪费,平均浪费了50%的内存空间,但时易于管理。
页式管理:把主存分为一页一页的,每一页的空间要比一块一块的空间小很多,显然这种方法的空间利用率要比块式管理高很多。
段式管理:把主存分为一段一段的,每一段的空间又要比一页一页的空间小很多,这种方法在空间利用率上又比页式管理高很多,但是也有另外一个缺点。一个程序片断可能会被分为几十段,这样很多时间就会被浪费在计算每一段的物理地址上(计算机最耗时间的大家都知道是I/O吧)。
段页式管理:结合了段式管理和页式管理的优点。把主存分为若干页,每一页又分为若干段。好处就很明显,不用我多说了吧。
各种内存管理都有它自己的方法来计算出程序片断在主存中的物理地址,其实都很相似。
这只是一个大概而已,不足以说明内存管理的皮毛。无论哪一本操作系统书上都有详细的讲解

--------------------------------------------------------------------------
4.
#include "stdafx.h"
#define SQR(X) X*X
int main(int argc, char* argv[])
{
int a = 10;
int k = 2;
int m = 1;
a /= SQR(k+m)/SQR(k+m);
printf("%d\n",a);
return 0;
}
这道题目的结果是什么啊?
define 只是定义而已,在编择时只是简单代换X*X而已,并不经过算术法则的
a /= (k+m)*(k+m)/(k+m)*(k+m);
=>a /= (k+m)*1*(k+m);
=>a = a/9;
=>a = 1;
--------------------------------------------------------------------------
5.
const 符号常量;
(1)const char *p
(2)char const *p
(3)char * const p
说明上面三种描述的区别;

如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。
(1)const char *p
一个指向char类型的const对象指针,p不是常量,我们可以修改p的值,使其指向不同的char,但是不能改变它指向非char对象,如:
const char *p;
char c1='a';
char c2='b';
p=&c1;//ok
p=&c2;//ok
*p=c1;//error
(3)char * const p
此时*p可以修改,而p不能修改。
(4)const char * const p
这种是地址及指向对象都不能修改。
--------------------------------------------------------------------------
6.下面是C语言中两种if语句判断方式。请问哪种写法更好?为什么?
int n;
if (n == 10) // 第一种判断方式
if (10 == n) // 第二种判断方式
如果少了个=号,编译时就会报错,减少了出错的可能行,可以检测出是否少了=
--------------------------------------------------------------------------
7.下面的代码有什么问题?
void DoSomeThing(...)
{
char* p;
...
p = malloc(1024); // 分配1K的空间
if (NULL == p)
return;
...
p = realloc(p, 2048); // 空间不够,重新分配到2K
if (NULL == p)
return;
...
}
A:
p = malloc(1024);     应该写成: p = (char *) malloc(1024);
        没有释放p的空间,造成内存泄漏。

--------------------------------------------------------------------------
8.下面的代码有什么问题?并请给出正确的写法。
void DoSomeThing(char* p)
{
char str[16];
int n;
assert(NULL != p);
sscanf(p, "%s%d", str, n);
if (0 == strcmp(str, "something"))
{
...
}
}
A:
sscanf(p, "%s%d", str, n);   这句该写成: sscanf(p, "%s%d", str, &n);
--------------------------------------------------------------------------
9.下面代码有什么错误?
Void test1()
{
char string[10];
char *str1="0123456789";
strcpy(string, str1);
}
数组越界
--------------------------------------------------------------------------
10.下面代码有什么问题?
Void test2()
{
char string[10], str1[10];
for(i=0; i<10;i++)
{
     str1[i] ='a';
}
strcpy(string, str1);
}
数组越界

11.下面代码有什么问题?
Void test3(char* str1)
{
char string[10];
if(strlen(str1)<=10)
{
    strcpy(string, str1);
}
}
==数组越界
==strcpy拷贝的结束标志是查找字符串中的\0 因此如果字符串中没有遇到\0的话 会一直复制,直到遇到\0,上面的123都因此产生越界的情况

建议使用 strncpy 和 memcpy
--------------------------------------------------------------------------
12.下面代码有什么问题?
#define MAX_SRM 256
DSN get_SRM_no()
{
static int SRM_no; //是不是这里没赋初值?
int I;
for(I=0;I<MAX_SRM;I++,SRM_no++)
{
    SRM_no %= MAX_SRM;
    if(MY_SRM.state==IDLE)
    {
      break;
    }
}
if(I>=MAX_SRM)
    return (NULL_SRM);
else
    return SRM_no;
}
系统会初始化static int变量为0,但该值会一直保存,所谓的不可重入...
--------------------------------------------------------------------------
13.写出运行结果:
{// test1
    char str[] = "world"; cout << sizeof(str) << ": ";
    char *p    = str;     cout << sizeof(p) << ": ";
    char i     = 10;      cout << sizeof(i) << ": ";
    void *pp   = malloc(10); cout << sizeof(p) << endl;
}
6:4:1:4
--------------------------------------------------------------------------
14.写出运行结果:
{// test2
    union V {
struct X {
   unsigned char s1:2;
   unsigned char s2:3;
   unsigned char s3:3;
} x;
unsigned char c;
    } v;
    v.c = 100;
    printf("%d", v.x.s3);
}
3
--------------------------------------------------------------------------
15.用C++写个程序,如何判断一个操作系统是16位还是32位的?不能用sizeof()函数
A1:
16位的系统下,
int i = 65536;
cout << i; // 输出0;
int i = 65535;
cout << i; // 输出-1;
32位的系统下,
int i = 65536;
cout << i; // 输出65536;
int i = 65535;
cout << i; // 输出65535;
A2:
int a = ~0;
if( a>65536 )
{
    cout<<"32 bit"<<endl;
}
else
{
    cout<<"16 bit"<<endl;
}

--------------------------------------------------------------------------
16.C和C++有什么不同?
从机制上:c是面向过程的(但c也可以编写面向对象的程序);c++是面向对象的,提供了类。但是,
c++编写面向对象的程序比c容易
从适用的方向:c适合要求代码体积小的,效率高的场合,如嵌入式;c++适合更上层的,复杂的; llinux核心大部分是c写的,因为它是系统软件,效率要求极高。
从名称上也可以看出,c++比c多了+,说明c++是c的超集;那为什么不叫c+而叫c++呢,是因为c++比
c来说扩充的东西太多了,所以就在c后面放上两个+;于是就成了c++
C语言是结构化编程语言,C++是面向对象编程语言。
C++侧重于对象而不是过程,侧重于类的设计而不是逻辑的设计。
--------------------------------------------------------------------------
17.在不用第三方参数的情况下,交换两个参数的值
#include <stdio.h>
void main()
{
        int i=60;
        int j=50;
        i=i+j;
        j=i-j;
        i=i-j;
        printf("i=%d\n",i);
        printf("j=%d\n",j);
}
方法二:
i^=j;
j^=i;
i^=j;
方法三:
// 用加减实现,而且不会溢出
a = a+b-(b=a)
--------------------------------------------------------------------------
18.有关位域的面试题(为什么输出的是一个奇怪的字符)
a.t = 'b';效果相当于 a.t= 'b' & 0xf;
'b' --> 01100010
'b' & 0xf -->>00000010
所以输出Ascii码为2的特殊字符

char t:4;就是4bit的字符变量,同样
unsigned short i:8;就是8bit的无符号短整形变量
--------------------------------------------------------------------------
19.int i=10, j=10, k=3; k*=i+j; k最后的值是?
60
--------------------------------------------------------------------------
20.进程间通信的方式有?
进程间通信的方式有 共享内存, 管道 ,Socket ,消息队列 , DDE等

21.
struct A
{
char t:4;
char k:4;
unsigned short i:8;
unsigned long m;
}
sizeof(A)=?(不考虑边界对齐)
7
struct CELL             // Declare CELL bit field
{
   unsigned character : 8; // 00000000 ????????
   unsigned foreground : 3; // 00000??? 00000000
   unsigned intensity : 1; // 0000?000 00000000
   unsigned background : 3; // 0???0000 00000000
   unsigned blink      : 1; // ?0000000 00000000
} screen[25][80];       // Array of bit fields
二、位结构
    位结构是一种特殊的结构, 在需按位访问一个字节或字的多个位时, 位结构
比按位运算符更加方便。
    位结构定义的一般形式为:
     struct位结构名{
          数据类型 变量名: 整型常数;
          数据类型 变量名: 整型常数;
     } 位结构变量;
    其中: 数据类型必须是int(unsigned或signed)。 整型常数必须是非负的整
数, 范围是0~15, 表示二进制位的个数, 即表示有多少位。
    变量名是选择项, 可以不命名, 这样规定是为了排列需要。
    例如: 下面定义了一个位结构。
     struct{
          unsigned incon: 8; /*incon占用低字节的0~7共8位*/
          unsigned txcolor: 4;/*txcolor占用高字节的0~3位共4位*/
          unsigned bgcolor: 3;/*bgcolor占用高字节的4~6位共3位*/
          unsigned blink: 1; /*blink占用高字节的第7位*/
     }ch;
    位结构成员的访问与结构成员的访问相同。
    例如: 访问上例位结构中的bgcolor成员可写成:
      ch.bgcolor

    注意:
    1. 位结构中的成员可以定义为unsigned, 也可定义为signed, 但当成员长
度为1时, 会被认为是unsigned类型。因为单个位不可能具有符号。
    2. 位结构中的成员不能使用数组和指针, 但位结构变量可以是数组和指针,
如果是指针, 其成员访问方式同结构指针。
    3. 位结构总长度(位数), 是各个位成员定义的位数之和, 可以超过两个字
节。
    4. 位结构成员可以与其它结构成员一起使用。
    例如:
     struct info{
          char name[8];
          int age;
          struct addr address;
          float pay;
          unsigned state: 1;
          unsigned pay: 1;
          }workers;
    上例的结构定义了关于一个工人的信息。其中有两个位结构成员, 每个位结
构成员只有一位, 因此只占一个字节但保存了两个信息, 该字节中第一位表示工
人的状态, 第二位表示工资是否已发放。由此可见使用位结构可以节省存贮空间。

--------------------------------------------------------------------------
22.下面的函数实现在一个固定的数上加上一个数,有什么错误,改正
int add_n(int n)
{
static int i=100;
i+=n;
return i;
}
答:
因为static使得i的值会保留上次的值。
去掉static就可了
--------------------------------------------------------------------------
23.下面的代码有什么问题?
class A
{
public:
A() { p=this; }
~A() { if(p!=NULL) { delete p; p=NULL; } }
A* p;
};
答:
会引起无限递归
--------------------------------------------------------------------------
24.
union a {
int a_int1;
double a_double;
int a_int2;
};
typedef struct
{
a a1;
char y;
} b;
class c
{
double c_double;
b b1;
a a2;
};
输出cout<<sizeof(c)<<endl;的结果?
答:
VC6环境下得出的结果是32
另:
我(sun)在VC6.0+win2k下做过试验:
short - 2
int-4
float-4
double-8
指针-4
sizeof(union),以结构里面size最大的为union的size
----------------------------------------------------------------------------------
25.i最后等于多少?
int i = 1;
int j = i++;
if((i>j++) && (i++ == j)) i+=j;
答:
i = 5
--------------------------------------------------------------------------
26.
unsigned short array[]={1,2,3,4,5,6,7};
int i = 3;
*(array + i) = ?
答:
4
--------------------------------------------------------------------------
27.
class A
{
virtual void func1();
void func2();
}
Class B: class A
{
void func1(){cout << "fun1 in class B" << endl;}
virtual void func2(){cout << "fun2 in class B" << endl;}
}
A, A中的func1和B中的func2都是虚函数.
B, A中的func1和B中的func2都不是虚函数.
C, A中的func2是虚函数.,B中的func1不是虚函数.
D, A中的func2不是虚函数,B中的func1是虚函数.
答:
A
--------------------------------------------------------------------------
28.
数据库:抽出部门,平均工资,要求按部门的字符串顺序排序,不能含有"human resource"部门,
employee结构如下:employee_id, employee_name, depart_id,depart_name,wage
答:
select depart_name, avg(wage)
from employee
where depart_name <> 'human resource'
group by depart_name
order by depart_name
--------------------------------------------------------------------------
29.
给定如下SQL数据库:Test(num INT(4)) 请用一条SQL语句返回num的最小值,但不许使用统计功能,如MIN,MAX等
答:
select top 1 num
from Test
order by num desc
--------------------------------------------------------------------------
30.
输出下面程序结果。
#include <iostream.h>
class A
{
public:
virtual void print(void)
{
    cout<<"A::print()"<<endl;
}
};
class B:public A
{
public:
virtual void print(void)
{
   cout<<"B::print()"<<endl;
};
};
class C:public B
{
public:
virtual void print(void)
{
   cout<<"C::print()"<<endl;
}
};
void print(A a)
{
   a.print();
}
void main(void)
{
   A a, *pa,*pb,*pc;
   B b;
   C c;
 
   pa=&a;
   pb=&b;
   pc=&c;
 
   a.print();
   b.print();
   c.print();
 
   pa->print();
   pb->print();
   pc->print();
 
   print(a);
   print(b);
   print(c);
}
A:
A::print()
B::print()
C::print()
A::print()
B::print()
C::print()
A::print()
A::print()
A::print()

1.求下面函数的返回值(微软)

  int func(x)

  {

  int countx = 0;

  while(x)

  {

  countx ++;

  x = x&(x-1);

  }

  return countx;

  }

  假定x = 9999。 答案:8

  思路:将x转化为2进制,看含有的1的个数。

  2. 什么是“引用”?申明和使用“引用”要注意哪些问题?

  答:引用就是某个目标变量的“别名”(alias),对应用的操作和对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,他只表示该引用名是目标变量名的一个别名,他本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。

  3. 将“引用”作为函数参数有哪些特点?

  (1)传递引用给函数和传递指针的效果是相同的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

  (2)使用引用传递函数的参数,在内存中并没有产生实参的副本,他是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

  (3)使用指针作为函数的参数虽然也能达到和使用引用的效果,不过,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这非常容易产生错误且程式的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

  4. 在什么时候需要使用“常引用”?

  如果既要利用引用提高程式的效率,又要保护传递给函数的数据不在函数中被改动,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名;

  例1

  int a ;

  const int &ra=a;

  ra=1; //错误

  a=1; //正确

  例2

  string foo( );

  void bar(string & s);

  那么下面的表达式将是非法的:

  bar(foo( ));

  bar("hello world");

  原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。

  引用型参数应该在能被定义为const的情况下,尽量定义为const 。

  5. 将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?

  格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }

  好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error!

  注意事项:

  (1)不能返回局部变量的引用。这条能参照Effective C++[1]的Item 31。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程式会进入未知状态。

  (2)不能返回函数内部new分配的内存的引用。这条能参照Effective C++[1]的Item 31。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其他尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。

  (3)能返回类成员的引用,但最佳是const。这条原则能参照Effective C++[1]的Item 30。主要原因是当对象的属性是和某种业务规则(business rule)相关联的时候,其赋值常常和某些其他属性或对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其他对象能获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

  (4)流操作符重载返回值申明为“引用”的作用:

  流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << "hello" << endl;

  因此这两个操作符的返回值应该是个仍然支持这两个操作符的流引用。可选的其他方案包括:返回一个流对象和返回一个流对象指针。不过对于返回一个流对象,程式必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择非常关键,他说明了引用的重要性及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。赋值操作符=。这个操作符象流操作符相同,是能连续使用的,例如:x = j = 10;或(x=10)=100;赋值操作符的返回值必须是个左值,以便能被继续赋值。因此引用成了这个操作符的惟一返回值选择。

  例3

  #i nclude <iostream.h>

  int &put(int n);

  int vals[10];

  int error=-1;

  void main()

  {

  put(0)=10; //以put(0)函数值作为左值,等价于vals[0]=10;

  put(9)=20; //以put(9)函数值作为左值,等价于vals[9]=20;

  cout<<vals[0];

  cout<<vals[9];

  }

  int &put(int n)

  {

  if (n>=0 && n<=9 ) return vals[n];

  else { cout<<"subscript error"; return error; }

  }

  (5)在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。他们不能返回引用,Effective C++[1]的Item23周详的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,他们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。

  6. “引用”和多态的关系?

  引用是除指针外另一个能产生多态效果的手段。这意味着,一个基类的引用能指向他的派生类实例。

  例4

  Class A; Class B : Class A{...}; B b; A& ref = b;

  7. “引用”和指针的差别是什么?

  指针通过某个指针变量指向一个对象后,对他所指向的变量间接操作。程式中使用指针,程式的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。此外,就是上面提到的对函数传ref和pointer的差别。

  8. 什么时候需要“引用”?

  流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其他情况都推荐使用引用。

  以上 2-8 参考:http://blog.csdn.net/wfwd/archive/2006/05/30/763551.aspx

  9. 结构和联合有和差别?

  1. 结构和联合都是由多个不同的数据类型成员组成, 但在所有同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。

  2. 对于联合的不同成员赋值, 将会对其他成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。

  10. 下面关于“联合”的题目的输出?

  a)

  #i nclude <stdio.h>

  union

  {

  int i;

  char x[2];

  }a;

  void main()

  {

  a.x[0] = 10;

  a.x[1] = 1;

  printf("%d",a.i);

  }

  答案:266 (低位低地址,高位高地址,内存占用情况是Ox010A)

  b)

  main()

  {

  union{ /*定义一个联合*/

  int i;

  struct{ /*在联合中定义一个结构*/

  char first;

  char second;

  }half;

  }number;

  number.i=0x4241; /*联合成员赋值*/

  printf("%c%c\n", number.half.first, mumber.half.second);

  number.half.first=’a’; /*联合中结构成员赋值*/

  number.half.second=’b’;

  printf("%x\n", number.i);

  getch();

  }

  答案: AB (0x41对应’A’,是低位;Ox42对应’B’,是高位)

  6261 (number.i和number.half共用一块地址空间)

  11. 已知strcpy的函数原型:char *strcpy(char *strDest, const char *strSrc)其中strDest 是目的字符串,strSrc 是源字符串。不调用C++/C 的字符串库函数,请编写函数 strcpy。

  答案:

  char *strcpy(char *strDest, const char *strSrc)

  {

  if ( strDest == NULL || strSrc == NULL)

  return NULL ;

  if ( strDest == strSrc)

  return strDest ;

  char *tempptr = strDest ;

  while( (*strDest++ = *strSrc++) != ‘\0’)

  ;

  return tempptr ;

  }

  12. 已知String类定义如下:

  class String

  {

  public:

  String(const char *str = NULL); // 通用构造函数

  String(const String &another); // 拷贝构造函数

  ~ String(); // 析构函数

  String & operater =(const String &rhs); // 赋值函数

  private:

  char *m_data; // 用于保存字符串

  };

  尝试写出类的成员函数实现。

  答案:

  String::String(const char *str)

  {

  if ( str == NULL ) //strlen在参数为NULL时会抛异常才会有这步判断

  {

  m_data = new char[1] ;

  m_data[0] = ’\0’ ;

  }

  else

  {

  m_data = new char[strlen(str) + 1];

  strcpy(m_data,str);

  }

  }

  String::String(const String &another)

  {

  m_data = new char[strlen(another.m_data) + 1];

  strcpy(m_data,other.m_data);

  }

  String& String::operator =(const String &rhs)

  {

  if ( this == &rhs)

  return *this ;

  delete []m_data; //删除原来的数据,新开一块内存

  m_data = new char[strlen(rhs.m_data) + 1];

  strcpy(m_data,rhs.m_data);

  return *this ;

  }

  String::~String()

  {

  delete []m_data ;

  }

  13. .h头文件中的ifndef/define/endif 的作用?

  答:防止该头文件被重复引用。

  14. #i nclude<file.h> 和 #i nclude "file.h"的差别?

  答:前者是从Standard Library的路径寻找和引用file.h,而后者是从当前工作路径搜寻并引用file.h。

  15.在C++ 程式中调用被C 编译器编译后的函数,为什么要加extern “C”?

  首先,作为extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量能在本模块或其他模块中使用。

  通常,在模块的头文件中对本模块提供给其他模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然未找到该函数,不过并不会报错;他会在连接阶段中从模块A编译生成的目标代码中找到此函数

  extern "C"是连接申明(linkage declaration),被extern "C"修饰的变量和函数是按照C语言方式编译和连接的,来看看C++中对类似C的函数是怎样编译的:

  作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字和C语言的不同。例如,假设某个函数的原型为:

  void foo( int x, int y );

  该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,不过都采用了相同的机制,生成的新名字称为“mangled name”)。

  _foo_int_int 这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )和void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。

  同样地,C++中的变量除支持局部变量外,还支持类成员变量和全局变量。用户所编写程式的类成员变量可能和全局变量同名,我们以"."来区分。而本质上,编译器在进行编译时,和函数的处理相似,也为类中的变量取了一个独一无二的名字,这个名字和用户程式中同名的全局变量名字不同。

  未加extern "C"声明时的连接方式

  假设在C++中,模块A的头文件如下:

  // 模块A头文件

  moduleA.h

  #ifndef MODULE_A_H

  #define MODULE_A_H

  int foo( int x, int y );

  #endif

  在模块B中引用该函数:

  // 模块B实现文件

  moduleB.cpp

  #i nclude "moduleA.h"

  foo(2,3);

  实际上,在连接阶段,连接器会从模块A生成的目标文件moduleA.obj中寻找_foo_int_int这样的符号!

  加extern "C"声明后的编译和连接方式

  加extern "C"声明后,模块A的头文件变为:

  // 模块A头文件

  moduleA.h

  #ifndef MODULE_A_H

  #define MODULE_A_H

  extern "C" int foo( int x, int y );

  #endif

  在模块B的实现文件中仍然调用foo( 2,3 ),其结果是:

  (1)模块A编译生成foo的目标代码时,没有对其名字进行特别处理,采用了C语言的方式;

  (2)连接器在为模块B的目标代码寻找foo(2,3)调用时,寻找的是未经修改的符号名_foo。

  如果在模块A中函数声明了foo为extern "C"类型,而模块B中包含的是extern int foo( int x, int y ) ,则模块B未找到模块A中的函数;反之亦然。

  所以,能用一句话概括extern “C”这个声明的真实目的(所有语言中的所有语法特性的诞生都不是随意而为的,来源于真实世界的需求驱动。我们在思考问题时,不能只停留在这个语言是怎么做的,还要问一问他为什么要这么做,动机是什么,这样我们能更深入地理解许多问题):实现C++和C及其他语言的混合编程。

  明白了C++中extern "C"的设立动机,我们下面来具体分析extern "C"通常的使用技巧:

  extern "C"的惯用法

  (1)在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:

  extern "C"

  {

  #i nclude "cExample.h"

  }

  而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。

  C++引用C函数例子工程中包含的三个文件的原始码如下:

  /* c语言头文件:cExample.h */

  #ifndef C_EXAMPLE_H

  #define C_EXAMPLE_H

  extern int add(int x,int y);

  #endif

  /* c语言实现文件:cExample.c */

  #i nclude "cExample.h"

  int add( int x, int y )

  {

  return x + y;

  }

  // c++实现文件,调用add:cppFile.cpp

  extern "C"

  {

  #i nclude "cExample.h"

  }

  int main(int argc, char* argv[])

  {

  add(2,3);

  return 0;

  }

  如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C" {

  }。

  (2)在C中引用C++语言中的函数和变量时,C++的头文件需添加extern "C",不过在C语言中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型。

  C引用C++函数例子工程中包含的三个文件的原始码如下:

  //C++头文件 cppExample.h

  #ifndef CPP_EXAMPLE_H

  #define CPP_EXAMPLE_H

  extern "C" int add( int x, int y );

  #endif

  //C++实现文件 cppExample.cpp

  #i nclude "cppExample.h"

  int add( int x, int y )

  {

  return x + y;

  }

  /* C实现文件 cFile.c

  /* 这样会编译出错:#i nclude "cExample.h" */

  extern int add( int x, int y );

  int main( int argc, char* argv[] )

  {

  add( 2, 3 );

  return 0;

  }

  15题目的解答请参考《C++中extern “C”含义深层探索》注解:

  16. 关联、聚合(Aggregation)及组合(Composition)的差别?

  涉及到UML中的一些概念:关联是表示两个类的一般性联系,比如“学生”和“老师”就是一种关联关系;聚合表示has-a的关系,是一种相对松散的关系,聚合类不必对被聚合类负责,如下图所示,用空的菱形表示聚合关系:

  从实现的角度讲,聚合能表示为:

  class A {...} class B { A* a; .....}

  而组合表示contains-a的关系,关联性强于聚合:组合类和被组合类有相同的生命周期,组合类要对被组合类负责,采用实心的菱形表示组合关系:

  实现的形式是:

  class A{...} class B{ A a; ...}

  参考文章:http://blog.csdn.net/wfwd/archive/2006/05/30/763753.aspx

  http://blog.csdn.net/wfwd/archive/2006/05/30/763760.aspx

  17.面向对象的三个基本特征,并简单叙述之?

  1. 封装:将客观事物抽象成类,每个类对自身的数据和方法实行protection(private, protected,public)

  2. 继承:广义的继承有三种实现形式:实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。前两种(类继承)和后一种(对象组合=>接口继承及纯虚函数)构成了功能复用的两种方式。

  3. 多态:是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就能根据当前赋值给他的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

  18. 重载(overload)和重写(overried,有的书也叫做“覆盖”)的差别?

  常考的题目。从定义上来说:

  重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

  重写:是指子类重新定义复类虚函数的方法。

  从实现原理上来说:

  重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已确定了,是静态的。也就是说,他们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!

  重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给他的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。

  19. 多态的作用?

  主要是两个:1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;2. 接口重用:为了类在继承和派生的时候,确保使用家族中任一类的实例的某一属性时的正确调用。

  20. Ado和Ado.net的相同和不同?

  除了“能够让应用程式处理存储于DBMS 中的数据“这一基本相似点外,两者没有太多一起之处。不过Ado使用OLE DB 接口并基于微软的COM 技术,而ADO.NET 拥有自己的ADO.NET 接口并且基于微软的.NET 体系架构。众所周知.NET 体系不同于COM 体系,ADO.NET 接口也就完全不同于ADO和OLE DB 接口,这也就是说ADO.NET 和ADO是两种数据访问方式。ADO.net 提供对XML 的支持。

  21. New delete 和malloc free 的联系和差别?

  答案:都是在堆(heap)上进行动态的内存操作。用malloc函数需要指定内存分配的字节数并且不能初始化对象,new 会自动调用对象的构造函数。delete 会调用对象的destructor,而free 不会调用对象的destructor.

  22. #define DOUBLE(x) x+x ,i = 5*DOUBLE(5); i 是多少?

  答案:i 为30。

  23. 有哪几种情况只能用intialization list 而不能用assignment?

  答案:当类中含有const、reference 成员变量;基类的构造函数都需要初始化表。

  24. C++是不是类型安全的?

  答案:不是。两个不同类型的指针之间能强制转换(用reinterpret cast)。C#是类型安全的。

  25. main 函数执行以前,还会执行什么代码?

  答案:全局对象的构造函数会在main 函数之前执行。

  26. 描述内存分配方式及他们的差别?

  1) 从静态存储区域分配。内存在程式编译的时候就已分配好,这块内存在程式的整个运行期间都存在。例如全局变量,static 变量。

  2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都能在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。

  3) 从堆上分配,亦称动态内存分配。程式在运行的时候用malloc 或new 申请任意多少的内存,程式员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程式员决定,使用非常灵活,但问题也最多。

  27.struct 和 class 的差别

  答案:struct 的成员默认是公有的,而类的成员默认是私有的。struct 和 class 在其他方面是功能相当的。

  从感情上讲,大多数的研发者感到类和结构有非常大的差别。感觉上结构仅仅象一堆缺乏封装和功能的开放的内存位,而类就象活的并且可靠的社会成员,他有智能服务,有牢固的封装屏障和一个良好定义的接口。既然大多数人都这么认为,那么只有在你的类有非常少的方法并且有公有数据(这种事情在良好设计的系统中是存在的!)时,你也许应该使用 struct 关键字,否则,你应该使用 class 关键字。

  28.当一个类A 中没有生命所有成员变量和成员函数,这时sizeof(A)的值是多少,如果不是零,请解释一下编译器为什么没有让他为零。(Autodesk)

  答案:肯定不是零。举个反例,如果是零的话,声明一个class A[10]对象数组,而每一个对象占用的空间是零,这时就没办法区分A[0],A[1]…了。

  29. 在8086 汇编下,逻辑地址和物理地址是怎样转换的?(Intel)

  答案:通用寄存器给出的地址,是段内偏移地址,相应段寄存器地址*10H+通用寄存器内地址,就得到了真正要访问的地址。

  30. 比较C++中的4种类型转换方式?

  请参考:http://blog.csdn.net/wfwd/archive/2006/05/30/763785.aspx,重点是static_cast, dynamic_cast和reinterpret_cast的差别和应用。

  31.分别写出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)

  32.请说出const和#define 相比,有何好处?

  答案:1) const 常量有数据类型,而宏常量没有数据类型。编译器能对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

  2) 有些集成化的调试工具能对const 常量进行调试,不过不能对宏常量进行调试。

  33.简述数组和指针的差别?

  数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针能随时指向任意类型的内存块。

  (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 字节

  }

  34.类成员函数的重载、覆盖和隐藏差别?

  答案:

  a.成员函数被重载的特征:

  (1)相同的范围(在同一个类中);

  (2)函数名字相同;

  (3)参数不同;

  (4)virtual 关键字可有可无。

  b.覆盖是指派生类函数覆盖基类函数,特征是:

  (1)不同的范围(分别位于派生类和基类);

  (2)函数名字相同;

  (3)参数相同;

  (4)基类函数必须有virtual 关键字。

  c.“隐藏”是指派生类的函数屏蔽了和其同名的基类函数,规则如下:

  (1)如果派生类的函数和基类的函数同名,不过参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别和重载混淆)。

  (2)如果派生类的函数和基类的函数同名,并且参数也相同,不过基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别和覆盖混淆)

  35. There are two int variables: a and b, don’t use “if”, “? :”, “switch”or other judgement statements, find out the biggest one of the two numbers.

  答案:( ( a + b ) + abs( a - b ) ) / 2

  36. 怎么打印出当前源文件的文件名及源文件的当前行号?

  答案:

  cout << __FILE__ ;

  cout<<__LINE__ ;

  __FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。

  37. main 主函数执行完毕后,是否可能会再执行一段代码,给出说明?

  答案:能,能用_onexit 注册一个函数,他会在main 之后执行int fn1(void), fn2(void), fn3(void), fn4 (void);

  void main( void )

  {

  String str("zhanglin");

  _onexit( fn1 );

  _onexit( fn2 );

  _onexit( fn3 );

  _onexit( fn4 );

  printf( "This is executed first.\n" );

  }

  int fn1()

  {

  printf( "next.\n" );

  return 0;

  }

  int fn2()

  {

  printf( "executed " );

  return 0;

  }

  int fn3()

  {

  printf( "is " );

  return 0;

  }

  int fn4()

  {

  printf( "This " );

  return 0;

  }

  The _onexit function is passed the address of a function (func) to be called when the program terminates normally. Successive calls to _onexit create a register of functions that are executed in LIFO (last-in-first-out) order. The functions passed to _onexit cannot take parameters.

  38. 怎么判断一段程式是由C 编译程式还是由C++编译程式编译的?

  答案:

  #ifdef __cplusplus

  cout<<"c++";

  #else

  cout<<"c";

  #endif

  39.文件中有一组整数,需求排序后输出到另一个文件中

  答案:

  #i nclude<iostream>

  #i nclude<fstream>

  using namespace std;

  void Order(vector<int>& data) //bubble sort

  {

  int count = data.size() ;

  int tag = false ; // 设置是否需要继续冒泡的标志位

  for ( int i = 0 ; i < count ; i++)

  {

  for ( int j = 0 ; j < count - i - 1 ; j++)

  {

  if ( data[j] > data[j+1])

  {

  tag = true ;

  int temp = data[j] ;

  data[j] = data[j+1] ;

  data[j+1] = temp ;

  }

  }

  if ( !tag )

  break ;

  }

  }

  void main( void )

  {

  vector<int>data;

  ifstream in("c:\\data.txt");

  if ( !in)

  {

  cout<<"file error!";

  exit(1);

  }

  int temp;

  while (!in.eof())

  {

  in>>temp;

  data.push_back(temp);

  }

  in.close(); //关闭输入文件流

  Order(data);

  ofstream out("c:\\result.txt");

  if ( !out)

  {

  cout<<"file error!";

  exit(1);

  }

  for ( i = 0 ; i < data.size() ; i++)

  out<<data[i]<<" ";

  out.close(); //关闭输出文件流

  }

  40. 链表题:一个链表的结点结构

  struct Node

  {

  int data ;

  Node *next ;

  };

  typedef struct Node Node ;

  (1)已知链表的头结点head,写一个函数把这个链表逆序 ( Intel)

  Node * ReverseList(Node *head) //链表逆序

  {

  if ( head == NULL || head->next == NULL )

  return head;

  Node *p1 = head ;

  Node *p2 = p1->next ;

  Node *p3 = p2->next ;

  p1->next = NULL ;

  while ( p3 != NULL )

  {

  p2->next = p1 ;

  p1 = p2 ;

  p2 = p3 ;

  p3 = p3->next ;

  }

  p2->next = p1 ;

  head = p2 ;

  return head ;

  }

  (2)已知两个链表head1 和head2 各自有序,请把他们合并成一个链表依然有序。(保留所有结点,即便大小相同)

  Node * Merge(Node *head1 , Node *head2)

  {

  if ( head1 == NULL)

  return head2 ;

  if ( head2 == NULL)

  return head1 ;

  Node *head = NULL ;

  Node *p1 = NULL;

  Node *p2 = NULL;

  if ( head1->data < head2->data )

  {

  head = head1 ;

  p1 = head1->next;

  p2 = head2 ;

  }

  else

  {

  head = head2 ;

  p2 = head2->next ;

  p1 = head1 ;

  }

  Node *pcurrent = head ;

  while ( p1 != NULL && p2 != NULL)

  {

  if ( p1->data <= p2->data )

  {

  pcurrent->next = p1 ;

  pcurrent = p1 ;

  p1 = p1->next ;

  }

  else

  {

  pcurrent->next = p2 ;

  pcurrent = p2 ;

  p2 = p2->next ;

  }

  }

  if ( p1 != NULL )

  pcurrent->next = p1 ;

  if ( p2 != NULL )

  pcurrent->next = p2 ;

  return head ;

  }

  (3)已知两个链表head1 和head2 各自有序,请把他们合并成一个链表依然有序,这次需求用递归方法进行。 (Autodesk)

  答案:

  Node * MergeRecursive(Node *head1 , Node *head2)

  {

  if ( head1 == NULL )

  return head2 ;

  if ( head2 == NULL)

  return head1 ;

  Node *head = NULL ;

  if ( head1->data < head2->data )

  {

  head = head1 ;

  head->next = MergeRecursive(head1->next,head2);

  }

  else

  {

  head = head2 ;

  head->next = MergeRecursive(head1,head2->next);

  }

  return head ;

  }

  41. 分析一下这段程式的输出 (Autodesk)

  class B

  {

  public:

  B()

  {

  cout<<"default constructor"<<endl;

  }

  ~B()

  {

  cout<<"destructed"<<endl;

  }

  B(int i):data(i) //B(int) works as a converter ( int -> instance of B)

  {

  cout<<"constructed by parameter " << data <<endl;

  }

  private:

  int data;

  };

  B Play( B b)

  {

  return b ;

  }

  (1) results:

  int main(int argc, char* argv[]) constructed by parameter 5

  { destructed B(5)形参析构

  B t1 = Play(5); B t2 = Play(t1);

  destructed t1形参析构

  return 0;

  destructed t2

  注意顺序!

  } destructed t1

  (2) results:

  int main(int argc, char* argv[]) constructed by parameter 5

  { destructed B(5)形参析构

  B t1 = Play(5); B t2 = Play(10);

  constructed by parameter 10

  return 0;

  destructed B(10)形参析构

  } destructed t2

  注意顺序!

  destructed t1

  42. 写一个函数找出一个整数数组中,第二大的数 (microsoft)

  答案:

  const int MINNUMBER = -32767 ;

  int find_sec_max( int data[] , int count)

  {

  int maxnumber = data[0] ;

  int sec_max = MINNUMBER ;

  for ( int i = 1 ; i < count ; i++)

  {

  if ( data[i] > maxnumber )

  {

  sec_max = maxnumber ;

  maxnumber = data[i] ;

  }

  else

  {

  if ( data[i] > sec_max )

  sec_max = data[i] ;

  }

  }

  return sec_max ;

  }

  43. 写一个在一个字符串(n)中寻找一个子串(m)第一个位置的函数。

  KMP算法效率最佳,时间复杂度是O(n+m)。

  44. 多重继承的内存分配问题:

  比如有class A : public class B, public class C {}

  那么A的内存结构大致是怎么样的?

  这个是compiler-dependent的, 不同的实现其细节可能不同。

  如果不考虑有虚函数、虚继承的话就相当简单;否则的话,相当复杂。

  能参考《深入探索C++对象模型》,或:

  http://blog.csdn.net/wfwd/archive/2006/05/30/763797.aspx

  45. 怎么判断一个单链表是有环的?(注意不能用标志位,最多只能用两个额外指针)

  struct node { char val; node* next;}

  bool check(const node* head) {} //return false : 无环;true: 有环

  一种O(n)的办法就是(搞两个指针,一个每次递增一步,一个每次递增两步,如果有环的话两者必然重合,反之亦然):

  bool check(const node* head)

  {

  if(head==NULL) return false;

  node *low=head, *fast=head->next;

  while(fast!=NULL && fast->next!=NULL)

  {

  low=low->next;

  fast=fast->next->next;

  if(low==fast) return true;

  }

  return false;

  }

1.已知strcpy 函数的原型是:

char *strcpy(char *strDest, const char *strSrc);

其中strDest 是目的字符串,strSrc 是源字符串。不调用C++/C 的字符串库函数,请编写函数 strcpy

答案:

char *strcpy(char *strDest, const char *strSrc)

{

if ( strDest == NULL || strSrc == NULL)

return NULL ;

if ( strDest == strSrc)

return strDest ;

char *tempptr = strDest ;

while( (*strDest++ = *strSrc++) != ‘’)

;

return tempptr ;

}

2.已知类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(const char *str)

{

if ( str == NULL ) //strlen在参数为NULL时会抛异常才会有这步判断

{

m_data = new char[1] ;

m_data[0] = '' ;

}

else

{

m_data = new char[strlen(str) + 1];

strcpy(m_data,str);

}

}

String::String(const String &other)

{

m_data = new char[strlen(other.m_data) + 1];

strcpy(m_data,other.m_data);

}

String & String::operator =(const String &other)

{

if ( this == &other)

return *this ;

delete []m_data;

m_data = new char[strlen(other.m_data) + 1];

strcpy(m_data,other.m_data);

return *this ;

}

String::~ String(void)

{

delete []m_data ;

}

3.简答

3.1 头文件中的ifndef/define/endif 干什么用?

答:防止该头文件被重复引用。

3.2#i nclude <filename.h> 和#i nclude “filename.h” 有什么区别?

答:对于#i nclude <filename.h> ,编译器从标准库路径开始搜索filename.h

对于#i nclude “filename.h”,编译器从用户的工作路径开始搜索filename.h

3.3 在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.4 一个类有基类、内部有一个其他类的成员对象,构造函数的执行顺序是怎样的。(Autodesk)

答:先执行基类的(如果基类当中有虚基类,要先执行虚基类的,其他基类则按照声明派生类时的顺序依次执行),再执行成员对象的,最后执行自己的。

3.5 请描述一个你熟悉的设计模式(Autodesk)

3.6 在UML 中,聚合(aggregation)和组合(composition)有什么区别 Autodesk)

答案:聚合关系更强,类似于pages 和book 的关系;组合关系要弱,类似于books和bookshelf 的关系。

3.7C#和C++除了语法上的差别以外,有什么不同的地方?(Autodesk,Microsoft)

答案:(C#我只是了解,不是很精通)

(1) c#有垃圾自动回收机制,程序员不用担心对象的回收。(2)c#严禁使用指针,只能处理对象。如果希望使用指针,则仅可在unsafe 程序块中能使用指针。(3)c#只能单继承。(4)必须通过类名访问静态成员。不能像C++中那样,通过对象访问静态成员。(5)在子类中覆盖父类的虚函数时必须用关键字override,覆盖父类的方法要用关键字new

3.8ADO.net 和ADO 的区别?

答案:实际上除了“能够让应用程序处理存储于DBMS 中的数据“这一基本相似点外,两者没有太多共同之处。但是ADO 使用OLE DB 接口并基于微软的COM 技术,而ADO.NET 拥有自己的ADO.NET 接口并且基于微软的.NET 体系架构。众所周知.NET 体系不同于COM 体系,ADO.NET 接口也就完全不同于ADO和OLE DB 接口,这也就是说ADO.NET 和ADO是两种数据访问方式。ADO.net 提供对XML 的支持。

3.9 New delete 与malloc free 的区别 ( Autodesk)

答案:用malloc 函数不能初始化对象,new 会调用对象的构造函数。Delete 会调用对象的destructor,而free 不会调用对象的destructor.

3.10 #define DOUBLE(x) x+x (Autodesk)

i = 5*DOUBLE(10); i 是多少?正确的声明是什么?

答案:i 为60。正确的声明是#define DOUBLE(x) (x+x)

3.11 有哪几种情况只能用intialization list 而不能用assignment? (Autodesk)

答案:当类中含有const、reference 成员变量;基类的构造函数都需要参数;类中含有其他类的成员对象,而该类的构造函数都需要参数。

3.11 C++是不是类型安全的? (Autodesk)

答案:不是。两个不同类型的指针之间可以强制转换。C#是类型安全的。

3.12 main 函数执行以前,还会执行什么代码? (Autodesk)

答案:全局对象的构造函数会在main 函数之前执行。

3.13 描述内存分配方式以及它们的区别。 (Autodesk , Microsoft)

答案:1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。

(2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。

(3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

3.14 什么是虚拟存储器?virtual memory 怎样映射到physical memory?页面替换算法有哪些? (Microsoft)

见操作系统 p238 页。掌握的页面替换算法NRU,FIFO,第二次机会页面替换算法,LRU

3.15 有四个同样的容器,里面装满了粒数相同的药丸,正常药丸的质量为m,变质药丸的质量为m+1,现在已知这四个容器中,有一个装的全是变质药丸,用电子秤只称一次,找出哪个容器装的是变质药丸 (Microsoft)

答案:把四个容器依次编号为1、2、3、4,然后从中分别取出1、2、3、4 粒药丸,称这10 粒药丸的质量,如果质量为10m+1,则说明第一个容器装的是变质药丸,如果为10m+2 则说明第二个装的变质药丸,依次类推。

3.16 比较一下C++中static_cast 和 dynamic_cast 的区别。 (Autodesk)

dynamic_casts在帮助你浏览继承层次上是有限制的。它不能被用于缺乏虚函数的类型上,它被用于安全地沿着类的继承关系向下进行类型转换。如你想在没有继承关系的类型中进行转换,你可能想到static_cast

3.17 Struct 和class 的区别 (Autodesk)

答案:struct 中成员变量和成员函数默认访问权限是public,class 是private

3.18 当一个类A 中没有生命任何成员变量与成员函数,这时sizeof(A)的值是多少,如果不是零,请解释一下编译器为什么没有让它为零。(Autodesk)

答案:肯定不是零。我举个反例,如果是零的话,声明一个class A[10]对象数组,而每一个对象占用的空间是零,这时就没办法区分A[0],A[1]…了

3.19 在8086 汇编下,逻辑地址和物理地址是怎样转换的?(Intel)

答案:通用寄存器给出的地址,是段内偏移地址,相应段寄存器地址*10H+通用寄存器内地址,就得到了真正要访问的地址。

3.20 描述一下C++的多态 (microsoft)

答案:C++的多态表现在两个部分,一个是静态连编下的函数重载,运算符重载;动态连编下的虚函数、纯虚函数(抽象类)

4.写出BOOL,int,float,指针类型的变量a 与零的比较语句。

答案:

BOOL : if ( !a )

int : if ( a == 0)

float : const EXPRESSION EXP = 0.000001

if ( a < EXP && a >-EXP)

pointer : if ( a != NULL)

5.请说出const 与#define 相比优点

答案:

(1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

(2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

6.简述数组与指针的区别

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

(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 字节

}

7.类成员函数的重载、覆盖和隐藏区别

答案:

成员函数被重载的特征:

(1)相同的范围(在同一个类中);

(2)函数名字相同;

(3)参数不同;

(4)virtual 关键字可有可无。

覆盖是指派生类函数覆盖基类函数,特征是:

(1)不同的范围(分别位于派生类与基类);

(2)函数名字相同;

(3)参数相同;

(4)基类函数必须有virtual 关键字。

“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

8.There are two int variables: a and b, don’t use “if”, “? :”, “switch”

or other judgement statements, find out the biggest one of the two

numbers.

答案:( ( a + b ) + abs( a – b ) ) / 2

9.如何打印出当前源文件的文件名以及源文件的当前行号?

答案:

cout << __FILE__ ;

cout<<__LINE__ ;

__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。

10.main 主函数执行完毕后,是否可能会再执行一段代码,给出说明?

答案:可以,可以用_onexit 注册一个函数,它会在main 之后执行int fn1(void), fn2(void), fn3(void), fn4 (void);

void main( void )

{

String str("zhanglin");

_onexit( fn1 );

_onexit( fn2 );

_onexit( fn3 );

_onexit( fn4 );

printf( "This is executed first.\n" );

}

int fn1()

{

printf( "next.\n" );

return 0;

}

int fn2()

{

printf( "executed " );

return 0;

}

int fn3()

{

printf( "is " );

return 0;

}

int fn4()

{

printf( "This " );

return 0;

}

The _onexit function is passed the address of a function (func) to be called when the program terminates normally. Successive calls to _onexit create a register of functions that are executed in LIFO (last-in-first-out) order. The functions passed to _onexit cannot take parameters.

11.如何判断一段程序是由C 编译程序还是由C++编译程序编译的?

答案:

#ifdef __cplusplus

cout<<"c++";

#else

cout<<"c";

#endif

12.文件中有一组整数,要求排序后输出到另一个文件中

答案:

void Order(vector<int> &data) //起泡排序

{

int count = data.size() ;

int tag = false ;

for ( int i = 0 ; i < count ; i++)

{

for ( int j = 0 ; j < count - i - 1 ; j++)

{

if ( data[j] > data[j+1])

{

tag = true ;

int temp = data[j] ;

data[j] = data[j+1] ;

data[j+1] = temp ;

}

}

if ( !tag )

break ;

}

}

void main( void )

{

vector<int>data;

ifstream in("c:\\data.txt");

if ( !in)

{

cout<<"file error!";

exit(1);

}

int temp;

while (!in.eof())

{

in>>temp;

data.push_back(temp);

}

in.close();

Order(data);

ofstream out("c:\\result.txt");

if ( !out)

{

cout<<"file error!";

exit(1);

}

for ( i = 0 ; i < data.size() ; i++)

out<<data[i]<<" ";

out.close();

}

13.排序方法比较 (intel)

排序方法 平均时间 最坏时间 辅助存储

直接插入排序

起泡排序

快速排序

简单选择排序

堆排序

归并排序

基数排序

14.一个链表的结点结构

struct Node

{

int data ;

Node *next ;

};

typedef struct Node Node ;

(1)已知链表的头结点head,写一个函数把这个链表逆序 ( Intel)

Node * ReverseList(Node *head) //链表逆序

{

if ( head == NULL || head->next == NULL )

return head;

Node *p1 = head ;

Node *p2 = p1->next ;

Node *p3 = p2->next ;

p1->next = NULL ;

while ( p3 != NULL )

{

p2->next = p1 ;

p1 = p2 ;

p2 = p3 ;

p3 = p3->next ;

}

p2->next = p1 ;

head = p2 ;

return head ;

}

(2)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序。

Node * Merge(Node *head1 , Node *head2)

{

if ( head1 == NULL)

return head2 ;

if ( head2 == NULL)

return head1 ;

Node *head = NULL ;

Node *p1 = NULL;

Node *p2 = NULL;

if ( head1->data < head2->data )

{

head = head1 ;

p1 = head1->next;

p2 = head2 ;

}

else

{

head = head2 ;

p2 = head2->next ;

p1 = head1 ;

}

Node *pcurrent = head ;

while ( p1 != NULL && p2 != NULL)

{

if ( p1->data <= p2->data )

{

pcurrent->next = p1 ;

pcurrent = p1 ;

p1 = p1->next ;

}

else

{

pcurrent->next = p2 ;

pcurrent = p2 ;

p2 = p2->next ;

}

}

if ( p1 != NULL )

pcurrent->next = p1 ;

if ( p2 != NULL )

pcurrent->next = p2 ;

return head ;

}

(2)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序,这次要求用递归方法进行。 ( Autodesk)

答案:

Node * MergeRecursive(Node *head1 , Node *head2)

{

if ( head1 == NULL )

return head2 ;

if ( head2 == NULL)

return head1 ;

Node *head = NULL ;

if ( head1->data < head2->data )

{

head = head1 ;

head->next = MergeRecursive(head1->next,head2);

}

else

{

head = head2 ;

head->next = MergeRecursive(head1,head2->next);

}

return head ;

}

15.分析一下这段程序的输出 (Autodesk)

class B

{

public:

B()

{

cout<<"default constructor"<<endl;

}

~B()

{

cout<<"destructed"<<endl;

}

B(int i):data(i)

{

cout<<"constructed by parameter" << data <<endl;

}

private:

int data;

};

B Play( B b)

{

return b ;

}

int main(int argc, char* argv[])

{

B temp = Play(5);

return 0;

}

请自己执行一下看看。

16.写一个函数找出一个整数数组中,第二大的数 (microsoft)

答案:

const int MINNUMBER = -32767 ;

int find_sec_max( int data[] , int count) //类似于1 4 4 4这样的序列将认为1是第二大数

{

int maxnumber = data[0] ;

int sec_max = MINNUMBER ;

for ( int i = 1 ; i < count ; i++)

{

if ( data[i] > maxnumber )

{

sec_max = maxnumber ;

maxnumber = data[i] ;

}

else

{

if ( data[i] > sec_max )

sec_max = data[i] ;

}

}

return sec_max ;

}

17 写一个在一个字符串中寻找一个子串第一个位置的函数

这个题目的一般算法比较简单我就不给出了,如果要求高效率的话请参见数据结构中的KMP 算法,不过在笔试时间有限情况下,写出那个算法还是挺难的。

英文题目

1. Introduce yourself in English

2. What is your great advantage you think of yourself?

3. What is your drawback you think of yourself?

Maybe I will feel very tense if I make a speech in front of a lot of people.

4. How do you feel shanghai?

16. 关联、聚合(Aggregation)以及组合(Composition)的区别?

涉及到UML中的一些概念:关联是表示两个类的一般性联系,比如“学生”和“老师”就是一种关联关系;聚合表示has-a的关系,是一种相对松散的关系,聚合类不需要对被聚合类负责,如下图所示,用空的菱形表示聚合关系:

 

                           500){this.resized=true;this.style.width=500;}" border=0>

从实现的角度讲,聚合可以表示为:

class A {...}  class B { A* a; .....}

而组合表示contains-a的关系,关联性强于聚合:组合类与被组合类有相同的生命周期,组合类要对被组合类负责,采用实心的菱形表示组合关系:

                            500){this.resized=true;this.style.width=500;}" border=0>

实现的形式是:

class A{...} class B{ A a; ...}

参考文章:http://www.cnitblog.com/Lily/archive/2006/02/23/6860.html

          http://www.vckbase.com/document/viewdoc/?id=422

17.面向对象的三个基本特征,并简单叙述之?

1. 封装:将客观事物抽象成类,每个类对自身的数据和方法实行protection(private, protected,public)

2. 继承:广义的继承有三种实现形式:实现继承(指使用基类的属性和方法而无需额外编码的能力)、可视继承(子窗体使用父窗体的外观和实现代码)、接口继承(仅使用属性和方法,实现滞后到子类实现)。前两种(类继承)和后一种(对象组合=>接口继承以及纯虚函数)构成了功能复用的两种方式。

3. 多态:是将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

18. 重载(overload)和重写(overried,有的书也叫做“覆盖”)的区别?

常考的题目。从定义上来说:

重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。

重写:是指子类重新定义复类虚函数的方法。

从实现原理上来说:

重载:编译器根据函数不同的参数表,对同名函数的名称做修饰,然后这些同名函数就成了不同的函数(至少对于编译器来说是这样的)。如,有两个同名函数:function func(p:integer):integer;和function func(p:string):integer;。那么编译器做过修饰后的函数名称可能是这样的:int_func、str_func。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!

重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。

19. 多态的作用?

主要是两个:1. 隐藏实现细节,使得代码能够模块化;扩展代码模块,实现代码重用;2. 接口重用:为了类在继承和派生的时候,保证使用家族中任一类的实例的某一属性时的正确调用。

20. Ado与Ado.net的相同与不同?

除了“能够让应用程序处理存储于DBMS 中的数据“这一基本相似点外,两者没有太多共同之处。但是Ado使用OLE DB 接口并基于微软的COM 技术,而ADO.NET 拥有自己的ADO.NET 接口并且基于微软的.NET 体系架构。众所周知.NET 体系不同于COM 体系,ADO.NET 接口也就完全不同于ADO和OLE DB 接口,这也就是说ADO.NET 和ADO是两种数据访问方式。ADO.net 提供对XML 的支持。

1. New delete 与malloc free 的联系与区别?

答案:都是在堆(heap)上进行动态的内存操作。用malloc函数需要指定内存分配的字节数并且不能初始化对象,new 会自动调用对象的构造函数。delete 会调用对象的destructor,而free 不会调用对象的destructor.

22. #define DOUBLE(x) x+x ,i = 5*DOUBLE(5); i 是多少?

答案:i 为30。

23. 有哪几种情况只能用intialization list 而不能用assignment?

答案:当类中含有const、reference 成员变量;基类的构造函数都需要初始化表。

24. C++是不是类型安全的?

答案:不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)。C#是类型安全的。

25. main 函数执行以前,还会执行什么代码?

答案:全局对象的构造函数会在main 函数之前执行。

26. 描述内存分配方式以及它们的区别?

1) 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static 变量。

2) 在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集。

3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc 或new 申请任意多少的内存,程序员自己负责在何时用free 或delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活,但问题也最多。

27.struct 和 class 的区别

答案:struct 的成员默认是公有的,而类的成员默认是私有的。struct 和 class 在其他方面是功能相当的。

从感情上讲,大多数的开发者感到类和结构有很大的差别。感觉上结构仅仅象一堆缺乏封装和功能的开放的内存位,而类就象活的并且可靠的社会成员,它有智能服务,有牢固的封装屏障和一个良好定义的接口。既然大多数人都这么认为,那么只有在你的类有很少的方法并且有公有数据(这种事情在良好设计的系统中是存在的!)时,你也许应该使用 struct 关键字,否则,你应该使用 class 关键字。

28.当一个类A 中没有生命任何成员变量与成员函数,这时sizeof(A)的值是多少,如果不是零,请解释一下编译器为什么没有让它为零。(Autodesk)

答案:肯定不是零。举个反例,如果是零的话,声明一个class A[10]对象数组,而每一个对象占用的空间是零,这时就没办法区分A[0],A[1]…了。

29. 在8086 汇编下,逻辑地址和物理地址是怎样转换的?(Intel)

答案:通用寄存器给出的地址,是段内偏移地址,相应段寄存器地址*10H+通用寄存器内地址,就得到了真正要访问的地址。

30. 比较C++中的4种类型转换方式?

请参考:http://blog.bioon.com/user1/8688/archives/2006/45399.shtml,重点是static_cast, dynamic_cast和reinterpret_cast的区别和应用。

31.分别写出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)

 

32.请说出const与#define 相比,有何优点?

答案:1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

      2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

33.简述数组与指针的区别?

数组要么在静态存储区被创建(如全局数组),要么在栈上被创建。指针可以随时指向任意类型的内存块。

(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 字节

}

34.类成员函数的重载、覆盖和隐藏区别?

答案:

a.成员函数被重载的特征:

(1)相同的范围(在同一个类中);

(2)函数名字相同;

(3)参数不同;

(4)virtual 关键字可有可无。

b.覆盖是指派生类函数覆盖基类函数,特征是:

(1)不同的范围(分别位于派生类与基类);

(2)函数名字相同;

(3)参数相同;

(4)基类函数必须有virtual 关键字。

c.“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下:

(1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)

35. There are two int variables: a and b, don’t use “if”, “? :”, “switch”or other judgement statements, find out the biggest one of the two numbers.

答案:( ( a + b ) + abs( a - b ) ) / 2

36. 如何打印出当前源文件的文件名以及源文件的当前行号?

答案:

cout << __FILE__ ;

cout<<__LINE__ ;

__FILE__和__LINE__是系统预定义宏,这种宏并不是在某个文件中定义的,而是由编译器定义的。

37. main 主函数执行完毕后,是否可能会再执行一段代码,给出说明?

答案:可以,可以用_onexit 注册一个函数,它会在main 之后执行int fn1(void), fn2(void), fn3(void), fn4 (void);

void main( void )

{

String str("zhanglin");

_onexit( fn1 );

_onexit( fn2 );

_onexit( fn3 );

_onexit( fn4 );

printf( "This is executed first.\n" );

}

int fn1()

{

printf( "next.\n" );

return 0;

}

int fn2()

{

printf( "executed " );

return 0;

}

int fn3()

{

printf( "is " );

return 0;

}

int fn4()

{

printf( "This " );

return 0;

}

The _onexit function is passed the address of a function (func) to be called when the program terminates normally. Successive calls to _onexit create a register of functions that are executed in LIFO (last-in-first-out) order. The functions passed to _onexit cannot take parameters.

38. 如何判断一段程序是由C 编译程序还是由C++编译程序编译的?

答案:

#ifdef __cplusplus

cout<<"c++";

#else

cout<<"c";

#endif

39.文件中有一组整数,要求排序后输出到另一个文件中

答案:

#i nclude<iostream>

#i nclude<fstream>

using namespace std;

void Order(vector<int>& data) //bubble sort

{

int count = data.size() ;

int tag = false ; // 设置是否需要继续冒泡的标志位

for ( int i = 0 ; i < count ; i++)

{

for ( int j = 0 ; j < count - i - 1 ; j++)

{

if ( data[j] > data[j+1])

{

tag = true ;

int temp = data[j] ;

data[j] = data[j+1] ;

data[j+1] = temp ;

}

}

if ( !tag )

break ;

}

}

void main( void )

{

vector<int>data;

ifstream in("c:\\data.txt");

if ( !in)

{

cout<<"file error!";

exit(1);

}

int temp;

while (!in.eof())

{

in>>temp;

data.push_back(temp);

}

in.close(); //关闭输入文件流

Order(data);

ofstream out("c:\\result.txt");

if ( !out)

{

cout<<"file error!";

exit(1);

}

for ( i = 0 ; i < data.size() ; i++)

out<<data[i]<<" ";

out.close(); //关闭输出文件流

}

 

40. 链表题:一个链表的结点结构

struct Node

{

int data ;

Node *next ;

};

typedef struct Node Node ;

(1)已知链表的头结点head,写一个函数把这个链表逆序 ( Intel)

Node * ReverseList(Node *head) //链表逆序

{

if ( head == NULL || head->next == NULL )

return head;

Node *p1 = head ;

Node *p2 = p1->next ;

Node *p3 = p2->next ;

p1->next = NULL ;

while ( p3 != NULL )

{

p2->next = p1 ;

p1 = p2 ;

p2 = p3 ;

p3 = p3->next ;

}

p2->next = p1 ;

head = p2 ;

return head ;

}

(2)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序。(保留所有结点,即便大小相同)

Node * Merge(Node *head1 , Node *head2)

{

if ( head1 == NULL)

return head2 ;

if ( head2 == NULL)

return head1 ;

Node *head = NULL ;

Node *p1 = NULL;

Node *p2 = NULL;

if ( head1->data < head2->data )

{

head = head1 ;

p1 = head1->next;

p2 = head2 ;

}

else

{

head = head2 ;

p2 = head2->next ;

p1 = head1 ;

}

Node *pcurrent = head ;

while ( p1 != NULL && p2 != NULL)

{

if ( p1->data <= p2->data )

{

pcurrent->next = p1 ;

pcurrent = p1 ;

p1 = p1->next ;

}

else

{

pcurrent->next = p2 ;

pcurrent = p2 ;

p2 = p2->next ;

}

}

if ( p1 != NULL )

pcurrent->next = p1 ;

if ( p2 != NULL )

pcurrent->next = p2 ;

return head ;

}

(3)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序,这次要求用递归方法进行。 (Autodesk)

答案:

Node * MergeRecursive(Node *head1 , Node *head2)

{

if ( head1 == NULL )

return head2 ;

if ( head2 == NULL)

return head1 ;

Node *head = NULL ;

if ( head1->data < head2->data )

1.写出判断ABCD四个表达式的是否正确, 若正确, 写出经过表达式中 a的值(3分)

int a = 4;

(A)a += (a++); (B) a += (++a) ;(C) (a++) += a;(D) (++a) += (a++);

a = ?

答:C错误,左侧不是一个有效变量,不能赋值,可改为(++a) += a;

改后答案依次为9,10,10,11

2.某32位系统下, C++程序,请计算sizeof 的值(5分).

char str[] = “www.ibegroup.com”

char *p = str ;

int n = 10;

请计算

sizeof (str ) = ?(1)

sizeof ( p ) = ?(2)

sizeof ( n ) = ?(3)

void Foo ( char str[100]){

请计算

sizeof( str ) = ?(4)

}

void *p = malloc( 100 );

请计算

sizeof ( p ) = ?(5)

答:(1)17 (2)4 (3) 4 (4)4 (5)4

3. 回答下面的问题. (4分)

(1).头文件中的 ifndef/define/endif 干什么用?预处理

答:防止头文件被重复引用

(2). #i nclude 和 #i nclude “filename.h” 有什么区别?

答:前者用来包含开发环境提供的库头文件,后者用来包含自己编写的头文件。

(3).在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明?

答:函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern "C"修饰的变

量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调

用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。

(4). switch()中不允许的数据类型是?

答:实型

4. 回答下面的问题(6分)

(1).Void GetMemory(char **p, int num){

*p = (char *)malloc(num);

}

void Test(void){

char *str = NULL;

GetMemory(&str, 100);

strcpy(str, "hello");

printf(str);

}

请问运行Test 函数会有什么样的结果?

答:输出“hello”

(2). void Test(void){

char *str = (char *) malloc(100);

strcpy(str, “hello”);

free(str);

if(str != NULL){

strcpy(str, “world”);

printf(str);

}

}

请问运行Test 函数会有什么样的结果?

答:输出“world”

(3). char *GetMemory(void){

char p[] = "hello world";

return p;

}

void Test(void){

char *str = NULL;

str = GetMemory();

printf(str);

}

请问运行Test 函数会有什么样的结果?

答:无效的指针,输出不确定

5. 编写strcat函数(6分)

已知strcat函数的原型是char *strcat (char *strDest, const char *strSrc);

其中strDest 是目的字符串,strSrc 是源字符串。

(1)不调用C++/C 的字符串库函数,请编写函数 strcat

答:

VC源码:

char * __cdecl strcat (char * dst, const char * src)

{

char * cp = dst;

while( *cp )

cp++; /* find end of dst */

while( *cp++ = *src++ ) ; /* Copy src to end of dst */

return( dst ); /* return dst */

}

(2)strcat能把strSrc 的内容连接到strDest,为什么还要char * 类型的返回值?

答:方便赋值给其他变量

6.MFC中CString是类型安全类么?

答:不是,其它数据类型转换到CString可以使用CString的成员函数Format来转换

7.C++中为什么用模板类。

答:(1)可用来创建动态增长和减小的数据结构

(2)它是类型无关的,因此具有很高的可复用性。

(3)它在编译时而不是运行时检查数据类型,保证了类型安全

(4)它是平台无关的,可移植性

(5)可用于基本数据类型

8.CSingleLock是干什么的。

答:同步多个线程对一个数据类的同时访问

9.NEWTEXTMETRIC 是什么。

答:物理字体结构,用来设置字体的高宽大小

10.程序什么时候应该使用线程,什么时候单线程效率高。

答:1.耗时的操作使用线程,提高应用程序响应

2.并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求。

3.多CPU系统中,使用线程提高CPU利用率

4.改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独

立的运行部分,这样的程序会利于理解和修改。

其他情况都使用单线程。

11.Windows是内核级线程么。

答:见下一题

12.Linux有内核级线程么。

答:线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两

种类型:“用户级线程”和“内核级线程”。 用户线程指不需要内核支持而在用户程序

中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度

和管理线程的函数来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现

,但线程的调度需要用户程序完成,这有些类似 Windows 3.x 的协作式多任务。另外一

种则需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部

需求进行创建和撤销,这两种模型各有其好处和缺点。用户线程不需要额外的内核开支

,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线

程因 I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不

到运行的机会;而内核线程则没有各个限制,有利于发挥多处理器的并发优势,但却占

用了更多的系统开支。

Windows NT和OS/2支持内核线程。Linux 支持内核级的多线程

13.C++中什么数据分配在栈或堆中,New分配数据是在近堆还是远堆中?

答:栈: 存放局部变量,函数调用参数,函数返回值,函数返回地址。由系统管理

堆: 程序运行时动态申请,new 和 malloc申请的内存就在堆上

14.使用线程是如何防止出现大的波峰。

答:意思是如何防止同时产生大量的线程,方法是使用线程池,线程池具有可以同时提

高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队

等候。

15函数模板与类模板有什么区别?

答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化

必须由程序员在程序中显式地指定。

16一般数据库若出现日志满了,会出现什么情况,是否还能使用?

答:只能执行查询等读操作,不能执行更改,备份等写操作,原因是任何写操作都要记

录日志。也就是说基本上处于不能使用的状态。

17 SQL Server是否支持行级锁,有什么好处?

答:支持,设立封锁机制主要是为了对并发操作进行控制,对干扰进行封锁,保证数据

的一致性和准确性,行级封锁确保在用户取得被更新的行到该行进行更新这段时间内不

被其它用户所修改。因而行级锁即可保证数据的一致性又能提高数据操作的迸发性。

18如果数据库满了会出现什么情况,是否还能使用?

答:见16

19 关于内存对齐的问题以及sizof()的输出

答:编译器自动对齐的原因:为了提高程序的性能,数据结构(尤其是栈)应该尽可能

地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问

;然而,对齐的内存访问仅需要一次访问。

20 int i=10, j=10, k=3; k*=i+j; k最后的值是?

答:60,此题考察优先级,实际写成: k*=(i+j);,赋值运算符优先级最低

21.对数据库的一张表进行操作,同时要对另一张表进行操作,如何实现?

答:将操作多个表的操作放入到事务中进行处理

22.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状态,完成三次握手。

23.ICMP是什么协议,处于哪一层?

答:Internet控制报文协议,处于网络层(IP层)

24.触发器怎么工作的?

答:触发器主要是通过事件进行触发而被执行的,当对某一表进行诸如UPDATE、 INSERT

、 DELETE 这些操作时,数据库就会自动执行触发器所定义的SQL 语句,从而确保对数

据的处理必须符合由这些SQL 语句所定义的规则。

25.winsock建立连接的主要实现步骤?

答:服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept()

等待客户端连接。

客户端:socker()建立套接字,连接(connect)服务器,连接上后使用send()和recv(

),在套接字上写读数据,直至数据交换完毕,closesocket()关闭套接字。

服务器端:accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连

接。该新产生的套接字使用send()和recv()写读数据,直至数据交换完毕,closesock

et()关闭套接字。

26.动态连接库的两种方式?

答:调用一个DLL中的函数有两种方法:

1.载入时动态链接(load-time dynamic linking),模块非常明确调用某个导出函数

,使得他们就像本地函数一样。这需要链接时链接那些函数所在DLL的导入库,导入库向

系统提供了载入DLL时所需的信息及DLL函数定位。

2.运行时动态链接(run-time dynamic linking),运行时可以通过LoadLibrary或Loa

dLibraryEx函数载入DLL。DLL载入后,模块可以通过调用GetProcAddress获取DLL函数的

出口地址,然后就可以通过返回的函数指针调用DLL函数了。如此即可避免导入库文件了

27.IP组播有那些好处?

答:Internet上产生的许多新的应用,特别是高带宽的多媒体应用,带来了带宽的急剧

消耗和网络拥挤问题。组播是一种允许一个或多个发送者(组播源)发送单一的数据包

到多个接收者(一次的,同时的)的网络技术。组播可以大大的节省网络带宽,因为无

论有多少个目标地址,在整个网络的任何一条链路上只传送单一的数据包。所以说组播

技术的核心就是针对如何节约网络资源的前提下保证服务质量。

发布之前先申明两点:
    1 所有资料来自网络(主要是CSDN),本人只是收集和转发。
    2 所有问题解答(尤其是代码)只是参考,不保证正确。

先发基本问题,再发编程问题..........

想成为嵌入式程序员应知道的0x10个基本问题:
预处理器(Preprocessor)

1 . 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
         #define SECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
我在这想看到几件事情:
1) #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)
2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒而不是计算出实际的值,是更清晰而没有代价的。
3) 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
4) 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。

2 . 写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个。
        #define MIN(A,B) ((A) <= (B) ? (A) : (B))
这个测试是为下面的目的而设的:
1) 标识#define在宏中应用的基本知识。这是很重要的。因为在  嵌入(inline)操作符 变为标准C的一部分之前,宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入代码经常是必须的方法。
2)三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比if-then-else更优化的代码,了解这个用法是很重要的。
3) 懂得在宏中小心地把参数用括号括起来
4) 我也用这个问题开始讨论宏的副作用,例如:当你写下面的代码时会发生什么事?
        least = MIN(*p++, b);

3. 预处理器标识#error的目的是什么?
如果你不知道答案,请看参考文献1。这问题对区分一个正常的伙计和一个书呆子是很有用的。只有书呆子才会读C语言课本的附录去找出象这种问题的答案。当然如果你不是在找一个书呆子,那么应试者最好希望自己不要知道答案。


死循环(Infinite loops)

4. 嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?
这个问题用几个解决方案。我首选的方案是:

while(1)
{

}

一些程序员更喜欢如下方案:

for(;;)
{

}

这个实现方式让我为难,因为这个语法没有确切表达到底怎么回事。如果一个应试者给出这个作为方案,我将用这个作为一个机会去探究他们这样做的基本原理。如果他们的基本答案是:"我被教着这样做,但从没有想到过为什么。"这会给我留下一个坏印象。

第三个方案是用 goto
Loop:
...
goto Loop;
应试者如给出上面的方案,这说明或者他是一个汇编语言程序员(这也许是好事)或者他是一个想进入新领域的BASIC/FORTRAN程序员。


数据声明(Data declarations)

5. 用变量a给出下面的定义
a) 一个整型数(An integer)
b)一个指向整型数的指针( A pointer to an integer)
c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an intege)r
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

人们经常声称这里有几个问题是那种要翻一下书才能回答的问题,我同意这种说法。当我写这篇文章时,为了确定语法的正确性,我的确查了一下书。但是当我被面试的时候,我期望被问到这个问题(或者相近的问题)。因为在被面试的这段时间里,我确定我知道这个问题的答案。应试者如果不知道所有的答案(或至少大部分答案),那么也就没有为这次面试做准备,如果该面试者没有为这次面试做准备,那么他又能为什么出准备呢?

Static

6. 关键字static的作用是什么?
这个简单的问题很少有人能回答完全。在C语言中,关键字static有三个明显的作用:
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

大多数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能懂得第三部分。这是一个应试者的严重的缺点,因为他显然不懂得本地化数据和代码范围的好处和重要性。

Const

7.关键字const有什么含意?
我只要一听到被面试者说:"const意味着常数",我就知道我正在和一个业余者打交道。去年Dan Saks已经在他的文章里完全概括了const的所有用法,因此ESP(译者:Embedded Systems Programming)的每一位读者应该非常熟悉const能做什么和不能做什么.如果你从没有读到那篇文章,只要能说出const意味着"只读"就可以了。尽管这个答案不是完全的答案,但我接受它作为一个正确的答案。(如果你想知道更详细的答案,仔细读一下Saks的文章吧。)
如果应试者能正确回答这个问题,我将问他一个附加的问题:
下面的声明都是什么意思?

const int a;
int const a;
const int *a;
int * const a;
int const * a const;

/******/
前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。如果应试者能正确回答这些问题,那么他就给我留下了一个好印象。顺带提一句,也许你可能会问,即使不用关键字 const,也还是能很容易写出功能正确的程序,那么我为什么还要如此看重关键字const呢?我也如下的几下理由:
1) 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)
2) 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
3) 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。


Volatile

8. 关键字volatile有什么含意?并给出三个不同的例子。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1) 并行设备的硬件寄存器(如:状态寄存器)
2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3) 多线程应用中被几个任务共享的变量

回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。
假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
1)一个参数既可以是const还可以是volatile吗?解释为什么。
2); 一个指针可以是volatile 吗?解释为什么。
3); 下面的函数有什么错误:

int square(volatile int *ptr)
{
        return *ptr * *ptr;
}

下面是答案:
1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2); 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3) 这段代码有点变态。这段代码的目的是用来返指针*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;
}

位操作(Bit manipulation)

9. 嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量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;
}

一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。我希望看到几个要点:说明常数、|=和&=~操作。


访问固定的内存位置(Accessing fixed memory locations)

10. 嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。
这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下:
    int *ptr;
    ptr = (int *)0x67a9;
    *ptr = 0xaa55;

 A more obscure approach is:
一个较晦涩的方法是:

    *(int * const)(0x67a9) = 0xaa55;

即使你的品味更接近第二种方案,但我建议你在面试时使用第一种方案。

中断(Interrupts)

11. 中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准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()经常有重入和性能上的问题。如果你丢掉了第三和第四点,我不会太为难你的。不用说,如果你能得到后两点,那么你的被雇用前景越来越光明了。


代码例子(Code examples)

12 . 下面的代码输出是什么,为什么?

void foo(void)
{
    unsigned int a = 6;
    int b = -20;
    (a+b > 6) ? puts("> 6") : puts("<= 6");
}
这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 ">6"。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。

13. 评价下面的代码片断:

unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
/*1's complement of zero */

对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:

unsigned int compzero = ~0;

这一问题真正能揭露出应试者是否懂得处理器字长的重要性。在我的经验里,好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。
到了这个阶段,应试者或者完全垂头丧气了或者信心满满志在必得。如果显然应试者不是很好,那么这个测试就在这里结束了。但如果显然应试者做得不错,那么我就扔出下面的追加问题,这些问题是比较难的,我想仅仅非常优秀的应试者能做得不错。提出这些问题,我希望更多看到应试者应付问题的方法,而不是答案。不管如何,你就当是这个娱乐吧...

动态内存分配(Dynamic memory allocation)

14. 尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(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"。我用这个来开始讨论这样的一问题,看看被面试者是否想到库例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和你做决定的基本原理更重要些。

Typedef
 
15 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 两个指针。晦涩的语法

16 . C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?

int a = 5, b = 7, c;
c = a+++b;

这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成:

c = a++ + b;

因此, 这段代码持行后a = 6, b = 7, c = 12。
如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。

华为笔试题
1.写出判断ABCD四个表达式的是否正确, 若正确, 写出经过表达式中 a的值(3分)
int a = 4;
(A)a += (a++); (B) a += (++a) ;(C) (a++) += a;(D) (++a) += (a++);
a = ?
答:C错误,左侧不是一个有效变量,不能赋值,可改为(++a) += a;
改后答案依次为9,10,10,11

2.某32位系统下, C++程序,请计算sizeof 的值(5分).
char str[] = “http://www.ibegroup.com/”
char *p = str ;
int n = 10;
请计算
sizeof (str ) = ?(1)
sizeof ( p ) = ?(2)
sizeof ( n ) = ?(3)
void Foo ( char str[100]){
请计算
sizeof( str ) = ?(4)
}
void *p = malloc( 100 );
请计算
sizeof ( p ) = ?(5)
答:(1)17 (2)4 (3) 4 (4)4 (5)4

3. 回答下面的问题. (4分)
(1).头文件中的 ifndef/define/endif 干什么用?预处理
答:防止头文件被重复引用
(2). #i nclude 和 #i nclude “filename.h” 有什么区别?
答:前者用来包含开发环境提供的库头文件,后者用来包含自己编写的头文件。
(3).在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明?
答:函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern "C"修饰的变
量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调
用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。
(4). switch()中不允许的数据类型是?
答:实型

4. 回答下面的问题(6分)
(1).Void GetMemory(char **p, int num){
*p = (char *)malloc(num);
}
void Test(void){
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
请问运行Test 函数会有什么样的结果?
答:输出“hello”
(2). void Test(void){
char *str = (char *) malloc(100);
strcpy(str, “hello”);
free(str);
if(str != NULL){
strcpy(str, “world”);
printf(str);
}
}
请问运行Test 函数会有什么样的结果?
答:输出“world”
(3). char *GetMemory(void){
char p[] = "hello world";
return p;
}
void Test(void){
char *str = NULL;
str = GetMemory();
printf(str);
}
请问运行Test 函数会有什么样的结果?
答:无效的指针,输出不确定

5. 编写strcat函数(6分)
已知strcat函数的原型是char *strcat (char *strDest, const char *strSrc);
其中strDest 是目的字符串,strSrc 是源字符串。
(1)不调用C++/C 的字符串库函数,请编写函数 strcat
答:
VC源码:
char * __cdecl strcat (char * dst, const char * src)
{
char * cp = dst;
while( *cp )
cp++; /* find end of dst */
while( *cp++ = *src++ ) ; /* Copy src to end of dst */
return( dst ); /* return dst */
}
(2)strcat能把strSrc 的内容连接到strDest,为什么还要char * 类型的返回值?
答:方便赋值给其他变量

6.MFC中CString是类型安全类么?
答:不是,其它数据类型转换到CString可以使用CString的成员函数Format来转换

7.C++中为什么用模板类。
答:(1)可用来创建动态增长和减小的数据结构
(2)它是类型无关的,因此具有很高的可复用性。
(3)它在编译时而不是运行时检查数据类型,保证了类型安全
(4)它是平台无关的,可移植性
(5)可用于基本数据类型

8.CSingleLock是干什么的。
答:同步多个线程对一个数据类的同时访问

9.NEWTEXTMETRIC 是什么。
答:物理字体结构,用来设置字体的高宽大小

10.程序什么时候应该使用线程,什么时候单线程效率高。
答:1.耗时的操作使用线程,提高应用程序响应
2.并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求。
3.多CPU系统中,使用线程提高CPU利用率
4.改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独
立的运行部分,这样的程序会利于理解和修改。
其他情况都使用单线程。

11.Windows是内核级线程么。
答:见下一题

12.Linux有内核级线程么。
答:线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两
种类型:“用户级线程”和“内核级线程”。 用户线程指不需要内核支持而在用户程序
中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度
和管理线程的函数来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现
,但线程的调度需要用户程序完成,这有些类似 Windows 3.x 的协作式多任务。另外一
种则需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部
需求进行创建和撤销,这两种模型各有其好处和缺点。用户线程不需要额外的内核开支
,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线
程因 I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不
到运行的机会;而内核线程则没有各个限制,有利于发挥多处理器的并发优势,但却占
用了更多的系统开支。
Windows NT和OS/2支持内核线程。Linux 支持内核级的多线程

13.C++中什么数据分配在栈或堆中,New分配数据是在近堆还是远堆中?
答:栈: 存放局部变量,函数调用参数,函数返回值,函数返回地址。由系统管理
堆: 程序运行时动态申请,new 和 malloc申请的内存就在堆上

14.使用线程是如何防止出现大的波峰。
答:意思是如何防止同时产生大量的线程,方法是使用线程池,线程池具有可以同时提
高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队
等候。

15函数模板与类模板有什么区别?
答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化
必须由程序员在程序中显式地指定。

16一般数据库若出现日志满了,会出现什么情况,是否还能使用?
答:只能执行查询等读操作,不能执行更改,备份等写操作,原因是任何写操作都要记
录日志。也就是说基本上处于不能使用的状态。

17 SQL Server是否支持行级锁,有什么好处?
答:支持,设立封锁机制主要是为了对并发操作进行控制,对干扰进行封锁,保证数据
的一致性和准确性,行级封锁确保在用户取得被更新的行到该行进行更新这段时间内不
被其它用户所修改。因而行级锁即可保证数据的一致性又能提高数据操作的迸发性。

18如果数据库满了会出现什么情况,是否还能使用?
答:见16

19 关于内存对齐的问题以及sizof()的输出
答:编译器自动对齐的原因:为了提高程序的性能,数据结构(尤其是栈)应该尽可能
地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问
;然而,对齐的内存访问仅需要一次访问。

20 int i=10, j=10, k=3; k*=i+j; k最后的值是?
答:60,此题考察优先级,实际写成: k*=(i+j);,赋值运算符优先级最低

21.对数据库的一张表进行操作,同时要对另一张表进行操作,如何实现?
答:将操作多个表的操作放入到事务中进行处理

22.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状态,完成三次握手。

23.ICMP是什么协议,处于哪一层?
答:Internet控制报文协议,处于网络层(IP层)

24.触发器怎么工作的?
答:触发器主要是通过事件进行触发而被执行的,当对某一表进行诸如UPDATE、 INSERT
、 DELETE 这些操作时,数据库就会自动执行触发器所定义的SQL 语句,从而确保对数
据的处理必须符合由这些SQL 语句所定义的规则。

25.winsock建立连接的主要实现步骤?
答:服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept()
等待客户端连接。
客户端:socker()建立套接字,连接(connect)服务器,连接上后使用send()和recv(
),在套接字上写读数据,直至数据交换完毕,closesocket()关闭套接字。
服务器端:accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连
接。该新产生的套接字使用send()和recv()写读数据,直至数据交换完毕,closesock
et()关闭套接字。

26.动态连接库的两种方式?
答:调用一个DLL中的函数有两种方法:
1.载入时动态链接(load-time dynamic linking),模块非常明确调用某个导出函数
,使得他们就像本地函数一样。这需要链接时链接那些函数所在DLL的导入库,导入库向
系统提供了载入DLL时所需的信息及DLL函数定位。
2.运行时动态链接(run-time dynamic linking),运行时可以通过LoadLibrary或Loa
dLibraryEx函数载入DLL。DLL载入后,模块可以通过调用GetProcAddress获取DLL函数的
出口地址,然后就可以通过返回的函数指针调用DLL函数了。如此即可避免导入库文件了

27.IP组播有那些好处?
答:Internet上产生的许多新的应用,特别是高带宽的多媒体应用,带来了带宽的急剧
消耗和网络拥挤问题。组播是一种允许一个或多个发送者(组播源)发送单一的数据包
到多个接收者(一次的,同时的)的网络技术。组播可以大大的节省网络带宽,因为无
论有多少个目标地址,在整个网络的任何一条链路上只传送单一的数据包。所以说组播
技术的核心就是针对如何节约网络资源的前提下保证服务质量。

1.static有什么用途?(请至少说明两种)
    1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
    2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
    3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用

2.引用与指针有什么区别?
    1) 引用必须被初始化,指针不必。
    2) 引用初始化以后不能被改变,指针可以改变所指的对象。
    3) 不存在指向空值的引用,但是存在指向空值的指针。

3.描述实时系统的基本特性
       在特定时间内完成特定的任务,实时性与可靠性。

4.全局变量和局部变量在内存中是否有区别?如果有,是什么区别?
      全局变量储存在静态数据库,局部变量在堆栈。

5.什么是平衡二叉树?
      左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1。

6.堆栈溢出一般是由什么原因导致的?
      没有回收垃圾资源。

7.什么函数不能声明为虚函数?
      constructor函数不能声明为虚函数。

8.冒泡排序算法的时间复杂度是什么?
      时间复杂度是O(n^2)。

9.写出float x 与“零值”比较的if语句。
      if(x>0.000001&&x<-0.000001)

10.Internet采用哪种网络协议?该协议的主要层次结构?
      Tcp/Ip协议
      主要层次结构为: 应用层/传输层/网络层/数据链路层/物理层。

11.Internet物理地址和IP地址转换采用什么协议?
      ARP (Address Resolution Protocol)(地址解析協議)

12.IP地址的编码分为哪俩部分?
     IP地址由两部分组成,网络号和主机号。不过是要和“子网掩码”按位与上之后才能区分哪些是网络位哪些是主机位。

13.用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。
     循环链表,用取余操作做
14.不能做switch()的参数类型是:
     switch的参数不能为实型。

華為
1、局部变量能否和全局变量重名?
     答:能,局部会屏蔽全局。要用全局变量,需要使用"::"
局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。

2、如何引用一个已经定义过的全局变量?
     答:extern
     可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。

3、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?
    答:可以,在不同的C文件中以static形式来声明同名全局变量。
    可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错。

4、语句for( ;1 ;)有什么问题?它是什么意思?
    答:无限循环,和while(1)相同。

5、do……while和while……do有什么区别?
    答:前一个循环一遍再判断,后一个判断以后再循环。

6、请写出下列代码的输出内容
#i nclude<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

1、static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?
    答:全局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
    从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。
    static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件
    static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;
    static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;
    static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

2、程序的局部变量存在于(堆栈)中,全局变量存在于(静态区 )中,动态申请数据存在于( 堆)中。

3、设有以下说明和定义:
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

4、队列和栈有什么区别?
    队列先进先出,栈后进先出

5、写出下列代码的输出内容
#i nclude<stdio.h>
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("%d\n",*arg2);
}

main()
{
int a;
show(multi,10,&a);
return 0;
}
答:110
7、请找出下面代码中的所以错误
说明:以下代码是把一个字符串倒序,如“abcd”倒序后变为“dcba”

1、#i nclude"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);//要为\0分配一个空间
char* d = dest;
char* s = &src[len-1];//指向最后一个字符
while( len-- != 0 )
*d++=*s--;
*d = 0;//尾部要加\0
printf("%s\n",dest);
free(dest);// 使用完,应当释放空间,以免造成内存汇泄露
return 0;
}
方法2:
#i nclude <stdio.h>
#i nclude <string.h>
main()
{
char str[]="hello,world";
int len=strlen(str);
char t;
for(int i=0; i<len/2; i++)
{
t=str[i];
str[i]=str[len-i-1]; str[len-i-1]=t;
}
printf("%s",str);
return 0;
}
1.-1,2,7,28,,126请问28和126中间那个数是什么?为什么?
第一题的答案应该是4^3-1=63
规律是n^3-1(当n为偶数0,2,4)
      n^3+1(当n为奇数1,3,5)
答案:63
2.用两个栈实现一个队列的功能?要求给出算法和思路!
设2个栈为A,B, 一开始均为空.

入队:
将新元素push入栈A;

出队:
(1)判断栈B是否为空;
(2)如果不为空,则将栈A中所有元素依次pop出并push到栈B;
(3)将栈B的栈顶元素pop出;

这样实现的队列入队和出队的平摊复杂度都还是O(1), 比上面的几种方法要好。3.在c语言库函数中将一个字符转换成整型的函数是atool()吗,这个函数的原型是什么?
函数名: atol
功 能: 把字符串转换成长整型数
用 法: long atol(const char *nptr);
程序例:
#i nclude <stdlib.h>
#i nclude <stdio.h>
int main(void)
{
long l;
char *str = "98765432";

l = atol(lstr);
printf("string = %s integer = %ld\n", str, l);
return(0);
}

2.对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?
c用宏定义,c++用inline
  3.直接链接两个信令点的一组链路称作什么?
PPP点到点连接
  4.接入网用的是什么接口?
  5.voip都用了那些协议?
  6.软件测试都有那些种类?
黑盒:针对系统功能的测试    白合:测试函数功能,各函数接口
  7.确定模块的功能和模块的接口是在软件设计的那个队段完成的?
概要设计阶段
  8.enum string
    {
    x1,
    x2,
    x3=10,
    x4,
    x5,
    }x;
   问x= 0x801005,0x8010f4  ;
  9.unsigned char *p1;
    unsigned long *p2;
    p1=(unsigned char *)0x801000;
    p2=(unsigned long *)0x810000;
    请问p1+5=  ;
        p2+5=  ;
三.选择题:
  1.Ethternet链接到Internet用到以下那个协议?
  A.HDLC;B.ARP;C.UDP;D.TCP;E.ID
  2.属于网络层协议的是:
  A.TCP;B.IP;C.ICMP;D.X.25
  3.Windows消息调度机制是:
  A.指令队列;B.指令堆栈;C.消息队列;D.消息堆栈;
  4.unsigned short hash(unsigned short key)
    {
      return (key>>)%256
    }
   请问hash(16),hash(256)的值分别是:
  A.1.16;B.8.32;C.4.16;D.1.32
四.找错题:
  1.请问下面程序有什么错误?
   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;
把循环语句内外换一下
  2.#define Max_CB 500
    void LmiQueryCSmd(Struct MSgCB * pmsg)
     {
     unsigned char ucCmdNum;
     ......
  
     for(ucCmdNum=0;ucCmdNum<Max_CB;ucCmdNum++)
      {
      ......;
      }
死循环
   3.以下是求一个数的平方的程序,请找出错误:
    #define SQUARE(a)((a)*(a))
    int a=5;
    int b;
    b=SQUARE(a++);
   4.typedef unsigned char BYTE
     int examply_fun(BYTE gt_len; BYTE *gt_code)
      {
      BYTE *gt_buf;
      gt_buf=(BYTE *)MALLOC(Max_GT_Length);
      ......
      if(gt_len>Max_GT_Length)
        {
        return GT_Length_ERROR;
        }
        .......
      }
五.问答题:
   1.IP Phone的原理是什么?
IPV6
   2.TCP/IP通信建立的过程怎样,端口有什么作用?
三次握手,确定是哪个应用程序使用该协议
   3.1号信令和7号信令有什么区别,我国某前广泛使用的是那一种?
   4.列举5种以上的电话新业务?

微软亚洲技术中心的面试题!!!
1.进程和线程的差别。
线程是指进程内的一个执行单元,也是进程内的可调度实体.
与进程的区别:
(1)调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位
(2)并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可并发执行
(3)拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源.
(4)系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
2.测试方法
人工测试:个人复查、抽查和会审
机器测试:黑盒测试和白盒测试

2.Heap与stack的差别。
Heap是堆,stack是栈。
Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释放。
Stack空间有限,Heap是很大的自由存储区
C中的malloc函数分配的内存空间即在堆上,C++中对应的是new操作符。
程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行
3.Windows下的内存是如何管理的?
4.介绍.Net和.Net的安全性。
5.客户端如何访问.Net组件实现Web Service?
6.C/C++编译器中虚表是如何完成的?
7.谈谈COM的线程模型。然后讨论进程内/外组件的差别。
8.谈谈IA32下的分页机制
小页(4K)两级分页模式,大页(4M)一级
9.给两个变量,如何找出一个带环单链表中是什么地方出现环的?
一个递增一,一个递增二,他们指向同一个接点时就是环出现的地方
10.在IA32中一共有多少种办法从用户态跳到内核态?
通过调用门,从ring3到ring0,中断从ring3到ring0,进入vm86等等
11.如果只想让程序有一个实例运行,不能运行两个。像winamp一样,只能开一个窗口,怎样实现?
用内存映射或全局原子(互斥变量)、查找窗口句柄..
FindWindow,互斥,写标志到文件或注册表,共享内存。. 
12.如何截取键盘的响应,让所有的‘a’变成‘b’?
键盘钩子SetWindowsHookEx
 13.Apartment在COM中有什么用?为什么要引入?
 14.存储过程是什么?有什么用?有什么优点?
我的理解就是一堆sql的集合,可以建立非常复杂的查询,编译运行,所以运行一次后,以后再运行速度比单独执行SQL快很多
 15.Template有什么特点?什么时候用?
16.谈谈Windows DNA结构的特点和优点。


网络编程中设计并发服务器,使用多进程 与 多线程 ,请问有什么区别?
1,进程:子进程是父进程的复制品。子进程获得父进程数据空间、堆和栈的复制品。
2,线程:相对与进程而言,线程是一个更加接近与执行体的概念,它可以与同进程的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列。
两者都可以提高程序的并发度,提高程序运行效率和响应时间。
线程和进程在使用上各有优缺点:线程执行开销小,但不利于资源管理和保护;而进程正相反。同时,线程适合于在SMP机器上运行,而进程则可以跨机器迁移。

思科
1. 用宏定义写出swap(x,y)
#define swap(x, y)\
x = x + y;\
y = x - y;\
x = x - y;
2.数组a[N],存放了1至N-1个数,其中某个数重复一次。写一个函数,找出被重复的数字.时间复杂度必须为o(N)函数原型:
int do_dup(int a[],int N)
3 一语句实现x是否为2的若干次幂的判断
int i = 512;
cout << boolalpha << ((i & (i - 1)) ? false : true) << endl;
4.unsigned int intvert(unsigned int x,int p,int n)实现对x的进行转换,p为起始转化位,n为需要转换的长度,假设起始点在右边.如x=0b0001 0001,p=4,n=3转换后x=0b0110 0001
unsigned int intvert(unsigned int x,int p,int n){
unsigned int _t = 0;
unsigned int _a = 1;
for(int i = 0; i < n; ++i){
_t |= _a;
_a = _a << 1;
}
_t = _t << p;
x ^= _t;
return x;
}

慧通:
什么是预编译
何时需要预编译:
1、总是使用不经常改动的大型代码体。
2、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。
char * const p;
char const * p
const char *p

上述三个有什么区别?
char * const p; //常量指针,p的值不可以修改
char const * p;//指向常量的指针,指向的常量值不可以改
const char *p; //和char const *p

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用法有问题吗?[C易]
void UpperCase( char str[] ) // 将 str 中的小写字母转换成大写字母
{
    for( size_t i=0; i<sizeof(str)/sizeof(str[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。

一个32位的机器,该机器的指针是多少位
指针是多少位只要看地址总线的位数就行了。80386以后的机子都是32的数据总线。所以指针的位数就是4个字节了。

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].


1.请问以下代码有什么问题:
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]的赋值操作是不合法的。

1、写一个“标准”宏,这个宏输入两个参数并返回较小的一个。
.#define Min(X, Y) ((X)>(Y)?(Y):(X))//结尾没有;
2、嵌入式系统中经常要用到无限循环,你怎么用C编写死循环。
while(1){}或者for(;;)
3、关键字static的作用是什么?
定义静态变量
4、关键字const有什么含意?
表示常量不可以修改的变量。
5、关键字volatile有什么含意?并举出三个不同的例子?
提示编译器对象的值可能在编译器未监测到的情况下改变。


int (*s[10])(int) 表示的是什么啊
int (*s[10])(int) 函数指针数组,每个指针指向一个int func(int param)的函数。


1.有以下表达式:
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; 禁止
2.交换两个变量的值,不使用第三个变量。即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;
3.c和c++中的struct有什么不同?
c和c++中struct的主要区别是c中的struct不可以含有成员函数,而c++中的struct可以。c++中struct和class的主要区别在于默认的存取权限不同,struct默认为public,而class默认为private
4.#i nclude <stdio.h>
  #i nclude <stdlib.h>
  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操作很危险
5.char szstr[10];
  strcpy(szstr,"0123456789");
  产生什么结果?为什么?
  长度不一样,会造成非法的OS
6.列举几种进程的同步机制,并比较其优缺点。
   原子操作
信号量机制
   自旋锁
   管程,会合,分布式系统

7.进程之间通信的途径
共享存储系统
消息传递系统
管道:以文件系统为基础
11.进程死锁的原因
资源竞争及进程推进顺序非法
12.死锁的4个必要条件
互斥、请求保持、不可剥夺、环路
13.死锁的处理
鸵鸟策略、预防策略、避免策略、检测与解除死锁
15.   操作系统中进程调度策略有哪几种?
FCFS(先来先服务),优先级,时间片轮转,多级反馈
8.类的静态成员和非静态成员有何区别?
类的静态成员每个类只有一个,非静态成员每个对象一个
9.纯虚函数如何定义?使用时应注意什么?
virtual void f()=0;
是接口,子类必须要实现
10.数组和链表的区别
数组:数据顺序存储,固定大小
连表:数据可以随机存储,大小可动态改变

12.ISO的七层模型是什么?tcp/udp是属于哪一层?tcp/udp有何优缺点?
应用层
表示层
会话层
运输层
网络层
物理链路层
物理层
tcp /udp属于运输层
TCP 服务提供了数据流传输、可靠性、有效流控制、全双工操作和多路复用技术等。
与 TCP 不同, UDP 并不提供对 IP 协议的可靠机制、流控制以及错误恢复功能等。由于 UDP 比较简单, UDP 头包含很少的字节,比 TCP 负载消耗少。
tcp: 提供稳定的传输服务,有流量控制,缺点是包头大,冗余性不好
udp: 不提供稳定的服务,包头小,开销小 


1:(void *)ptr 和 (*(void**))ptr的结果是否相同?其中ptr为同一个指针
.(void *)ptr 和 (*(void**))ptr值是相同的
2:int main()
   {
    int x=3;
    printf("%d",x);
    return 1;
 
   }
问函数既然不会被其它函数调用,为什么要返回1?
mian中,c标准认为0表示成功,非0表示错误。具体的值是某中具体出错信息


1,要对绝对地址0x100000赋值,我们可以用
(unsigned int*)0x100000 = 1234;
那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?
*((void (*)( ))0x100000 ) ( );
首先要将0x100000强制转换成函数指针,即:
(void (*)())0x100000
然后再调用它:
*((void (*)())0x100000)();
用typedef可以看得更直观些:
typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0x100000)();
2,已知一个数组table,用一个宏定义,求出数据的元素个数
#define NTBL
#define NTBL (sizeof(table)/sizeof(table[0]))

面试题: 线程与进程的区别和联系? 线程是否具有相同的堆栈? dll是否有独立的堆栈?
进程是死的,只是一些资源的集合,真正的程序执行都是线程来完成的,程序启动的时候操作系统就帮你创建了一个主线程。

每个线程有自己的堆栈。
DLL中有没有独立的堆栈,这个问题不好回答,或者说这个问题本身是否有问题。因为DLL中的代码是被某些线程所执行,只有线程拥有堆栈,如果DLL中的代码是EXE中的线程所调用,那么这个时候是不是说这个DLL没有自己独立的堆栈?如果DLL中的代码是由DLL自己创建的线程所执行,那么是不是说DLL有独立的堆栈?

以上讲的是堆栈,如果对于堆来说,每个DLL有自己的堆,所以如果是从DLL中动态分配的内存,最好是从DLL中删除,如果你从DLL中分配内存,然后在EXE中,或者另外一个DLL中删除,很有可能导致程序崩溃


unsigned short A = 10;
printf("~A = %u\n", ~A);

char c=128;
printf("c=%d\n",c);

输出多少?并分析过程
第一题,~A =0xfffffff5,int值 为-11,但输出的是uint。所以输出4294967285
第二题,c=0x10,输出的是int,最高位为1,是负数,所以它的值就是0x00的补码就是128,所以输出-128。
这两道题都是在考察二进制向int或uint转换时的最高位处理。

分析下面的程序:
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一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的,只不过。。。。。。。。楼上都说过了,最好别这么干。

char a[10],strlen(a)为什么等于15?运行的结果

#i nclude "stdio.h"
#i nclude "string.h"

void main()
{

char aa[10];
printf("%d",strlen(aa));
}

sizeof()和初不初始化,没有关系;
strlen()和初始化有关。


char (*str)[20];/*str是一个数组指针,即指向数组的指针.*/
char *str[20];/*str是一个指针数组,其元素为指针型数据.*/

long a=0x801010;
a+5=?
0x801010用二进制表示为:“1000 0000 0001 0000 0001 0000”,十进制的值为8392720,再加上5就是8392725罗

1)给定结构struct A
{
       char t:4;
       char k:4;
       unsigned short i:8;
       unsigned long m;
};问sizeof(A) = ?
给定结构struct A
{
       char t:4; 4位
       char k:4; 4位
       unsigned short i:8; 8位    
       unsigned long m; // 偏移2字节保证4字节对齐
}; // 共8字节
2)下面的函数实现在一个数上加一个数,有什么错误?请改正。
int add_n ( int n )
{
    static int i = 100;
    i += n;
    return i;
}
当你第二次调用时得不到正确的结果,难道你写个函数就是为了调用一次?问题就出在 static上?


// 帮忙分析一下
#i nclude<iostream.h>
#i nclude <string.h>
#i nclude <malloc.h>
#i nclude <stdio.h>
#i nclude <stdlib.h>
#i nclude <memory.h>
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 <<endl;
        cout << aa.b2 <<endl;
}
答案是 -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

求函数返回值,输入x=9999;
int func ( x )
{
    int countx = 0;
    while ( x )
    {
        countx ++;
        x = x&(x-1);
    }
    return countx;
}
结果呢?
知道了这是统计9999的二进制数值中有多少个1的函数,且有
9999=9×1024+512+256+15

9×1024中含有1的个数为2;
512中含有1的个数为1;
256中含有1的个数为1;
15中含有1的个数为4;
故共有1的个数为8,结果为8。
1000 - 1 = 0111,正好是原数取反。这就是原理。
用这种方法来求1的个数是很效率很高的。
不必去一个一个地移位。循环次数最少。

int a,b,c 请写函数实现C=a+b ,不可以改变数据类型,如将c改为long int,关键是如何处理溢出问题
bool add (int a, int b,int *c)
{
*c=a+b;
return (a>0 && b>0 &&(*c<a || *c<b) || (a<0 && b<0 &&(*c>a || *c>b)));
}


分析:
struct bit
{   int a:3;
    int  b:2;
    int c:3;
};
int main()
{
  bit s;
  char *c=(char*)&s;
   cout<<sizeof(bit)<<endl;
  *c=0x99;
   cout << s.a <<endl <<s.b<<endl<<s.c<<endl;
     int a=-1;
   printf("%x",a);
  return 0;
}
输出为什么是
4
1
-1
-4
ffffffff
因为0x99在内存中表示为 100 11 001 , a = 001, b = 11, c = 100
当c为有符合数时, c = 100, 最高1为表示c为负数,负数在计算机用补码表示,所以c = -4;同理
b = -1;
当c为有符合数时, c = 100,即 c = 4,同理 b = 3


位域 : 
有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有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  
};  
从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。  

二、位域的使用位域的使用和结构成员的使用相同,其一般形式为: 位域变量名&#8226;位域名 位域允许用各种格式输出。  
main(){  
struct bs  
{  
unsigned a:1;  
unsigned b:3;  
unsigned c:4;  
} bit,*pbit;  
bit.a=1;  
bit.b=7;  
bit.c=15;  
pri

改错:
#i nclude <stdio.h>

int main(void) {

    int **p;
    int arr[100];

    p = &arr;

    return 0;
}
解答:
搞错了,是指针类型不同,
int **p; //二级指针
&arr; //得到的是指向第一维为100的数组的指针
#i nclude <stdio.h>
int main(void) {
int **p, *q;
int arr[100];
q = arr;
p = &q;
return 0;
}


下面这个程序执行后会有什么错误或者效果:
 #define MAX 255
 int main()
{
   unsigned char A[MAX],i;//i被定义为unsigned char
   for (i=0;i<=MAX;i++)
      A[i]=i;
}
解答:死循环加数组越界访问(C/C++不进行数组越界检查)
MAX=255
数组A的下标范围为:0..MAX-1,这是其一..
其二.当i循环到255时,循环内执行:
  A[255]=255;
这句本身没有问题..但是返回for (i=0;i<=MAX;i++)语句时,
由于unsigned char的取值范围在(0..255),i++以后i又为0了..无限循环下去.

struct name1{
   char  str;
   short x;
   int   num;
}

struct name2{
   char str;
   int num;
   short x;
}

sizeof(struct name1)=8,sizeof(struct name2)=12
在第二个结构中,为保证num按四个字节对齐,char后必须留出3字节的空间;同时为保证整个结构的自然对齐(这里是4字节对齐),在x后还要补齐2个字节,这样就是12字节。

intel:
A.c 和B.c两个c文件中使用了两个相同名字的static变量,编译的时候会不会有问题?这两个static变量会保存到哪里(栈还是堆或者其他的)?
static的全局变量,表明这个变量仅在本模块中有意义,不会影响其他模块。
他们都放在数据区,但是编译器对他们的命名是不同的。
如果要使变量在其他模块也有意义的话,需要使用extern关键字。

struct s1
{
  int i: 8;
  int j: 4;
  int a: 3;
  double b;
};

struct s2
{
  int i: 8;
  int j: 4;
  double b;
  int a:3;
};

printf("sizeof(s1)= %d\n", sizeof(s1));
printf("sizeof(s2)= %d\n", sizeof(s2));
result: 16, 24
第一个struct s1
{
  int i: 8;
  int j: 4;
  int a: 3;
  double b;
};
理论上是这样的,首先是i在相对0的位置,占8位一个字节,然后,j就在相对一个字节的位置,由于一个位置的字节数是4位的倍数,因此不用对齐,就放在那里了,然后是a,要在3位的倍数关系的位置上,因此要移一位,在15位的位置上放下,目前总共是18位,折算过来是2字节2位的样子,由于double是8 字节的,因此要在相对0要是8个字节的位置上放下,因此从18位开始到8个字节之间的位置被忽略,直接放在8字节的位置了,因此,总共是16字节。

第二个最后会对照是不是结构体内最大数据的倍数,不是的话,会补成是最大数据的倍数

上面是基本问题,接下来是编程问题:


本人很弱,这几个题也搞不定,特来求救:
1)读文件file1.txt的内容(例如):
12
34
56
输出到file2.txt:
56
34
12
(逆序)
2)输出和为一个给定整数的所有组合
例如n=5
5=1+4;5=2+3(相加的数不能重复)
则输出
1,4;2,3。
望高手赐教!!

第一题,注意可增长数组的应用.
#i nclude <stdio.h>
#i nclude <stdlib.h>

int main(void)
{
         int MAX = 10;
int *a = (int *)malloc(MAX * sizeof(int));
int *b;
  
FILE *fp1;
FILE *fp2;

fp1 = fopen("a.txt","r");
if(fp1 == NULL)
{printf("error1");
    exit(-1);
}

    fp2 = fopen("b.txt","w");
if(fp2 == NULL)
{printf("error2");
    exit(-1);
}

int i = 0;
    int j = 0;

while(fscanf(fp1,"%d",&a[i]) != EOF)
{
i++;
j++;
if(i >= MAX)
{
MAX = 2 * MAX;
b = (int*)realloc(a,MAX * sizeof(int));
if(b == NULL)
{
printf("error3");
exit(-1);
}
a = b;
}
}

for(;--j >= 0;)
   fprintf(fp2,"%d\n",a[j]);

fclose(fp1);
fclose(fp2);

return 0;
}

第二题.
#i nclude <stdio.h>

int main(void)
{
unsigned long int i,j,k;

printf("please input the number\n");
scanf("%d",&i);
    if( i % 2 == 0)
        j = i / 2;
else
j = i / 2 + 1;

printf("The result is \n");
    for(k = 0; k < j; k++)
     printf("%d = %d + %d\n",i,k,i - k);
return 0;
}

#i nclude <stdio.h>
void main()
{
unsigned long int a,i=1;
scanf("%d",&a);
if(a%2==0)
{
     for(i=1;i<a/2;i++)
     printf("%d",a,a-i);
}
else
for(i=1;i<=a/2;i++)
        printf(" %d, %d",i,a-i);
}

兄弟,这样的题目若是做不出来实在是有些不应该, 给你一个递规反向输出字符串的例子,可谓是反序的经典例程.

void inverse(char *p)
{
    if( *p = = '\0' )
return;
    inverse( p+1 );
    printf( "%c", *p );
}

int main(int argc, char *argv[])
{
    inverse("abc\0");

    return 0;
}

借签了楼上的“递规反向输出”
#include <stdio.h>
void test(FILE *fread, FILE *fwrite)
{
        char buf[1024] = {0};
        if (!fgets(buf, sizeof(buf), fread))
    &nb

这段代码似乎还没有完,因为是转的别人的,我也就不动了,各位可以自己补上。
{

head = head1 ;

head->next = MergeRecursive(head1->next,head2);

}

else

{

head = head2 ;

head->next = MergeRecursive(head1,head2->next);

}

return head ;

}

1. C++ 中类与结构体的区别

答:类成员默认访问权限为私有(private),结构体成员默认访问权限为公共(public),其他地方完全一样。

---------------------------------------------------------------------------

2. 关于私有继承

私有继承实际上和组合比较相像,应该说是一个设计概念,而不是具体的实现概念。具体实现中私有继承使用比较少,因为大多数情况使用组合更加清晰。

公有继承意味着 is-a,私有继承意味着 is-implemented-in-terms-of (根据...实现)。


参考:《C++箴言:谨慎使用私有继承》
http://tech.163.com/05/1124/14/23B2S17F0009159Q.html

---------------------------------------------------------------------------

3. 写出下面程序的输出。

Code
#include <stdio.h>

class abc;

void del(abc *pobj)
{
      delete pobj;
}

class abc
{
 public:
      abc(){
          printf("abc\r\n");
      }

      ~abc(){
          printf("~abc\r\n");
      }
};


int main(int argc, char *argv[])
{
      abc *pobj = new abc;
      del(pobj);
}

答:
abc

说明:定义 del 函数的时候,abc 的析构函数未定义,因此不会调用。

---------------------------------------------------------------------------

4. 写出下面程序的输出。
Code
#include <stdio.h>
#include <stdlib.h>

void * operator new(size_t size)
{
      printf("malloc %u\r\n", size);
      return malloc(size);
}

void operator delete(void * memblock)
{
      printf("free\r\n");
      return free(memblock);
}

class abc
{
public:
      abc()
      {
          printf("abc\r\n");
          throw int();
      }

      ~abc()
      {
          printf("~abc\r\n");
      }
};

int main(int argc, char * argv[])
{
      try
      {
          new abc;
      }
      catch (int & i)
      {
          printf("%d\r\n", i);
      }

      return 0;
}

答:
malloc 1
abc
free
0
如果将“new abc;”换成“abc a;”,结果将是:
abc
0

说明:
1. 在 C++ 中,构造函数抛出异常后不会触发析构函数的调用,这和 object pascal 不一样。C++ 认为构造失败意味着对象没有产生,既然没有生就没有死。然而,当构造函数抛出异常时,仍会调用 delete 函数以释放内存。

operator new 重载全局 new,所以下面构造的时候 new 肯定会调用该 operator new。operator delete 同样。因此先输出 malloc 1。
new 分配完内存后,会自动调用构造函数,所以输出 abc。
在构造函数内部,抛出异常,throw int();
这个异常被捕获,输出 i,其值为 0。
但是在捕获的异常被处理之前,必须先释放内存。因为异常出错,但此时 new 分配内存的工作已经完成,如果不进行 delete 的话,势必会内存泄露。

2. 当生成堆栈对象时,C++ 自动调用的 operator new 和 operator delete 是全局的 operator new 和 operator delete。

参考:
《More Effective C++》条款10: 在构造函数中防止资源泄漏
…… 不用为 BookEntry 中的非指针数据成员操心,在类的构造函数被调用之前数据成员就被自动地初始化。所以如果 BookEntry 构造函数体开始执行,对象的 theName、theAddress 和 thePhones 数据成员已经被完全构造好了。这些数据可以被看做是完全构造的对象,所以它们将被自动释放,不用你介入操作。……

---------------------------------------------------------------------------

5. 写出下面程序的输出。

Code
 1
 2 #include <stdio.h>
 3
 4 template <typename T>
 5 class abc{
 6 public:
 7       abc(){
 8           printf("primary\r\n");
 9       }
10 };
11
12 template<>
13 abc<int>::abc()
14 {
15       printf("member spec\r\n");
16 };
17
18 template<typename T, typename P>
19 class abc<T (*)(P)>
20 {
21 public:
22       abc(){
23           printf("partial spec\r\n");
24       }
25 };
26
27 int main(int argc, char *argv[])
28 {
29       abc<void* (*)(int)> f_abc;
30       abc<int> i_abc;
31 }
32
答:
partial spec
member spec

说明:模板部分特化。

---------------------------------------------------------------------------

6. 下面的代码能否通过编译?为什么?

 1 class a
 2 {
 3 public:
 4       virtual ~a()
 5       {
 6       }
 7 private:
 8       void operator delete(void *p);
 9 };
10
11 int main(int argc, char *argv[])
12 {
13       a _1;
14 }
15
答:
不能

说明:
1) 如果一个类有虚析构函数的话,那么自定义 delete 函数必须有函数体。
2) 这个题目中,并不会调用 delete 函数。
3) 对于本题,delete 函数不被调用,但是编译器需要它,因此,不能没有定义。

参考:
如果对象是动态创建(也就是 new 出来的),那么在 delete 的时候系统会先调析构函数然后调 operator delete。编译器在编译的时候会把这 2 个步骤合并到一个函数里,看反汇编就知道了,函数名字类似 'scalar deleting destructor'。如果只是声明了 operator delete 函数而没有定义,那么编译的时候会得不到函数地址,这样生成 'scalar deleting destructor' 内置函数的时候就会报错了。

---------------------------------------------------------------------------

7. C 与 C++ 中 static 函数有什么区别?

      C 语言中 static 关键字作用于函数时起限制函数作用域的作用,其 static 函数作用域被限制为当前文件中,该函数定义之后部分。
      C++ 的全局函数用 static 修饰和 C 语言中一个意思 (C++ 标准建议此种情况用匿名名字空间包含该函数来代替 static 关键字);但类成员函数如果用 static 修饰表示是类的作用域而不是对象作用域,可以直接通过类名来引用。

---------------------------------------------------------------------------

8. const 函数的作用。

      类的设计者通过将函数声明为 const 以表明它们不修改类对象。
      const 函数内不允许修改数据成员 (需要注意的是,虽然在 const 函数内,指针成员变量不允许被修改,但对指针所指向内容的修改是允许的)。
      一个 const 类对象只能调用 const 成员函数 (构造和析构函数除外)。

---------------------------------------------------------------------------

9. 拷贝构造函数什么情况下会用到?实现时有什么注意点?

      拷贝构造函数的几个用处:
      1) 用一个类对象初始化该类的另一个对象的时候:
         A a;
         A b(a);
         A b = a;
      2) 把一个类对象赋值给该类的另一个对象的时候:
         A b;
         b = a;
      3) 传参数和返回时:
         A f (A a) // 传参和返回都会调用拷贝构造函数
         {
             // ...
         }

      实现时要注意的是:
      1) 当类中有指针变量成员时,确认是直接拷贝指针变量的值,还是重新分配内存然后递归拷贝构造。
      2) 是否有每个对象必须有唯一值的成员变量 (比如账号)。其实 1) 也可以归为 2)。

---------------------------------------------------------------------------

10. 写出函数指针,返回指针的函数,const 指针,指向 const 的指针,指向 const 的 const 指针。
      void (* f)()
      void * f()
      int * const f
      const int * f
      const int * const f

---------------------------------------------------------------------------

11. 智能指针。

      智能指针就是模拟指针动作的类。所有的智能指针都会重载 -> 和 * 操作符。
      智能指针还有许多其他功能,比较有用的是自动销毁。这主要是利用栈对象的有限作用域以及临时对象(有限作用域实现)析构函数释放内存。当然,智能指针还不止这些,还包括复制时可以修改源对象等。智能指针根据需求不同,设计也不同(写时复制,赋值即释放对象拥有权限,引用计数等,控制权转移等)。这个主题可以讲一本书。
      auto_ptr 即是一种常见的智能指针。
      智能指针通常用类模板实现:
          template <class T>
          class smpoint
          {
          public:
              smpoint(T * p): _p(p){}
              T & operator * (){return *_p;}
              T * operator -> (){return _p;}
              ~smpoint(){delete _p;}
          private:
              T * _p;
          }

---------------------------------------------------------------------------

12. 标准模板库 vector 追加数据如何实现?注意是底层如何实现,而不是如何使用。

      关键点是,在追加对象前先判断预留的空间是否满足需求,如果不满足则根据分配策略,另分配足够的空间 (一般使用平方增加策略),复制以前的对象数组,再释放原来的空间,然后把对象追加到尾部。如果任何一个操作环节失败,则至少保留原数组不受影响(异常安全保证策略)。

基础算法题目精简集合

题目相对来说简要了一些,算是有代表性了,各方面都有题目,偶不希望像别的帖子那样像为了凑数般弄够100题,相反这里不过二三十。
前六章均为算法基础入门必会解答的题目,也就是若当中有任何一题,您无法给出正确解答,就不算有算法基础(带星的题目例外),并且这里不提供基础题解答,若你实在需要,请自行查资料或者找人帮你。
下文假定阅读者具有良好的小学数学基础,以及懂得使用C/C++/Pascal语言当中的任何一种,尽管你会其它的语言也行,但算法描述方面以及代码效率还是推荐以上三种。
如果以下算法基础的题目您学习了很久也无法正确解决的话,那么本人不建议你继续学习编程(基础题不用STL库独立解答出才算是会)。

第一章。循环控制
1.输入一个奇数n,输出对角线长为n的实心或者空心的菱形图案
  如当n=5时,有:
    *
   ***
  *****
   ***
    *
  实现代码:
Code
 1 #include <stdio.h>
 2
 3 int main()
 4 {
 5     int n,i,j;
 6     scanf("%d",&n);
 7     for(i=1;i<=n/2+1;i++)
 8     {
 9         for(j=1;j<=(n-2*i+1)/2;j++)
10             printf(" ");
11         for(j=1;j<=2*i-1;j++)
12             printf("*");
13         for(j=1;j<=(n-2*i+1)/2;j++)
14             printf(" ");
15         printf("\n");
16     }
17     for(i=1;i<=n/2;i++)
18     {
19         for(j=1;j<=i;j++)
20             printf(" ");
21         for(j=1;j<=n-2*i;j++)
22             printf("*");
23         for(j=1;j<=i;j++)
24             printf(" ");
25         printf("\n");
26     }
27     return 0;
28 }
29

2.输入一个奇数n,构造并输出一个n阶等和幻方,
  即每一行每一列和两对角线上的n个数的和相等
  如当n=5时,有(构造方法请自行搜索或者观察下表):
  03 16 09 22 15
  20 08 21 14 02
  07 25 13 01 19
  24 12 05 18 06
  11 04 17 10 23

Code
  1 #include <stdio.h>
  2
  3
  4 void Magic(int n)
  5 {   
  6     int m[15][15];
  7     int i,j,p;
  8     p=1;
  9     for(i=0;i<n;i++)
 10         for(j=0;j<n;j++)
 11             m[i][j]=0;
 12     i=0;j=n/2;
 13     while(p<=n*n)
 14     {
 15         m[i][j]=p++;
 16
 17
 18         if(i==0&&j<(n-1)&&j>=0)//走到第一行非右上角时
 19         {
 20        
 21             if(0==m[n-1][j+1])
 22             {
 23                 i=n-1;j=j+1;
 24             }
 25             else
 26                 i=i+1;   
 27         }
 28         else if(i>0&&i<(n-1)&&j==(n-1))//走到最右端非右上角和右下角时
 29         {
 30            
 31             if(0==m[i-1][0])
 32             {
 33                 i=i-1;j=0;
 34             }
 35             else
 36                 i=i+1;       
 37         }
 38         else if(i==(n-1)&&j>=0&&j<(n-1))//走到最后一排非右下角时
 39         {
 40        
 41             if(0==m[i-1][j+1])
 42             {
 43                 i=i-1;j=j+1;
 44             }
 45             else
 46                 i=0;
 47        
 48
 49         }
 50         else if(i==0&&(n-1)==j)//走到右上角时
 51         {
 52            
 53             if(0==m[n-1][0])
 54             {
 55                 i=n-1;j=0;
 56             }
 57             else
 58                 i=i+1;
 59
 60         }
 61         else if(i==(n-1)&&j==(n-1))//走到右下角时
 62         {
 63            
 64             if (0==m[n-2][0])
 65             {
 66                 i=n-2;j=0;
 67
 68             }
 69             else
 70                 i=0;
 71
 72         }
 73         else//其它情况
 74         {
 75             if(0==m[i-1][j+1])
 76             {
 77                 i=i-1;j=j+1;
 78             }
 79             else
 80                 i=i+1;
 81         }
 82     }
 83
 84     for(i=0;i<n;i++)
 85     {
 86         for(j=0;j<n;j++)
 87             printf("%3d  ",m[i][j]);
 88         printf("\n");
 89     }
 90    
 91 }
 92
 93 int main(void)
 94 {
 95     int n;
 96     scanf("%d",&n);
 97     Magic(n);
 98    
 99     return 0;
100 }
101

3.输入一个1e9以内的整数n和k(2<=k<=36),输出相应的k进制数。
 
Code
 1 #include<stdio.h>
 2 int NumConvert(int n,int k)
 3 {
 4     int t = n;
 5     char buf[100];
 6     int i,j;
 7     for(i=0;i<100 && t>0;i++,t/=k)
 8     {
 9         j = t % k;
10         if(j < 10) buf[i]= '0'+j;
11         else buf[i] = 'A'-10+j;
12     }
13     for(;i>0;) printf("%c",buf[--i]);
14     return 0;
15 }
16
17 int main()
18 {
19     int n,k;
20     scanf("%d,%d",&n,&k);
21     NumConvert(n,k);
22     return 0;
23 }

4.输入一个整数n(2<=n<=1e9),判断它是不是质数,
  时间复杂度必须为O(n^0.5),不得为O(n)
  质数就是2,以及其它有且只有两个约数的整数,
  如3,5,7,11,13,17,19,23,29,31,......(以上为前10个奇质数)
Code
#include <stdio.h>
int main()
{
    int m,i,k;
    scanf("%d,&m");
    k=sqrt(m+1);
    for(i=2;i<=k;i++)
       if(m%i==0)
          break;
    if(i>=k+1)printf("%d is a prime muber\n",m);
    else printf("%d is not a prime muber\n",m);
return 0;
}

5.字符串模式匹配。输入两个字符串a,b,判断b是否在a中出现,
  如有,请输出第一次出现的位置。
Code
 1 #include <stdio.h>
 2 #include <string.h>
 3
 4 void StrFind(char * a,char *str)
 5 {
 6     char *p;
 7     int lenStr=strlen(str);
 8     int lenA=strlen(a);
 9     int k=lenA-lenStr;
10     int i;
11     int j;
12
13     for(p=a,i=1;p<=a+k;p++,i++)
14     {
15         j=0;
16         while(j<lenStr)
17         {
18            
19             if(*(p+j)!=*(str+j))
20                 break;
21             j++;
22         }
23         if('\0'==*(str+j))
24         {
25             printf("The fahter string has the son string,the position is %d\n",i);
26             break;
27         }
28     }
29 }
30
31 int main()
32 {
33     char fathStr[100],sonStr[50];
34     scanf("%s %s",fathStr,sonStr);
35     StrFind(fathStr,sonStr);
36
37     return 0;
38 }

第二章。简单穷举搜索
1.输入两个数a,b(1 <= a,b <= 1e5),统计a,b之间一共有多少个质数
  相关题目请参阅http://yzfy.org/bbs/viewthread.php?tid=392
  若要通过链接里那个题目还需要优化,单纯做出本题并不难。
      如何优化?
Code
 1 #include <iostream>
 2 #include <math.h>
 3 using namespace std;
 4
 5 bool IsPrime(int n)
 6 {
 7     int k;
 8     if(n<2)
 9         return false;
10     for(k=2;k<=sqrt((double)n);k++)
11         if(0==n%k)
12             return false;
13     return true;
14 }
15
16 int main()
17 {
18     int a,b,count=0;
19     cin>>a>>b;
20     for(int i=a;i<=b;i++)
21     {
22         if(IsPrime(i))
23             ++count;
24     }
25     cout<<"The count of  prime numbers between a and b is "<<count<<endl;
26     return 0;
27
28 }

2.查找1000以内的所有水仙花数,如153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27
  各位上的数字的三次方的和等于自己本身
Code
 1 #include <iostream>
 2 using namespace std;
 3
 4 bool IsShuixianNum(int n)
 5 {
 6     int a,b,c;
 7     a=b=c=0;
 8     a=n/100;
 9     c=n%10;
10     b=(n-100*a)/10;
11     if(n==a*a*a+b*b*b+c*c*c)
12         return true;
13     else
14         return false;
15 }
16
17 int main()
18 {
19     int i,count=0;
20     for(i=10;i<1000;i++)
21         if(IsShuixianNum(i))
22         {
23             ++count;
24             cout<<i<<endl;
25         }
26     cout<<"the count of Shuixian number is "<<count<<endl;
27     return 0;
28 }


3.输入一个数n(1<=n<=1e4),然后再输入n个不同的整数,再输入一个整数k,
  判断k是否在那n个整数上出现过。
Code
 1 #include <iostream>
 2 using namespace std;
 3
 4 int main()
 5 {
 6     int n,i=0;
 7     int j,k;
 8     int a[10000];
 9     cout<<"please input the count of numbers"<<endl;
10     cin>>n;
11     cout<<"please input n different numbers"<<endl;
12     while(i<n)
13     {
14         cin>>a[i];
15         for(j=0;j<i;j++)//保证输入的数字不重复
16             if(a[j]==a[i])
17             {
18                 cout<<"the number is illegal,please input another number!"<<endl;
19                 break;//重复则退出
20             }
21         if(j>=i)
22             i++;
23     }
24     cout<<"please input k"<<endl;
25     cin>>k;
26     for(i=0;i<n;i++)
27     {
28         if(k==a[i])
29         {
30             cout<<"k is the "<<i+1<<"th number of the "<<n<<" numbers"<<endl;
31             break;
32         }
33     }
34     if(i>=n)
35         cout<<"k is not in the "<<n<<" numbers"<<endl;
36
37     return 0;
38 }

第三章。迭代、递推和递归
1.斐波那契数列和:输入整数n,求1/1+2/1+3/2+5/3+8/5+.....
  一直到第n项的结果。
Code
 1 #include <iostream>
 2 using namespace std;
 3
 4 int main()
 5 {
 6     long int f1,f2;
 7     int i,a,n;
 8     cin>>n;
 9     if(1==n)
10         cout<<"1"<<endl;
11     else if(2==n)
12         cout<<"3"<<endl;
13     else
14     {
15         f1=2;
16         f2=1;
17         float m,sum=3;
18         for(i=3;i<=n;i++)
19         {
20             a=f1;
21             f1=f1+f2;
22             f2=a;
23             sum+=(float)(f1)/f2;   
24         }
25         cout<<sum<<endl;
26     }
27     return 0;
28 }


2.★经典Hanoi塔问题。
Code
 1 //分析:
 2 //将n个盘子从A针移到C针可以分解为下面3个步骤:
 3 //1.将A上n-1个盘子移动到B针(借助C针)
 4 //2.将A剩下的一个盘子移动到C针
 5 //3.将n-1个盘子从B针移动到C针
 6 //以上三个步骤都包括以下两种操作:
 7 //1.将多个盘子移动到另一针上,利用递归
 8 //2.将一个盘子移动到另一针上
 9 //于是程序就可以如下编写:
10 #include <iostream>
11 using namespace std;
12
13 void move(char getone,char putone)
14 {
15     cout<<getone<<" --> "<<putone<<endl;//将一个盘子移动到另一针上
16 }
17
18 //将多个盘子移动到另一针上,利用递归
19 void hanoi(int n,char one,char two,char three)
20 {
21     void move(char getone,char putone);
22     if(1==n) move(one,three);
23     else //将多个盘子移动从A针移动到C针的步骤
24     {
25         hanoi(n-1,one,three,two);//将A上n-1个盘子移动到B针(借助C针)
26         move(one,three);//将A剩下的一个盘子移动到C针
27         hanoi(n-1,two,one,three);//将n-1个盘子从B针移动到C针
28     }
29 }
30 int main()
31 {
32     void hanoi(int n,char one,char two,char three);
33     int m;
34     cout<<"Enter the number of diskes:";
35     cin>>m;
36     cout<<"the steps to moving "<<m<<" diskes:"<<endl;
37     hanoi(m,'A','B','C');
38     return 0;
39 }

第四章。简单计算几何
1.简单碰撞检测
编写一个函数
int IsOnRECT(float x, float y, float r,float x1, float y1, float x2, float y2)
x,y表示一个圆的圆心,r是圆的半径,x1,y1,x2,y2表示一个矩形的一条对角线上的两个顶点的坐标矩形的四边均与坐标轴平行或者垂直,要判断这个圆和这个矩形有没有重合的部分。
输入:
按照参数传递进行输入,样例中一行就是一次输入
按顺序分别对应x,y,d,x1, ....
输出:
使用函数返回值作为输出,当有重合的时候返回非0,否则请返回0
函数中不得含有任何其它输出,否则作为错误
样例输入:
1 1 1  0 0 2 2
1 1 1  2 2 3 3
样例输出:
1
0
其它:
答题时请不要粘贴上main函数,需要任何头文件请自己包含因精度方面问题,不需要考虑相切,相切作为不重合处理即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值