Mise en œuvre de « strtok »

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Dans le cours sur le langage C, au TP sur l’entête <string.h>, j’ai réalisé la fonction zstrtok suivante pour l’exercice sur la mise en œuvre de strtok.

char *zstrtok(char *restrict string, const char *const restrict DELIMITERS)
{
    static char *current_substring;

    if (string == NULL && current_substring == NULL)
        return NULL;

    if (string != NULL)
        current_substring = string;
    else
        string = current_substring;

    while (*current_substring != '\0')
    {
        size_t j = 0;

        while (DELIMITERS[j] != '\0')
        {
            if (*current_substring == DELIMITERS[j])
            {
                string++;
                current_substring++;
                j = 0;

                continue;
            }

            j++;
        }

        break;
    }

    if (*current_substring == '\0')
        return NULL;

    while (*current_substring != '\0')
    {
        size_t j = 0;

        while (DELIMITERS[j] != '\0')
        {
            if (*current_substring == DELIMITERS[j])
            {
                *current_substring = '\0';
                current_substring++;

                return string;
            }

            j++;
        }

        current_substring++;
    }

    return string;
}

Elle semble fonctionner correctement, en tout cas elle donne le résultat attendu avec l’exemple du cours.

Je voudrais simplement avoir vos avis sur cette réalisation. J’ai évité l’utilisation de goto alors qu’elle est suggérée dans l’énoncé, mais je ne suis pas sûr que son remplacement par des continue et break soit réellement plus lisible (et cela ressemble peut-être à un goto déguisé…). Par ailleurs, je ne suis pas sûr que les restrict soient utiles ici, étant donné que les types sur lesquels ces mots-clés sont appliqués sont, il me semble, incompatibles.

+0 -0

Je me suis rendu compte que le premier while était inutile, j’ai donc réécrit la fonction ainsi.

char *zstrtok(char *restrict string, const char *const restrict DELIMITERS)
{
    static char *current_substring;

    if (string == NULL && current_substring == NULL)
        return NULL;

    if (string != NULL)
        current_substring = string;
    else
        string = current_substring;

    // Start of the rewrite
    size_t j = 0;

    while (DELIMITERS[j] != '\0')
    {
        if (*current_substring == DELIMITERS[j])
        {
            string++;
            current_substring++;
            j = 0;

            continue;
        }

        j++;
    }
    // End of the rewrite

    if (*current_substring == '\0')
        return NULL;

    while (*current_substring != '\0')
    {
        size_t j = 0;

        while (DELIMITERS[j] != '\0')
        {
            if (*current_substring == DELIMITERS[j])
            {
                *current_substring = '\0';
                current_substring++;

                return string;
            }

            j++;
        }

        current_substring++;
    }

    return string;
}
+0 -0

Salut,

Si je comprends bien, la première boucle avance dans la chaine tant qu’on a pas trouvé un élément de DELIMITERS. Ton approche marche sans doute, mais je la trouve un peu complexe. Personnellement, j’aurais fait une boucle for qui parcourt les caractères des deux chaînes, et à l’intérieur, pour savoir si le caractère actuel est dans DELIMITERS, j’aurais utilisé une autre boucle for.

Je trouve ça plus clair parce qu’on a bien deux itérations sur deux séquences différentes (la chaine, et la chaine de délimiteurs) imbriquées. Et je trouve plus intuitif que la boucle extérieure se fasse sur la chaine qu’on analyse plutôt que sur les délimiteurs, mais ça, on pourrait dire que c’est une préférence personnelle.

La première boucle permet de passer les délimiteurs qui se trouvent au début de la chaîne. Dit autrement, on parcourt les délimiteurs, et on avance dans la chaîne que si le caractère pointé actuellement dans celle-ci est un délimiteur. Si c’est le cas, on reprend également au début le parcours des délimiteurs (et on fait aussi avancer le pointeur qui permet d’obtenir la sous-chaîne que l’on renvoie par la suite).

+0 -0

Salut,

Je voudrais simplement avoir vos avis sur cette réalisation. J’ai évité l’utilisation de goto alors qu’elle est suggérée dans l’énoncé, mais je ne suis pas sûr que son remplacement par des continue et break soit réellement plus lisible (et cela ressemble peut-être à un goto déguisé…).

Ifrit

Dans l’exemple du cours, le goto est surtout utilisé pour simplifier l’écriture, mais ce que tu as fait est tout aussi lisible de mon point de vue. Je notes juste une chose.

if (*current_substring == '\0')
    return NULL;

Si je ne dis pas de bêtises, tu dois également mettre current_substring à NULL sans quoi, si tu utilises la fonction sur une autre chaîne par après, cela ne fonctionnera pas.

Par ailleurs, je ne suis pas sûr que les restrict soient utiles ici, étant donné que les types sur lesquels ces mots-clés sont appliqués sont, il me semble, incompatibles.

Ifrit

Le pointeur sur char est une exception à la règle de strict aliasing : n’importe quel objet est susceptibe d’être accéder via un tel pointeur.

An object shall have its stored value accessed only by an lvalue expression that has one of the following types:

  • a type compatible with the effective type of the object,
  • a qualified version of a type compatible with the effective type of the object,
  • a type that is the signed or unsigned type corresponding to the effective type of the object,
  • a type that is the signed or unsigned type corresponding to a qualified version of the effective type of the object,
  • an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union), or
  • a character type.
ISO/IEC 9899:2017, doc. N2176, 6.5 Expressions, § 7, pp. 56–57.

L’emploie du qualificateur restrict est donc justifié. ;)

+0 -0

J’ai fait quelques tests après ta remarque Taurre, et j’en conclus qu’il n’est pas nécessaire de mettre current_substring à NULL dans ce cas, car si on appelle la fonction avec une nouvelle chaîne, la condition if (string != NULL) sera vérifiée et permettra alors de « réinitialiser » current_substring.

Ok pour le pointeur sur char, j’ignorais cette caractéristique.

Merci pour vos retours, je pense que le code est dans un état satisfaisant maintenant.

+0 -0

J’ai fait quelques tests après ta remarque Taurre, et j’en conclus qu’il n’est pas nécessaire de mettre current_substring à NULL dans ce cas, car si on appelle la fonction avec une nouvelle chaîne, la condition if (string != NULL) sera vérifiée et permettra alors de « réinitialiser » current_substring.

Ifrit

Ah ! Oui, en effet, au temps pour moi. :)

+0 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte