我正在编写一个递归泛洪填充算法来查找图像中的连接组件,我的代码编译并运行良好的MSVC 2008编译器; 但是mingw编译的二进制文件在运行时崩溃了.
在我使用std :: stack将算法转换为非递归后,一切顺利.
但是,如果我必须在某些情况下使用递归算法,并且mingw无法处理呢?
如何增加二进制文件的堆栈大小,是否有任何编译选项?
谢谢
我需要一些关于向被调用方"转发"参数的建议(在LLVM-IR中).
假设我有一个在模块F中所有其他函数开头调用的函数.从F我需要访问(读取)传递给其直接调用者的参数.
现在要做到这一点,我在结构中的调用者中包含所有参数,并将i8*指针传递给结构F,以及一个标识符,告诉调用哪个调用者F.F然后有一个巨大的开关,分支到适当的拆箱代码.必须这样做,因为模块中的函数具有不同的签名(不同的参数/返回值计数和类型;甚至不同的调用约定),但它显然是次优的(从性能和代码大小的角度来看)因为我需要在堆栈上分配结构,复制其中的参数,传递一个额外的指针F,然后执行拆箱.
我想知道是否有更好的方法来做到这一点,即从函数访问其直接调用者的堆栈帧的方法(知道,由于标识符,调用函数的调用者),或者更一般地,任意在其直接调用者中定义的值.有什么建议?
注意:我正在研究的重点是拥有完成所有这一切的单一功能F; 拆分/内联/专门化/模板化F不是一种选择.
澄清一下,假设我们有以下功能FuncA和FuncB(注意:以下只是伪C代码,永远记住我们在谈论LLVM-IR!)
Type1 FuncA(Type2 ArgA1) {
F();
// ...
}
Type3 FuncB(Type4 ArgB1, Type5 ArgB2, Type6 ArgB3) {
F();
// ...
}
Run Code Online (Sandbox Code Playgroud)
我需要的是一个有效的方法F来执行以下功能:
void F() {
switch (caller) {
case FuncA:
// do something with ArgA1
break;
case FuncB:
// do something with …Run Code Online (Sandbox Code Playgroud) 精简版:
有没有办法在Python中实现与Perl
Carp::carp实用程序相同的效果?
长版(适合那些不熟悉的人Carp::carp):
假设我们正在实现一些库API函数(即,它意味着其他程序员在其代码中使用它们)spam,并且假设spam包含一些代码来检查传递给它的参数的有效性.当然,如果检测到这些参数的任何问题,则此代码应该引发异常.假设我们希望将相关的错误消息和回溯尽可能地用于调试某些客户端代码的人.
理想情况下,由此引发的异常产生的回溯的最后一行应该精确定位"违规代码",即客户端代码中spam使用无效参数调用的行.
不幸的是,至少在默认情况下,使用Python不会发生这种情况.相反,回溯的最后一行将引用库代码内部的某个地方,其中异常实际上是raise'd,这对于这个特定回溯的目标受众来说是非常模糊的.
例:
# spam.py (library code)
def spam(ham, eggs):
'''
Do something stupid with ham and eggs.
At least one of ham and eggs must be True.
'''
_validate_spam_args(ham, eggs)
return ham == eggs
def _validate_spam_args(ham, eggs):
if not (ham or eggs):
raise ValueError('if we had ham '
'we could have ham and eggs ' …Run Code Online (Sandbox Code Playgroud) (我认为这个问题很可能已经重复或者已经在这里回答了,但是由于"堆栈分配"和相关术语的干扰,寻找答案很难.)
我有一个玩具编译器,我一直在研究脚本语言.为了能够在脚本正在进行时暂停执行并返回到主机程序,它有自己的堆栈:带有"堆栈指针"变量的简单内存块,使用正常的C代码操作递增对于那种事情等等.到目前为止没有意思.
目前我编译为C.但我也有兴趣调查编译机器代码 - 同时保持二级堆栈和在预定义控制点返回主机程序的能力.
所以...我认为在我自己的代码中使用传统的堆栈寄存器不太可能是一个问题,我假设寄存器会发生什么,只要一切都在我完成时就恢复了(如果我做的话,请纠正我)在这一点上我错了.但是 ......如果我希望脚本代码调用其他库代码,使用这个"虚拟堆栈"离开程序是否安全,或者为此目的将它返回原始堆栈是否必不可少?
像这一个和这个的答案表明堆栈不是传统的内存块,但它依赖于特殊的,系统特定的行为来处理页面错误和诸如此类的东西.
所以:
这显然不是必需的,因为简单地将指针返回到实际堆栈将是完全可用的,或者就此而言,首先不要滥用它们并且只是放置更少的寄存器,我可能不应该尝试这样做在所有(尤其是由于明显超出我的深度).但无论如何我仍然很好奇.想知道这些事情是如何运作的.
编辑:对不起,当然应该说.我正在研究x86(我自己的机器为32位),Windows和Ubuntu.没有异国情调.
假设我有一些操纵图结构的递归函数:
typedef struct Node {
Data data;
size_t visited;
size_t num_neighbors;
struct Node *neighbors[];
} Node;
void doSomethingWithNode(Node *node)
{
if ((!node) || node->visited)
return;
node->visited = 1;
/* Do something with node->data */
size_t /* some local variables */;
size_t i;
for (i = 0; i < node->num_neighbors; i++)
{
/* Do some calculations with the local variables */
if (/* something */)
doSomethingWithNode(node->neighbors[i]);
}
}
Run Code Online (Sandbox Code Playgroud)
由于我在循环中使用的局部变量,compile(gcc)为这个函数创建了一个大于I-like的堆栈帧(即使有很多pushq和popq指令-O3),这是一个问题,因为它是深刻的递归.由于我访问节点的顺序无关紧要,我可以重构此代码以使用一堆Node指针,从而减少每次迭代一个指针的开销.
gcc …我正在学习Promise,为了理解它我读了一些关于JavaScript的事件循环.此文章简要介绍了事件循环的工作,如调用堆栈,事件表和消息队列.
但我不知道调用堆栈如何处理包含'return'的行,以及之后会发生什么.下面是我写的一个例子,希望了解Promise如何基于事件循环工作.如果你想试一试,请参阅http://jsbin.com/puqogulani/edit?js,console.
var p1 = new Promise(
function(resolve, reject){
resolve(0);
});
p1.then(function(val){
console.log(val);
p1.then(function(){
console.log("1.1.1");
p1.then(function(){
console.log("1.1.2");
p1.then(function(){
console.log("1.1.3");
});
});
});
p1.then(function(){
console.log("1.2");
})
return 30;
//return new Promise(function(resolve, reject){
// resolve(30);
//});
})
.then(function(val){
console.log(val/2);
});
p1.then(function(){
console.log("2.1");
});
console.log("Start");
Run Code Online (Sandbox Code Playgroud)
可以看出,有两个"返回",使用它们中的每一个都会给出不同的输出顺序.具体来说,使用时return 30;,1.1.2, 1.1.3都是经过15,但使用的时候return new Promise(...),1.1.2, 1.1.3是前15.那么当代码达到两个不同的"返回"时究竟发生了什么?
在x86汇编语言中:
我假设我有一个正常的功能序言,请阅读
push ebp
mov ebp,esp
Run Code Online (Sandbox Code Playgroud)
我知道我可以通过访问内存目标操作数来读取或写入寄存器,假设我想要第一个参数。我会做
mov eax,[ebp +8]
Run Code Online (Sandbox Code Playgroud)
从堆栈中获取一个整数参数。
那为什么我不直接使用stackpointer呢?
add esp,8 ; point ESP at the data we want
pop eax
sub esp,12 ; restore ESP to its original position
Run Code Online (Sandbox Code Playgroud)
这会导致错误吗?在任何情况下都使用吗?
我当然知道第一个操作的大小较小,因为它只是一个操作码,mov而不是三个,但这不是问题的重点。
(编者注:mov eax, [ebp+8]。在x86机器代码的3字节的指令 add/ subESP,imm8指定为3个字节的每个,pop eax是1个字节。
mov eax, [esp+8]是一个4字节的指令:不像在16位寻址模式,ESP可以是一个基址寄存器。但是它确实需要一个SIB字节来对其进行编码。
这些都是现代CPU上的单联指令,不包括额外的堆栈同步联指令。)
为什么这样做是错误的做法?
背景信息:我有一个函数可以直接或通过其他函数间接调用自身,并且希望有一种廉价的方法可以在程序耗尽调用堆栈时优雅地失败。我可以手动计算呼叫数,但我想要一种更优雅、更强大的方式来实现这一点。
是否可以使用稳定的 Rust 确定当前的调用堆栈深度?
我能找到的唯一选择是:
就我而言,以下代码将在堆栈上创建一个数组:
unsafe struct Foo
{
public fixed int bar[10];
}
var foo = new Foo();
Run Code Online (Sandbox Code Playgroud)
stackalloc 语句会做同样的事情:
public void unsafe foo(int length)
{
Span<int> bar = stackalloc int[length];
}
Run Code Online (Sandbox Code Playgroud)
所以我想知道这些方法之间有什么区别。固定大小缓冲区的目的是什么?每个人都在谈论性能提升,但我不明白为什么我需要它们,当我已经可以使用 stackalloc 在堆栈上创建数组时。MSDN 表示固定大小的缓冲区用于与其他平台“互操作”。那么这个“互操作”是什么样的呢?
当鼠标触发单击事件时,它的行为符合预期:
首先,侦听器 1 被推入堆栈,并在微任务队列(或作业队列)中对 Promise 1 进行排队。当侦听器 1 弹出时,堆栈变空。Promise 1 回调在侦听器 2 之前执行(侦听器 2 在任务队列(或回调队列)中等待)。在 Promise 1 回调弹出后,侦听器 2 被压入堆栈。因此输出为:
侦听器 1 微任务 1 侦听器 2 微任务 2
但是,当通过 JavaScript 代码触发点击时,其行为会有所不同:
即使在 click() 函数完成之前,回调也会被推入堆栈(即调用堆栈不为空)。这里的输出是:
监听器 1 监听器 2 微任务 1 微任务 2
这是代码:
window.onload = function(){
document.getElementById("myBtn").addEventListener('click', () => {
Promise.resolve().then(() => console.log('Microtask 1'));
console.log('Listener 1');
} );
document.getElementById("myBtn").addEventListener('click', () => {
Promise.resolve().then(() => console.log('Microtask 2'));
console.log('Listener 2');
} );
}
function clickB(){
document.getElementById("myBtn").click();
}Run Code Online (Sandbox Code Playgroud)
<!DOCTYPE html>
<html>
<button id="myBtn">Manual …Run Code Online (Sandbox Code Playgroud)callstack ×10
stack ×3
assembly ×2
c ×2
event-loop ×2
javascript ×2
performance ×2
recursion ×2
abi ×1
c# ×1
c++ ×1
dom-events ×1
function ×1
gcc ×1
llvm ×1
mingw ×1
python ×1
red-zone ×1
rust ×1
stack-trace ×1
x86 ×1