Memory Safety
Memory Safety
Today’s lecture will focus on memory errors that often arise in programs. We will look at how these errors motivate Rust’s ownership model, and explain how Rust prevents them.
Pre-class exercise
This exercise was developed by Will Crichton for CS 242. Thank you, Will, for letting us borrow this material!
Before class on Thursday, please spend just 10 minutes reviewing the following C implementation of a vector. There are at least 7 bugs. You don’t need to catch ‘em all, but try to spot as many as you can.
Please write down the bugs you find. We will be reviewing your findings at the start of class.
1 |
|
代码结构
Vec
是一个结构体,包含一个动态数组指针data
、当前数组的长度length
和数组容量capacity
。- 提供了几个主要功能:
vec_new()
创建新的Vec
实例;vec_push()
向Vec
添加元素并在必要时调整容量;vec_free()
释放Vec
的内存;main()
函数测试Vec
的功能。
错误分析
局部变量地址错误(
vec_new
函数)1
2Vec vec;
return &vec;vec_new()
函数返回了一个局部变量vec
的地址。在函数返回后,
vec
的内存会被释放,因此返回的指针指向无效内存,会导致不确定的行为。修正建议:使用
malloc
分配Vec
的内存。1
2
3
4
5Vec* vec = (Vec*) malloc(sizeof(Vec));
vec->data = NULL;
vec->length = 0;
vec->capacity = 0;
return vec;
未初始化容量(
vec_push
函数)1
int new_capacity = vec->capacity * 2;
当
capacity
为0时,vec->capacity * 2
依旧为0,这导致无法为data
分配空间。修正建议:在容量为0时,可以将
new_capacity
设为1。1
int new_capacity = (vec->capacity == 0) ? 1 : vec->capacity * 2;
malloc
分配大小错误1
int* new_data = (int*) malloc(new_capacity);
malloc
接受的参数是字节数,而不是元素数量。因此应该分配
new_capacity * sizeof(int)
字节的空间。修正建议:
1
int* new_data = (int*) malloc(new_capacity * sizeof(int));
未释放旧
data
内存(内存泄漏)在调整容量时,旧的
data
指针没有被释放,这导致内存泄漏。修正建议:在重新分配新空间前释放旧的
data
指针。1
free(vec->data);
空指针访问(
vec_push
函数)1
vec->data[vec->length] = n;
- 在初始情况下,如果
vec->data
为NULL
(即尚未分配空间),访问vec->data[0]
会导致空指针解引用,造成运行错误。 - 修正建议:确保在分配内存后才访问
vec->data
。
- 在初始情况下,如果
错误释放顺序(
vec_free
函数)1
2free(vec);
free(vec->data);应先释放
data
,再释放Vec
结构体本身的指针,否则data
的内存可能在释放vec
后失效。修正建议:
1
2free(vec->data);
free(vec);
重复释放(
main
函数)1
2free(vec->data);
vec_free(vec);- 在
main
中,vec->data
被手动释放后,又在vec_free
中再次释放,导致“双重释放”错误。 - 修正建议:只在
vec_free
中释放data
,无需在main
中重复释放。
- 在
悬挂指针问题
1
2
3int* n = &vec->data[0];
vec_push(vec, 110);
printf("%d\n", *n);vec_push
可能导致data
重新分配新地址,因此n
指向的地址可能无效,导致悬挂指针(即指向已释放或无效的内存)。- 修正建议:在
vec_push
后避免使用旧的指针n
。
修正后
1 |
|