C/C++面试题-1

1. C语言中的 sizeof 问题
(1)类型转换的问题

    #include <stdio.h>  
    #define NUM (sizeof(arr) / sizeof(arr[0]))        
    int arr[] = { 1, 2, 3, 4, 5, 6, 7 };        
    int main()  
    {  
        int i;         
        for( i = -1; i <= ( NUM - 2 ); i++)  
        {       
        printf("%d\n",arr[i+1]);  
        }            
        return 0;  
    } 


输出的结果是空的!
解释:我们要知道的是:sizeof 返回的是一个unsinged int的值!那么在for循环的时候,i = -1就会向后面转型,将其值转化为sizeof的值,那么我们很清楚,-1转化的sizeof的值是很大的一个正整数!那么很显然就直接不符合i <= ( NUM - 2 )的条件了,所以就不会输出!!!
(2)sizeof是操作符

    #include <stdio.h>  
    int main()  
    {  
        int i;  
        i = 10;        
        printf("i : %d\n",i);  
        printf("sizeof(i++) is: %d\n",sizeof(i++));  
        printf("i : %d\n",i);  
          return 0;  
    } 


结果是
 i : 10
 sizeof(i++) is: 4
 i : 10
为什么第三个还是输出10呢?!
解释:解决这个问题就是要理解编译器编译原理。 我们要知道sizeof仅仅是一个操作符而已并不是函数,sizeof要做的仅仅是获得i++的字节数,那么所以在编译的时候就直接用4代替了i++了,反正编译器知道结果都是一样,所以最终i++并没有执行!

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. C++对象声明的陷阱

struct Test
{
    Test( int ) {}
    Test() {}
    void fun() {}
};
void main( void )
{
    Test a(1);
    a.fun();
    Test b();//这儿的问题,C++中默认构造函数生成实例不用加括号,如果加上括号这儿就像定义了一个函数
    b.fun();
}

4. 以下代码有什么问题?[C++易]
cout << (true?1:"1") << endl;
这儿条件操作符中的前后的类型要一致
一个条件操作符是下面这样的形式:
exp1 ? exp2 : exp3
表达式exp1总是会被求值。exp2和exp3是否被执行依赖于exp1的值,如果exp1为真则exp2会被求值,否则exp3被求值。
Side Effect:
在执行exp2或者exp3之前,exp1的所有side effect必须全部求值或者更新完成,因为条件操作符的第一个操作数求值之后就是一个sequence point。如果exp2和exp3都有side effect,那么只有一个会被求值。
返回类型:
条件操作符的返回类型取决于exp2和exp3类型,编译器会检查exp2(可能是一个class type)能否转换为exp3或者exp3能否转换为exp2,如果两个都不满足,编译器就会抛出一个错误。

5. C++中的空类,默认产生哪些类成员函数?[C++易]
答:

class Empty
{
public:
    Empty();                          // 缺省构造函数
    Empty( const Empty& );            // 拷贝构造函数
    ~Empty();                         // 析构函数
    Empty& operator=( const Empty& ); // 赋值运算符
    Empty* operator&();               // 取址运算符
    const Empty* operator&() const;   // 取址运算符 const
};

 

6. 转换为引用类型:浮点数内存表示

   float a = 1.0f;
   cout << (int)a << endl;
   cout << (int&)a << endl;
   cout << boolalpha << ( (int)a == (int&)a ) << endl;

   float b = 0.0f;
   cout << (int)b << endl;
   cout << (int&)b << endl;
   cout << boolalpha << ( (int)b == (int&)b ) << endl;

问:上述代码的输出结果是什么?
这种转换为引用类型的情况的确比较少见,但有些时候必须用它!(EC++ 3rd)中讲解copy assignment operator时就提到过一种必须用它的情形。大概需要记住的就是,转向普通类型,则返回的其实是一个为你新创建的临时对象;而转换为引用类型,则返回的其实是你要转换的那个对象本身,只是reinterpret了一下而已,原来的对象内容也没改变。
"转换为引用类型"操作的本质: "(int&)a 等价于 *(int*)(&a); 就是把原始a 的内存里面数据按照整形输出。因此不等价于(int)a;"
另外的解释:(int&)a 不经过转换, 直接得到a在内存单元的值,并将其转换成整数输出。

 

