在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)支持重载吗的更多相关文章

  1. C++中对封装的语法支持——重载运算符

    重载运算符 1.对于自定义类型,编译器不知道运算规则,而重载运算符会将两个对象相加转换为函数调用. 2.运算符重载转换的函数调用,函数名字是固定的规则. (1) 如果重载+号运算符,函数名字就是:op ...

  2. gcc对c++标准的支持

    GCC 4.8.1完全支持c++11核心部分,对应的glibc为2.17 gcc 4.9支持c++11正则表达式,卧槽...4.8.5会报terminate called after throwing ...

  3. 交叉编译支持SVE ACLE的gcc

    最近在学习AArch64的SVE技术时,发现目前可以在网上找到的gcc版本都不支持SVE intrinsic方式调用,在看文档时发现,GCC要到2020年的GCC10时才会支持: 在github上看到 ...

  4. gcc,g++,extern “C” :一些编译错误的缘由

    正好是我们代码中遇到的问题,之前不求甚解,只用g++编译没有错误就不管了,现在要跨平台到windows下就遇到了问题.全文转载自:http://user.qzone.qq.com/75172588/b ...

  5. c++的重载 缺省参数和命名空间详解

    参加了几次笔试,发现有很多c++方面的问题被卡了.从现在开始进攻c++.之后会陆续更新c++学习笔记. 先说说我学习的书籍,大家如果有好的书籍推荐,感谢留言. 暂时是在看这些书自学. 1.C++介绍. ...

  6. boost::bind 不能处理函数重载 (error: no matching function for call to 'bind')

    前言 最近任务多.工期紧,没有时间更新博客,就水一期吧.虽然是水,也不能太失水准,刚好最近工作中遇到一个 boost::bind 的问题,花费了半天时间来定位解决,就说说它吧. 问题背景 项目中使用了 ...

  7. GCC 预处理、编译、汇编、链接..

    1简介 GCC 的意思也只是 GNU C Compiler 而已.经过了这么多年的发展,GCC 已经不仅仅能支持 C 语言:它现在还支持 Ada 语言.C++ 语言.Java 语言.Objective ...

  8. Linux GCC常用命令

    1简介 2简单编译 2.1预处理 2.2编译为汇编代码(Compilation) 2.3汇编(Assembly) 2.4连接(Linking) 3多个程序文件的编译 4检错 5库文件连接 5.1编译成 ...

  9. PHP面向对象编程——深入理解方法重载与方法覆盖(多态)

    什么是多态? 多态(Polymorphism)按字面的意思就是“多种状态”.在面向对象语言中,接口的多种不同的实现方式即为多态.引用Charlie Calverts对多态的描述——多态性是允许你将父对 ...

随机推荐

  1. 神器与经典--DMVsinSQLServer

    前言: 在不经意间发现这个SQL,正能量瞬间饱满,赶紧向周边的人分享,私藏是罪过啊! 请复制粘贴然后F5,经典无须多说! /************************************** ...

  2. 自己从0开始学习Unity的笔记 VI (C#的for循环练习)

    最近学到了for循环,我觉得其实看情况吧,和while挺像的,不过适合于累加或者累减这类的,for循环要更好用一点 for循环首先格式是 ; i < length; i++) { } 意思很简单 ...

  3. 开源日志收集Exceptionless简单使用

    这两天在研究一个开源的日志收集工具Exceptionless 官网地址:https://exceptionless.com/GitHub地址:https://github.com/exceptionl ...

  4. Mycat SqlServer 技术栈 实现 主从分离

    先说明下版本:SqlServer2008R2 + MyCat 1.6 现在主从分离 一主一从 用的是 代码 写死的方式  转换下思路 一主两从 或者多从 怎么实现 负载均衡 或者 按权重调用相应库呢 ...

  5. 【Asp.net MVC】AJAXHelper 和jQueryAjax

    在ASP.NET MVC中,有一个官方提供的AJAXHelper帮助我们做AJAX相关的东西.我用传统的jQuery AJAX技术和AJAXHelper分别实现同一个demo,特此记录一下. 由于是在 ...

  6. 3、Orcal表空间分配、新建用户、新用户创建连接

    1.创建表空间: 在管理员连接打开sql面板,输入如下内容: CREATE TABLESPACE DXYX DATAFILE 'E:\app\Administrator\product\11.2.0\ ...

  7. Java50道经典习题-程序10 自由落体

    题目:一球从100米高度自由落下,每次落地后反跳回原高度的一半:再落下,求它在 第10次落地时,共经过多少米?第10次反弹多高? import java.util.Scanner; public cl ...

  8. 域名可以解析(ping域名可以获取正确ip),服务器本地telnet 域名+端口 无法连接,通过建立本地虚拟域名指定的方法解决该问题

    环境: 服务器A,网管已为A开通外网ip,且设置有映射域名:假如内网ip为172.16.2.6.外网ip为123.123.123.123.域名为test.sstest.com 现象: 服务器A,tel ...

  9. 【文文殿下】[BZOJ4327] JSOI2012 玄武密码

    SAM裸题.这道题卡空间.要小心数组别开炸了. #include<cstdio> #include<cstring> typedef long long ll; const i ...

  10. poj 3133 Manhattan Wiring

    http://poj.org/problem?id=3133 考虑插头 dp 用四进制表示一个插头的状态,0 表示没有插头,2 表示这个插头是连接两个 2 的,3 同理 然后就是大力分类讨论了 这题还 ...