曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 8757|回复: 11
打印 上一主题 下一主题

面试题目集锦[基本概念]

[复制链接]

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
跳转到指定楼层
楼主
发表于 2011-11-12 19:45:42 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
赋值语句-1
下面程序输出的结果是什么?
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;


int i = 1;


int main()
{
    int i = i;
    
    cout << i << endl;


    return 0;
}
A. main() 中的 i 是一个未定义的值。
B. main() 中的 i 值为 1。
C. 编译器不允许这种写法。
D. main() 中的 i 值为 0。
这段代码奇特的地方在于赋值语句 i = i; 这种做法,合法而不合理,编译器不会报错。main() 中的 i 是局部变量,赋值语句中的右值 i 不要认为是全局变量且值等于 1 的 i 。这是因为在 main() 中, int i = i; 从左到右,i 从声明的那一刻起就是可见的,所以右边的 i 也就是局部变量 i 。 所以正确答案为 A 。但是需要注意的是,在某些编译器上可能打印出的是一个随意数字,但用 g++ 来编译,一个未赋值的整型变量一般都会初始化为 0 。

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
沙发
 楼主| 发表于 2011-11-13 00:17:04 | 只看该作者

求运算结果

下面程序的输出结果如何:
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;


int main()
{
    int x = 2, y, z;
    x *= (y = z = 5);
    cout << x << endl;


    z = 3;
    x == (y = z);
    cout << x << endl;


    x = (y == z);
    cout << x << endl;


    x = (y & z);
    cout << x << endl;


    x = (y && z);
    cout << x << endl;


    y = 4;
    x = (y | z);
    cout << x << endl;
    
    x = (y || z);
    cout << x << endl;
    return 0;
}

运行输出:
./exp2
10
10
1
3
1
7
1
x *= (y = z = 5); 这个语句中,5 赋值给 z ,z 再赋值给 y,然后有 x = x*y; ,所以 x 为 2*5 = 10 。
x == (y = z); 这个语句中,== 符号不是赋值运算符,它只判断左右两边的值是否相等,但不管相等与否,都并不会 x 的值,所以 x 的值仍然为 10 。
x = (y == z); 这个语句中,y == z 的结果要么是 1 要么是 0,所以 x 的结果要么是 1 要么为 0 。因为上面的赋值语句已经令 y 等于 z ,所以这里 x 的值为 1 。
在 x = (y & z); 这个语句中,y 和 z 做按位与运算,且因为 y 和 z 的值都为 3,所以 x 的值也为 3 。
x = (y && z); 这个语句中,只要 y 和 z 同时不为零,那么 x 就为 1,否则 x 为 0 。
x = (y | z); 这个语句中,y 和 z 做按位或运算,此时 y 为 4,z 为 3,所以运算后的结果为 7 。
x = (y || z); 这个语句中,只要 y 或 z 不为 0 ,那么 x 为 1 。

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
板凳
 楼主| 发表于 2011-11-13 01:05:59 | 只看该作者

求代码的结果

下面的代码结果是什么?
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;


int func (int x)
{
    int count = 0;
    while(x) {
        count++;
        x = x & (x - 1);
    }
    return count;
}


int main()
{
    cout << func(9999) << endl;
    return 0;
}

该段程序的关键在于 x = x & (x - 1); 这条语句上。这里的规律是这样的,当一个数和比它小于 1 的数做与运算时,运算结果末尾连续的 0 的个数比原来的数末尾连续的 0 的个数大 1 。比如 1111 & 1110 结果为 1110 ,1110 比 1111 多出一个 0 ;又如 1100 & (1100 - 1) , 即 1100 & 1011 的结果为 1000 ,1000 末尾连续的 0 的个数为 3 个,而 1100 的末尾连续的 0 的个数为 2 个。换句话来说就是,原来的数如果从开头算起,其连续 1 的个数比从结果连续 1 的个数多 1 个。

在上题中,9999 化为二进制数为 10011100001111 。因此,每一次循环后的结果(x)比原来的数减少一个 1,而多出一个 0,这样一来,func 函数的功能就是计算二进制里包含 1 的数量。所以要最后的输出结果为 8 。

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
地板
 楼主| 发表于 2011-11-14 12:01:12 | 只看该作者

i++

下面两段代码的输出结果有什么不同?

