潜在的动态内存问题
Potential dynamic memory problems
我正在使用 C++ 创建一个 Arduino 设备。我需要一个具有可变大小和可变数据类型的堆栈对象。本质上,这个堆栈需要能够调整大小并与字节、字符、整数、双精度数、浮点数、短裤和长数一起使用。
我有一个基本的 class 设置,但是由于需要动态内存分配量,我想确保我使用的数据释放足够的空间 space 让程序继续没有记忆问题。这不使用标准方法,而是内置 Arduino 的版本。
为了澄清,我的问题是:我的代码中是否存在任何潜在的内存问题?
注意:这不在 Arduino 堆栈交换中,因为它需要深入了解 C/C++ 内存分配,这可能对所有 C 和 C++ 程序员都有用。
代码如下:
Stack.h
#pragma once
class Stack {
public:
void init();
void deinit();
void push(byte* data, size_t data_size);
byte* pop(size_t data_size);
size_t length();
private:
byte* data_array;
};
Stack.cpp
#include "Arduino.h"
#include "Stack.h"
void Stack::init() {
// Initialize the Stack as having no size or items
data_array = (byte*)malloc(0);
}
void Stack::deinit() {
// free the data so it can be re-used
free(data_array);
}
// Push an item of variable size onto the Stack (byte, short, double, int, float, long, or char)
void Stack::push(byte* data, size_t data_size) {
data_array = (byte*)realloc(data_array, sizeof(data_array) + data_size);
for(size_t i = 0; i < sizeof(data); i++)
data_array[sizeof(data_array) - sizeof(data) + i] = data[i];
}
// Pop an item of variable size off the Stack (byte, short, double, int, float, long, or char)
byte* Stack::pop(size_t data_size) {
byte* data;
if(sizeof(data_array) - data_size >= 0) {
data = (byte*)(&data_array + sizeof(data_array) - data_size);
data_array = (byte*)realloc(data_array, sizeof(data_array) - data_size);
} else {
data = NULL;
}
// Make sure to free(data) when done with the data from pop()!
return data;
}
// Return the sizeof the Stack
size_t Stack::length() {
return sizeof(data_array);
}
显然有一些小的代码错误,虽然很重要,但很容易解决。以下回答仅适用于本class的整体设计:
仅显示的代码没有任何问题。
但只有显示的代码。对于未显示的任何代码,不提供任何意见。
而且,在尝试使用此 class.
的其余代码中,很可能会出现大量问题和内存泄漏。
以泄漏或损坏内存的方式使用此 class 将非常非常容易。正确使用 class 会更难,而且更容易搞砸。如果您所要做的只是朝错误的方向打喷嚏,那么这些功能本身似乎可以正确完成工作这一事实将无济于事,最终导致这些功能未按正确的顺序或顺序使用。
仅举出前两个显而易见的问题:
1) 未能调用 deinit()
,当此 class 的任何实例超出范围并被销毁时,将泄漏内存。每次使用此 class 时,您都必须意识到此 class 的实例何时超出范围并被销毁。每次创建这个 class 的实例很容易跟踪,并且很容易记住每次调用 init()
。但是跟踪此 class 的实例可能超出范围并被销毁的所有可能方式,因此您必须调用 deinit()
并释放内部内存,这要困难得多。当发生这种情况时,很容易甚至没有意识到。
2) 如果这个 class 的一个实例被复制构造,或者默认赋值运算符被调用,这肯定会导致内存损坏,并附带内存泄漏。
请注意,您不必特意编写复制构造的代码,或将对象的一个实例分配给另一个实例。如果您不注意,编译器将非常乐意为您完成。
一般来说,避免此类问题的最佳方法是通过正确使用语言使其不可能发生。即:
1) 关注RAII design pattern。删除 init()
和 deinit()
。相反,在对象的构造函数和析构函数中执行此工作。
2) 删除复制构造函数和赋值运算符,或者正确实现它们。所以,如果这个 class 的实例永远不应该被复制构造或分配给,如果你不小心写了一些这样做的代码,最好让编译器对你大喊大叫,而不是花一个星期的时间来追踪发生的地方。或者,如果 class 可以复制构造或赋值,则正确执行。
当然,如果这种 class 的实例很少,应该可以安全地使用它,严格控制,多加注意,而不用做这种事重新设计。但是,即使是这样,最好还是把工作做好,而不是现在耸耸肩,然后决定在更多地方扩大这个 class 的使用,然后忘记这个事实class 太容易出错了。
P.S.: 开头提到的几个小bug:
data_array = (byte*)realloc(data_array, sizeof(data_array) + data_size);
这不对。 data_array
是一个 byte *
,因此 sizeof(data_array)
将始终是一个编译时常量,即 sizeof(byte *)
。这显然不是你想要的。您需要明确跟踪分配的数组的大小。
这里的其他几个地方也出现了相同的一般错误,但很容易修复。整体 class 设计是更大的问题。
我正在使用 C++ 创建一个 Arduino 设备。我需要一个具有可变大小和可变数据类型的堆栈对象。本质上,这个堆栈需要能够调整大小并与字节、字符、整数、双精度数、浮点数、短裤和长数一起使用。
我有一个基本的 class 设置,但是由于需要动态内存分配量,我想确保我使用的数据释放足够的空间 space 让程序继续没有记忆问题。这不使用标准方法,而是内置 Arduino 的版本。
为了澄清,我的问题是:我的代码中是否存在任何潜在的内存问题?
注意:这不在 Arduino 堆栈交换中,因为它需要深入了解 C/C++ 内存分配,这可能对所有 C 和 C++ 程序员都有用。
代码如下:
Stack.h
#pragma once
class Stack {
public:
void init();
void deinit();
void push(byte* data, size_t data_size);
byte* pop(size_t data_size);
size_t length();
private:
byte* data_array;
};
Stack.cpp
#include "Arduino.h"
#include "Stack.h"
void Stack::init() {
// Initialize the Stack as having no size or items
data_array = (byte*)malloc(0);
}
void Stack::deinit() {
// free the data so it can be re-used
free(data_array);
}
// Push an item of variable size onto the Stack (byte, short, double, int, float, long, or char)
void Stack::push(byte* data, size_t data_size) {
data_array = (byte*)realloc(data_array, sizeof(data_array) + data_size);
for(size_t i = 0; i < sizeof(data); i++)
data_array[sizeof(data_array) - sizeof(data) + i] = data[i];
}
// Pop an item of variable size off the Stack (byte, short, double, int, float, long, or char)
byte* Stack::pop(size_t data_size) {
byte* data;
if(sizeof(data_array) - data_size >= 0) {
data = (byte*)(&data_array + sizeof(data_array) - data_size);
data_array = (byte*)realloc(data_array, sizeof(data_array) - data_size);
} else {
data = NULL;
}
// Make sure to free(data) when done with the data from pop()!
return data;
}
// Return the sizeof the Stack
size_t Stack::length() {
return sizeof(data_array);
}
显然有一些小的代码错误,虽然很重要,但很容易解决。以下回答仅适用于本class的整体设计:
仅显示的代码没有任何问题。
但只有显示的代码。对于未显示的任何代码,不提供任何意见。
而且,在尝试使用此 class.
的其余代码中,很可能会出现大量问题和内存泄漏。以泄漏或损坏内存的方式使用此 class 将非常非常容易。正确使用 class 会更难,而且更容易搞砸。如果您所要做的只是朝错误的方向打喷嚏,那么这些功能本身似乎可以正确完成工作这一事实将无济于事,最终导致这些功能未按正确的顺序或顺序使用。
仅举出前两个显而易见的问题:
1) 未能调用 deinit()
,当此 class 的任何实例超出范围并被销毁时,将泄漏内存。每次使用此 class 时,您都必须意识到此 class 的实例何时超出范围并被销毁。每次创建这个 class 的实例很容易跟踪,并且很容易记住每次调用 init()
。但是跟踪此 class 的实例可能超出范围并被销毁的所有可能方式,因此您必须调用 deinit()
并释放内部内存,这要困难得多。当发生这种情况时,很容易甚至没有意识到。
2) 如果这个 class 的一个实例被复制构造,或者默认赋值运算符被调用,这肯定会导致内存损坏,并附带内存泄漏。
请注意,您不必特意编写复制构造的代码,或将对象的一个实例分配给另一个实例。如果您不注意,编译器将非常乐意为您完成。
一般来说,避免此类问题的最佳方法是通过正确使用语言使其不可能发生。即:
1) 关注RAII design pattern。删除 init()
和 deinit()
。相反,在对象的构造函数和析构函数中执行此工作。
2) 删除复制构造函数和赋值运算符,或者正确实现它们。所以,如果这个 class 的实例永远不应该被复制构造或分配给,如果你不小心写了一些这样做的代码,最好让编译器对你大喊大叫,而不是花一个星期的时间来追踪发生的地方。或者,如果 class 可以复制构造或赋值,则正确执行。
当然,如果这种 class 的实例很少,应该可以安全地使用它,严格控制,多加注意,而不用做这种事重新设计。但是,即使是这样,最好还是把工作做好,而不是现在耸耸肩,然后决定在更多地方扩大这个 class 的使用,然后忘记这个事实class 太容易出错了。
P.S.: 开头提到的几个小bug:
data_array = (byte*)realloc(data_array, sizeof(data_array) + data_size);
这不对。 data_array
是一个 byte *
,因此 sizeof(data_array)
将始终是一个编译时常量,即 sizeof(byte *)
。这显然不是你想要的。您需要明确跟踪分配的数组的大小。
这里的其他几个地方也出现了相同的一般错误,但很容易修复。整体 class 设计是更大的问题。