我做了一个成员结构,我在我的iOS应用程序的ViewDidLoad中分配。我使用malloc为这个结构分配空间,然后在整个类中使用。这样的:
self.myData = malloc(sizeof(MyData));
但我真正做的是:
self.myData = malloc(sizeof(MyOtherStruct));
我不小心将malloc调用中的sizeof()设置为不同的结构体(大小不同)。我很长一段时间都没有注意到这个错误,因为应用很少崩溃。操作系统的更新导致更频繁地发生崩溃。
我的问题是,为什么编译器不能警告这种事情?这是编译器不知道的还是允许用户任意设置malloc的设计选择?
"我怎样才能更快地找到这个错误?"
有很多方法可以更快地找到错误。
解决方案# 1
静态分析器捕获此错误。在Xcode中按command-shift-B。例如,使用以下代码:
#include <stdlib.h>
struct x { double x; };
struct y { char y; };
int main(int argc, char **argv) {
struct x *p = malloc(sizeof(struct y));
p->x = 1.0;
return 0;
}
运行分析器会产生以下错误:
malloc的结果被转换为struct x类型的指针,这与struct y类型的sizeof操作数不兼容
解决方案# 2
建议这样写代码:
self.myData = malloc(sizeof(*self.myData));
以后就这样做吧。这不仅不容易出错,而且更容易记住。
解决方案# 3
使用Swift或c++这样的语言,语言的类型系统可以帮助你避免这种错误。C语言在很多方面都不那么宽容。它是在20世纪70年代早期发明的,如果你想使用它,你必须接受它,这些类型的错误是c++和Swift存在的主要原因之一。解决方案# 4
使用运行时内存边界检查器,如地址消毒程序。这将在访问内存时检测错误,而不是在分配内存时检测错误,但它仍然会为访问和分配(如果内存已被释放,则为free)提供堆栈跟踪。现在任何写C的人都应该熟悉地址消毒器和它的朋友,tsan, ubsan等。
Valgrind也达到了同样的效果,但是地址消毒器在普通用例中有更好的用户体验。
提问
编译器只给出类型错误的错误和警告。这不是类型错误,这是运行时错误。编译器可以检测到一些"可能的"运行时错误,但它们的数量非常少。比如忘记使用malloc()
的返回值……例如,
void f(void) {
malloc(1); // warning
}
编译器也好不到哪里去
再一次,这是像c++和Swift这样的新语言的动力,它们的类型系统允许你在分配错误时生成错误,这也是静态分析的动力(这是一个棘手的问题)。
这是因为ARC没有责任处理malloc()
甚至free()
ARC只处理像[Object alloc]
这样分配的对象
在您的情况下,当您执行self.myData = malloc(sizeof(MyOtherStruct));
时,可以这样解释:
self.myData = malloc(N*sizeof(MyData));
//what can represents self.myData[0]..self.myData[N-1]
最后一点,请记住,当您使用sizeof()
时,它将告诉您作为参数传递的类型的大小,并在编译时计算。
您可以查看此链接以获取有关对象分配的更多信息
还要查看Apple文档关于内存分配