代码一
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;

int main()
{
    int a, x;
    for (a = 0, x = 0; a <= 1 && !x++; a++) {
        a++;
    }
    cout << a << “ ” << x << endl;
    return 0;
} 


分析
一开始,a 和 x 都被初始化为 0,然后进行 for 中的条件判断,a <= 1 条件成立,!x++ 条件成立,接着执行 a++ 语句,此时 x 也自增为 1。当 a++ 执行完毕后 a 值为 1,接着再执行 for 中的 a++ ,当继续回到判断条件时,a 的值已经为 2,循环条件不成立,注意此时不会再去判断 && 符号后的 x 情况而直接退出循环,所以最后 a 的值为 2 ,x 的值仍然为 1。

代码二
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;

int main()
{
    int a, x;
    for (a = 0, x = 0; a <= 1 && !x++;) {
        a++;
    }
    cout << a << " " << x << endl;
    return 0;
} 

分析
在上面代码中,在 a++ 后,x 和 a 的值都为 1 。然后再次进入循环判断,a <= 1 语句成立,接着执行 !x++ 判断,此时条件不成立,退出循环,需要注意的是,在退出循环后,x++ 仍然是要执行的,所以最后 x 的值为 2,而 a 的值为 1 。

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
5#
 楼主| 发表于 2011-11-14 13:50:19 | 只看该作者

函数参数入栈顺序考察

下面代码的输出结果是什么?
[C++] 纯文本查看 复制代码
#include <stdio.h>

int main()
{
    int b = 3;
    int arr[] = {6, 7, 8, 9, 10};

    int *ptr = arr;

    *(ptr++) += 123;

    printf ("%d, %d\\n", *ptr, *(++ptr));

    return 0;
}


在执行 *(ptr++) += 123; 后,ptr 指向 arr 中的第 2 个元素 7 。注意,在 C 语言中函数参数入栈顺序是从右到左,所以在 printf() 函数中,首先入栈的是 *(++ptr) ,也就是 ptr 先指向 8 后,再将 8 入栈保存( *(++ptr) 也可以写成 *++ptr ,因为 ++ 的优先级比 * 符号要高),接着 *ptr 入栈保存。因此最后的结果输出是 8,  8 。

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
6#
 楼主| 发表于 2011-11-14 14:24:21 | 只看该作者

编程风格

下面两段程序分别有两种写法,你更青睐哪一种呢?
程序一写法1
[C++] 纯文本查看 复制代码
if ( 'A' == a ) {
   a++;
}

程序一写法2
[C++] 纯文本查看 复制代码
if ( a == 'A' ) {
a++;
}

比较说明
第 1 种写法比较好些。这是因为,如果把 '==' 符号误写作 '=' 的话,因为编译器不允许对常量复制,所以会报错;然而换作第 2 种写法,编译器就不会报错,并且是执行了赋值语句,而正常的赋值语句永远为真。

程序二写法1
[C++] 纯文本查看 复制代码
for (i = 0; i < 8; i++) {
                x = i+Y + J*7;
                printf ("%d\\n", x);
        }


程序二写法2
[C++] 纯文本查看 复制代码
 S = Y + J*7;
        for (i = 0; i < 8; i++) {
                printf ("%d\\n", i + S);
        }


比较说明
第 2 种写法较好些,因为将部分运算结果为常量的运算放到循环体外,不用在每次循环时都重复进行相同的运算,从而提高了效率。缺点是程序不够简洁。

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
7#
 楼主| 发表于 2011-11-15 12:49:16 | 只看该作者

类型转换一

下面程序的结果是多少?
[C++] 纯文本查看 复制代码
#include <iostream>
using namespace std;

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

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

    return 0;
}

运行输出:
1
0xbfeb3c4c
1065353216
false
0
0xbfeb3c48
0
true
这里面需要特别注意的一个地方是 (int &)a 。首先这里要确定的是输出的是 a 的地址还是 a 的值,如果是 a 的值,那么该值又是多少?可以单独观察 cout << (int &)a << endl; 这条语句的反汇编代码:
[Plain Text] 纯文本查看 复制代码
  
        movl    $0x3f800000, %eax
        movl    %eax, 28(%esp)
        leal    28(%esp), %eax
        movl    (%eax), %eax
        movl    %eax, 4(%esp)
        movl    $_ZSt4cout, (%esp)

