C++中常见的未定义行为
前言
在一些程序语言中,在某些情况下存在着一些未定义行为,其中以C和C++最为著名,在C++中,规定某些操作的行为是未定义的。这些未定义行为往往会导致一些程序的错误问题的出现,作为一名合格的程序员,应该极力避免这些未定义行为的出现,让程序的运行结果不会说出现不确定的情况。
总结
在C++中的常见的未定义行为主要有以下几种:
数组越界问题
在C++中,数组越界问题是一个重灾区,也是很多人容易疏忽的点。比如以下的程序
const int ARR_SIZE = 10; int arr[ARR_SIZE]; bool func(int i) { cout<<arr[i]<<endl; return true; }
不对函数的入参进行校验,以判断其下标是否越界,就容易出现数组的越界问题,导致程序在某些情况下可能访问了非法内存进而导致程序崩溃。正确的做法,应该严格校验下标参数,防止出现数组越界问题。如下:
const int ARR_SIZE = 10; int arr[ARR_SIZE]; bool func(int i) { if(i >= 0 && i < ARR_SIZE) { cout<<arr[i]<<endl; return true; } return false; }
非法多次释放同一块内存空间
在C++中,提供了我们new跟delete的操作符来进行内存管理,这就是使得程序员有了很大的便利性,但是,对这两个操作符倘若使用不够恰当就可能会导致内存泄漏或者未定义行为。比如以下代码:
bool func() { int *p = new int; delete p; delete p; }
在这里,由于程序员的疏忽,导致p指针指向的内存空间被释放了两次,这种非法操作也会导致程序未定义行为,导致程序出现不确定的运行结果。
不恰当使用容器的erase操作
在C++中,容器的出现确实给程序员带来了很多便利,但是如果操作不当便可能导致一些未定义行为,比如erase操作
成员函数erase是从容器中指定位置删除元素。这无疑是个非常便利以及常用的成员函数,但是如果使用不恰当,便很容易出现程序出现错误的情况,一些粗心的程序员可能会写出下面的程序:
bool func() { vector<int> vec = {1, 3, 4,5,9,5,6,7,5}; vector<int>::iterator it = vec.begin(); for(; it != vec.end(); ++it) { if(*it == 5) { vec.erase(it); } } return true; }
这里的原因便是erase函数在删除完元素,迭代器会失效,成为一个无效值,这样,再次执行 ++it 操作时便会导致未定义行为,因此,为了避免删除完元素的迭代器失效,导致程序崩溃,程序应该修改成这样:
bool func() { vector<int> vec = {1, 3, 4, 5, 9, 5, 6, 7, 5}; vector<int>::iterator it = vec.begin(); for(; it != vec.end();) { if(*it == 5) { vec.erase(it++); //在C++11中,也可以这样写: it = vec.erase(it); } else { ++it; } } return true; }
其他
除了这些之外,在C++中还有很多其他未定义行为,比如说:
-
除以0
int res = 10 / 0;
使用指针进行非法操作,如下:
const int i = 10; int *p = (int*)&i; *p = 2;
此时i被程序被声明为const的,即不能修改的,直接通过赋值对i进行修改并不能通过编译,但是通过指针,则能通过编译,i的值也由编译器决定。