[C++] 经典练习题与解析(一)

        reference:http://cppquiz.org/q

(1)

#include <iostream>

void f(int) { std::cout << 1; }
void f(unsigned) { std::cout << 2; }

int main() {
  f(-2.5);
}


答案:编译错误

        这个重载是不明确的,为什么呢?

        对于f(-2.5),有两个可以调用的函数,为了让编译器选择一个,其中一个函数需要比另外一个函数更合适,否则这个程序就是不合乎语法的。在我们这个例子中,这两个函数是同等合适的,这就导致了程序的不规范。

        根据C++标准:如果一个单参数函数的参数转换顺序比另一个重载函数更好,那么这个函数就是更合适的。那么,在给出的参数是有符号浮点数时,为什么int不比unsigned更好呢?

        所有的转换都有一个次序,double=>int和double=>unsignedint都属于浮点数到整数的转换,它们是等价地位的。所以不存在谁更合适一说。

 

        我的补充:如果直接写unsigned作为参数,那么编译器会将其默认为unsigned int


(2)

#include <iostream>
using namespace std;
 
class C {
public:
  explicit C(int) {
    std::cout << "i";
  };
  C(double) {
    std::cout << "d";
  };
};

int main() {
  C c1(7);
  C c2 = 7;
}

 答案: id

        这是两个初始化的例子。第一个 C c1(7),叫做直接初始化,第二个C c2 = 7,叫做拷贝初始化。在大多数例子中它们都是等价的,但是在这个例子中,它们却不是,因为int构造函数是explicit的。
        关键点在于C++标准中这样的规定:仅当直接初始化或者显示使用强制转换时,一个explicit的构造函数才能像非explicit的构造函数一样构造。(我的补充:不支持隐式转换)
        那么,直接初始化是怎么定义的呢?
        C++标准中指出:初始化在如下情况发生:

            T x(a);

            T x{a};

        (…)被称作直接初始化。
        所以在第二个例子中,int构造函数不被考虑。反之,int被转换为double传入double的构造函数中。

(3)

#include <iostream>
using namespace std;

template <class T> void f(T) {
  static int i = 0;
  cout << ++i;
} 

int main() {
  f(1);
  f(1.0);
  f(1);
}

答案:112


        对于每次函数调用,模版函数都会实例化出拥有所有静态变量的拷贝的函数。
        这就意味着,我们得到了两个f的实例,一个是T= int,另一个是T = double。所以,i在两次int调用时是共享的变量,但是在double中却不是。

(4)

#include <iostream>
using namespace std;

size_t get_size_1(int* arr)
{
  return sizeof arr;
}

size_t get_size_2(int arr[])
{
  return sizeof arr;
}

size_t get_size_3(int (&arr)[10])
{
  return sizeof arr;
}

int main()
{
  int array[10];
  cout << (sizeof(array)== get_size_1(array));
  cout << (sizeof(array)== get_size_2(array));
  cout << (sizeof(array)== get_size_3(array));
}


答案:001

        这个问题比较了以数组为参数的函数的三种传参方式,其中有两种是完全一样的。

        在main中,数组的类型就是数组,因此sizeof运算符返回的就是数组字节大小。(C++标准:对于数组而言,sizeof运算的结果就是数组所占的总字节数,这就意味着有n个元素的数组的大小就是每个元素大小的n倍。

        在get_size_3函数中,参数是一个大小为10的数组的引用,因此sizeof操作符返回了数组的字节大小。(C++标准:sizeof对于引用或者引用类型,结果是引用类型的大小。)

        在get_size_1和get_size_2中,参数是指针类型,所以sizeof返回的就是指针的大小。虽然get_size_2的参数是数组,但是它退化成了指针。(C++标准:所有数组类型的参数都会退化为指针类型)


(5)


#include <iostream>

int f(int &a, int &b) {
  a = 3;
  b = 4;
  return a + b;
}

int main() {
  int a = 1;
  int b = 2;
  int c = f(a, a);
  std::cout <<a << b << c;
}

答案: 428

 

        当f()的两个参数都为a时,两个参数都引用了同一个变量。这被称为混淆现象。首先a被设置为3,然后又被设置为4,所以返回的结果是4+4.b从来没有被修改过

(6)

#include <iostream>

int main() {
  void * p = &p;
  std::cout <<bool(p);
}

答案:1

 

        C++标准:指针名字的定义发生在完整的定义后,以及初始化前。这就意味着void* p = &p是合乎语法的C++,因为它能够用存在的变量的地址来初始化p,哪怕那是它自身的地址。

        p的值是未知的,但是肯定不是空指针值。所以计算出来的结果是1.

(7)

#include <iostream>

struct X {
  X() { std::cout <<"X"; }
};

struct Y {
  Y(const X &x){ std::cout << "Y"; }
  void f() { std::cout<< "f"; }
};

int main() {
  Y y(X());
  y.f();
}

答案:编译错误

 

        编译错误发生在y.f()这一行,但是真正的错误源头是Y y(X());

        这条语句可以被看作是变量定义(这也是编程者的意图),但是也可以被看作是函数y的声明,它返回了类型为Y的对象,把一个函数(没有参数,返回类型为X的对象)作为参数。

        编译器根据标准选择了第二种解析方式,这也就意味着y.f()不能通过编译(因为y现在是一个函数,而不是一个类型为Y的对象)

        为了解决这一问题,把Y y(X())改为Y y{X{}}或者Y y((X()))

(8)

#include <iostream>

extern "C" int x;
extern "C" { int y; }

int main() {
        std::cout <<x << y;
        return 0;
}


答案:未定义行为

      C++标准:在C/C++混合编程中,就像extern标识符的效果一样,语句被视为直接的声明

      extern "C" int x; //只是声明

     extern "C" { int y; } //是定义


       C++标准:所有程序都应当对每个所有的非内联的函数或变量包含恰好一个定义。

 

       结果:x从未被定义,所以编译可能出错。这个程序的行为是未定义的。

(9)


#include <iostream>

int main() {
  int a = 10;
  int b = 20;
  int x;
  x = a, b;
  std::cout <<x;
}

答案: 10

 

        逗号运算符有最低的优先级(比=还要低)

        在这个例子中,它把两个表达式x = a和b分开。

        首先x=a被计算,把x置为10.

         然后b被计算,什么也没有发生。

(10)

#include <iostream>

struct X {
  X() { std::cout << "X"; }
};


int main() { X x(); }
 


答案:没有输出

    

        X x(),只是函数声明,并不是变量定义。拿掉圆括号,或者改为{},程序就会输出X。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值