上面,0x3f800000 正是浮点数 1.0 的表示方法。汇编代码里,先将 0x3f800000 这个数放入栈中,然后取得其存放的地址( leal    28(%esp), %eax),接着再次取出改值,然后传递到 cout 函数中。由此可见,(int &)a 表示的还是 a 的值,只不过在这里它强制将 a 的地址表示为整型地址,所以输出来的结果也就有所不同,1065353216 这个树就是 0x3f800000 的十进制形式。

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
8#
 楼主| 发表于 2011-11-15 13:34:59 | 只看该作者

类型转换二

下面程序的结果是多少?
[C++] 纯文本查看 复制代码
#include <stdio.h>

int main()
{
    unsigned int a = 0xFFFFFFF7;
    unsigned char i = (unsigned char)a;

    char *b = (char *)&a;

    
    printf("%08x, %08x\\n", i, *b);

    return 0;
}

运行输出
000000f7, fffffff7
说明
unsigned int 变量赋值给 unsigned char 变量时会发生字节截断,即 3位 和高于 3位 的部分会被程序自动丢弃。但是如果是赋值给 char 型,那么需要分情况讨论,一种是 unsigned int 中的低字节大于 0x80 的(符号位为1),一种是小于 0x80 的(符号位为0)。如果是小于 0x80 的,那么也发生截断,比如上面 a 的值为 0xFFFFFF78 ,那么输出 i 的值就是 00000078 ;如果是大于 0x80 的,那么不会发生截断,而是将符号位(1)进行扩展,比如 a 的值为 0xFFFFFF81,那么输出 i 的值为 0xFFFFFF81 。

char *b = (char *)&a; 这条语句是将一个 unsigned int 型的指针强制转换成一个 char 型的指针。注意,这只是指针转换,而非将其中的数值进行 char 类型转换。转换后,b 变量增加或减少的 1 个单位是以字节来算,而 a 变量仍然是以 4 个字节来算(32 位平台)。

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
9#
 楼主| 发表于 2011-11-15 16:40:31 | 只看该作者

运算符问题

下面程序的结果是多少?
[C++] 纯文本查看 复制代码
#include <stdio.h>

int main()
{
        unsigned char a = 0xA5;
        unsigned char b = ~a >> 4 + 1;

        printf ("b = %d\\n", b);

        return 0;
}


这道题目考察两个知识点,一个是类型转换,一个是运算符优先级。在 ~a >> 4 + 1 中,~ 取反运算符的级别最高,其次到 + 符号,然后再到移位符号 >> 。那么我们可能会直接认为:a 取反后的值为 0101 1010 (0xA5 二进制为1010 0101) ,然后向右移动 5 位,最后值为 0000 0010 。实际上这是错误的,因为还遗漏了一种类型转换的情况,如果观察汇编代码会发现对 a 取反时会有如下片段的汇编代码(GNU 汇编语法):
[Plain Text] 纯文本查看 复制代码
movb    $-91, 31(%esp)
movzbl  31(%esp), %eax
notl    %eax


这里使用了 movzbl “领扩展指令”。所以实际上是先将 0xA5 扩展为 0000 0000 1010 0101 ,然后再取反,结果为:1111 1111 0101 1010 。最后再将该结果右移 5 位得 0000 0111 1111 1010 ,又由于是 unsigned char 类型,只能表示低 8 位,即 250 。

关于 movzbl 和 movsbl 指令可参考:http://www.groad.net/bbs/read.php?tid-5251.html

4917

主题

5879

帖子

3万

积分

GROAD

曲径通幽,安觅芳踪。

Rank: 6Rank: 6

积分
34382
10#
 楼主| 发表于 2011-11-15 17:03:17 | 只看该作者

判断 2 的 N 次方

用一个表达式,判断一个数 X 是否是 2^N 次方(2, 4, 6, 8, 16,...),不可用循环语句。

解析
2, 4, 6, 8, 16 这样的数转化为二进制分别为 10, 100, 1000, 10000 。这些数的一个特点是,用它和自身减一后的值做与运算结果为 0 。因此可以知道该表达式为 !(X & (X-1)) 。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2024-4-27 21:58 , Processed in 0.075579 second(s), 22 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表