谜题 之 C语言

本篇文章展示了14个C语言的迷题以及答案,代码应该是足够清楚的,而且我也相信有相当的一些例子可能是我们日常工作可能会见得到的。通过这些迷题,希望你能更了解C语言。如果你不看答案,不知道是否有把握回答各个谜题?让我们来试试。

1、下面的程序并不见得会输出 hello-std-out,你知道为什么吗?

01.#include
<stdio.h>

02.#include
<unistd.h>

03.int main()

04.{

05.    while(1)

06.    {

07.        fprintf(stdout,"hello-std-out");

08.        fprintf(stderr,"hello-std-err");

09.        sleep(1);

10.    }

11.    return 0;

12.}

参考答案:stdout和stderr是不是同设备描述符。stdout是块设备,stderr则不是。对于块设备,只有当下面几种情况下才会被输入,1)遇到回车,2)缓冲区满,3)flush被调用。而stderr则不会。

2、下面的程序看起来是正常的,使用了一个逗号表达式来做初始化。可惜这段程序是有问题的。你知道为什么呢?

1.#include
<stdio.h>

2. 

3.int main()

4.{

5.    int a
= 1,2;

6.    printf("a
: %d "
,a);

7.    return 0;

8.}

参考答案:这个程序会得到编译出错(语法出错),逗号表达式是没错,可是在初始化和变量声明时,逗号并不是逗号表达式的意义。这点要区分,要修改上面这个程序,你需要加上括号: int a = (1,2);

3、下面的程序会有什么样的输出呢?

1.#include
<stdio.h>

2.int main()

3.{

4.    int i=43;

5.    printf("%d
"
,printf("%d",printf("%d",i)));

6.    return 0;

7.}

参考答案:程序会输出4321,你知道为什么吗?要知道为什么,你需要知道printf的返回值是什么。printf返回值是输出的字符个数。

4、下面的程序会输出什么?

01.#include
<stdio.h>

02.int main()

03.{

04.    float a
= 12.5;

05.    printf("%d
"
, a);

06.    printf("%d
"
, (int)a);

07.    printf("%d
"
, *(int *)&a);

08.    return 0;

09.}

参考答案

该项程序输出如下所示,

0

12

1095237632

原因是:浮点数是4个字节,12.5f 转成二进制是:01000001010010000000000000000000,十六进制是:0×41480000,十进制是:1095237632。所以,第二和第三个输出相信大家也知道是为什么了。而对于第一个,为什么会输出0,我们需要了解一下float和double的内存布局,如下:

  • float: 1位符号位(s)、8位指数(e),23位尾数(m,共32位)
  • double: 1位符号位(s)、11位指数(e),52位尾数(m,共64位)

然后,我们还需要了解一下printf由于类型不匹配,所以,会把float直接转成double,注意,12.5的float和double的内存二进制完全不一样。别忘了在x86芯片下使用是的反字节序,高位字节和低位字位要反过来。所以:

  • float版:0×41480000 (在内存中是:00 00 48 41)
  • double版:0×4029000000000000 (在内存中是:00 00 00 00 00 00 29 40)

而我们的%d要求是一个4字节的int,对于double的内存布局,我们可以看到前四个字节是00,所以输出自然是0了。

这个示例向我们说明printf并不是类型安全的,这就是为什么C++要引如cout的原因了。

5、下面,我们再来看一个交叉编译的事情,下面的两个文件可以编译通过吗?如果可以通过,结果是什么?

file1.c

1.int arr[80];

file2.c

1.extern int *arr;

2.int main()

3.{

4.    arr[1]
= 100;

5.    printf("%d
"
, arr[1]);

6.    return 0;

7.}

参考答案:该程序可以编译通过,但运行时会出错。为什么呢?原因是,在另一个文件中用 extern int *arr来外部声明一个数组并不能得到实际的期望值,因为他们的类型并不匹配。所以导致指针实际并没有指向那个数组。注意:一个指向数组的指针,并不等于一个数组。修改:extern int arr[]。(参考:ISO C语言 6.5.4.2 节)

6、请说出下面的程序输出是多少?并解释为什么?(注意,该程序并不会输出 “b is 20″)

01.#include
<stdio.h>

02.int main()

03.{

04.    int a=1;

05.    switch(a)

06.    {

07.        int b=20;

08.        case 1:

09.            printf("b
is %d "
,b);

10.            break;

11.        default:

12.            printf("b
is %d "
,b);

13.            break;

14.    }

15.    return 0;

16.}

参考答案:该程序在编译时,可能会出现一条warning: unreachable code at beginning of switch statement。我们以为进入switch后,变量b会被初始化,其实并不然,因为switch-case语句会把变量b的初始化直接就跳过了。所以,程序会输出一个随机的内存值。

7、请问下面的程序会有什么潜在的危险?

01.#include
<stdio.h>

02.int main()

03.{

04.    char str[80];

05.    printf("Enter
the string:"
);

06.    scanf("%s",str);

07.    printf("You
entered:%s "
,str);

08.    return 0;

09.}

参考答案:本题很简单了。这个程序的潜在问题是,如果用户输入了超过80个长度的字符,那么就会有数组越界的问题了,你的程序很有可以及会crash了。

