qrs吧 关注:33贴子:833

经典C/C++面试题(一)

收藏回复

  • 123.112.125.*
1.介绍一下STL,详细说明STL如何实现vector。
     STL (标准模版库,Standard Template Library)它由容器算法迭代器组成。
     STL有以下的一些优点:
     可以方便容易地实现搜索数据或对数据排序等一系列的算法;
     调试程序时更加安全和方便;
     即使是人们用STL在UNIX平台下写的代码你也可以很容易地理解(因为STL是跨平台的)。
     vector实质上就是一个动态数组,会根据数据的增加,动态的增加数组空间。
 
2.如果用VC开发程序,常见这么几个错误,C2001,c2005,c2011,这些错误的原因是什么。
 
  在学习VC++的过程中,遇到的LNK2001错误的错误消息主要为:
  unresolved external symbol “symbol”(不确定的外部“符号”)。
    如果连接程序不能在所有的库和目标文件内找到所引用的函数、变量或标签,将产生此错误消息。
     一般来说,发生错误的原因有两个:一是所引用的函数、变量不存在、拼写不正确或者使用错误;其次可能使用了不同版本的连接库。
     编程中经常能遇到LNK2005错误——重复定义错误,其实LNK2005错误并不是一个很难解决的错误.
 
3.继承和委派有什么分别,在决定使用继承或者委派的时候需要考虑什么。
     在OOD,OOP中,组合优于继承.
     当然多态的基础是继承,没有继承多态无从谈起。
     当对象的类型不影响类中函数的行为时,就要使用模板来生成这样一组类。
     当对象的类型影响类中函数的行为时,就要使用继承来得到这样一组类.
 
4.指针和引用有什么分别;如果传引用比传指针安全,为什么?如果我使用常量指针难道不行吗?
     (1) 引用在创建的同时必须初始化,即引用到一个有效的对象;而指针在定义的时候不必初始化,可以在定义后面的任何地方重新赋值.
     (2) 不存在NULL引用,引用必须与合法的存储单元关联;而指针则可以是NULL.
     (3) 引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用;而指针在任何时候都可以改变为指向另一个对象.给引用赋值并不是改变它和原始对象的绑定关系.
     (4) 引用的创建和销毁并不会调用类的拷贝构造函数
     (5) 语言层面,引用的用法和对象一样;在二进制层面,引用一般都是通过指针来实现的,只不过编译器帮我们完成了转换.
     不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,显得很安全。
     const 指针仍然存在空指针,并且有可能产生野指针.
     总的来说:引用既具有指针的效率,又具有变量使用的方便性和直观性.
 
5.参数传递有几种方式;实现多态参数传递采用什么方式,如果没有使用某种方式原因是什么;
     传值,传指针或者引用



