gcc对open(2)支持重载吗
在Linux中,如果man -s2 open, 我们看到两种不同的函数原型声明:
$ man -s2 open
NAME
open, creat - open and possibly create a file or device SYNOPSIS
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h> int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode); ...<snip>...
大约14年前(刚迈出大学校园没多久),第一次看到这样的声明的时候,我很纳闷,难道gcc能像g++一样支持c++的重载(overload)?当时没搞明白,反正能编译通过,就没深究(当然那时候也没能力深究)。什么是重载?所谓重载,就是函数名相同,但是参数列表不同。
那么为什么gcc支持编译同一个函数名open但是具有不同的参数列表呢?
先用一个小例子证明gcc不支持重载但是g++支持(当然也必须支持)。
o jade.c
int add(int a, int b)
{
return (a + b);
} int add(int a, int b, int c)
{
return (a + b + c);
} int main(int argc, char *argv[])
{
int m = add(, );
int n = add(, , );
return (m + n);
}
o 用gcc编译看看,有报错哦
$ gcc -g -Wall -o jade jade.c
jade.c::: error: conflicting types for ‘add’
int add(int a, int b, int c)
^
jade.c::: note: previous definition of ‘add’ was here
int add(int a, int b)
^
jade.c: In function ‘main’:
jade.c::: error: too few arguments to function ‘add’
int m = add(, );
^
jade.c::: note: declared here
int add(int a, int b, int c)
^
看来gcc真的不支持重载啊, 让一个c编译器去支持c++的语法,怎么可能呢?! 当然没有这种可能。
o 改用g++编译看看
$ g++ -g -Wall -o jade jade.c
$ ./jade
$ echo $?
没得错,g++能正确处理重载函数。那么回到原来的问题,为什么gcc支持编译两种不同的open()函数原型? 还是先写个demo看看。
o foo.c
/*
* Demo to dig out how these two open() as follows are compiled by gcc.
* o int open(const char *pathname, int flags);
* o int open(const char *pathname, int flags, mode_t mode);
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h> int
main(int argc, char *argv[])
{
/* XXX: Do NOT have any error handling to make things simple */ if (argc != )
return -; char *file1 = argv[];
char *file2 = argv[]; int fd1 = open(file1, O_RDWR|O_APPEND);
int fd2 = open(file2, O_RDWR|O_APPEND|O_CREAT, ); write(fd1, "hello\n", );
write(fd2, "world\n", ); close(fd1);
close(fd2); return ;
}
o 编译并测试
$ gcc -g -Wall -o foo foo.c
$ rm -f /tmp/f1 /tmp/f2
$ echo "world" > /tmp/f1 && cat /tmp/f1
world
$ ls -l /tmp/f1
-rw-r--r-- veli veli Apr : /tmp/f1
$ ./foo /tmp/f1 /tmp/f2
$ ls -l /tmp/f1 /tmp/f2
-rw-r--r-- veli veli Apr : /tmp/f1
-rwxr-xr-x veli veli Apr : /tmp/f2
$ cat /tmp/f1
world
hello
$ cat /tmp/f2
world
神啦,gcc貌似支持重载。请注意,貌似!好了,反汇编看看。
(gdb) set disassembly-flavor intel
(gdb) disas /m main
Dump of assembler code for function main:
{
0x0804847d <+>: push ebp
0x0804847e <+>: mov ebp,esp
0x08048480 <+>: and esp,0xfffffff0
0x08048483 <+>: sub esp,0x20 /* XXX: Do NOT have any error handling to make things simple */ if (argc != )
0x08048486 <+>: cmp DWORD PTR [ebp+0x8],0x3
0x0804848a <+>: je 0x8048496 <main+> return -;
0x0804848c <+>: mov eax,0xffffffff
0x08048491 <+>: jmp 0x8048537 <main+> char *file1 = argv[];
0x08048496 <+>: mov eax,DWORD PTR [ebp+0xc]
0x08048499 <+>: mov eax,DWORD PTR [eax+0x4]
0x0804849c <+>: mov DWORD PTR [esp+0x10],eax char *file2 = argv[];
0x080484a3 <+>: mov eax,DWORD PTR [eax+0x8]
0x080484a6 <+>: mov DWORD PTR [esp+0x14],eax int fd1 = open(file1, O_RDWR|O_APPEND);
0x080484aa <+>: mov DWORD PTR [esp+0x4],0x402
0x080484b2 <+>: mov eax,DWORD PTR [esp+0x10]
0x080484b6 <+>: mov DWORD PTR [esp],eax
0x080484b9 <+>: call 0x8048340 <open@plt>
0x080484be <+>: mov DWORD PTR [esp+0x18],eax int fd2 = open(file2, O_RDWR|O_APPEND|O_CREAT, );
0x080484c2 <+>: mov DWORD PTR [esp+0x8],0x1ed
0x080484ca <+>: mov DWORD PTR [esp+0x4],0x442
0x080484d2 <+>: mov eax,DWORD PTR [esp+0x14]
0x080484d6 <+>: mov DWORD PTR [esp],eax
0x080484d9 <+>: call 0x8048340 <open@plt>
0x080484de <+>: mov DWORD PTR [esp+0x1c],eax ...<snip>... return ;
0x08048532 <+>: mov eax,0x0 }
0x08048537 <+>: leave
0x08048538 <+>: ret End of assembler dump.
(gdb)
对于open()函数,L22有两个参数, L23则有三个参数,gcc都能将其优雅地压入栈(stack)中。这里我们就可以大胆地猜测一下了,open()函数一定是支持变参的!接下来就是找证据。用gcc -E foo.c看一看,
$ gcc -E foo.c | egrep open
extern int open (const char *__file, int __oflag, ...) __attribute__ ((__nonnull__ ()));
extern int openat (int __fd, const char *__file, int __oflag, ...)
int fd1 = open(file1, |);
int fd2 = open(file2, ||, );
注意L2行,open() 果然支持变参,Bingo! 而open()的函数原型定义在fcntl.h中,
$ egrep -in " open .*nonnull.*" /usr/include/fcntl.h
:extern int open (const char *__file, int __oflag, ...) __nonnull (());
相比之下,ioctl(2)一看就知道其支持变参。
$ man -s2 ioctl
NAME
ioctl - control device SYNOPSIS
#include <sys/ioctl.h> int ioctl(int d, int request, ...); ...<snip>...
小结:
乍一看,open(2)的man page确实给了我们这样一个假象,一个c编译器gcc竟然支持c++的重载,简直太不可思议啦。然而,透过现象看本质,gcc之所以能够友好地编译两种不同的open()函数原型,是因为,gcc不支持也不可能支持c++的重载,但是open()函数原型支持变参。(Aha, 原来是open(2)的man page误导了我!) 另外,如果对ABI有所了解,那么很容易想明白,open(2)作为一种系统调用(syscall),支持变参,合情合理。
gcc对open(2)支持重载吗的更多相关文章
- C++中对封装的语法支持——重载运算符
重载运算符 1.对于自定义类型,编译器不知道运算规则,而重载运算符会将两个对象相加转换为函数调用. 2.运算符重载转换的函数调用,函数名字是固定的规则. (1) 如果重载+号运算符,函数名字就是:op ...
- gcc对c++标准的支持
GCC 4.8.1完全支持c++11核心部分,对应的glibc为2.17 gcc 4.9支持c++11正则表达式,卧槽...4.8.5会报terminate called after throwing ...
- 交叉编译支持SVE ACLE的gcc
最近在学习AArch64的SVE技术时,发现目前可以在网上找到的gcc版本都不支持SVE intrinsic方式调用,在看文档时发现,GCC要到2020年的GCC10时才会支持: 在github上看到 ...
- gcc,g++,extern “C” :一些编译错误的缘由
正好是我们代码中遇到的问题,之前不求甚解,只用g++编译没有错误就不管了,现在要跨平台到windows下就遇到了问题.全文转载自:http://user.qzone.qq.com/75172588/b ...
- c++的重载 缺省参数和命名空间详解
参加了几次笔试,发现有很多c++方面的问题被卡了.从现在开始进攻c++.之后会陆续更新c++学习笔记. 先说说我学习的书籍,大家如果有好的书籍推荐,感谢留言. 暂时是在看这些书自学. 1.C++介绍. ...
- boost::bind 不能处理函数重载 (error: no matching function for call to 'bind')
前言 最近任务多.工期紧,没有时间更新博客,就水一期吧.虽然是水,也不能太失水准,刚好最近工作中遇到一个 boost::bind 的问题,花费了半天时间来定位解决,就说说它吧. 问题背景 项目中使用了 ...
- GCC 预处理、编译、汇编、链接..
1简介 GCC 的意思也只是 GNU C Compiler 而已.经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言:它现在还支持 Ada 语言.C++ 语言.Java 语言.Objective ...
- Linux GCC常用命令
1简介 2简单编译 2.1预处理 2.2编译为汇编代码(Compilation) 2.3汇编(Assembly) 2.4连接(Linking) 3多个程序文件的编译 4检错 5库文件连接 5.1编译成 ...
- PHP面向对象编程——深入理解方法重载与方法覆盖(多态)
什么是多态? 多态(Polymorphism)按字面的意思就是“多种状态”.在面向对象语言中,接口的多种不同的实现方式即为多态.引用Charlie Calverts对多态的描述——多态性是允许你将父对 ...
随机推荐
- ASP.NET Core2调用Azure云上的PowerBI报表展示
在开发企业应用中,报表功能是当之无愧的重头戏,如何将数据通过合适的报表呈现出来成为每个项目人员必需面临的问题.而找到一款合适的报表往往都需要考率价格.开发.风格.支撑等因素.那么,我在这里给大家介绍一 ...
- 关于Unity中MonoBehaviour的构造函数
关于Unity中MonoBehaviour的构造函数 在学习Unity MVVM UI框架的时候,一不小给一个继承自MonoBehaviour类的子类编写了自定义构造函数,结果调Bug调了两个钟,特此 ...
- Asp.NetCore取配置信息
本文简单介绍Asp.NetCore取自定义配置信息的方法,要取配置信息首先得有配置文件. 1, 本例新建了一个TimeOut.json配置文件,和其对应的一个TimeOut类 2, 首先在Startu ...
- Android 多图,大图内存优化
策略: 1. 图片压缩 如果所需尺寸大于图片原始尺寸,可以压缩图片节省内存. 2. 图片缓存 每个图片加载时都会生成一个 Bitmap.把这些 Bitmap 缓存起来以重用相同的图片,避免重复创建. ...
- c语言第一次作业--顺序、分支结构
1.1思维导图 1.2.1本周学习体会以及代码量学习体会 1.2.2学习体会 因为在暑假时候没有对c语言进行学习,没太关注一些学习资料,一些教学视频也没看,感觉对c语言是陌生的,刚开课的时候自 ...
- ClamAV学习【9】——cvd文件解析及cli_untgz函数浏览
这个cli_untgz函数,是用来解压CVD文件的. 那么,就刚先搞清楚CVD文件的功能作用.下了源码,我们会发现,没有前面提到的*.mdb或者*.hbd等病毒签名文件.原因就是,那些文件都是由CVD ...
- java 实验4 异常
异常(实际使用直接try-catch) 1.常见系统异常 异常 异常的解释 ClassNotFoundException 未找到要装载的类 ArrayIndexOutOfBoundsException ...
- Elasticsearch5.4 删除type
首先要说明的是现在的Elasticsearch已经不支持删除一个type了,所以使用delete命令想要尝试删除一个type的时候会出现如下错误,如果存在一个名为edemo的index和tets的ty ...
- Vmware下Kali设置桥接网络无法上网
1.检查是否设置桥接 2.编辑>首选项>虚拟网络编辑器>选对本机上网的网卡 3.检查上网的网卡>适配器属性栏有没有 Vmware Bridge Protocol 桥接的服务. ...
- JVM调优总结 -Xms -Xmx -Xmn -Xss(转自:iteye unixboy)
堆大小设置JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制:系统的可用虚拟内存限制:系统的可用物理内存限制.32位系统下,一般限制在1.5G~2G:64为操作 ...