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对多态的描述——多态性是允许你将父对 ...
随机推荐
- Android-HttpUtil工具类
Http(Java 版 HttpURLConnection)请求的相关工具类 public class HttpUtil { private static final int TIMEOUT_IN_M ...
- Android-自定义ViewPager
效果图: 布局去指定自定义ViewPager: view.custom.shangguigucustomview.MyCustomViewPager <!-- 仿viewpager --> ...
- ES6摘抄
1.函数可选参数function log(x, y = 'World') {} 只能作为尾参数使用,因为如果不是尾参数还是要输入的.2.函数参数默认值与解构赋值结合使用.(注意对象冒号解构等号)fun ...
- 获取outlook联系人寻呼字段
这称不上一篇技术文. 这边记录解决一个问题的过程和感受.这种感觉每个搞IT的人或多或少都感受过,是程序人独有的快乐之一.只是大部分人没有将这种感觉记录下来.但是当你记录时,这种感觉也早已消失. 需求: ...
- 总结目前为止学到的关键字(break,continue,private,static,this,super,final,abstract)
1.控制跳转语句:break(结束) 使用的场景: a.循环当中 b.switch break关键字需要注意的问题: 1.break关键字只能用于循环和switch语句当中,其本质就是结束整段语句的意 ...
- python--内置常用模块(一) collections 队列 time模块 functiontools
一. 模块的简单认识 模块就是我们把装有特定功能的代码进行归类的结果.从代码编写的单位来看我们的程序,从小到大的顺序:一条代码 < 语句块 < 代码块(函数,类) < 模块,我们目前 ...
- 用TIdIPWatch获取本地IP
Indy的“indy misc”下有一个“TIdIPWatch”组件,可以正确获得本地IP,即使有多块网卡,也可以正确获得真正本地IP. 一,引入单元文件“IdIPWatch”. 二,实际代码: pr ...
- pandas iterrows()
按照行遍历,第一个是行索引,第二个是每一行,series类型.
- sql server中的 trimtrailingblanks
使用sp_help 查出 发现有个这个属性, 如何修改呢? SET ANSI_PADDING ONAlter Table Sys_users_History Alter column PveSit ...
- 深入了解java虚拟机(JVM) 第八章 常见的jvm调优策略
一般来说,jvm的调优策略是没有一种固定的方法,只有依靠我们的知识和经验来对项目中出现的问题进行分析,正如吉德林法则那样当你已经把问题清楚写出来,就已经解决了一半.虽然JVM调优中没有固定的策略,但是 ...