我今天一直在思考一个问题,在google上很难找到答案。
在处理指向在堆上和堆栈上分配的对象的指针时,我试图了解 STL 容器的行为。
所以,从对象开始,没有指针......想象我有......
std::vector<int> myVec;
while(true)
{
int myInt = 5;
myVec.push_back(myInt);
myVec.pop_back();
}
Run Code Online (Sandbox Code Playgroud)
我的理解是 pop_back() 方法将确保删除向量中包含的整数,而不仅仅是从容器中删除。因此,如果它运行并进行了 10 亿次迭代,我不应该期望内存泄漏。我插入的所有内容都将被删除。内存检查显示了这种行为。
现在考虑我使用一个指针向量(指向堆上的对象)......
std::vector<int*> myVec;
while(true)
{
int * myIntP = new int(5);
myVec.push_back(myIntP);
myVec.pop_back();
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,每次调用 pop_back() 时只应删除指针本身,并且底层对象保持未删除状态,从而导致内存泄漏。因此,经过十亿次迭代后,我使用了相当多的内存,即使我的向量中没有条目。
现在,如果我有一个指针向量(指向堆栈上的对象)怎么办...
std::vector<int*> myVec;
while(true)
{
int myInt = 5;
int * myIntP = &myInt;
myVec.push_back(myIntP);
myVec.pop_back();
}
Run Code Online (Sandbox Code Playgroud)
这里的指针指向堆栈对象。他们的内存是否在调用 pop_back() 时被释放?内存检查表明这种行为没有内存泄漏。使用的少量内存表明这就像堆栈上的对象一样。然而,这对我来说不是预期的,因为如果指针是从另一个函数传递给我的,则是一个堆栈变量,即
void myFunc(int * myIntP)
{
std::vector<int*> myVec;
myVec.push_back(myIntP);
myVec.pop_back();
}
int main()
{
int myInt = 5;
int * myIntP = &myInt; …Run Code Online (Sandbox Code Playgroud) 所以我有一个作业说:
请使用包/类的通用实例化。必须在通用包/模板内的系统堆栈中为每个 BMR(矩阵)分配空间,可能是在通用实例化期间!您特别不能使用“new”、“malloc”或任何其他运算符,这些运算符在运行时以任何语言在堆中分配空间。用荧光笔清楚地标记这部分代码!您必须阅读所有事务并打印所有结果在通用包/模板中。I/O 例程必须作为通用参数传递
和不相关的代码:
generic
type subscript is (<>); --range variable to be instantiated
type myType is private; --type variable to be intantiated
package genericArray is
type userDefinedArray is array(subscript range <>) of myType;
end genericArray;
Run Code Online (Sandbox Code Playgroud)
with ada.text_io; use ada.text_io;
with genericArray;
procedure useGenericArray is
type month_name is (jan, feb, mar, apr, may, jun,
jul, aug, sep, oct, nov, dec);
type date is
record
day: integer range 1..31; month: month_name; year: integer;
end record;
type family is (mother, father, …Run Code Online (Sandbox Code Playgroud) 据我所知,局部变量和参数存储在堆栈内存中,其中包括对象引用,而实际对象存储在堆内存中。那么当你使用 var-args 时到底会发生什么?
public static int[] test(int... x) {
return x;
}
public static void main(String[] args) {
int[] a = test(1,2,3,4,5);
int[] b = test(6,7,8,9,0);
System.out.println(a);
System.out.println(b);
for (int i : a) {
System.out.println(i);
}
for (int i : b) {
System.out.println(i);
}
Run Code Online (Sandbox Code Playgroud)
test在这里,我相信作为参数传递给in 的所有值都x存储在堆栈中,因此当我们调用 时test(1,2,3,4,5),我们正在使用堆栈空间,相应地,当我们调用时,test(6,7,8,9,0)我们应该在堆栈上引起内存冲突......但是当我运行时上面,我得到以下结果。
[I@2db0f6b2
[I@3cd1f1c8
1
2
3
4
5
6
7
8
9
0
Run Code Online (Sandbox Code Playgroud)
a可以看出,由于第二次调用test生成 ,因此没有损坏 中的项目b。两者的存储方式似乎不同。
这是否意味着参数以某种方式存储在堆上?这是否意味着任何形式的调用function(param)都会转换为(原始或内存引用)的值,param而不一定位于堆栈内存中?
在 C++ 中,我们可以通过取消引用来将类的堆分配对象分配给堆栈分配的对象。看起来没有问题,它可以按预期工作,即使析构函数也能正常工作,但是这样编写代码是好是坏?
#include <iostream>
class cls {
public:
cls(int n) : pInt{new int{n}} {
std::cout << "Constructor\n";
}
int *pInt;
~cls() {
std::cout << "Destructor\n";
delete pInt;
}
};
int main() {
cls *hObj = new cls{100};
cls sObj = *hObj;
}
Run Code Online (Sandbox Code Playgroud) 我想我的问题不需要最小的工作示例;这可能很容易并且易于描述。
让我们假设有一个类实例将一些对象存储为成员。现在其中一个成员在运行时增长。创建实例后,member1 消耗了 10 个字节,member2 消耗了 20 个字节。然后 object1 以某种方式被修改,现在需要 15 个字节。
我的问题是,member1(的第一个字节)的地址是否不变?或者,member1 的第一个字节现在有可能像以前一样有另一个地址吗?成员变量是否在堆中分配?
感谢您的反馈意见!
最好的事物
我一直在研究一些已有 30 多年历史的 Fortran 代码,试图找出为什么它会因为看似随机的原因间歇性地不起作用。一组输入适用于一个用户,但不适用于另一个用户,使用 -O1 或 -O3 编译时可以工作的代码,但使用 -O2 时会出现段错误,以及其他奇怪的废话。
经过大约一周的时间挑选了 30 年没有碰过的代码,我找到了问题所在——有很多未初始化的变量,而且这些变量中的随机垃圾是否崩溃基本上是随机的整个计划。老实说,我不知道这段代码是如何工作的。
现在我回到客户那里并告诉他们发生了什么事,他们的回答是“如果这是一个问题,为什么代码在过去 30 年里一直有效......?” 这是一个公平的问题——为什么这个代码在过去 30 年里对他们有效?我想我是从那时起第一个从源代码重建它的人,90 年代的 Fortran 编译器是否将堆栈归零或其他什么?为什么几十年前的程序员认为将变量放在赋值运算符的右侧是合理的,而不考虑这些变量是否已首先初始化?
让\xe2\x80\x99s 在我的 main 方法中说我有代码行LinkedList<E> myLinkedList = new LinkedList<>(),所以现在我有一个命名myLinkedList1为对象的引用/指针变量(并且构造函数存储在另一个.java文件中自己的 LinkedList 类中,而不是同一个.java文件主要方法在)。
现在我创建另一个名为 的引用/指针变量myLinkedList2。我使用了方法addLast(E newElement)(这个方法当然是存储在LinkedList类中的),但是我只在on上使用myLinkedList1(所以是这样myLinkedList.addLast(E newElement)),JVM怎么知道只在on上使用这个方法myLinkedList1而不是myLinkedList2在堆上存储的对象方法用它?我以为它们被放在堆栈上。
我有一个小的 C 代码,它通过修改堆栈地址处的数据来演示运行时堆栈功能。
#include <stdio.h>
int * fun() {
int a = 10;
return &a;
}
int * fun2() {
int b = 20;
return &b;
}
int main () {
int *a = fun();
int *b = fun2();
printf("a:%d b:%d\n", *a, *b);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
其输出为:a:20 b:20,显示“fun2”中的“b”使用与“fun”中的“a”相同的堆栈地址。
我也想在 Rust 中测试这个。做到这一点最简单的方法是什么?
我尝试借用,但编译器说“没有借用价值”。
我知道原始值存储在堆栈中,非原始值存储在堆中。据我所知,堆栈还包含对非原始值的引用。现在我的疑问是我们在哪里存储对原语的引用?即,例如,
int a=10;
Run Code Online (Sandbox Code Playgroud)
现在据我所知,值 10 存储在堆栈中,但我的问题是该值a存储在哪里?
另外请推荐一些学习 Java 内存管理的好资源。我在网上读了很多东西,但没有一个对我来说是清楚的。
一些最优化的函数版本,例如popcount并count consecutive zeros使用表查找来获得最终答案。
在 C 和 C++ 中,可以在堆栈上分配数组并快速访问它们。
有没有办法在 C# 中做到这一点?据我所知,stackalloc只能在函数内使用,因此数组不会持久化。
我有一个小的查找表,我希望能够尽快访问它,因此更愿意在堆栈而不是堆上分配它。
stack-memory ×10
heap-memory ×8
c++ ×3
java ×3
ada ×1
arrays ×1
c ×1
c# ×1
class ×1
constructor ×1
containers ×1
destructor ×1
fortran ×1
generics ×1
linked-list ×1
memory ×1
object ×1
performance ×1
pointers ×1
rust ×1