L’implémentation d’une nouvelle fonction strcpy redéfinit la fonction de bibliothèque strcpy?

On dit que nous pouvons écrire plusieurs déclarations mais une seule définition. Maintenant, si j’implémente ma propre fonction strcpy avec le même prototype:

char * strcpy ( char * destination, const char * source ); 

Alors, ne redéfinis-je pas la fonction de bibliothèque existante? Cela ne devrait-il pas afficher une erreur? Ou est-ce lié au fait que les fonctions de la bibliothèque sont fournies sous forme de code object?

EDIT: L’exécution du code suivant sur ma machine indique “Erreur de segmentation (kernel vidé)”. Je travaille sur linux et ai compilé sans utiliser de drapeaux.

 #include  #include  #include  char *strcpy(char *destination, const char *source); int main(){ char *s = strcpy("a", "b"); printf("\nThe function ran successfully\n"); return 0; } char *strcpy(char *destination, const char *source){ printf("in duplicate function strcpy"); return "a"; } 

Veuillez noter que je n’essaye pas d’implémenter la fonction. J’essaie simplement de redéfinir une fonction et de demander les conséquences.

EDIT 2: Après avoir appliqué les modifications suggérées par Mats, le programme ne donne plus d’erreur de segmentation, bien que je redéfinisse toujours la fonction.

 #include  #include  #include  char *strcpy(char *destination, const char *source); int main(){ char *s = strcpy("a", "b"); printf("\nThe function ran successfully\n"); return 0; } char *strcpy(char *destination, const char *source){ printf("in duplicate function strcpy"); return "a"; } 

C11 (ISO / IEC 9899: 201x) §7.1.3 Identifiants réservés

– Chaque nom de macro dans l’un des sous-paragraphes suivants (y compris les instructions futures de la bibliothèque) est réservé pour être utilisé comme spécifié si l’un des en-têtes associés est inclus. sauf indication explicite contraire.

– Tous les identifiants avec une liaison externe dans l’un des sous-paragraphes suivants (y compris les instructions futures de la bibliothèque) sont toujours réservés à une utilisation en tant qu’identificateurs avec une liaison externe.

– Chaque identifiant avec une étendue de fichier répertoriée dans l’un des sous-paragraphes suivants (y compris les instructions futures de la bibliothèque) est réservé à l’utilisation en tant que nom de macro et en tant qu’identifiant avec une étendue de fichier dans le même espace de nom si l’un des en-têtes associés est inclus.

Si le programme déclare ou définit un identifiant dans un contexte dans lequel il est réservé, ou définit un identifiant réservé en tant que nom de macro, le comportement est indéfini. Notez que cela ne signifie pas que vous ne pouvez pas le faire, comme le montre ce message , cela peut être fait dans gcc et glibc.

glibc §1.3.3 Les noms réservés fournissent une raison plus claire:

Les noms de tous les types de bibliothèque, macros, variables et fonctions issus de la norme ISO C sont réservés sans condition. votre programme ne peut pas redéfinir ces noms. Tous les autres noms de bibliothèque sont réservés si votre programme inclut explicitement le fichier d’en-tête qui les définit ou les déclare. Il y a plusieurs raisons à ces ressortingctions:

Si vous utilisez une fonction nommée exit, les autres utilisateurs qui lisent votre code risquent d’être très confus, par exemple. La prévention de cette situation facilite la compréhension de vos programmes et consortingbue à la modularité et à la facilité de maintenance.

Cela évite qu’un utilisateur ne redéfinisse accidentellement une fonction de bibliothèque appelée par d’autres fonctions de bibliothèque. Si la redéfinition était autorisée, ces autres fonctions ne fonctionneraient pas correctement.

Il permet au compilateur de faire les optimisations spéciales qu’il souhaite pour les appels à ces fonctions, sans qu’elles puissent éventuellement avoir été redéfinies par l’utilisateur. Certaines installations de la bibliothèque, telles que celles qui traitent des arguments variadiques (voir Fonctions variadiques) et des sorties non locales (voir Sorties non locales), nécessitent en réalité une coopération considérable de la part du compilateur C, et en ce qui concerne Lors de la mise en œuvre, il pourrait être plus facile pour le compilateur de les traiter comme des parties intégrées du langage.

C’est presque certainement parce que vous passez dans une destination qui est un “littéral de chaîne”.

char * s = strcpy (“a”, “b”);

En même temps que le compilateur sait “je peux faire strcpy ligne”, votre fonction ne sera jamais appelée.

Vous essayez de copier "b" sur le littéral de chaîne "a" et cela ne fonctionnera pas.

Faites un char a[2]; et strcpy(a, "b"); et il fonctionnera – il n’appellera probablement pas votre fonction strcpy , car le compilateur insère un petit strcpy même si l’optimisation n’est pas disponible.

Si l’on essaie de modifier la mémoire non modifiable, gardez à l’esprit que vous n’êtes pas officiellement autorisé à redéfinir les fonctions de bibliothèque standard.

Cependant, dans certaines mises en œuvre, vous remarquerez peut-être que fournir une autre définition pour une fonction de bibliothèque standard ne déclenche pas l’erreur habituelle de “définition multiple”. Cela est dû au fait que dans de telles implémentations, les fonctions de bibliothèque standard sont définies comme des “symboles faibles”. Par exemple, la bibliothèque standard GCC est connue pour cela.

