关于cstdio和stdio.h的scanf效率

P1001 A+B Problem

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_USINGcstdio是包含了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

当我没说,我蠢了。


上一页 | 下一页