C 语言中的一些谜题(二)

接上一篇:《C 语言中的一些谜题(一)》

1. puzzle 12

下面两个文件中的简单程序,当两个文件同时参与编译能通过吗?结果又是什么?

file1.c

int arr[80];

file2.c

#include <stdio.h>

extern int *arr;
int main()
{
    arr[1] = 100;
    return 0;
}

这两个文件编译时可以通过的,但是运行时会出错。原因是在file2.c 中声明 extern int *arr 一个数组并不能得到实际的期望值,因为它们的类型并不匹配,所以导致指针实际并没有指向那个数组。修改:extern int arr[];

2. puzzle 13

#include <stdio.h>

#define SIZE 10

void size(int arr[SIZE])
{
    printf("size of array is:%d\n",sizeof(arr));
}

int main()
{
    int arr[SIZE];
    size(arr);
    return 0;
}

提问:上面这段程序将输出什么内容?

回答:4 或 8

对于 size() 函数来说,参数为一个数组,而这个数组的地址是根据实参来的,形参中指定的数目并没有实际意义,所以在编译的时候会提示 warning:

test.cpp: In function ‘void size(int*)’:
test.cpp:7:46: warning: ‘sizeof’ on array function parameter ‘arr’ will return size of ‘int*’ [-Wsizeof-array-argument]
     printf("size of array is:%d\n",sizeof(arr));

在size() 函数中arr 只会被看成实参的地址进行运行,sizeof 也会将 arr 看成 int*。所以,最终输出为 32系统中的 4个字节和64位系统中的 8个字节。

3. puzzle 14

#include <stdio.h>
int main()
{
    char str[80];
    printf("Enter the string:");
    scanf("%s",str);
    printf("You entered:%s\n",str);

    return 0;
}

提问:上面这段代码会有什么样潜在的危险?

回答:当str 输入超过 80 字符时会出现越界问题。

4. puzzle 15

#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,4,11,那就错了,最容易错在第三个。因为 sizeof() 不是一个函数,而是一个操作符,其求 i++ 的类型的size,在编译时就完成的事情,编译的时候会优化掉 i++,因为 i 的类型就是 int,无需进行运算。

5. puzzle 16

#include <stdio.h>
void foo(const char **p) { }
int main(int argc, char **argv)
{
    foo(argv);
    return 0;
}

这段代码会有什么样的问题呢?

在编译的时候会进行提示,c 和 c++ 中编译提示不同,在C 中会warning 提示,而在 C++ 中会进行报错。

test.cpp: In function ‘int main(int, char**)’:
test.cpp:5:19: error: invalid conversion from ‘char**’ to ‘const char**’ [-fpermissive]
           foo(argv);
                   ^
test.cpp:2:8: note:   initializing argument 1 of ‘void foo(const char**)’
   void foo(const char **p) { }
        ^~~
Makefile:6: recipe for target 'test.o' failed
make: *** [test.o] Error 1

main 中的参数为 char** 表示该变量是指向字符串的指针变量,foo() 中的参数是指向 const char* 的指针,与原本的实际参数不符。 对于 const char** 和 char**来说,二者都是没有限定符的指针类型,但是它们指向的类型不一样,后者指向 char*,而前者指向const char*,因此它们不相容,所以 char** 类型的操作数不能赋值给 const char** 类型的操作数。

6. puzzle 17

#include <stdio.h>
int main()
{
    int i;
    i = 1,2,3;
    printf("i: %d\n",i);
    return 0;
}

上面这段代码将输出什么内容呢?i 的值最终为多少呢?

这段程序应该很简单,最终i 的值为1,因为赋值运算符的优先级比逗号运算符高,或者说逗号运算符是运算优先级里面最低的。

7. puzzle 18

#include <stdio.h>
#include <stdlib.h>

#define SIZEOF(arr) (sizeof(arr)/sizeof(arr[0]))
#define PrintInt(expr) printf("%s:%d\n",#expr,(expr))

int main()
{
    /* The powers of 10 */
    int pot[] = {
                    0001,
                    0010,
                    0100,
                    1000
                };

    int i;
    for(i=0;i<SIZEOF(pot);i++)
        PrintInt(pot[i]);
        
    return 0;
}

