7
votes

Pourquoi C et C ++ sont-ils différents même après la compilation?

Je l'ai deviné mais j'étais toujours surpris de voir que la production de ces deux programmes, écrite en C et C ++, lors de la compilation était très différente. Cela me fait penser que le concept d'objets existait toujours au plus bas niveau le plus bas. Est-ce que cela ajoutez des frais généraux? Si oui, est-il actuellement une optimisation impossible de convertir le code orienté objet en style procédural ou tout simplement très difficile à faire?

Helloworld.c H2>
    .file   "helloworld.cpp"
    .text
    .p2align 4,,15
    .type   _GLOBAL__I_main, @function
_GLOBAL__I_main:
.LFB1007:
    .cfi_startproc
    .cfi_personality 0x0,__gxx_personality_v0
    pushl   %ebp
    .cfi_def_cfa_offset 8
    movl    %esp, %ebp
    .cfi_offset 5, -8
    .cfi_def_cfa_register 5
    subl    $24, %esp
    movl    $_ZStL8__ioinit, (%esp)
    call    _ZNSt8ios_base4InitC1Ev
    movl    $__dso_handle, 8(%esp)
    movl    $_ZStL8__ioinit, 4(%esp)
    movl    $_ZNSt8ios_base4InitD1Ev, (%esp)
    call    __cxa_atexit
    leave
    ret
    .cfi_endproc
.LFE1007:
    .size   _GLOBAL__I_main, .-_GLOBAL__I_main
    .section    .ctors,"aw",@progbits
    .align 4
    .long   _GLOBAL__I_main
    .section    .rodata.str1.1,"aMS",@progbits,1
.LC0:
    .string "Hello World!"
    .text
    .p2align 4,,15
.globl main
    .type   main, @function
main:
.LFB997:
    .cfi_startproc
    .cfi_personality 0x0,__gxx_personality_v0
    pushl   %ebp
    .cfi_def_cfa_offset 8
    movl    %esp, %ebp
    .cfi_offset 5, -8
    .cfi_def_cfa_register 5
    andl    $-16, %esp
    pushl   %ebx
    subl    $28, %esp
    movl    $12, 8(%esp)
    movl    $.LC0, 4(%esp)
    movl    $_ZSt4cout, (%esp)
    .cfi_escape 0x10,0x3,0x7,0x55,0x9,0xf0,0x1a,0x9,0xfc,0x22
    call    _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_i
    movl    _ZSt4cout, %eax
    movl    -12(%eax), %eax
    movl    _ZSt4cout+124(%eax), %ebx
    testl   %ebx, %ebx
    je  .L9
    cmpb    $0, 28(%ebx)
    je  .L5
    movzbl  39(%ebx), %eax
.L6:
    movsbl  %al,%eax
    movl    %eax, 4(%esp)
    movl    $_ZSt4cout, (%esp)
    call    _ZNSo3putEc
    movl    %eax, (%esp)
    call    _ZNSo5flushEv
    addl    $28, %esp
    xorl    %eax, %eax
    popl    %ebx
    movl    %ebp, %esp
    popl    %ebp
    ret
    .p2align 4,,7
    .p2align 3
.L5:
    movl    %ebx, (%esp)
    call    _ZNKSt5ctypeIcE13_M_widen_initEv
    movl    (%ebx), %eax
    movl    $10, 4(%esp)
    movl    %ebx, (%esp)
    call    *24(%eax)
    jmp .L6
.L9:
    call    _ZSt16__throw_bad_castv
    .cfi_endproc
