2010-10-23 3 views
4

Supposons que j'ai une fonctionLa signature de char est-elle un problème d'interface?

void foo(char *) 

qui, en interne, doit traiter son entrée comme un bloc de terminaison NUL octets (par exemple, il est une fonction de hachage sur les chaînes). Je pourrais jeter l'argument à unsigned char* dans la fonction. Je pourrais aussi changer la déclaration

void foo(unsigned char *) 

Maintenant, étant donné que char, signed char et unsigned char sont three different types, ce serait un changement constitue l'interface, en vertu de toute définition raisonnable du terme « interface » en C?

(Cette question est destinée à régler une discussion soulevée par une autre question. J'ai mes opinions, mais n'acceptera pas une réponse jusqu'à ce que l'on vient comme un « gagnant » par les votes des autres.)

+2

Pourquoi avez-vous tagué ceci avec C++ si vous posez simplement la question pour C? –

+0

Balise C++ supprimée. – Puppy

+0

@DeadMG: Battez-moi! ':)' – sbi

Répondre

4

Selon ISO/IEC 9899: TC3,

  • appeler une fonction par une expression de type incompatible est un comportement non défini (6.5.2.2 §9)
  • types de fonctions compatibles doivent avoir des types de paramètres compatibles (6.7.5.3 §15)
  • types de pointeurs compatibles doivent pointer vers les types compatibles (6.7.5.1) §2
  • char, signed char et unsigned char sont les différents types de base (6.2.5) §14 et donc incompatibles (6.2.7 §1), qui est également explicitement mentionné dans la note de bas de page 35 à la page 35

Alors oui, c'est clairement un changement à l'interface de programmation. Toutefois, étant donné que char *, signed char * et unsigned char * auront des représentations et des exigences d'alignement identiques dans toute mise en œuvre raisonnable du langage C, l'interface binaire restera inchangée.

+0

Je sais que 'char',' signed char' et 'unsigned char' sont des types de base différents/incompatibles. Mais les types de caractères incompatibles sont-ils: char *, char signé *, et unsigned char *? Je sais que la section sur la représentation des types le rend bien défini pour accéder aux versions signées et non signées du même type par l'un ou l'autre type de pointeur tant que la valeur correspond à la plage positive du type signé, parmi d'autres garanties 'unsigned char *' peut être utilisé pour accéder à n'importe quoi). –

+0

@R ..: vous avez raison, raté cette étape (fixe maintenant ...) – Christoph

+0

+1 et accepté. Merci pour les pointeurs (signés ou non signés;) à la norme. –

3

Oui c'est. Le code client compilé précédemment ne sera plus compilé (ou générera probablement de nouveaux avertissements), il s'agit donc d'un changement radical.

+1

+1: Bien sûr, cela dépend d'une définition de "interface", mais si cela nécessite une recompilation, alors vous avez divulgué l'abstraction aux utilisateurs de votre API. –

+1

@Oli, pourquoi cela nécessite-t-il une recompilation? En C, ce n'est pas le cas. 'char *' et 'unsigned char *' sont alignés de la même manière. (Bien sûr * je * recompilerais mon code, mais il n'y a rien qui le force.) –

+0

@Jens: Vous avez raison. Je suppose que ce que je voulais dire était "si cela demande au client de modifier son code avant de le recompiler". –

0

Non, ce n'est pas le cas. Toute modification du code client est triviale (surtout si c'est juste pour éviter un avertissement), et en pratique dans pratiquement toutes les implémentations C, vous n'avez même pas besoin d'une recompilation car les pointeurs vers char* et unsigned char* seront passé exactement de la même manière dans la convention d'appel.

+0

Vous faites ici certaines suppositions qui ne tiendront pas toujours. Si vous acceptez qu'il y a un changement à apporter au code client, même si vous pensez qu'il est trivial (et dans ce cas je pense que ce n'est pas trivial), vous acceptez qu'il y a * un * changement d'interface. – Tim

+0

@Tim: exactement, même si j'espérais que les gens voteraient +1 pour l'une de mes deux réponses, plutôt que -1 pour l'une d'entre elles. -1 pour les deux serait également acceptable. –

+0

Je pars pour celui-ci. La phrase 7 de 6.3.2.3 autorise explicitement la conversion entre des pointeurs vers différents types d'objets, tant qu'il n'y a pas de problème d'alignement, ce qui ne peut pas arriver ici. Et ça continue par une déclaration sur "un type de personnage". Donc, pour le cas ici, tout appel qui a été valide reste ainsi. Le compilateur peut émettre de bons avertissements après coup, mais c'est tout. –

0

Un caractère * est-il implicitement converti en un caractère * non signé?

  • Oui - vous n'avez pas cassé votre interface
  • Non - vous avez eu une fuite mise en œuvre
    détails.
+1

Pour répondre à partir de ce POV, vous avez également besoin de 'void (*) (unsigned char *)' pour convertir implicitement à 'void (*) (char *)' (et fonctionne quand on l'appelle), car quelqu'un aurait pu écrire 'void (* pfoo) (char *) = &foo;'. Je suis un peu confus ce que C permet, comme il arrive ... –

+0

@Steve Jessop: Cela peut être un problème. Cependant, il serait plutôt trivial d'écrire une fonction qui a simplement passé l'argument. – Puppy

+0

vrai. Pour le contexte, cependant, la question originale a jeté des réponses "changer la signature à' unsigned char * '," jeter le 'char *' à 'unsigned char *' dans foo ", et' jeter le 'char' à' unsigned' une fois que vous l'avez lu ". Si vous finissez par fournir une fonction wrapper pour compenser la modification de la signature de 'foo', je pense que cela signifie que lancer dans' foo' aurait été préférable en premier lieu. L'argument original était, je pense, à propos d'une toute petite chose - ce nouveau truc est-il un remplacement compatible avec l'ancien? –

2

Je choisis "C - rien de ce qui précède."

Bien que ce ne soit pas une réponse directe à la question que vous avez réellement posée, la bonne solution à la situation me semble assez simple et évidente: vous ne devriez pas vraiment utiliser ce qui précède. Au moins IMO, vous avez une très bonne raison de faire autrement, votre fonction devrait accepter un void * ou (de préférence) void const *. Ce que vous cherchez est fondamentalement un pointeur opaque, et c'est exactement ce que fournit void *. L'utilisateur n'a pas besoin de savoir quoi que ce soit sur les composants internes de votre implémentation, et puisque tout autre type de pointeur se convertira en void * implicitement, c'est l'une des rares possibilités qui ne casse aucun code existant non plus.

+0

N'est-ce pas une réponse à l'autre question qui a provoqué celle-ci? Cette question est, "appelleriez-vous ceci un changement d'interface?", Pas "que devrais-je faire?" –

+0

Pourquoi le changeriez-vous en un «void *»? C'est moins de type sûr et blessera l'utilisateur quand il obtient _no warning_. – alternative

+0

@mathepic: La sécurité de type a du sens si et seulement si vous pouvez définir le ou les types pour lesquels la fonction a du sens. Il dit qu'il traite le contenu simplement comme des octets, ce qui indique qu'il est indépendant du type. –

Questions connexes