glassy @ 2019-11-12 13:34:17
校内的模拟赛遇到了这样的题面
本题可能涉及到大量输入,请妥善选择输入方式。对于C++,一个简单有效的方式是使用stdio.h 文件下的输入而非cstdio;VC++则不需要考虑这一点。
然后实测也确实快了(近乎一倍)。
蒟蒻如何想也不清楚为什么会有区别,因为在cstdio
的定义中有这样一句话
#ifdef _STD_USING
#undef _STD_USING
#include <stdio.h>
#define _STD_USING
#else /* _STD_USING */
#include <stdio.h>
#endif /* _STD_USING */
所以理论上不管有没有定义_STD_USING
,cstdio
是包含了stdio.h
的。
为了弄清楚到底为什么,蒟蒻尝试观察它们的汇编代码,结果,两者的汇编代码几乎完全一致(唯一的不同是两者的文件名称) 源代码如下:
//stdio.cpp
#include<stdio.h>
int main(){
int n;
scanf("%d",&n);
}
//cstdio.cpp
#include<cstdio>
int main(){
int n;
scanf("%d",&n);
}
汇编代码如下
.file "stdio.cpp"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "%d\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB8:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
call ___main
leal 28(%esp), %eax
movl %eax, 4(%esp)
movl $LC0, (%esp)
call _scanf
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE8:
.ident "GCC: (GNU) 4.8.1"
.def _scanf; .scl 2; .type 32; .endef
.file "cstdio.cpp"
.def ___main; .scl 2; .type 32; .endef
.section .rdata,"dr"
LC0:
.ascii "%d\0"
.text
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
LFB8:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
call ___main
leal 28(%esp), %eax
movl %eax, 4(%esp)
movl $LC0, (%esp)
call _scanf
movl $0, %eax
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
LFE8:
.ident "GCC: (GNU) 4.8.1"
.def _scanf; .scl 2; .type 32; .endef
经比对几乎完全一致,然而编译出的exe文件大小却不一样。
stdio.exe:102KB
cstdio.exe:109KB
蒟蒻想问一下它们效率到底差在哪里了,以及为何同一份汇编代码会得到不同的机器代码。
感激不尽。
by glassy @ 2019-11-12 13:44:04
emmmm我这里没开O2也没开c++11
by glassy @ 2019-11-12 13:44:57
@StudyingFather 但是我这里编译出来的汇编文件除了第一行文件名之外没有区别啊
by 倍镜 @ 2019-11-12 13:45:12
哈喽
by 倍镜 @ 2019-11-12 13:45:43
dalao们
Q~Q
by daqirui @ 2019-11-12 13:46:17
@glassy VS2010官方的解释:
C stdio is inherited from stdio. H of C by C + +. Add C before and do not use h suffix. In C + + environment, of course, the former is selected. Both contents are the same, but the name defined in cstdio header file is defined in namespace STD. Using the latter will bring extra burden.
翻译过来应该是:
cstdio是c++从C的stdio.h继承来的,在前面加C同时不要H后缀,在C++环境当然是选用前者,两者内容都一样,只是cstdio头文件中定义的名字被定义在命名空间std中。使用后者就会带来额外的负担。
里面说了:只是cstdio头文件中定义的名字被定义在命名空间std中。使用后者就会带来额外的负担。
by 木木! @ 2019-11-12 13:46:28
@StudyingFather 这是预编译一步,我们讨论的是已经编译成汇编代码的文件的差别(糊
by glassy @ 2019-11-12 13:49:11
@daqirui ???好谜啊,我这里的cstdio的源代码长下面这样,并没有把stdio.h的东西放进std里面啊
// cstdio standard header
#pragma once
#ifndef _CSTDIO_
#define _CSTDIO_
#include <yvals.h>
#ifdef _STD_USING
#undef _STD_USING
#include <stdio.h>
#define _STD_USING
#else /* _STD_USING */
#include <stdio.h>
#endif /* _STD_USING */
//上面就已经引用完成stdio.h了啊
// undef common macro overrides
#undef clearerr
#undef feof
#undef ferror
#undef getc
#undef getchar
#undef putc
#undef putchar
#define _HAS_CONVENTIONAL_CLIB 1
#define _IOBASE _base
#define _IOPTR _ptr
#define _IOCNT _cnt
#ifndef _FPOSOFF
#define _FPOSOFF(fp) ((long)(fp))
#endif /* _FPOSOFF */
typedef FILE _Filet;
#ifndef RC_INVOKED
#if _GLOBAL_USING
_STD_BEGIN//namespace std开始位置
using ::_Filet;
using ::size_t; using ::fpos_t; using ::FILE;
using ::clearerr; using ::fclose; using ::feof;
using ::ferror; using ::fflush; using ::fgetc;
using ::fgetpos; using ::fgets; using ::fopen;
using ::fprintf; using ::fputc; using ::fputs;
using ::fread; using ::freopen; using ::fscanf;
using ::fseek; using ::fsetpos; using ::ftell;
using ::fwrite; using ::getc; using ::getchar;
using ::gets; using ::perror;
using ::putc; using ::putchar;
using ::printf; using ::puts; using ::remove;
using ::rename; using ::rewind; using ::scanf;
using ::setbuf; using ::setvbuf; using ::sprintf;
using ::sscanf; using ::tmpfile; using ::tmpnam;
using ::ungetc; using ::vfprintf; using ::vprintf;
using ::vsprintf;
_STD_END
#endif /* _GLOBAL_USING */
#endif /* RC_INVOKED */
#endif /* _CSTDIO_ */
/*
* Copyright (c) 1992-2006 by P.J. Plauger. ALL RIGHTS RESERVED.
* Consult your license regarding permissions and restrictions.
V5.02:0009 */
by daqirui @ 2019-11-12 13:52:07
@glassy
printf和scanf在这里被重载了吧:
_STD_BEGIN//namespace std开始位置
using ::_Filet;
using ::size_t; using ::fpos_t; using ::FILE;
using ::clearerr; using ::fclose; using ::feof;
using ::ferror; using ::fflush; using ::fgetc;
using ::fgetpos; using ::fgets; using ::fopen;
using ::fprintf; using ::fputc; using ::fputs;
using ::fread; using ::freopen; using ::fscanf;
using ::fseek; using ::fsetpos; using ::ftell;
using ::fwrite; using ::getc; using ::getchar;
using ::gets; using ::perror;
using ::putc; using ::putchar;
using ::printf; using ::puts; using ::remove;
using ::rename; using ::rewind; using ::scanf;
using ::setbuf; using ::setvbuf; using ::sprintf;
using ::sscanf; using ::tmpfile; using ::tmpnam;
using ::ungetc; using ::vfprintf; using ::vprintf;
using ::vsprintf;
_STD_END
by glassy @ 2019-11-12 13:54:04
@daqirui 哦哦哦,还是好迷啊,为什么出来的汇编会是一样的?
by glassy @ 2019-11-12 13:54:48
当我没说,我蠢了。