c 的陷阱
c语言算是非常古老了,像瑞士军刀灵活却也很容易伤到自己,即使是多年的老杆子,以致于市面上都有一本经典的C的书叫《C陷阱与缺陷》的书。
这个文章总结下c中常见的陷阱,可能在日常工作或面试题目中遇到。
1. sizeof 陷阱
sizeof  它是一个编译时运算符而非函数,用于判断变量或数据类型的字节大小。
比较常见的用法:
    int arr[] = { 1, 2, 3 };
    for (int i = 0; i < sizeof(arr) / sizeof(int); i++) {
        printf("%d,", arr[i]);
    }
sizeof(arr)的是整个数组的占字节数大小,除int占字节大小就是整个数组的大小了,但是如果不小心这样用了:
void clear(char array[])
{
    int i;
    for (i = 0; i < sizeof(array) / sizeof(array[0]); i++) 
    {
        array[i] = 0x00;
    }
}
int main(void)
{
    char arr[20];
    clear(arr);
}
问题: 这段代码的问题,在于clear中传递的是指针,这时候sizeof(char*) 一般为4,sizeof(array[0]),造成了结果是只对数组的前四个变量赋值为0,其他的没有赋值!
2. 小心无符号类型
先看段代码:
#include <stdio.h>
int main()
{
    if (-1L < 1U) {
        printf("true");
    } else {
        printf("false");
    }
    return 0;
}
问题: 结果为false,原因,有符号和无符号比较的时候,将有符号转成无符号再比较。
还有一些看上去很傻的代码: 无符号char 最大255, 永远大于等于0,所以下面循环为死循环:a. unsigned char i; b. unsigned char i; for(i=0;i<256;i++) {… } for(i=10;i>=0;i--) { … }
3. volatile
volatile 标识变量是容易变化的,每次读取的时候不能从寄存器读取,需要从内存重新加载,多用在随时变化的变量,比如两个线程程序中,一个程序通过更改一个volatile类型的flag,另外一个线程判断这个flag为真还是假来决定是否继续执行。用volatile声明的变量编译器不会进行优化。
如果在一个a.h头文件中定义:
volatile unsigned int a;
在b.h 中引用:
extern  unsigned int a;
编译器只会告警,但是运行的时候会有问题,隐藏的大Bug。
4. 局部变量
返回局部变量指针:
char * GetData(void)
{
  char buffer[100];                 //局部数组
  …
  return buffer;
}
说明: 很经典错误,buffer分配在栈上,函数调用结束后会释放。
5. 小心类型自动转换和提升
程序中,short,char,int,枚举,位段变量用在需要int的函数时候,自动转成int类型。
如果int可以完整的表示源类型的所有值,那么该源类型的值就转换为int, 否则转换为unsigned int,这被称为整体提升。——《C专家编程》
unsigned char -> int char ->int比如:
char a = 'a', b = 'b'; printf("%d\n", sizeof(a + b)); printf("%d\n", sizeof(a));说明: 输出的结果为:
4
1
当表达式使用到值的时候,就会进行进行类型提升。
6. free释放注意
free释放的传入的指针,必须是malloc申请的,在程序中,如果发生了计算,偏移了原始指针就会有问题:
#include<stdio.h> 
int main(int argc, char *argv[]) 
{ 
    char *ptr = (char*)malloc(10);
    if(NULL == ptr) 
    { 
        printf("\n Malloc failed \n"); 
        return -1; 
    } 
    else if(argc == 1) 
    { 
        printf("\n Usage  \n"); 
    } 
    else 
    { 
        memset(ptr, 0, 10);
        strncpy(ptr, argv[1], 9);
        while(*ptr != 'z') 
        { 
            if(*ptr == '') 
                break; 
            else 
                ptr++; 
        }
        if(*ptr == 'z') 
        { 
            printf("\n String contains 'z'\n"); 
            // Do some more processing 
        }
       free(ptr); 
    } 
    return 0; 
}
当传入的字符串第一个字符不是z的时候,循环会使ptr发生变化,再free的时候,会奔溃。
7. _exit退出
看下下面代码func为何未调用:
#include<stdio.h> 
void func(void) 
{ 
    printf("\n Cleanup function called \n"); 
    return; 
}
int main(void) 
{ 
    int i = 0;
    atexit(func);
    for(;i<0xffffff;i++);
    _exit(0); 
}
说明: _exit不会调用atexit注册的退出函数,需要调用需要return或exit(0)。
8 参数处理顺序
#include<stdio.h>
int main(void) 
{ 
    int a = 10, b = 20, c = 30; 
    printf("\n %d..%d..%d \n", a+b+c, (b = b*2), (c = c*2));
    return 0; 
}
说明: 输出为:110..40..60 原因:参数从右到左处理。
作者:明翼(XGogo)
-------------
公众号:TSparks
微信:shinelife
扫描关注我的微信公众号感谢
 
-------------

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号