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

X64 GCC内联汇编调用scanf:动态二维数组输入解析问询

How to Use Inline Assembly to Call scanf for a Dynamically Allocated 2D Int Array on x64 GCC

Alright, let's tackle this step by step. First, let's get clear on the ground rules for x64 GCC (which uses the System V AMD64 calling convention) and how scanf works as a variadic function. Then we'll build the code with inline assembly, following your requirement of nested loops and stack balancing.

Key Background

  • x64 System V Calling Convention: For variadic functions like scanf, the first 6 integer/pointer arguments go into registers rdi, rsi, rdx, rcx, r8, r9. Any extra arguments are pushed onto the stack in reverse order (rightmost parameter first). The caller is responsible for cleaning up the stack after the call.
  • Dynamic 2D Array: We're assuming your image is an int** — an array of pointers, each pointing to a row of ints.
  • Format String: We need a format string with exactly width * height instances of %d (since we're reading ints). We'll dynamically build this string to match your dimensions.

Complete Code Example

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

int main() {
    // Dynamic dimensions (can be set at runtime)
    int height = 3;
    int width = 3;
    int total_elements = height * width;

    // 1. Allocate the 2D array
    int** image = malloc(height * sizeof(int*));
    if (!image) { perror("malloc failed"); return 1; }
    for (int i = 0; i < height; i++) {
        image[i] = malloc(width * sizeof(int));
        if (!image[i]) { perror("malloc failed"); return 1; }
    }

    // 2. Build the format string: e.g., "%d %d %d %d %d %d %d %d %d"
    // Each %d is followed by a space to handle whitespace in input
    size_t format_len = total_elements * 3; // "%d " is 3 chars, minus trailing space
    char* format = malloc(format_len);
    if (!format) { perror("malloc failed"); return 1; }
    strcpy(format, "");
    for (int i = 0; i < total_elements; i++) {
        strcat(format, "%d");
        if (i != total_elements - 1) strcat(format, " ");
    }

    // 3. Inline assembly to call scanf
    __asm__ volatile (
        // Outer loop: start from last row (height-1) down to 0
        "movl %[height], %%ecx\n"          // ecx = height (outer loop counter)
        "movl %%ecx, %%ebx\n"
        "decl %%ebx\n"                     // ebx = i = height - 1
        "outer_loop:\n"
            // Inner loop: start from last column (width-1) down to 0
            "movl %[width], %%edx\n"       // edx = width (inner loop counter)
            "movl %%edx, %%esi\n"
            "decl %%esi\n"                 // esi = j = width - 1
            "inner_loop:\n"
                // Calculate address of image[i][j]
                "movq %[image], %%rdi\n"       // rdi = base pointer of 2D array
                "movq (%%rdi, %%rbx, 8), %%rdi\n" // rdi = pointer to row i (each int* is 8 bytes)
                "leaq (%%rdi, %%rsi, 4), %%r8\n"  // r8 = &image[i][j] (each int is 4 bytes)
                "pushq %%r8\n"                  // Push the element address to stack
                // Decrement inner loop counter, loop if not negative
                "decl %%esi\n"
                "jns inner_loop\n"
            // Decrement outer loop counter, loop if not negative
            "decl %%ebx\n"
            "jns outer_loop\n"

        // Push format string address (last parameter in stack order)
        "pushq %[format]\n"

        // Call scanf
        "call scanf\n"

        // Balance the stack: total_elements + 1 pointers, each 8 bytes
        "addq $%[stack_offset], %%rsp\n"

        // Input operands
        :
        : [image] "r" (image),
          [height] "r" (height),
          [width] "r" (width),
          [format] "r" (format),
          [stack_offset] "i" (8 * (total_elements + 1))
        // Clobbered registers: tell GCC which registers we modify
        : "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "r8", "memory"
    );

    // Verify the input (optional)
    printf("You entered:\n");
    for (int i = 0; i < height; i++) {
        for (int j = 0; j < width; j++) {
            printf("%d ", image[i][j]);
        }
        printf("\n");
    }

    // Cleanup memory
    for (int i = 0; i < height; i++) {
        free(image[i]);
    }
    free(image);
    free(format);

    return 0;
}

Breakdown of the Inline Assembly

  1. Loop Direction: We iterate from the last element to the first because scanf expects parameters in reverse stack order (rightmost argument pushed first). This ensures the first element's address is the first parameter read after the format string.
  2. Address Calculation:
    • movq (%%rdi, %%rbx, 8), %%rdi: Fetches the pointer to row i — since each int* is 8 bytes in x64, we scale the row index by 8.
    • leaq (%%rdi, %%rsi, 4), %%r8: Computes the address of image[i][j] — each int is 4 bytes, so we scale the column index by 4.
  3. Stack Management: We push all element addresses first, then the format string. After calling scanf, we adjust the stack pointer to clean up all pushed parameters — this is critical to avoid crashes.
  4. Clobber List: We explicitly list registers we modify so GCC doesn't use them for other variables. The memory clobber ensures GCC knows we're writing to the array memory.

Important Notes

  • Input Flexibility: scanf skips any whitespace (spaces, newlines, tabs), so you can enter the data in any format (single line, multiple lines, etc.).
  • Performance Alternative: For better efficiency, you could pass the first 6 element addresses in registers instead of pushing them to the stack. The code above sticks strictly to your requirement of using nested loops to push all addresses.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 09:18:12