zoukankan      html  css  js  c++  java
  • 10道C++输出易错笔试题收集

    下面这些题目都是我之前准备笔试面试过程中积累的,大部分都是知名公司的笔试题,C++基础薄弱的很容易栽进去。我从中选了10道简单的题,C++初学者可以进来挑战下,C++大牛也可以作为娱乐玩下(比如下面的第6题)。为了便于大家思考,将题目与答案分开,不过无论题目本身如何,我觉得后面的解析过程更值得学习,因为涉及很多我们学习C++过程中必知必会的小知识点 。

     

    第一部分:题目

    1. 如下函数,在32 bit系统foo(2^31-3)的值是:()

       int foo(int x)
       {
           return x&-x;
       }

      A:0 B: 1 C: 2 D: 4

    2. 运算符优先级
      unsigned char i=0x80;
      printf("0x%x ", ~i>>3+1);
      输出什么?

    3. 静态对象是否调用构造函数?

       #include <iostream>
       using namespace std;
      
       class A
       {
       public:
           A() { cout << "A's Constructor Called " << endl;  }
       };
      
       class B
       {
           static A a;
       public:
           B() { cout << "B's Constructor Called " << endl; }
       };
      
       int main()
       {
           B b;
           return 0;
       }

       

    4. union问题

       #include <stdio.h>
      
       union
       {
           int i;
           char x[2];
       }a;
       int main()
       {
           a.x[0] = 10;
           a.x[1] = 1;
           printf("%d",a.i);
           return 0;
       }

       

    5. 下面代码会报错吗?为什么?

      class A {
      public:
        int m;
        void print() {  cout << "A
      ";  } 
      };
      A *pa = 0;
      pa->print();

       

    6. 下面代码的输出是什么?(非常考基础水平的一道题)

      char *c[] = {"ENTER","NEW","POINT","FIRST"};  
      char **cp[] = { c + 3 , c + 2 , c + 1 , c};  
      char ***cpp = cp;  
      int main(void)  
      {  
       printf("%s",**++cpp);  
       printf("%s",*--*++cpp+3);  
       printf("%s",*cpp[-2]+3);  
       printf("%s
      ",cpp[-1][-1]+1);  
      
       return 0;  
      }

       

    7. 结构体

      #include <stdio.h>
      struct data
      {
      int a;
      unsigned short b;
      };
      int main(void)
      {
      data mData;
      mData.b = 0x0102;
      char *pData = (char *)&mData;
      printf("%d %d", sizeof(pData), (int)(*(pData + 4)));
      return 0;
      }

       

    8. 改变string变量的值?

      #include <iostream>
      #include <string>
      using namespace std;
      void chg_str(string str) {
       str = "ichgit";
      }
      int main() {
       string s = "sarrr";
       chg_str(s);
      
       printf("%s
      ", s.c_str());
       cout << s << endl;
       return 0;
      }

       

    9. 静态变量的输出

      #include <stdio.h>
      int sum(int a) {
       int c = 0;
       static int b = 3; // 只执行一次
       c++;
       b += 2;
       return (a + b + c);
      }
      int main() {
       int i;
       int a = 2;
       for(i = 0; i < 5; ++i) {
           printf("%d
      ", sum(a));
       }
       return 0;
      }

       

    10. 返回值加const修饰的必要性
      你觉得下面两种写法有区别吗?

      int GetInt(void) 
      const int GetInt(void)

      如果是下面的呢?其中A 为用户自定义的数据类型。

      A GetA(void)
      const A GetA(void)

       


     
     
     

    第二部分:答案详细解析

    1. 如下函数,在32 bit系统foo(2^31-3)的值是:

       int foo(int x)
       {
           return x&-x;
       }

      A:0 B: 1 C: 2 D: 4 

      答案:C
      解释:我只想说注意运算符优先级,注意^是异或而不是幂次方。

    2. 运算符优先级
      unsigned char i=0x80;
      printf("0x%x ", ~i>>3+1);
      输出什么?

      输出:0xfffffff7(提示:+的优先级优于>>)
      如果将unsigned去掉,则输出0x7。

    3. 静态对象是否调用构造函数?

      #include <iostream>
       using namespace std;
      
       class A
       {
       public:
           A() { cout << "A's Constructor Called " << endl;  }
       };
      
       class B
       {
           static A a;
       public:
           B() { cout << "B's Constructor Called " << endl; }
       };
      
       int main()
       {
           B b;
           return 0;
       }

      输出:

      B's Constructor Called

      解释:上面的程序只是调用了B的构造函数,没有调用A的构造函数。因为静态成员变量只是在类中声明,没有定义。静态成员变量必须在类外使用作用域标识符显式定义。
      如果我们没有显式定义静态成员变量a,就试图访问它,编译会出错,比如下面的程序编译出错:

      #include <iostream>
       using namespace std;
      
       class A
       {
           int x;
       public:
           A() { cout << "A's constructor called " << endl;  }
       };
      
       class B
       {
           static A a;
       public:
           B() { cout << "B's constructor called " << endl; }
           static A getA() { return a; }
       };
      
       int main()
       {
           B b;
           A a = b.getA();
           return 0;
       }

       

      输出:

      Compiler Error: undefined reference to `B::a

      如果我们加上a的定义,那么上面的程序可以正常运行,
      注意:如果A是个空类,没有数据成员x,则就算B中的a未定义也还是能运行成功的,即可以访问A。

      #include <iostream>
       using namespace std;
      
       class A
       {
           int x;
       public:
           A() { cout << "A's constructor called " << endl;  }
       };
      
       class B
       {
           static A a;
       public:
           B() { cout << "B's constructor called " << endl; }
           static A getA() { return a; }
       };
      
       A B::a;  // definition of a
      
       int main()
       {
           B b1, b2, b3;
           A a = b1.getA();
      
           return 0;
       }

      输出:

      A's constructor called
      B's constructor called
      B's constructor called
      B's constructor called

      上面的程序调用B的构造函数3次,但是只调用A的构造函数一次,因为静态成员变量被所有对象共享,这也是它被称为类变量的原因。同时,静态成员变量也可以通过类名直接访问,比如下面的程序没有通过任何类对象访问,只是通过类访问a。

       int main()
       {
           // static member 'a' is accessed without any object of B
           A a = B::getA();
      
           return 0;
       }

      输出:

      A's constructor called

    4. union问题

      #include <stdio.h>
      
       union
       {
           int i;
           char x[2];
       }a;
       int main()
       {
           a.x[0] = 10;
           a.x[1] = 1;
           printf("%d",a.i);
           return 0;
       }

      输出:266,自己画个内存结构图就知道了,注意union的存放顺序是所有成员都从低地址开始存放。Union的大小为其内部所有变量的最大值,并且按照类型最大值的整数倍进行内存对齐。

       

    5. 下面代码会报错吗?为什么?

      class A {
      public:
        int m;
        void print() {  cout << "A
      ";  } 
      };
      A *pa = 0;
      pa->print();

      答案:正常输出。上面的代码可以这样理解(这非常重要):

      void print(A *this) {  cout << "A
      ";  } 
      A *pa = 0;
      print_A();

       

      也就是:并不是类没有初始化就不能调用类的成员函数,如果成员函数只是简单的打印个东西,没有调用类成员啥的就不会报段错误。

    6. 下面代码的输出是什么?(非常考基础水平的一道题)

      char *c[] = {"ENTER","NEW","POINT","FIRST"};  
      char **cp[] = { c + 3 , c + 2 , c + 1 , c};  
      char ***cpp = cp;  
      int main(void)  
      {  
       printf("%s",**++cpp);  
       printf("%s",*--*++cpp+3);  
       printf("%s",*cpp[-2]+3);  
       printf("%s
      ",cpp[-1][-1]+1);  
      
       return 0;  
      }

       

      解答:
      c是一个指针数组,每个数组元素都是char*类型的指针,值分别是那些字符串(的首地址):

      c[0] = "ENTER"
      c[1] = "NEW"
      c[2] = "POINT"
      c[3] = "FIRST"

      而[]和*是本质一样的运算,即c[i]=*(c+i)

      c和c+i都是char *[]类型,它可以退化成char **类型,再看cp,它正好是一个char **的数组,来看它的值:

      cp[0] = c + 3
      cp[1] = c + 2
      cp[2] = c + 1
      cp[3] = c

      引用后就有:cp[0][0]=*(c + 3)=c[3]="FIRST",以此类推。

      cp是char **[]类型,它可以退化成char ***类型,看最后的cpp,它正是char ***类型,它是一个指针变量,和上面两个不同,上面两个是数组。

      这样分析过后,下面的解析就一目了然了:

      • printf("%s",**++cpp);
        ++cpp的值是cp+1,引用一次后是cp[1]再引用是*cp[1]=c[2]="POINT",第一句的输出
      • printf("%s",*--*++cpp+3);
        再++cpp的值是cp+2,引用一次是cp[2]=c+1,再对这进行--,减后是c再引用是c[0]="ENTER"再+3,字符串指针指到"ER",输出是"ER"
      • printf("%s",*cpp[-2]+3);
        这时cpp的值是cp+2,cpp[-2]=*(cpp-2)=*(cp+2-2)=cp[0]=c+3,再引用是c[3]="FIRST",+3 字符串指针指到"ST",输出是"ST"
      • printf("%s ",cpp[-1][-1]+1);
        cpp还是cp+2,cpp[-1]=*(cpp-1)=*(cp+2-1)=cp[1]=c+2,再[-1]得*(c+2-1)=c[1]="NEW",+1字符串指针指到"EW",输出是"EW"。

    7. 结构体

      #include <stdio.h>
      struct data
      {
      int a;
      unsigned short b;
      };
      int main(void)
      {
      data mData;
      mData.b = 0x0102;
      char *pData = (char *)&mData;
      printf("%d %d", sizeof(pData), (int)(*(pData + 4)));
      return 0;
      }

      输出:4 2 

      说明:一般变量都是从高到低分配内存地址,但对于结构体来说,结构体的成员在内存中顺序存放,所占内存地址依次增高,第一个成员处于低地址处,最后一个成员处于最高地址处,但结构体成员的内存分配不一定是连续的,编译器会对其成员变量依据前面介绍的 “对齐”原则进行处理。

      补充知识点:

      除了栈以外,堆、只读数据区、全局变量地址增长方向都是从低到高的。

    8. 改变string变量的值?

      #include <iostream>
      #include <string>
      using namespace std;
      void chg_str(string str) {
       str = "ichgit";
      }
      int main() {
       string s = "sarrr";
       chg_str(s);
      
       printf("%s
      ", s.c_str());
       cout << s << endl;
       return 0;
      }

       

      输出:仍为“sarrr”。
      解释:string是传值参数,不能修改其值。要想改变string变量的值,可以改为传地址方式:

      #include <iostream>
      #include <string>
      using namespace std;
      void chg_str(string *str) {
       *str = "ichgit";
      }
      int main() {
       string s = "sarrr";
       chg_str(&s);
      
       printf("%s
      ", s.c_str());
       cout << s << endl;
       return 0;
      }

       

    9. 静态变量的输出

      #include <stdio.h>
      int sum(int a) {
       int c = 0;
       static int b = 3; // 只执行一次
       c++;
       b += 2;
       return (a + b + c);
      }
      int main() {
       int i;
       int a = 2;
       for(i = 0; i < 5; ++i) {
           printf("%d
      ", sum(a));
       }
       return 0;
      }

       

      输出:8 10 12 14 16
      解释:存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化,此后该初始化不再执行,相当于一次执行后就作废,静态局部变量保存了前次被调用后留下的值。

    10. 返回值加const修饰的必要性
      你觉得下面两种写法有区别吗?

      int GetInt(void) 
      const int GetInt(void)

      如果是下面的呢?其中A 为用户自定义的数据类型。

      A GetA(void)
      const A GetA(void)

       

      答案:没有任何区别。
      解释:如果函数返回值采用“值传递方式”,由于函数会把返回值复制到外部临时的存储单元中,加const 修饰没有任何价值。所以,对于值传递来说,加const没有太多意义。
      所以:

      • 不要把函数int GetInt(void) 写成const int GetInt(void)。
      • 不要把函数A GetA(void) 写成const A GetA(void)。

      在编程中要尽可能多的使用const(比如函数参数采用const&修饰),这样可以获得编译器的帮助,以便写出健壮性的代码

     

  • 相关阅读:
    第二阶段冲刺01
    第十三周进度总结
    单词统计续
    sys模块
    os模块
    random模块
    datetime模块
    time模块
    模块基础
    内置函数
  • 原文地址:https://www.cnblogs.com/lanxuezaipiao/p/4148155.html
Copyright © 2011-2022 走看看