C语言谜题解析

本文汇总了一些容易犯错、不易察觉的C编程错误。有时候,你会遇到莫名其妙的错误而不知所措,本文中的一些例子可以给你提供经验,防患于未然。

 

#include <stdio.h>
#define f(a,b) a##b
#define g(a)   #a
#define h(a) g(a)

int main()
{
        printf("%s\n", h(f(1,2)));
        printf("%s\n", g(f(1,2)));
        return 0;
}

说明:输出结果为
12
f(1,2)
宏的解开不像函数那样执行;函数是由里及外展开,而宏则不是。

 

sizeof返回值

#include <stdio.h>
#define TOTAL_ELEMENTS (sizeof(array) / sizeof(array[0]))
int array[] = {23,34,12,17,204,99,16};
int main()
{
    int d;

    for(d=-1;d <= (TOTAL_ELEMENTS-2);d++)
        printf("%d\n",array[d+1]);

    return 0;
}

说明:无任何输出。sizeof的返回值是一个unsinged int,为此在比较int d 和TOTAL_ELEMENTS两个值都被转换成了unsigned int来进行比较,这样就导致-1被转换成一个非常大的值,以至于for循环不满足条件。

 

void参数

#include <stdio.h>
void foobar1(void)
{
        printf("In foobar1\n");
}

void foobar2()
{
        printf("In foobar2\n");
}

int main()
{
        char ch = 'a';

        foobar1();
        foobar2();
        foobar2(33, ch);
        foobar1(33);

        return 0;
}

说明:在foobar1(33);报错error: too many arguments to function ‘foobar1’。func(void)是没有参数,而func()等于参数类型未知,其调用者可以传入若干个参数,但在func()中,所有的参数将被忽略

 

块设备缓冲区

#include <stdio.h>
#include <unistd.h>
int main()  
{
    while(1)
    {
        fprintf(stdout,"hello-std-out");
        fprintf(stderr,"hello-std-err");
        sleep(1);
    }
    return 0;
}


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

 

printf返回值

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

说明:程序会输出4321,因为printf返回值是输出的字符个数。

 

switch内部定义

#include <stdio.h>
int main()  
{      
    int a=1;      
    switch(a)      
    {   
        int b=20;          
        case 1: 
            printf("b is %d\n",b);
            break;
        default:
            printf("b is %d\n",b);
            break;
    }
    return 0;
}

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

 

数组越界

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


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

 

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;
}

说明:sizeof不是一个函数,是一个操作符,其求i++的类型的size,这是一件可以在程序运行前(编译时)完全的事情,所以,sizeof(i++)直接就被4给取代了,在运行时也就不会有了i++这个表达式。所以输出是
i : 10
sizeof(i++) is: 4
i : 10

 

八进制

#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;
}


说明:在C/C++中,以0开头的数字都是八进制的。所以,输出是
pot[i]:1
pot[i]:8
pot[i]:64
pot[i]:1000

 

scanf结束符

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


说明:输入输入 Hello, World;那么输出为Hello, Wo;scanf中的”%[^r]“的意思是遇到字符r就结束了。

 

 

extern数组问题

//file1.c
int arr[80];
//file2.c
extern int *arr;
int main()  
{      
    arr[1] = 100;
    printf("%d\n", arr[1]);
    return 0;  
}


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

 

 

类型转换

#include <stdio.h>
int main()  
{
    float a = 12.5;
    printf("%d\n", a);
    printf("%d\n", (int)a);
    printf("%d\n", *(int *)&a);
    return 0;  
}

 


说明:
该项程序输出如下:
594771784(经多次运行,该数为随机数)
12
1095237632
原因是:浮点数是4个字节,12.5f 转成二进制是:01000001010010000000000000000000,十六进制是:0×41480000,十进制是:1095237632。

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

然后,我们还需要了解一下printf由于类型不匹配,所以,会把float直接转成double,注意,12.5的float和double的内存二进制完全不一样。
我的电脑是小端序字节。
double型表示12.5f的十六进制为:
而我们的%d要求是一个4字节的int,对于double的内存布局,我们取用前四个字节。
这个示例向我们说明printf并不是类型安全的,这就是为什么C++要引如cout的原因了。

你可能感兴趣的:(c,解析,谜题)