我正在尝试编译并运行以下程序,而不使用C
中的main()
函数。我使用以下命令编译了程序。
gcc -nostartfiles nomain.c
编译器发出警告
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400340
好的,没问题。然后,我已经运行了可执行文件(a.out),两个< code>printf语句都打印成功,然后得到分段错误。
所以,我的问题是,为什么成功执行打印语句后会出现分段错误?
我的代码:
#include <stdio.h>
void nomain()
{
printf("Hello World...\n");
printf("Successfully run without main...\n");
}
输出:
Hello World...
Successfully run without main...
Segmentation fault (core dumped)
注意:
在这里,-nostartfiles
gcc 标志阻止编译器在链接时使用标准启动文件
在C中,当调用函数/子例程时,堆栈填充为(按顺序):
main()作为起点,ELF以这样一种方式构造程序,即无论哪个指令先出现,都会先推送,在这种情况下是printfs。
现在,程序在没有返回地址或< code>__end__的情况下被截断了,事实上,它假设在那个(< code>__end__)位置上的任何东西都是返回地址,但不幸的是,它不是,因此它崩溃了。
让我们看一下生成的程序程序集:
.LC0:
.string "Hello World..."
.LC1:
.string "Successfully run without main..."
nomain:
push rbp
mov rbp, rsp
mov edi, OFFSET FLAT:.LC0
call puts
mov edi, OFFSET FLAT:.LC1
call puts
nop
pop rbp
ret
请注意 ret
语句。您的程序的入口点被确定为 nomain
,一切都很好。但是一旦函数返回,它就会尝试跳转到调用堆栈上的地址......未填充。这是非法访问,随之而来的是分段错误。
一个快速的解决方案是在程序的末尾调用< code>exit()(假设C11,我们也可以将该函数标记为< code>_Noreturn):
#include <stdio.h>
#include <stdlib.h>
_Noreturn void nomain(void)
{
printf("Hello World...\n");
printf("Successfully run without main...\n");
exit(0);
}
事实上,现在你的函数的行为与常规的
main 函数非常相似,因为在从 main 返回后,使用 main
的返回值调用 exit
函数。
Go语言是编译型的静态语言(和C语言一样),所以在运行Go语言程序之前,先要将其编译成二进制的可执行文件。 可以通过Go语言提供的 或者 命令对Go语言程序进行编译: 命令可以将Go语言程序代码编译成二进制的可执行文件,但是需要我们手动运行该二进制文件; 命令则更加方便,它会在编译后直接运行Go语言程序,编译过程中会产生一个临时文件,但不会生成可执行文件,这个特点很适合用来调试程序。 下面就来演示
上一个小节 C 语言的程序结构中我们简单的认识了一下 C 语言的程序到底由那些部分组成,但是仅仅了解 C 语言的程序结构还不够,仅仅是万里长征走完了第一步,后面还需要进行很多的步骤才能让程序成为一个可以使用的工业产品。还记的上一个小节中我们提到的 gcc 吗?其实这是一个 C 语言的编译器,这一个小节我们就来学习一下编译器这个概念。 1. C 语言编译器 每个语言都需要编译器,甚至包括可以直接操作
主要内容:C++源文件的后缀,g++ 命令C++ 和C语言类似,也要经过编译和链接后才能运行。在《 C语言编译器》专题中我们讲到了 VS、Dev C++、VC 6.0、Code::Blocks、C-Free、 GCC、Xcode 等常见 IDE 或编译器,它们除了可以运行C语言程序,还可以运行 C++ 程序,步骤是一样的,这里不再赘述,读者需要留意的是 C++ 源文件的后缀,以及 GCC 中的 命令。 下图是 C/C++ 代码生成可执行文
前面我们给出了一段完整的C语言代码,就是在显示器上输出“C语言中文网”,如下所示: #include <stdio.h> int main() { puts("C语言中文网"); return 0; } 本节我们就来看看如何通过 Dev C++ 来运行这段代码。 Dev C++ 支持单个源文件的编译,如果你的程序只有一个源文件(初学者基本都是在单个源文件下编写代码),那么不用创建
前面我们给出了一段完整的C语言代码,就是在显示器上输出“C语言中文网”,如下所示: #include <stdio.h> int main(){ puts("C语言中文网"); return 0; } 本节我们就来看看如何通过 VS2015 来运行这段代码。 1) 创建项目(Project)在 VS2015 下开发程序首先要创建项目,不同类型的程序对应不同类型的
在Windows下,可执行程序(Executable Program)大部分是 .exe 程序,它是一系列计算机指令和数据的集合。这些指令都是二进制形式的,CPU可以直接识别,毫无障碍;但是对于程序员,它们非常晦涩,难以记忆和使用。 计算机发展初期,程序员就是使用这些二进制形式的CPU指令来编写程序的。 直接使用CPU指令编程对程序员来说简直是噩梦,尤其是当程序比较大的时候,不但编写麻烦,需要频繁