在 C 语言中使用#define 值作为函数参数——为什么有效?

Using #define value as a function parameter in C — why does it work?

在我的上一个个人 C 项目中,我对文件进行了一些操作。我尝试过的一件事是将 #define 值作为参数。最后是一段代码,让你明白我的意思。

我的问题是:它为什么有效?数据存储在哪里?符合ANSI C吗?

#include <stdio.h>
#define FILE_NAME "test.txt"

void open_file(FILE**, char*);

int main(int argc, char *argv[])
{
   FILE* file;
   open_file(&file, FILE_NAME);
   return 0;
}

void open_file(FILE** file, char* filename)
{
   *(file)=fopen(filename, "r");
}

为什么我可以将文本作为参数而不是存储文件名的字符数组?

预处理器将您的代码扩展为 open_file(&file, "test.txt");

"test.txt" 是一个字符串文字。编译器将其嵌入到二进制可执行文件中。它在您加载程序时加载到内存中。

我们来分析这个简单的例子:

#include <stdio.h>

int main(int argc, char **argv) {
        printf("hello, wolrd!\n");
        return 0;
}

我们可以为此生成程序集:gcc -S hello.c:

        .file   "hello.c"
        .section        .rodata
.LC0:
        .string "hello, wolrd!"
        .text
        .globl  main
        .type   main, @function
main:
.LFB0:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        subq    , %rsp
        movl    %edi, -4(%rbp)
        movq    %rsi, -16(%rbp)
        movl    $.LC0, %edi
        call    puts
        movl    [=11=], %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609"
        .section        .note.GNU-stack,"",@progbits

如您所见,字符串被放置在 .rodata - 只读数据部分。您可以获得该字符串的内存地址并尝试访问它:

#include <stdio.h>

int main(int argc, char **argv) {
        const char *s1 = "hello, world!\n"; // both strings are identical
        char *s2 = "hello, world!\n";
        printf(s1);  // Read-only access - OK
        s2[0] = 'x'; // Write to .rodata - SEGFAULT (crash) here 
        return 0;    // we never reach here
}

顺便说一句,指针 s1s2 应该是相同的。编译器能够优化相同的字符串并只存储一次。