在 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
}
顺便说一句,指针 s1
和 s2
应该是相同的。编译器能够优化相同的字符串并只存储一次。
在我的上一个个人 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
}
顺便说一句,指针 s1
和 s2
应该是相同的。编译器能够优化相同的字符串并只存储一次。