提问:上面这段代码的输出结果是什么?

输出结果:

pot[i]:1
pot[i]:8
pot[i]:64
pot[i]:1000

这里考察的是,在 C/C++ 中,以0开头的数字,都是 8 进制数。

8. puzzle 19

#include <stdio.h>
#include <stdlib.h>

#define PrintInt(expr) printf("%s : %d\n",#expr,(expr))

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

上面这段代码会输出什么内容呢?

输出值为100,理论上 y 为100, *p 为10,y 的值应该为 10.

注意这里  y = y/*p; 因为没有空格,编译时会将 /* 看成是注释符。所以在编写代码的时候还是规范,可以修改为: y = y / *p; 或者是 y = y / (*p);

9. puzzle 20

#include <stdio.h>
int main()  
{
    int i = 6;
    if( ((++i < 7) && ( i++/6)) || (++i <= 9))
        ;

    printf("%d\n",i);
    return 0;
}

这段代码 i 的值最终为多少呢?

答案是8.

首先需要明白 || 当前面的条件为 true,那么后面的条件是不执行的。另外就是 && 的逻辑运算优先级是比算术运算符的优先级低的,所以在进行 &&运算之前,会先进行算术运算,最终经历 ++i 和 i++ 两次算术运算。

若条件稍微修改下更明显,把 ++i < 7 中的自加去掉,改成:

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

最终i 的结果为 7.

10. puzzle 21

#include <stdio.h>
int main()
{
    int n;
    printf("Enter a number:\n");
    scanf("%d", n);

    printf("You entered %d \n",n);
    return 0;
}

这段代码有没有问题?有什么样的问题?

代码比较简单,主要问题出现在scanf() 中,正常情况下要想输入 n 的值,应该是使用 &n,这里使用 n 编译也是允许的,但运行的时候会出现段错误。scanf() 将 n 所在地址中的随机值看成了地址,并试图将该地址中写入一个 stdin 中的整数。而这个随机值所指向的地址,有可能是核心地址或是重要地址,无法进行修改的。

11. puzzle 22

#include <stdio.h>
#define PrintInt(expr) printf("%s : %d\n",#expr,(expr))
int FiveTimes(int a)
{
    int t;
    t = a<<2 + a;
    return t;
}

int main()
{
    int a = 1, b = 2,c = 3;
    PrintInt(FiveTimes(a));
    PrintInt(FiveTimes(b));
    PrintInt(FiveTimes(c));
    return 0;
}

下面程序试图使用 “位操作” 来完成 “乘5” 的操作,但是程序中有个bug,知道是什么吗?

本段程序问题主要出在 FiveTimes() 中表达式 “t = a << 2 + a”,位移运算符的优先级没有 + 优先级高,所以,该表达式就成了 “t = a << (2 + a)”,所以正确做法应该是将位移运算用括号括起来。

12. puzzle 23

#include <stdlib.h>
#include <stdio.h>
#define SIZE 15 
int main()
{
    int *a, i;

    a = (int*)malloc(SIZE*sizeof(int));

    for (i=0; i<SIZE; i++)
        *(a + i) = i * i;
    for (i=0; i<SIZE; i++)
        printf("%d\n", *a++);
    free(a);
    return 0;
}

这段代码输出什么内容?有个bug 知道是什么吗?

输出内容应该没有问题,申请了一个堆上连续的空间,形成一个数组,第一个 for 将每个数组的元素都赋值为 i^2,第二个for 循环将数组中的元素都打印出来。但是在free() 的时候会出现大问题,读者看出来了吗?

13. puzzle 24

#include <stdio.h>
int main()
{
    char dummy[80];
    printf("Enter a string:\n");
    scanf("%[^d]",dummy);
    printf("%s\n",dummy);
    return 0;
}

上面这段代码将输出什么内容(假如输入内容为:Hello World)?

输出结果为:Hello Worl

scanf() 中的 “%[^d]” 表示输入截止到字符 d,当然这里的d 也可以改成其他的字符。

 接上一篇:《C 语言中的一些谜题(一)》  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

私房菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值