c 的陷阱

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;
}
</stdio.h>

问题: 结果为false,原因,有符号和无符号比较的时候,将有符号转成无符号再比较。
还有一些看上去很傻的代码: 无符号char 最大255, 永远大于等于0,所以下面循环为死循环:

a.     unsigned char i&#xFF1B;                    b.   unsigned char i;
       for(i=0;i<256;i++) {... } for(i="10;i">=0;i--) { &#x2026; }
</256;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];                 //&#x5C40;&#x90E8;&#x6570;&#x7EC4;
  &#x2026;
  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;
}
</stdio.h>

当传入的字符串第一个字符不是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); } < code></0xffffff;i++);></stdio.h>

说明: _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;
}
</stdio.h>

说明: 输出为:110..40..60 原因:参数从右到左处理。

Original: https://www.cnblogs.com/seaspring/p/12482883.html
Author: XGogo
Title: c 的陷阱

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/539876/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球