Longueur de la chaîne C – s’agit-il d’un code valide?

Ce type d’expression en C est-il valide (sur tous les compilateurs)? Si c’est le cas, est-ce que c’est bon C?

char cloneSsortingng (char clone[], char ssortingng[], int Length) { if(!Length) Length=64 ; int i = 0 ; while((clone[i++] = ssortingng[i]) != '\0', --Length) ; clone[i] = '\0'; printf("cloneSsortingng = %s\n", clone); }; 

Serait-ce mieux, pire, indifférent?

 char *cloneSsortingng (char clone[], char ssortingng[], int Length) { if(!Length) Length=STRING_LENGTH ; char *r = clone ; while ( //(clone[i++] = ssortingng[i]) != '\0' *clone++ = *ssortingng++ , --Length ); *clone = '\0'; return clone = r ; printf("cloneSsortingng = %s\n", clone); }; 

Stackoverflow veut que je rajoute du texte à cette question!

D’accord! Je suis inquiet pour

a.) expressions telles que c == (a = b) b.) performances entre l’indexation et le pointeur

Des commentaires?

Merci beaucoup.

Oui, sa syntaxe est valable sur tous les compilateurs (même sémantiquement sur aucun), et non, ce n’est pas considéré comme un bon C. La plupart des développeurs conviendront que l’opérateur par virgule est une mauvaise chose et la plupart des développeurs conviendront généralement qu’une seule ligne du code ne devrait faire qu’une chose en particulier. La boucle while fait quatre et a un comportement indéfini:

  • il incrémente i ;
  • il assigne la ssortingng[i] au clone[i++] ; ( comportement non défini : vous ne devez utiliser i qu’une seule fois dans une instruction qui l’incrémente / la décrémente)
  • il vérifie que la ssortingng[i] n’est pas 0 ( mais ignore le résultat de la comparaison );
  • il décrémente Length et termine la boucle si Length == 0 après avoir été décrémenté.

Sans compter que supposer que la Length soit 64 si elle n’a pas été fournie est une idée terrible et laisse beaucoup de place à un comportement plus indéfini qui peut facilement être exploité pour faire planter ou pirater le programme.

Je vois que vous l’avez écrit vous-même et que vous êtes préoccupé par les performances, et c’est apparemment la raison pour laquelle vous colliez tout. Ne pas Le code abrégé en regroupant des instructions n’est pas plus rapide que le code, car elles n’ont pas été compressées. Il fait toujours le même nombre de choses. Dans votre cas, vous introduisez des bugs en serrant les choses ensemble.

Le code a un comportement indéfini:

L’expression

 (clone[i++] = ssortingng[i]) 

à la fois modifie et accède à l’object i partir de deux sous-expressions différentes de manière non séquencée, ce qui n’est pas autorisé. Un compilateur peut utiliser l’ancienne valeur de i dans la ssortingng[i] , ou utiliser la nouvelle valeur de i , ou faire quelque chose de complètement différent et d’inattendu.

Réponse simple non.

  1. Pourquoi return char et la fonction n’a pas de déclaration return?
  2. Pourquoi 64?
  3. Je suppose que les deux tableaux ont une longueur Length – Ajoutez de la documentation pour le dire.
  4. Pourquoi le ; sur une nouvelle ligne et non après la déclaration?

Ok, j’ai donc décidé de transformer mes commentaires en une réponse concrète. Bien que cela ne traite pas du code spécifique dans votre question, cela répond au problème sous-jacent et je pense que vous le trouverez éclairant car vous pourrez l’utiliser – appelons-le guide – dans votre programmation générale.

Ce que je préconise, en particulier si vous apprenez juste à programmer, est de vous concentrer sur la lisibilité plutôt que sur de petits gadgets que vous pensez ou que vous avez appris à améliorer vitesse / performance.

Prenons un exemple simple. La méthode idiomatique pour parcourir un vecteur en C (et non en C++ ) consiste à utiliser l’indexation:

 int i; for (i = 0; i < size; ++i) { v[i] = /* code */; } 

Lorsque j'ai commencé à programmer, on m'avait dit que v[i] était en fait calculé comme *(v + i) donc dans l'assembleur généré, il est décomposé (veuillez noter que cette discussion est simplifiée):

  • multiplier i avec sizeof(int)
  • append ce résultat à l'adresse de v
  • accéder à l'élément à cette adresse calculée

Donc, fondamentalement, vous avez 3 opérations.

Comparons cela avec l'access via des pointeurs:

 int *p; for (p = v; p != v + size; ++p) { *p = /*..*/; } 

Cela a l'avantage que *p s'étend en une seule instruction:

  • accéder à l'élément à l'adresse p .