7. C语言字符串与字符数组

以下输出语句分别输出什么?[C易]

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";
char* str9 = "efg";
char* str0 = "efg";
cout<<"str1="<<(void*)str1<<" "<<"str2="<<(void*)str2<<" ";
cout << boolalpha << ( str1==str2 ) << endl; // 输出什么?
cout<<"str3="<<(void*)str3<<" "<<"str4="<<(void*)str4<<" ";
cout << boolalpha << ( str3==str4 ) << endl; // 输出什么?
cout<<"str5="<<(void*)str5<<" "<<"str6="<<(void*)str6<<" ";
cout << boolalpha << ( str5==str6 ) << endl; // 输出什
cout<<"str7="<<(void*)str7<<" "<<"str8="<<(void*)str8<<" ";
cout << boolalpha << ( str7==str8 ) << endl; // 输出什
cout<<"str9="<<(void*)str9<<" "<<"str0="<<(void*)str0<<" ";
cout << boolalpha << ( str9==str0 ) << endl; // 输出什

在linux平台下的结果是:

这个应该比较好理解,数组是要分配空间的,因为它的内容是可以改变的,所以地址是不同的。而字符串是不允许改变的,相同的字符串应该是统一存放的。

 

8. 其他类型到bool的隐式转换

下面的代码输出是什么?

#include <iostream>
#include <string>
using namespace std ;
void a(bool input) {
cout << "I amd first" <<endl;
cout <<input <<endl ;
}
void a(const string &input) {
cout<< "I amd second" <<endl ;
cout<<input <<endl ;
}
int main( int argc, char **argv)
{
a("str") ; // 是调用第二个a函数吗?
a(string("str")) ;
return 0 ;
}

 

输出结果竟然是:

char*类型的"str"居然被隐式转化成了bool类型

然后做实验验证一下:

#include <string>
using namespace std;
int main(int argc, const char *argv [])
{
int a =1;
char *b= "wo";
float c = 1.1;
bool x ;
x = a;
x = b;
x = c;
return 0;
}

 

int,char*,float确实都可以隐式转化成bool而不会有任何警告,导致出现问题很不容易发现,这里在重载函数的时候确实需要万分注意。

 

9. 堆排序在大数据中的应用

堆排序的时间复杂度为O(nlogn),最坏的情况也是这个时间复杂度,空间复杂度是O(1)。但是堆排序是不稳定的!

在面试过程中,很多时候都会用到堆排序,比如下面的题目都是堆排序的典型题目:

1.给你100w个数据求最大的10个元素。这个时候我们可以使用小顶堆!这个是为什么呢!

2.给你100w个数据求最小的10个元素。这个时候我们可以使用大顶堆!这个是为什么呢!

相信会有很多同学会问出上面的两个疑问,答案其实很简单,在求最大的元素的时候,我们建立一个有10个元素的小顶堆,那么堆顶元素肯定是最小的,然后拿剩余的元素和堆顶进行比较,如果比堆顶大,就替换这个元素,然后调整堆,调整完之后堆顶依然是10个元素中最小的,依次比较剩余的元素。

堆排序与直接插入排序的区别

直接选择排序中,为了从R[1..n]中选出关键字最小的记录,必须进行n-1次比较,然后在R[2..n]中选出关键字最小的记录,又需要做n-2次比 较。事实上,后面的n-2次比较中,有许多比较可能在前面的n-1次比较中已经做过,但由于前一趟排序时未保留这些比较结果,所以后一趟排序时又重复执行 了这些比较操作。 堆排序可通过树形结构保存部分比较结果,可减少比较次数。

posted @ 2012-08-25 21:13  Mr.Rico  阅读(398)  评论(0编辑  收藏  举报