在C中调用函数的Segfault

时间:2017-08-17 18:21:57

标签: c windows debugging segmentation-fault virtual-machine

所以我正在构建一个虚拟机,并尝试将其作为跨平台,并突然遇到一个奇怪的错误。我的机器有一条let指令,它为机器内存中的变量分配内存,并为该变量赋值。简而言之,let函数调用getAddress来获取变量的地址。 getAddress检查变量是否已定义,并返回地址。如果未定义变量,getAddress将调用memallocate为变量分配内存,并返回地址。以下是函数的定义:

static uint16_t memallocate(Machine *m, char *symbol){
    uint16_t allocationAddress = getFirstFree(*m);
    SymbolTable *newSymbol = (SymbolTable *)malloc(sizeof(SymbolTable));
    newSymbol->symbolName = strdup(symbol);
    newSymbol->next = NULL;
    newSymbol->mema = allocationAddress;
    if(m->symbolTable==NULL){
        m->symbolTable = newSymbol;
    }
    else{
        SymbolTable *temp = m->symbolTable;
        while(temp->next!=NULL)
            temp = temp->next;
        temp->next = newSymbol;
    }
    m->memory[allocationAddress].acquired = 1;
    m->memory[allocationAddress].data.value = 0;
    m->occupiedAddress++;
    return allocationAddress;
}

uint16_t getAddress(Machine *m, char *symbol){
    SymbolTable *table = m->symbolTable;
    while(table!=NULL){
        if(strcmp(symbol, table->symbolName)==0){
            return table->mema;
        }
        table = table->next;
    }
    uint16_t address = memallocate(m, symbol); // Here is the segfault happening
    return address;
}

此代码在Linux上编译并运行良好,但在Windows上我在memallocate调用时遇到了段错误。由于memallocate直接传递getAddress的参数,并且参数都是指针,因此它们不应该改变。但是在通过CLion进行调试时,我看到了memallocate调用的乱码参数,这表明某种堆栈违规(可能是)。同样,它只发生在Windows中。谁能告诉我我的代码出了什么问题? 该项目的完整代码可在GitHub找到。

1 个答案:

答案 0 :(得分:1)

我拿了你的代码并通过valgrind在linux上运行它:

import time
while True:
  time.sleep(1)
  do_work()

所以(幸运的是)我们不是Windows特有的问题。诀窍是,在==13768== Conditional jump or move depends on uninitialised value(s) ==13768== at 0x109ABE: getAddress (in /home/vonaka/VirtualMachine/machine) ==13768== by 0x10B714: let (in /home/vonaka/VirtualMachine/machine) ==13768== by 0x109425: run (in /home/vonaka/VirtualMachine/machine) ==13768== by 0x109F64: main (in /home/vonaka/VirtualMachine/machine) ==13768== Uninitialised value was created by a heap allocation ==13768== at 0x4C2BE7F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd ==13768== by 0x109C2F: main (in /home/vonaka/VirtualMachine/machine) ==13768== 的第一次调用时(当getAddressm->symbolTable时),您在NULL的开头调用getFirstFree(*m),但请查看此函数:

memallocate
static uint16_t getFirstFree(Machine m) { uint16_t add = 0; while(m.memory[add].acquired) add++; return add; } m.memory[i].acquired之间的i 0number_of_instructions_in_your_input_file - 1初始化时都等于1,但writeInstruction不是m.memory[number_of_instructions_in_your_input_file].acquired初始化了。

这样的事情可以解决你的问题:

void writeInstruction(Machine *m, uint16_t add, Instruction ins) {
    m->memory[add].acquired = 1;
    m->memory[add].type = INSTRUCTION;
    m->memory[add].data.instruction = ins;
    m->occupiedAddress++;
    if(add + 1 < NUM_MEM)
        m->memory[add + 1].acquired = 0;
}

或者这可能更优雅(如果有效):

static uint16_t getFirstFree(Machine m) {
    uint16_t add = 0;
    while (m.memory[add].acquired && add < m.occupiedAddress)
        add++;
    return add;
}

修改

首先关于你的评论:

  

默认情况下,结构的成员初始化为0

这不是真的!

现在说明为什么没有malloc的段错误以及它与valgrind的警告有什么联系。

您有m类型的变量Machine以及堆栈中的其他一些变量,m包含Cell memory[NUM_MEM],每个acquired都有Cell (未初始化!)。您的输入文件包含88个说明,因此在88次acquired调用后,首先88 writeInstruction将被正确初始化。然后程序开始执行您的指令,方法是调用包括memallocategetFirstFree在内的一些函数。在这个循环中:

while(m.memory[add].acquired)
    add++;
任何add m.memory[add].acquired

很可能与0不同,因此一旦add等于NUM_MEM,您就会遇到段错误。

为什么malloc没有发生这种情况?仅仅因为你很幸运(但它不是运气),你的堆比堆栈“更清洁”。为什么它只在Windows中发生?因为这次你没那么幸运(即使在Windows中我也没有段错)。