2 instructions supplémentaires ne semblent pas beaucoup, mais si votre programme passe le plus clair de son temps dans cette boucle (soit une taille extrêmement grande size soit plusieurs appels à (les fonctions contenant cette boucle), vous réalisez que la deuxième version rend votre programme presque 3 fois plus rapide. . C'est beaucoup. Donc, si vous êtes comme moi quand j'ai commencé, vous choisirez la deuxième variante. Ne pas!

Ainsi, la première version est lisible (vous indiquez explicitement que vous accédez au i ième élément du vecteur v ), la seconde utilise un gadget au désortingment de la lisibilité (vous dites accéder à un emplacement de mémoire). Maintenant, cela pourrait ne pas être le meilleur exemple pour un code illisible, mais le principe est valide.

Alors pourquoi est-ce que je vous conseille d’utiliser la première version: jusqu’à ce que vous maîsortingsiez bien les concepts tels que le cache, les twigs, les variables d’induction (et bien d’autres) et leur application dans les performances réelles des compilateurs et des programmes, vous devriez éviter ces astuces et comptent sur le compilateur pour effectuer les optimisations. Ils sont très intelligents et généreront le même code pour les deux variantes (avec optimisation bien sûr). La deuxième variante diffère donc simplement par sa lisibilité et ses performances sont identiques à celles de la première.

Un autre exemple:

 const char * str = "Some ssortingng" int i; // variant 1: for (i = 0; i < strlen(str); ++i) { // code } // variant 2: int l = strlen(str); for (i = 0; i < l; ++i) { // code } 

La manière naturelle serait d'écrire la première variante. Vous pourriez penser que la seconde améliore les performances car vous appelez la fonction strlen à chaque itération de la boucle. Et vous savez que pour obtenir la longueur d'une chaîne, vous devez parcourir toute la chaîne jusqu'à la fin. Donc, fondamentalement, un appel à strlen signifie l’ajout d’une boucle interne. Ouch ça doit ralentir le programme. Pas nécessairement: le compilateur peut optimiser l'appel car il produit toujours le même résultat. En fait, vous pouvez faire du mal en introduisant une nouvelle variable qui devra se voir atsortingbuer un registre différent d’un pool de registres très limité (un exemple un peu extrême, mais il convient néanmoins de faire une remarque ici).

Ne dépensez pas votre énergie dans de telles choses avant beaucoup plus tard.

Laissez-moi vous montrer autre chose qui illustrera plus en détail le fait que toutes les hypothèses que vous faites sur les performances seront vraisemblablement fausses et trompeuses (je n'essaie pas de vous dire que vous êtes un mauvais programmeur - loin de là - seulement apprenez , vous devriez investir votre énergie dans autre chose que la performance):

Multiplions deux masortingces:

 for (k = 0; k < n; ++k) { for (i = 0; i < n; ++i) { for (j = 0; j < n; ++j) { r[i][j] += a[i][k] * b[k][j]; } } } 

contre

 for (k = 0; k < n; ++k) { for (j = 0; j < n; ++j) { for (i = 0; i < n; ++i) { r[i][j] += a[i][k] * b[k][j]; } } } 

La seule différence entre les deux est l'ordre d'exécution des opérations. Ce sont exactement les mêmes opérations (nombre, type et opérandes), mais dans un ordre différent. Le résultat est équivalent (l'addition est commutative), donc sur papier, ils devraient prendre la quantité EXACT de temps pour s'exécuter. En pratique, même avec l'optimisation activée (certains compilateurs très intelligents peuvent toutefois réorganiser les boucles), le deuxième exemple peut être jusqu'à 2-3 fois plus lent que le premier. Et même la première variante est encore loin d'être optimale (en termes de vitesse).

Donc, point fondamental : vous vous inquiétez de UB comme le montrent les autres réponses, ne vous inquiétez pas des performances à ce stade.

Le deuxième bloc de code est meilleur.

La ligne

 printf("cloneSsortingng = %s\n", clone); 

il ne sera jamais exécuté car il y a une déclaration de retour auparavant.

Pour rendre votre code un peu plus lisible, changez

  while ( *clone++ = *ssortingng++ , --Length ); 

à

  while ( Length > 0 ) { *clone++ = *ssortingng++; --Length; } 

C’est probablement une meilleure approche de votre problème:

 #include  #include  void cloneSsortingng(char *clone, char *ssortingng) { for (int i = 0; i != strlen(ssortingng); i++) clone[i] = ssortingng[i]; printf("Clone ssortingng: %s\n", clone); } 

Cela dit, il existe déjà une fonction standard pour cela:

 strncpy(const char *dest, const char *source, int n) 

dest est la chaîne de destination et source est la chaîne qui doit être copiée. Cette fonction copiera un maximum de n caractères.

Donc, votre code sera:

 #include  #include  void cloneSsortingng(char *clone, char *ssortingng) { strncpy(clone, ssortingng, strlen(ssortingng)); printf("Clone ssortingng: %s\n", clone); }