8、请问下面的程序输出什么?

01.#include
<stdio.h>

02.int main()

03.{

04.    int i;

05.    i
= 10;

06.    printf("i
: %d "
,i);

07.    printf("sizeof(i++)
is: %d "
,sizeof(i++));

08.    printf("i
: %d "
,i);

09.    return 0;

10.}

参考答案:如果你觉得输出分别是,10,4,11,那么你就错了,错在了第三个,第一个是10没有什么问题,第二个是4,也没有什么问题,因为是32位机上一个int有4个字节。但是第三个为什么输出的不是11呢?居然还是10?原因是,sizeof不是一个函数,是一个操作符,其求i++的类型的size,这是一件可以在程序运行前(编译时)完全的事情,所以,sizeof(i++)直接就被4给取代了,在运行时也就不会有了i++这个表达式。

9、请问下面的程序的输出值是什么?

01.#include
<stdio.h>

02.#include
<stdlib.h>

03. 

04.#define
SIZEOF(arr) (sizeof(arr)/sizeof(arr[0]))

05.#define
PrintInt(expr) printf("%s:%d ",#expr,(expr))

06. 

07.int main()

08.{

09.    /*
The powers of 10 */

10.    int pot[]
= {

11.                    0001,

12.                    0010,

13.                    0100,

14.                    1000

15.                };

16. 

17.    int i;

18.    for(i=0;i<SIZEOF(pot);i++)

19.        PrintInt(pot[i]);

20. 

21.    return 0;

22.}

参考答案:好吧,如果你对于PrintInt这个宏有问题的话,你可以去看一看《语言的歧义》中的第四个示例。不过,本例的问题不在这里,本例的输出会是:1,8,64,1000,其实很简单了,以C/C++中,以0开头的数字都是八进制的。

10、请问下面的程序输出是什么?(绝对不是10)

#include
#define PrintInt(expr) printf("%s : %dn",#expr,(expr))

int main()
{
    int y = 100;
    int *p;
    p = malloc(sizeof(int));
    *p = 10;
    y = y/*p; /*dividing y by *p */;
    PrintInt(y);
    return 0;
}

参考答案:本题输出的是100。为什么呢?问题就出在 y = y/*p;上了,我们本来想的是 y / (*p) ,然而,我们没有加入空格和括号,结果y/*p中的 /*被解释成了注释的开始。于是,这也是整个恶梦的开始。

11、下面的输出是什么?

01.#include
<stdio.h>

02.int main()

03.{

04.    int i
= 6;

05.    if(
((++i < 7) && ( i++/6)) || (++i <= 9))

06.        ;

07. 

08.    printf("%d
"
,i);

09.    return 0;

10.}

参考答案:本题并不简单的是考前缀++或反缀++,本题主要考的是&&和||的短路求值的问题。所为短路求值:对于(条件1 && 条件2),如果“条件1”是false,那“条件2”的表达式会被忽略了。对于(条件1 || 条件2),如果“条件1”为true,而“条件2”的表达式则被忽略了。所以,我相信你会知道本题的答案是什么了。

12、下面的C程序是合法的吗?如果是,那么输出是什么?

01.#include
<stdio.h>

02.int main()

03.{

04.    int a=3,
b = 5;

05. 

06.    printf(&a["Ya!Hello!
how is this? %s "
], &b["junk/super"]);

07. 

08.    printf(&a["WHAT%c%c%c 
%c%c  %c ! "
], 1["this"],

09.        2["beauty"],0["tool"],0["is"],3["sensitive"],4["CCCCCC"]);

10. 

11.    return 0;

12.}

参考答案

本例是合法的,输出如下:

Hello! how is this? super

That is C !

本例主要展示了一种另类的用法。下面的两种用法是相同的:

“hello”[2]

2["hello"]

如果你知道:a[i] 其实就是 *(a+i)也就是 *(i+a),所以如果写成 i[a] 应该也不难理解了。

13、请问下面的程序输出什么?(假设:输入 Hello, World)

01.#include
<stdio.h>

02.int main()

03.{

04.    char dummy[80];

05.    printf("Enter
a string: "
);

06.    scanf("%[^r]",dummy);

07.    printf("%s
"
,dummy);

08.    return 0;

09.}

参考答案:本例的输出是“Hello, Wo”,scanf中的”%[^r]“是从中作梗的东西。意思是遇到字符r就结束了。

14、下面的程序试图使用“位操作”来完成“乘5”的操作,不过这个程序中有个BUG,你知道是什么吗?

01.#include
<stdio.h>

02.#define
PrintInt(expr) printf("%s : %d ",#expr,(expr))

03.int FiveTimes(int a)

04.{

05.    int t;

06.    t
= a<<2 + a;

07.    return t;

08.}

09. 

10.int main()

11.{

12.    int a
= 1, b = 2,c = 3;

13.    PrintInt(FiveTimes(a));

14.    PrintInt(FiveTimes(b));

15.    PrintInt(FiveTimes(c));

16.    return 0;

17.}

参考答案:本题的问题在于函数FiveTimes中的表达式“t = a<<2 + a;”,对于a<<2这个位操作,优先级要比加法要低,所以这个表达式就成了“t = a << (2+a)”,于是我们就得不到我们想要的值。该程序修正如下:

1.int FiveTimes(int a)

2.{

3.    int t;

4.    t
= (a<<2) + a;

5.    return t;

6.}

完!

时间: 2024-10-18 20:43:12

谜题 之 C语言的相关文章

Java解惑五:类之谜

本文是依据JAVA解惑这本书,做的笔记.电子书见:http://download.csdn.net/detail/u010378705/7527721 谜题46 函数重载的问题. JAVA重载解析过程:1. 选取全部可用的方法或者构造器:2. 从过程1中选取的方法或构造器中选择最精确的. 一般而言:能够强制要求编译器选择一个精确的重载版本号,将实參转型为形參所声明的类型. 谜题47 继承中静态域的问题. 静态域由声明它的类及其全部子类共享. 假设须要让每个子类都具有某个域的单独拷贝,必须在每个子

java解惑之字符之谜(谜题22)

谜题22:URL的愚弄 本谜题利用了一个java编程语言中一个鲜为人知的特性.请考虑下面的程序将会做什么? public class BrowerTest{ public static void main(String[] args){ System.out.ptintln("iexplore"); http://www.google.com; System.out.println(":maximize"); } } 这是个有点诡异的问题.当我们初次看到这个程序时,

读陈浩的《C语言结构体里的成员数组和指针》总结,零长度数组

原文链接:C语言结构体里的成员数组和指针 复制如下: 单看这文章的标题,你可能会觉得好像没什么意思.你先别下这个结论,相信这篇文章会对你理解C语言有帮助.这篇文章产生的背景是在微博上,看到@Laruence同学出了一个关于C语言的题,微博链接.微博截图如下.我觉得好多人对这段代码的理解还不够深入,所以写下了这篇文章. 为了方便你把代码copy过去编译和调试,我把代码列在下面: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <stdio.h>

[变]C#谜题(1-10)表达式篇

[变]C#谜题(1-10)表达式篇 最近偶然发现了<Java谜题>,很有意思,于是转到C#上研究一下. 本篇是关于表达式的一些内容. 谜题1:奇数性(负数的取模运算) 下面的方法意图确定它那唯一的参数是否是一个奇数.这个方法能够正确运转吗? 1 public static bool isOdd(int i) 2 { 3 return i % 2 == 1; 4 } 奇数可以被定义为被2 整除余数为1 的整数.表达式 i % 2 计算的是 i 整除 2时所产生的余数,因此看起来这个程序应该能够正

破解东京大学2013年招生海报谜题

这是一张美女海报,「東京大学情報理工学系研究科」每年的招生海报都非常有内涵,其内涵之一是身着华美传统服饰的海报女郎,其内涵之二则是隐藏在海报上那串二进制数字背后的谜题.hankcs今天突发兴致,顺手解开了2013海报背后的谜题,用通俗的语言在此做个记录.声明:此海报2013年5月份左右就已发布,各路高人肯定早已解明.hankcs并非专业人士,无门无派的浪人一名.但是浪人也有浪人的解法,加之今年的谜题特别简单,诸位笑看便是.情報理工学系研究科ポスター海报上的妹子老实说不是hankcs的菜,相比而言

1987年国际C语言混乱代码大赛获奖的一行代码

macb() ? lpcbyu(&gbcq/_\021%ocq\012\0_=w(gbcq)/_dak._=}_ugb_[0q60)s+ 这是CoolShell博主之前做了一个很有意思的在线puzzle,仿照一些前端过关的游戏,做了几个和程序员有关的迷题,一个通关游戏,这个事测试的第二题.并为通关的前十名送上<Unix环境高级编程(第三版)>(感谢@出版圈郭志敏 赞助)或一个马克杯(感谢@linux命令行精选网 赞助))这些谜题很有趣同时也有一定的难度.由于水平有限,我并没有通关,但我

Java——谜题

1.谜题36  try和finally语句,代码如下,判断输出 public class Indecisive { public static void main(String[] args) { System.out.println(decision()); } static boolean decision() { try { return true; } finally { return false; } } } 最后的输出为false,为什么呢?这是因为try的异常结束(例如return

java解惑之字符之谜(谜题18)

谜题18:字符串奶酪 下面这个程序从一个字节序列创建一个字符串,然后迭代遍历字符串中的字符,并将它们作为数字打印.请描述程序打印的数字序列: public class StringCheese{ public static void mian(String[] args){ byte bytes[] new byte[256]; for(int i = 0; i < 256; i++){ bytes[i] = (byte)i; } String str = new String(bytes);

谜题3:长整除

这个谜题之所以被称为长整除是因为它所涉及的程序是有关两个long型数值整除的.被除数表示的是一天里的微秒数:而除数表示的是一天里的毫秒数.这个程序会打印出什么呢? public class LongDivision{ public static void main(String args[]){ final long MICROS_PER_DAY = 24 * 60 * 60 * 1000 * 1000; final long MILLIS_PER_DAY = 24 * 60 * 60 * 10