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对多态的描述——多态性是允许你将父对 ...
随机推荐
- Git管理
在bitbucket用git用法 核心流程:从远端中心repo那里git clone到本地,再在本地开发(add,commit),通常会利用branch管理,如果觉得code没问题了,就push到远端 ...
- ASP.NET MVC 通过ActionFilterAttribute来实现防止重复提交
实现思想:每个页面打开的时候会在页面的隐藏控件自动生成一个值并将这个值赋值session,当提交方法的时候会在过滤器的时候进行获取session和页面传值过来的隐藏控件的值进行比较,如果值相同的话,重 ...
- 开源一款强大的文件服务组件(QJ_FileCenter)(系列二 安装说明)
系列文章 1. 开源一款强大的文件服务组件(QJ_FileCenter)(系列一) 2. 开源一款强大的文件服务组件(QJ_FileCenter)(系列二 安装说明) 3. 开源一款强大的文件服务组件 ...
- WPF圆角按钮
<ControlTemplate x:Key="CornerButton" TargetType="{x:Type Button}"> <Bo ...
- c++实验5 顺序/链式队列
链式队列及循环队列 1.循环队列的实现(请采用模板类及模板函数实现) [实现提示] 同时可参见教材p65-p67页的ADT描述及算法实现及ppt)函数.类名称等可自定义,部分变量请加上学号后3位.也可 ...
- Remoteland HDU - 4196
题意: 给出一个n,在[1, n] 中挑选几个不同的数相乘,求能的到的最大完全平方数 解析: 最大的肯定是n!, 然后n!不一定是完全平方数 (我们知道一个完全平方数,质因子分解后,所有质因子的质数均 ...
- 【ocp-12c】最新Oracle OCP-071考试题库(45题)
45.(9-16)choose the best answer: View the Exhibit and examine the data in the EMPLOYEES table. You w ...
- html页面pc显示正常,在手机端适配也可以看整个页面
<meta name="viewport" content="width=1250,initial-scale=0,maximum-scale=2"/&g ...
- jenkins详解(一)
还是以以下几个问题来学习这个软件: 1.jenkins是什么? 2.为什么要用jenkins? 3.怎么用jenkins? 1.jenkins是什么? Jenkins是一个开源的.提供友好操作界面的持 ...
- 栈的理解和代码实现(java)
从数据结构的角度来看,其实栈也是线性表.特殊性在于栈和队列的基本操作是线性表操作的子集,栈是操作受限制的线性表. 栈的定义 栈是限定仅在表尾进行插入或者删除操作的线性表.对于一个栈来说,表尾端有着特殊 ...