C++常见UB问题总结


在开发项目中,如果存在未定义行为的代码,整个程序的执行就会变得很诡异,之前在移动端开发的时候,就碰到UB后,Bugly的符号位出现了严重的偏移,只能通过分析代码来解决,耗费了很多的精力。

​ C++标准给出的未定义行为的解释:

未定义行为(undefined behavior,UB)——对程序的行为无任何限制。未定义行为的例子是数组边界外的内存访问,有符号整数溢出,空指针的解引用,在一个表达式中对同一标量多于一次的中间无序列点(C++11前)无序(C++11起)的修改,通过不同类型的指针访问对象,等等。编译器不需要诊断未定义行为(尽管许多简单情形确实会得到诊断),而且所编译的程序不需要做任何有意义的事

1.除零

int main()
{
    int x = 25, y = 0;
    int z = x / y;
    return 0;
}

2.变量未初始化

std::size_t f(int x)
{
    std::size_t a;
    if(x) // either x nonzero or UB
        a = 42;
    return a; 
}

3.访问空指针NULL

int main()
{
   int *ptr = NULL;
   printf("%d", *ptr);
   return 0;
}

4.访问越界

int main()
{
   int arr[5];

   for (int i=0; i<=5; i++)
      printf("%d ", arr[i]);
}

5.有符号数溢出

int main()
{
   int x = INT_MAX;
   printf("%d", x+1);
   return 0;
}

6.试图更改字符串字面值

int main()
{
   char *s = "geeksforgeeks";
   s[0] = 'e';
   return 0;
}

7.在一个序列操作值未确定的情况下对一个变量多次更改

#include <stdio.h>
int main()
{
   int i = 8;
   int p = i++*i++;
   printf("%d\n", p);
}

8.解引用空指针

int foo(int* p) {
    int x = *p;
    if(!p) return x; // Either UB above or this branch is never taken
    else return 0;
}
int bar() {
    int* p = nullptr;
    return *p;        // Unconditional UB
}

9.在对象的生命周期结束后访问对象

同3,对象生命周期结束后,内存被释放。

10.有返回类型的函数没有从return结束

int test(int i)
{
   if (i > 0) {
       retrun i++;
   }
}

当i<=0时,返回的是随机值

11.指针double free

void doDoubleFree() {
    char *p = new char[16];
    delete[] p;
    delete[] p;
}

12.不要判断this为nullptr

int Class::member() {
if (this == nullptr) {
    // removed by the compiler, it would be UB
    // if this were ever null
    return 42;
    } else {
        return 0;
    }
}

13.不要判断对象的引用是nullptr

14.sprintf或者snprint

sprintf(buf, "%s text", buf);

调用导致在重叠对象之间发生复制

15.const_cast

const int v = 1;
const int* a = &v;
int* b = const_cast<int*>(a);
++(*b);

强制转换为非常量后修改其值

16.先释放然后重新分配

  for (p = head; p != NULL; p = p->next) {
    free((char*) p);
  }

并非所有的内部实现在内存被释放后还能较长时间的保留

17.tls中pthread_getspecific

pthread_key_t pKey;
pthread_key_create(&pKey, nullptr);pthread_key_delete(pKey);
auto pValue = pthread_getspecific(pKey); /* 未定义行为*/

​ 除了以上的UB类型外,在开发的过程中,使用到的API可能也会出现UB行为,C++的API文档也会有相关的说明,出现UB后,问题经常特别的隐蔽,查找耗费大量的精力和时间。

https://xueqing.github.io/blog/cplusplus/undefined_behavior

https://leoleoasd.me/2021/04/04/undefined-behavior-in-c

https://zhuanlan.zhihu.com/p/427778091


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注