1楼2009-03-31 21:39回复
    • 123.112.125.*

     
    6.结合一个项目说明你怎样应用设计模式的理念。
         设计模式更多考虑是扩展和重用,而这两方面很多情况下,往往会被忽略。
         不过,我不建议滥用设计模式,以为它有可能使得简单问题复杂化.
     
    7.介绍一下你对设计模式的理解。(这个过程中有很多很细节的问题随机问的)
         设计模式概念是由建筑设计师Christopher Alexander提出:"每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心.这样,你就能一次又一次地使用该方案而不必做重复劳动."上述定义是对设计模式的广义定义.将其应用到面向对象软件的领域内,就形成了对设计模式的狭义定义.
         可以简单的认为:设计模式就是解决某个特定的面向对象软件问题的特定方法, 并且已经上升到理论程度。
         框架与设计模式的区别:
         1,设计模式和框架针对的问题域不同.设计模式针对面向对象的问题域;框架针对特定业务的问题域
         2,设计模式比框架更为抽象.设计模式在碰到具体问题后,才能产生代码;框架已经可以用代码表示
         3,设计模式是比框架更小的体系结构元素.框架中可以包括多个设计模式
         设计模式就像武术中基本的招式.将这些招式合理地纵组合起来,就形成套路(框架),框架是一种半成品.
     
    8.C++和C定义结构的分别是什么。
         C language 的结构仅仅是数据的结合
         C plus plus的struct 和 class 其实具备几乎一样的功能,只是默认的访问属性不一样而已。
     
    9.构造函数可否是虚汗数,为什么?析构函数呢,可否是纯虚的呢?
         构造函数不能为虚函数,要构造一个对象,必须清楚地知道要构造什么,否则无法构造一个对象。
         析构函数可以为纯虚函数。
     
    10.拷贝构造函数相关问题,深拷贝,浅拷贝,临时对象等。
         深拷贝意味着拷贝了资源和指针,而浅拷贝只是拷贝了指针,没有拷贝资源
         这样使得两个指针指向同一份资源,造成对同一份析构两次,程序崩溃。
         临时对象的开销比局部对象小些。
     
    11.结合1个你认为比较能体现OOP思想的项目,用UML来描述。(最好这个项目继承,多态,虚函数都有体现)这个问题大概会占面试时间的一半,并且会问很多问题,一不小心可能会被问住)
    12.基类的有1个虚函数,子类还需要申明为virtual吗?为什么。
         不申明没有关系的。
         不过,我总是喜欢显式申明,使得代码更加清晰。
     
    13.C也可以通过精心封装某些函数功能实现重用,那C++的类有什么优点吗,难道仅仅是为实现重用。
         并不仅仅是这样的。
         OOD,OOP从根本上改变了程序设计模式和设计思想,具备重大和深远的意义。
         类的三大最基本的特征:封装,继承,多态.
     
    14.C++特点是什么,如何实现多态?画出基类和子类在内存中的相互关系。
         多态的基础是继承,需要虚函数的支持,简单的多态是很简单的。
         子类继承父类大部分的资源,不能继承的有构造函数,析构函数,拷贝构造函数,operator=函数,友元函数等等
     
    15.为什么要引入抽象基类和纯虚函数?
         主要目的是为了实现一种接口的效果。
     
    16.介绍一下模板和容器。如何实现?(也许会让你当场举例实现)
         模板可以说比较古老了,但是当前的泛型编程实质上就是模板编程。
         它体现了一种通用和泛化的思想。
         STL有7种主要容器:vector,list,deque,map,multimap,set,multiset.
     
    17.你如何理解MVC。简单举例来说明其应用。
         MVC模式是observer 模式的一个特例,典型的有MFC里面的文档视图架构。
     
    18.多重继承如何消除向上继承的二义性。
         使用虚拟继承即可.
    


    2楼2009-03-31 21:39
    回复
      • 123.112.125.*
      1. 以下三条输出语句分别输出什么?[C易]
      char str1[] = "abc";
      char str2[] = "abc";
      const char str3[] = "abc";
      const char str4[] = "abc";
      const char* str5 = "abc";
      const char* str6 = "abc";
      cout << boolalpha << ( str1==str2 ) << endl; // 输出什么?
      cout << boolalpha << ( str3==str4 ) << endl; // 输出什么?
      cout << boolalpha << ( str5==str6 ) << endl; // 输出什么?
      2. 非C++内建型别 A 和 B,在哪几种情况下B能隐式转化为A?[C++中等]
      答:
      a. class B : public A { ……} // B公有继承自A,可以是间接继承的
      b. class B { operator A( ); } // B实现了隐式转化为A的转化
      c. class A { A( const B& ); } // A实现了non-explicit的参数为B(可以有其他带默认值的参数)构造函数
      d. A& operator= ( const A& ); // 赋值操作,虽不是正宗的隐式类型转换,但也可以勉强算一个
      3. 以下代码中的两个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;


      3楼2009-03-31 21:41
      回复
        • 123.112.125.*

        (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
        #include <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
        


        5楼2009-03-31 22:09
        回复
          • 123.112.125.*

          9. 结构与联合有和区别?
          1. 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。
          2. 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。
          10. 下面关于“联合”的题目的输出?
          a)
          #include <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];
          


          6楼2009-03-31 22:09
          回复
            • 123.112.125.*

            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. #include<file.h> 与 #include "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
            


            7楼2009-03-31 22:09
            回复
              • 123.112.125.*

              在模块B中引用该函数:
              // 模块B实现文件 moduleB.cpp
              #include "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"
              {
              #include "cExample.h"
              }
              而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。
              C++引用C函数例子工程中包含的三个文件的源代码如下:
              #ifndef C_EXAMPLE_H
              #define C_EXAMPLE_H
              extern int add(int x,int y);
              #endif
              #include "cExample.h"
              int add( int x, int y )
              {
              return x + y;
              }
              // c++实现文件,调用add:cppFile.cpp
              extern "C"
              {
              #include "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
              


              8楼2009-03-31 22:09
              回复
                • 123.112.125.*

                extern "C" int add( int x, int y );
                #endif
                //C++实现文件 cppExample.cpp
                #include "cppExample.h"
                int add( int x, int y )
                {
                return x + y;
                }
                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。对于这两个函数的调用,在编译器间就已经确定了,是静态的。也就是说,它们的地址在编译期就绑定了(早绑定),因此,重载和多态无关!
                重写:和多态真正相关。当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态的调用属于子类的该函数,这样的函数调用在编译期间是无法确定的(调用的子类的虚函数的地址无法给出)。因此,这样的函数地址是在运行期绑定的(晚绑定)。


                9楼2009-03-31 22:09
                回复
                  (六)
                  1) 什么是预编译,何时需要预编译:总是使用不经常改动的大型代码体。
                  程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况下,可以将所有包含文件预编译为一个预编译头。
                  2) char * const p;
                    char const * p
                    const char *p
                    上述三个有什么区别?
                    char * const p; //常量指针,p的值不可以修改
                    char const * p;//指向常量的指针,指向的常量值不可以改
                    const char *p; //和char const *p
                  3) 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是指针,它们指向相同的常量区域。
                  4) 以下代码中的两个sizeof用法有问题吗?
                    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。
                  5) 一个32位的机器,该机器的指针是多少位?
                  指针是多少位只要看地址总线的位数就行了。80386以后的机子都是32的数据总线。所以指针的位数就是4个字节了。
                  6) main()
                    {
                     int a[5]={1,2,3,4,5};
                  


                  删除|11楼2009-04-14 16:31
                  回复

                      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位。对于位域的定义尚有以下几点说明: 
                      一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如: 
                      struct bs 
                      { 
                       unsigned a:4 
                       unsigned :0 
                       unsigned b:4 
                       unsigned c:4 
                      } 
                      在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。 
                      由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说不能超过8位二进位。 
                      位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如: 
                      struct k 
                      { 
                       int a:1 
                       int :2 
                       int b:3 
                       int c:2 
                      }; 
                      从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。 
                      位域的使用位域的使用和结构成员的使用相同,其一般形式为:位域变量名?位域名位域允许用各种格式输出。 
                      main()
                      { 
                       struct bs 
                       { 
                        unsigned a:1; 
                        unsigned b:3; 
                        unsigned c:4; 
                       }
                       bit,*pbit; 
                       bit.a=1; 
                       bit.b=7; 
                       bit.c=15; 
                       pri
                    47) 改错:
                      #include <stdio.h>
                      int main(void)
                      {
                        int **p;
                      int arr[100];
                        p = &arr;
                        return 0;
                      }
                      解答:搞错了,是指针类型不同,int **p; //二级指针&arr; //得到的是指向第一维为100的数组的指针
                      #include <stdio.h>
                      int main(void)
                      {
                       int **p, *q;
                       int arr[100];
                       q = arr;
                       p = &q;
                       return 0;
                      }
                    


                    删除|17楼2009-04-14 16:31
                    回复
                      48) 下面这个程序执行后会有什么错误或者效果:
                        #define MAX 255
                        int main()
                        {
                        unsigned char A[MAX],i;//i被定义为unsigned char
                        for (i=0;i<=MAX;i++)
                       A[i]=i;
                      return 0; 
                        }
                        解答:死循环加数组越界访问(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了..无限循环下去。
                      49) 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字节。
                      50) intel:
                        A.c 和B.c两个c文件中使用了两个相同名字的static变量,编译的时候会不会有问题?这两个static变量会保存到哪里(栈还是堆或者其他的)?
                      static的全局变量,表明这个变量仅在本模块中有意义,不会影响其他模块。他们都放在数据区,但是编译器对他们的命名是不同的。如果要使变量在其他模块也有意义的话,需要使用extern关键字。
                      51) 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字节。 
                        第二个最后会对照是不是结构体内最大数据的倍数,不是的话,会补成是最大数据的倍数。


                      删除|18楼2009-04-14 16:31
                      回复
                        • 221.221.165.*
                        (五)
                        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)
                        


                        19楼2009-04-14 16:31
                        回复
                          • 221.221.165.*

                          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. C++的类和C里面的struct有什么区别?
                          struct成员默认访问权限为public,而class成员默认访问权限为private
                          2. 析构函数和虚函数的用法和作用
                          析构函数是在对象生存期结束时自动调用的函数,用来释放在构造函数分配的内存。
                          虚函数是指被关键字virtual说明的函数,作用是使用C++语言的多态特性
                          3. 全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?
                          1) 全局变量的作用用这个程序块,而局部变量作用于当前函数
                          2) 前者在内存中分配在全局数据区,后者分配在栈区
                          3) 生命周期不同:全局变量随主程序创建和创建,随主程序销毁而销毁,局部变量在局部函数内部,甚至局部循环体等内部存在,退出就不存在
                          4) 使用方式不同:通过声明后全局变量程序的各个部分都可以用到,局部变量只能在局部使用
                          4. 有N个大小不等的自然数(1--N),请将它们由小到大排序.要求程序算法:时间复杂度为O(n),空间复杂度为O(1)。
                          void sort(int e[], int n)
                          {
                          int i;
                          int t;
                          for (i=1; i<n+1; i++)
                          {
                          t = e[e[i]];
                          e[e[i]] = e[i];
                          e[i] = t;
                          }
                          }
                          5. 堆与栈的去区别
                          A. 申请方式不同
                          Stack由系统自动分配,而heap需要程序员自己申请,并指明大小。
                          B. 申请后系统的响应不同
                          Stack:只要栈的剩余空间大于申请空间,系统就为程序提供内存,否则将抛出栈溢出异常
                          Heap:当系统收到程序申请时,先遍历操作系统中记录空闲内存地址的链表,寻找第一个大于所申请空间的堆结点,然后将该结点从空间结点链表中删除,并将该结点的空间分配给程序。另外,大多数系统还会在这块内存空间中的首地址处记录本次分配的大小,以便于delete语句正确释放空间。而且,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动将多余的那部分重新放入空闲链表。
                          C. 申请大小限制的不同
                          Stack:在windows下,栈的大小是2M(也可能是1M它是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
                          Heap:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
                          


                          21楼2009-04-14 16:31
                          回复
                            • 221.221.165.*

                            答案应该是4^3-1=63
                            规律是n^3-1(当n为偶数0,2,4)
                            n^3+1(当n为奇数1,3,5)
                            答案:63
                            9、用两个栈实现一个队列的功能?要求给出算法和思路!
                            设2个栈为A,B, 一开始均为空.
                            入队:
                            将新元素push入栈A;
                             出队:
                            (1)判断栈B是否为空;
                            (2)如果不为空,则将栈A中所有元素依次pop出并push到栈B;
                            (3)将栈B的栈顶元素pop出;这样实现的队列入队和出队的平摊复杂度都还是O(1), 比上面的几种方法要好。
                            10、在c语言库函数中将一个字符转换成整型的函数是atool()吗,这个函数的原型是什么?
                            函数名: atol
                            功 能: 把字符串转换成长整型数
                            用 法: long atol(const char *nptr);
                            程序例:
                            #include <stdlib.h>
                            #include <stdio.h>
                            int main(void)
                            {
                            long l;
                            char *str = "98765432";
                            l = atol(lstr);
                            printf("string = %s integer = %ld\n", str, l);
                            return(0);
                            }
                            11、对于一个频繁使用的短小函数,在C语言中应用什么实现,在C++中应用什么实现?
                            c用宏定义,c++用inline
                            12、直接链接两个信令点的一组链路称作什么?
                            PPP点到点连接
                            13、接入网用的是什么接口?
                            14、voip都用了那些协议?
                            15、软件测试都有那些种类?
                            黑盒:针对系统功能的测试 白合:测试函数功能,各函数接口
                            16、确定模块的功能和模块的接口是在软件设计的那个队段完成的?
                            概要设计阶段
                            17、enum string
                            {
                            x1,
                            x2,
                            x3=10,
                            x4,
                            x5,
                            }x;
                            问x= 0x801005,0x8010f4;
                            18、unsigned char *p1;
                            unsigned long *p2;
                            p1=(unsigned char *)0x801000;
                            p2=(unsigned long *)0x810000;
                            请问p1+5= 0x801005;
                            p2+5= 0x801014;
                            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。
                            


                            24楼2009-04-14 16:32
                            回复
                              • 221.221.165.*

                              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 常量进行调试,但是不能对宏常量进行调试。
                              


                              25楼2009-04-14 16:32
                              回复