La conséquence directe en est que lorsque vous définissez votre propre “version” de la fonction de bibliothèque standard avec une liaison externe, votre définition remplace la définition standard “faible” pour l’ensemble du programme. Vous remarquerez que non seulement votre code appelle maintenant votre version de la fonction, mais que toutes les classes de toutes les bibliothèques [tierces] pré-compilées sont également envoyées à votre définition. Il s’agit d’une fonctionnalité, mais vous devez en prendre conscience pour éviter “d’utiliser” cette fonctionnalité par inadvertance.

Vous pouvez lire à ce sujet ici, pour un exemple

Comment remplacer la fonction de bibliothèque standard C?

Cette fonctionnalité de l’implémentation ne viole pas la spécification de langage, car elle opère dans une zone non cartographiée de comportement indéfini non régie par aucune exigence standard.

Bien entendu, les appels utilisant l’implémentation insortingnsèque / intégrée d’une fonction de bibliothèque standard ne seront pas affectés par la redéfinition.

Votre question est trompeuse.

Le problème que vous voyez n’a rien à voir avec la ré-implémentation d’une fonction de bibliothèque.

Vous essayez simplement d’écrire de la mémoire non inscriptible, c’est-à-dire la mémoire où le littéral chaîne a existe.

Pour faire simple, le programme suivant donne une erreur de segmentation sur ma machine (compilé avec gcc 4.7.3 , sans drapeaux):

 #include  int main(int argc, const char *argv[]) { strcpy("a", "b"); return 0; } 

Mais alors, pourquoi la faute de segmentation si vous appelez une version de strcpy (la vôtre) qui n’écrit pas la mémoire non inscriptible? Tout simplement parce que votre fonction n’est pas appelée.

Si vous comstackz votre code avec l’ -S et examinez le code d’assembly généré par le compilateur, strcpy ne sera pas call (car le compilateur a “en ligne” cet appel, le seul appel pertinent que vous puissiez voir de la main, est un appel à la puts ).

 .file "test.c" .section .rodata .LC0: .ssortingng "a" .align 8 .LC1: .ssortingng "\nThe function ran successfully" .text .globl main .type main, @function main: .LFB2: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movw $98, .LC0(%rip) movq $.LC0, -8(%rbp) movl $.LC1, %edi call puts movl $0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE2: .size main, .-main .section .rodata .LC2: .ssortingng "in duplicate function strcpy" .text .globl strcpy .type strcpy, @function strcpy: .LFB3: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 subq $16, %rsp movq %rdi, -8(%rbp) movq %rsi, -16(%rbp) movl $.LC2, %edi movl $0, %eax call printf movl $.LC0, %eax leave .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE3: .size strcpy, .-strcpy .ident "GCC: (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3" . 

Je pense que la réponse de Yu Hao a une excellente explication à cela, la citation du standard:

Les noms de tous les types de bibliothèque, macros, variables et fonctions issus de la norme ISO C sont réservés sans condition. votre programme ne peut pas redéfinir ces noms. Tous les autres noms de bibliothèque sont réservés si votre programme inclut explicitement le fichier d’en-tête qui les définit ou les déclare. Il y a plusieurs raisons à ces ressortingctions:

[…]

Il permet au compilateur de faire les optimisations spéciales qu’il souhaite pour les appels à ces fonctions, sans qu’elles puissent éventuellement avoir été redéfinies par l’utilisateur.

votre exemple peut fonctionner de cette façon: ( avec strdup )

 char *strcpy(char *destination, const char *source); int main(){ char *s = strcpy(strdup("a"), strdup("b")); printf("\nThe function ran successfully\n"); return 0; } char *strcpy(char *destination, const char *source){ printf("in duplicate function strcpy"); return strdup("a"); } 

sortie:

  in duplicate function strcpy The function ran successfully 

La façon d’interpréter cette règle est qu’il n’est pas possible que plusieurs définitions d’une fonction aboutissent dans l’object lié final (l’exécutable). Donc, si tous les objects inclus dans le lien n’ont qu’une seule définition d’une fonction, alors tout va bien. En gardant cela à l’esprit, envisagez les scénarios suivants.

  1. Supposons que vous redéfinissiez une fonction somefunction () définie dans une bibliothèque. Votre fonction est dans main.c (main.o) et dans la bibliothèque, dans un object nommé someobject.o (dans le libray). Rappelez-vous que dans le dernier lien, l’éditeur de liens recherche uniquement les symboles non résolus dans les bibliothèques. Du fait que somefunction () est déjà résolu à partir de main.o, l’éditeur de liens ne le recherche même pas dans les bibliothèques et n’a pas recours à someobject.o. Le dernier lien n’a qu’une définition de la fonction et tout va bien.
  2. Imaginez maintenant qu’il existe un autre symbole, anotherfunction (), défini dans someobject.o, que vous appelez également. L’éditeur de liens essaiera de résoudre anotherfunction () à partir de someobject.o, et le tirera de la bibliothèque, ce qui deviendra une partie du dernier lien. Vous avez maintenant deux définitions de somefunction () dans le dernier lien – une de main.o et une autre de someobject.o, et l’éditeur de liens lève une erreur.

J’utilise celui-ci fréquemment:

 void my_strcpy(char *dest, char *src) { int i; i = 0; while (src[i]) { dest[i] = src[i]; i++; } dest[i] = '\0'; } 

et vous pouvez aussi faire strncpy juste en modifiant une ligne

 void my_strncpy(char *dest, char *src, int n) { int i; i = 0; while (src[i] && i < n) { dest[i] = src[i]; i++; } dest[i] = '\0'; }