You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言调用汇编函数时出现Segmentation fault段错误问题求助

问题排查与修复方案

核心问题总览

你遇到的段错误是C代码缓冲区溢出、汇编函数调用逻辑不符合C调用约定、栈帧操作错误、结果类型不匹配四类问题共同导致的,逐一修复如下:


1. C代码问题修复

问题点

  • 你定义的num1、num2数组长度只有3字节,但是调用fgets时传入了1024的读取长度,直接发生栈缓冲区溢出,破坏栈结构。
  • switch的case 1分支没有加break,执行完加法逻辑后会直接落到case 5分支提前退出程序。
  • 关于你问的add函数声明是否要加extern:C语言中全局函数默认隐含extern属性,此处加不加都可以正常链接。

修正后C代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void add(char*, char*);

int main(){
    int choice;
    // 扩大缓冲区到32字节,足够存储常规整数字符串
    char num1[32];
    char num2[32];

    printf("Welcome to the back and forth program!\n\n");
    
    do{
        printf("What would you like to do?\n\n");
        printf("1. Add two numbers together.\n");
        printf("2. Find if a string is a palindrome. (ASM Version)\n");
        printf("3. Find the factorial of a number.\n");
        printf("4. Find if a string is a palindrome. (C Version)\n");
        printf("5. Exit Program.\n\n");
        printf("choose 1-5: ");

        scanf("%d", &choice);
        getchar();

        while(choice < 1 || choice > 5){
            printf("\nPlease choose an option between 1 and 5.\n");
            scanf("%d", &choice);
            getchar();
        }

        switch(choice){
            case 1:
                printf("\n*Add two numbers together*\n\n");
                printf("Please enter a number: ");
                // 读取长度和缓冲区匹配
                fgets(num1, sizeof(num1), stdin);
                num1[strcspn(num1, "\n")] = '\0'; // 更安全的换行符替换逻辑

                printf("\nPlease enter a second number: ");
                fgets(num2, sizeof(num2), stdin);
                num2[strcspn(num2, "\n")] = '\0';

                add(num1, num2);
                printf("\nResult: %s\n", num2);
                // 新增break,避免穿透到其他分支
                break;
            case 2:
            case 3:
            case 4:
            case 5:
                printf("\nThanks for using!\n");
                break;
        }
    }while(choice != 5);
    return 0;
}

2. 汇编代码问题修复

问题点

  • 调用atoi前没有把字符串参数压入栈,atoi取到的是栈上的垃圾值作为字符串指针,访问非法地址直接触发段错误。
  • 32位C的cdecl调用约定要求:调用方负责在函数调用后平衡栈,你两次调用atoi后都没有恢复栈指针。
  • 你直接把num1和num2的指针当整数相加,完全没有用到atoi的返回值(atoi的返回值存在eax寄存器中)。
  • 相加后的结果是整数,你没有把它转换为字符串写回num2的缓冲区,C代码用%s输出num2会把整数当字符串指针访问,必然触发非法访问。
  • 没有恢复栈帧:你开头push ebp,结尾没有pop ebp,ret指令会把栈上的ebp值当成返回地址,导致程序跳转到非法地址。

修正后汇编代码

BITS 32
GLOBAL add
EXTERN atoi, sprintf ; 调用sprintf完成整数转字符串

section .text
add:
    push ebp
    mov ebp, esp
    push ebx ; 保存cdecl约定需要保留的寄存器
    push esi
    push edi

    ; 转换第一个数字字符串:num1 = [ebp+8]
    push dword [ebp+8]
    call atoi
    add esp, 4 ; 平衡栈
    mov ebx, eax ; 把第一个整数存在ebx

    ; 转换第二个数字字符串:num2 = [ebp+12]
    push dword [ebp+12]
    call atoi
    add esp, 4 ; 平衡栈
    add eax, ebx ; 两个整数相加,结果存在eax

    ; 把相加结果转为字符串写入num2缓冲区
    push eax
    push dword fmt_str
    push dword [ebp+12]
    call sprintf
    add esp, 12 ; 三个参数,平衡栈

    ; 恢复寄存器和栈帧
    pop edi
    pop esi
    pop ebx
    pop ebp
    ret

section .data
fmt_str db "%d", 0 ; sprintf用的格式化字符串

3. 编译命令修正

因为你用的是64位系统编译32位程序,需要指定32位编译参数:

# 汇编生成32位目标文件
nasm -f elf32 add.asm -o add.o
# 编译C代码并链接,加-m32指定32位程序
gcc -m32 main.c add.o -o calc

运行./calc即可正常执行。

内容的提问来源于stack exchange,提问作者TheSkyeLord

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 17:15:10