.LFE997:
    .size   main, .-main
    .local  _ZStL8__ioinit
    .comm   _ZStL8__ioinit,1,1
    .weakref    _ZL20__gthrw_pthread_oncePiPFvvE,pthread_once
    .weakref    _ZL27__gthrw_pthread_getspecificj,pthread_getspecific
    .weakref    _ZL27__gthrw_pthread_setspecificjPKv,pthread_setspecific
    .weakref    _ZL22__gthrw_pthread_createPmPK14pthread_attr_tPFPvS3_ES3_,pthread_create
    .weakref    _ZL20__gthrw_pthread_joinmPPv,pthread_join
    .weakref    _ZL21__gthrw_pthread_equalmm,pthread_equal
    .weakref    _ZL20__gthrw_pthread_selfv,pthread_self
    .weakref    _ZL22__gthrw_pthread_detachm,pthread_detach
    .weakref    _ZL22__gthrw_pthread_cancelm,pthread_cancel
    .weakref    _ZL19__gthrw_sched_yieldv,sched_yield
    .weakref    _ZL26__gthrw_pthread_mutex_lockP15pthread_mutex_t,pthread_mutex_lock
    .weakref    _ZL29__gthrw_pthread_mutex_trylockP15pthread_mutex_t,pthread_mutex_trylock
    .weakref    _ZL31__gthrw_pthread_mutex_timedlockP15pthread_mutex_tPK8timespec,pthread_mutex_timedlock
    .weakref    _ZL28__gthrw_pthread_mutex_unlockP15pthread_mutex_t,pthread_mutex_unlock
    .weakref    _ZL26__gthrw_pthread_mutex_initP15pthread_mutex_tPK19pthread_mutexattr_t,pthread_mutex_init
    .weakref    _ZL29__gthrw_pthread_mutex_destroyP15pthread_mutex_t,pthread_mutex_destroy
    .weakref    _ZL30__gthrw_pthread_cond_broadcastP14pthread_cond_t,pthread_cond_broadcast
    .weakref    _ZL27__gthrw_pthread_cond_signalP14pthread_cond_t,pthread_cond_signal
    .weakref    _ZL25__gthrw_pthread_cond_waitP14pthread_cond_tP15pthread_mutex_t,pthread_cond_wait
    .weakref    _ZL30__gthrw_pthread_cond_timedwaitP14pthread_cond_tP15pthread_mutex_tPK8timespec,pthread_cond_timedwait
    .weakref    _ZL28__gthrw_pthread_cond_destroyP14pthread_cond_t,pthread_cond_destroy
    .weakref    _ZL26__gthrw_pthread_key_createPjPFvPvE,pthread_key_create
    .weakref    _ZL26__gthrw_pthread_key_deletej,pthread_key_delete
    .weakref    _ZL30__gthrw_pthread_mutexattr_initP19pthread_mutexattr_t,pthread_mutexattr_init
    .weakref    _ZL33__gthrw_pthread_mutexattr_settypeP19pthread_mutexattr_ti,pthread_mutexattr_settype
    .weakref    _ZL33__gthrw_pthread_mutexattr_destroyP19pthread_mutexattr_t,pthread_mutexattr_destroy
    .ident  "GCC: (Ubuntu 4.4.3-4ubuntu5) 4.4.3"
    .section    .note.GNU-stack,"",@progbits


1 commentaires

Ce sont deux programmes différents, la seule chose à laquelle ils ont en commun sont la sortie finale de l'écran. Bien sûr, ils doivent compiler différemment.


5 Réponses :


19
votes

Différents compilateurs produisent un code différent. Une première version de GCC par rapport à la version actuelle de GCC produise probablement un code différent.

Plus important encore, iostream gère beaucoup de choses stdio ne doit donc pas être évidemment des frais généraux substantiels. Je comprends que, en théorie, ceux-ci pourraient être compilés au code indicial, mais ce qu'ils font n'est pas techniquement identique.


0 commentaires

8
votes

Votre problème ici ne concerne pas les objets ou l'optimisation: c'est que printf et cout sont des bêtes fondamentalement différentes. Pour une comparaison plus équitable, remplacez votre instruction dans le code C ++ avec printf . L'optimisation est un point de théâtre lorsque vous émettez sur stdout, car le goulot d'étranglement sera certainement le tampon du terminal.


3 commentaires

+1 Pour avoir repéré la différence, mais s'il vous plaît noter que stdout ne signifie pas un terminal, et la surcharge de COUT est souvent négativement sur le débit lorsqu'il est redirigé vers un fichier de disque.


@Tony: true, mais lors de la redirection de / dev / null , la fonction à battre n'est pas printf , mais celle qui reconnaît / dev / null et saute la mise en forme tout à fait.


@Ben VOIGT: Le point ne concerne pas les frais généraux, l'efficacité ou quoi que ce soit. C'est que les programmes originaux sont deux programmes différents qui font des choses différentes, elles ont être compilées différemment. Si le même programme a été introduit aux deux compilateurs, le code serait plus similaire.



2
votes

Vous devez vous rendre compte qu'il y a beaucoup d'autres choses «autres» en C ++. Constructeurs globaux par exemple. Aussi les bibliothèques sont différentes.

L'objet de flux C ++ est beaucoup plus compliqué que c io, et si vous regardez via l'assembleur, vous pouvez voir tout le code de Pthreads dans la version C ++.

Ce n'est pas nécessairement plus lent, mais c'est certainement différent.


0 commentaires

6
votes

Vous n'appelez pas les mêmes fonctions dans l'exemple C ++ comme exemple C. Remplacez les tuyaux STD :: COUT COUT AVEC VIEUX PRINTF UNILE Comme le code C et vous devriez voir une corrélation beaucoup plus grande entre la sortie des deux compilateurs.


0 commentaires

2
votes

Je l'ai deviné, mais il était toujours surpris de voir que la production de ces deux programmes, écrite en C et C ++, lors de la compilation était très différente.

Je suis surpris que vous ayez été surpris - ce sont des programmes totalement différents.

Cela me fait penser que le concept d'objets existe toujours au niveau le plus bas.

absolument ... Les objets sont la mémoire de la manière dont la mémoire est disposée et utilisée lors de l'exécution du programme (sous réserve des optimisations).

Est-ce que cela ajoutez des frais généraux?

Pas nécessairement ou typiquement - les mêmes données devraient être quelque part de toute façon si le travail était coordonné de la même manière logique.

Si oui, est-il actuellement une optimisation impossible de convertir le code orienté objet en style de procédure ou tout simplement très difficile à faire?

Le problème n'a rien à voir avec le code de procédure de OO VS, ou d'être plus efficace que l'autre. Le problème principal que vous observez ici est que les ostreams de C ++ nécessitent un peu plus de configuration et de démolition et d'avoir plus d'E / S coordonné par le code en ligne, tandis que PrintF () a plus de lignes dans la bibliothèque précompilie afin que vous Je ne peux pas le voir dans votre petite liste de codes. Ce n'est pas clair vraiment ce qui est "meilleur", et à moins que vous n'ayez un problème de performance que les émissions de profilage sont liées, vous devez l'oublier et obtenir une programmation utile effectuée.

Modifier en réponse au commentaire:

Appel équitable - était un peu de mots durement - désolé. C'est une distinction difficile à faire en réalité ... "Seul le compilateur [sait] des objets" est vrai dans un sens - ils ne sont pas encapsulés, "choses" discrètes à moitié saintes au compilateur comme ils peuvent être au programmeur . Et, nous pourrions écrire un objet qui pourrait être utilisé exactement comme si vous avez utilisé cout qui disparaîtrait lors de la compilation et de produire du code équivalent à la version Printf (). Mais, COUT et iOSTreams implique une certaine configuration, car il s'agit de fil en sécurité et de plus en plus inlincé et gère différents locaux, et c'est un objet réel avec les exigences de stockage, car il existe des informations plus indépendantes sur l'état d'erreur, que vous souhaitiez des exceptions Des conditions de fin de fichier (Printf () affecte "errno", qui est ensuite classée par le prochain appel de la bibliothèque / système d'exploitation) ....

Qu'est-ce que vous pourriez trouver plus perspicace est de comparer comment Beaucoup de code supplémentaire est généré lorsque vous imprimez une chaîne supplémentaire, car la quantité de code est fondamentalement une surcharge constante + une quantité perfontrée de l'utilisation, et de ce dernier considération ostream code-bille peut être aussi efficace. que Printf (), en fonction des types et de la mise en forme demandée. Il convient également de noter que ... xxx

... est correct et plus analogue à votre instruction PrintF () ... std :: endl demande explicitement une opération de rinçage inutile, en tant que programme C ++ conforme à la normalisation, affleurera et fermera ses tampons lorsque le flux est hors de portée de toute façon (cela dit, il y a un poste intéressant aujourd'hui où il semble que le compilateur Microsoft Visualc ++ de quelqu'un ne soit pas le faire pour eux. ! - la peine de garder un œil sur mais difficile à croire).


1 commentaires

Pourquoi avez-vous été surpris? Je ne prétends pas être un expert. L'industrie du logiciel est dur ... J'étais surpris parce que je pensais que dans le capuchon Cout était juste en train de prendre une corde et de la sortir à la console comme si le printf était avec quelques différences, je n'ai pas pensé qu'un objet réel devait être créé. Je pensais que seul le compilateur connaissait des objets.