Utilisation des pointeurs pour créer une fonction

Bonjour,

Je désirerais créer une fonction qui, à l'image de "Mid" en VBA, va extraire une certaine partie d'un tableau char, mais en "jouant" avec les pointeurs.

Mais si je comprends le principe, je m'embrouille lorsque je dois en passer un dans une fonction et avoir un en retour.

Je recherche donc de l'aide, mais Google n'est pas mon ami, j'ai bien des résultats, mais cela manque de pédagogie et/ou de clarté.

Je vous remercie d'avance de me brancher sur une bonne aide

votre tableau de char, c'est du texte ?

si vous voulez faire simple (avec quelques risques sur la mémoire), utilisez la classe String. vous aurez la fonction substring() qui fait ce que vous voulez

sinon souhaitez vous une copie de cette extraction? si oui il faut allouer un buffer pour y copier le bon nombre d'octets (memcpy() par exemple) et rajouter un caractère nul à la fin pour marquer la fin du texte (donc à prendre en compte dans la taille du buffer)

➜ donnez un exemple du code que vous avez

Oui, c'est du texte, et non je ne veux justement pas utiliser la classe String.
Je vais réessayer de recoder parce que pour le moment je n'ai rien qui fonctionne

Sans utiliser d'allocation dynamique de mémoire, il faut que l'appelant fournisse l'adresse des chaînes source et destination.
Un exemple ici :

Ce que tu demande est un peu bizarre, Mid n'utilise pas les pointeurs.
Peu tu déjà exprimer clairement ce que tu veux.
Il est assez simple par exemple d'avoir une fonction "Find" qui prend un char** et qui renvoi un char*.
Mais pourquoi vouloir réinventer la roue?

Bonjour @Renard32,

En partant du postulat qu'en VBA la fonction Mid prend en paramètres :

  • la chaîne de caractère ;
  • le point de départ de l'extraction de la sous-chaîne ;
  • et le nombre de caractères à extraire dans la sous-chaîne à partir du point de départ.
    Etant donné que vous souhaitez travailler avec des pointeurs ;
    Je vous propose comme base de travail :
char tab[] = "1234567890abcdefgh"; 

void setup() {
  Serial.begin(115200);
  Serial.print( mid(tab, 1, 4));
}

void loop() {



}

char *mid(char *txt, int debut, int nbCaracteres ) // retourne le nombre de caractères d'une chaine partant de debut dans buff
{
  static char buff[30];
  int j = 0;
  for (int i = debut - 1; i < (debut - 1) + nbCaracteres  ; i++, j++)
  {
    *(buff + j) = *(txt + i);
  }
  *(buff + j) = '\0';
  return buff;
}

ou :

char tab[] = "1234567890abcdefgh";
char *ptr = tab;
void setup() {

  Serial.begin(115200);
  Serial.print( mid(ptr, 11, 4));
}

void loop() {



}

char *mid(char *txt, int debut, int nbCaracteres ) // retourne le nombre de caractères d'une chaîne partant de debut dans buff
{
  static char buff[30];
  char *ptrBuff = buff;
  int j = 0;
  for (int i = debut - 1; i < (debut - 1) + nbCaracteres  ; i++, j++)
  {
    *(ptrBuff + j) = *(txt + i);
  }
  *(ptrBuff + j) = '\0';
  return buff;
}


Attention, j'ai écrit ce code rapidement, il vous faudra introduire dans le code une limite pour ne pas dépasser la longueur de la chaîne à transmettre ...
Bonne journée.

PS : en C C++, les possibilités de programation sont nombreuses...
Je n'ai pas cherché à être lisible...
Et surtout je suis pressé car je dois partir de chez moi.

ne pas oublier le const !
char *mid ( const char *txt, int debut, int nbCaracteres )
sinon la fonction est inutilisable sur un texte déclaré const.

Merci @biggil pour cet ajout.

Maintenant, le code suivant donne exactement le même résultat !

char tab[] = "1234567890abcdefgh";
void setup() {

  Serial.begin(115200);
  Serial.print( mid(tab, 11, 4));
}

void loop() {}

char *mid(const char *txt, int debut, int nbCaracteres ) // retourne le nombre de caractères d'une chaine partant de debut dans buff
{
  static char buff[30];
  int j = 0;
  for (int i = debut - 1; i < (debut - 1) + nbCaracteres  ; i++, j++)  buff[j] = txt[i];
  buff[j] = '\0';
  return buff;
}

Pourquoi ?
Lorsque l'on travaille sur un tableau en C, on accéde obligatoirement à ses éléments par l'intermédiaire des pointeurs. D'ailleurs le nom d'un tableau est un pointeur constant sur son premier élément ! donc avec le tableau char buff[30] par exemple, les expressions buff et &buff[0] signifient exactement la même chose !
L'expression buff[j] est équivalente à &(buff[0]) [j].
l'opérateur [] n'est qu'une commodité syntaxique qui combine les effets d'une addition entre un pointeur et un entier suivi d'un déréférencement.
Si buff est un tableau buff[j] est équivalent à *(buff+j)

Le lien de @hbachetti https://www.techiedelight.com/fr/implement-substring-function-c/ est très Intéressant, le code est bien plus lisible avec la boucle while :wink:.
Vous noterez qu'il est possible d'appliquer l'opérateur * à un tableau, en effet, *buff est équivalent à *(&buff[0]) et à buff[0] d'où dans le code cité par @hbachetti : *destination = *(source + beg);
Cette fonction est bien meilleure que la mienne :

char* substring(char *destination, const char *source, int beg, int n)
{
    // extrait `n` caractères de la string source à partir de l'index `beg`
    // et les copier dans la string de destination
    while (n > 0)
    {
        *destination = *(source + beg);
 
        destination++;
        source++;
        n--;
    }
 
    // null terminer la string de destination
    *destination = '\0';
 
    // renvoie la string de destination
    return destination;
}

Voilà, il est tard et j'espère avoir été clair mais j'en suis pas sur !
Bonne continuation.

PS :

Il a certainement envie de comprendre et il a raison ...

Hé oui : je veux comprendre ces pointeurs.

Désolé pour le délai de réponse : un membre de la famille hospitalisé.
Je vais regarder avec attention tout ce que vous m'avez envoyé,
c'est génial, merci !

Bien, ceci dit, j'avais bien vu des roues (mais octogonales - en référence à l'octet :crazy_face:) c'est-à-dire sans véritable pédagogie ou un sérieux manque d'ergonomie (généralement plutôt un étalement de savoirs).


Pour mes défauts, sachez que j'ai débuté en informatique lors de la charnière des années 70/80 avec le BASIC (dont certains écrits par Bill Gates du temps de Micro Soft en deux mots) avec les TRS-80 de Tandy/Radio Shack.
Je continue de temps en temps avec du VB de dot.net, mais je développais des applications professionnelles en Excel et Access avec du VBA.

Maintenant pensionné, je développe encore de temps en temps des application pro comme citées +haut et je fais surtout du touche-à-tout avec le C++ de l'Arduino et du Python avec Raspberry. Et j'oubliais le PHP et peut-être d'autres trucs !
J'apprends au fur et à mesure de mes besoins et ce n'est peut-être pas tip-top. C'est pour cela que votre aide est très précieuse.

Je compte publier mon gros projet sur mon blog en // avec github

Encore merci, je vais regarder tout cela !

Et je vais bientôt présenter mon code : il avance et sera probablement fonctionnel !

pourquoi ne pas utiliser simplement memcpy() ?

char* substring(char *destination, const char *source, size_t beg, size_t n) {
  memcpy (destination, source+beg, n);  // on copie les octets
  destination[n]='\0';                  // on termine la cString
  return destination;
}

Bonjour @J-M-L
Pourquoi pas.
Tout est possible ...

Bonne journée.

@J-M-L
Il y a une chose que je ne comprends pas : ce code fonctionne sous CodeBlocks :

#include <stdio.h>

// Function to implement substring function in C
char* substring(char *destination, const char *source, int beg, int n)
{
	// extracts `n` characters from the source string starting from `beg` index
	// and copy them into the destination string
	while (n > 0)
	{
		*destination = *(source + beg);

		destination++;
		source++;
		n--;
	}

	// null terminate destination string
	*destination = '\0';

	// return the destination string
	return destination;
}

// Implement `substring()` function in C
int main()
{
	char source[] = "Techie Delight – Ace the Technical Interviews";
	char destination[25];

	int start = 4;
	int len = 7;

	substring(destination, source, start, len);

	printf("%s\n", destination);

	return 0;
}



Celui-ci ne fonctionne pas sur arduino avec une UNO :

char source[] = "0123456789abcdefghijklmnopqrstuvwxyz";
char destination[25];

int start = 4;
int len = 7;

char* substring(char *destination, const char *source, int beg, int n)
{
  // extracts `n` characters from the source string starting from `beg` index
  // and copy them into the destination string
  while (n > 0)
  {
    *destination = *(source + beg);

    destination++;
    source++;
    n--;
  }

  // null terminate destination string
  *destination = '\0';

  // return the destination string
  return destination;
}

void setup() {
  Serial.begin(115200);
  Serial.print(substring(destination, source, start, len));

}

void loop() {}
 

Merci par avance @J-M-L

Il lui manque un petit détail tout de même : vérifier que la longueur de la chaîne source est bien au moins égale à beg + n.

non non tout fonctionne bien :slight_smile:

je vous laisse chercher un peu.

Allez, un indice quand même

➜ dans le premier code vous faites

	substring(destination, source, start, len);
	printf("%s\n", destination);

et dans le second

  Serial.print(substring(destination, source, start, len));

➜ question: imprimez vous la même chose?

:innocent: :thinking:

Merci @J-M-L

Je rentre du brame du cerf et je vois votre message :

char source[] = "0123456789abcdefghijklmnopqrstuvwxyz";
char destination[25];

int start = 0;
int len = 7;

char* substring(char *destination, const char *source, int beg, int n)
{
  // extracts `n` characters from the source string starting from `beg` index
  // and copy them into the destination string
  while (n > 0)
  {
    *destination = *(source + beg);

    destination++;
    source++;
    n--;
  }

  // null terminate destination string
  *destination = '\0';

  // return the destination string
  return destination;
}

void setup() {
  Serial.begin(115200);
  substring(destination, source, start, len);
  Serial.print(destination);

}


C'est mieux comme ça...
C'était tout bête comme moi :mask:
Heureusement que vous êtes là !!!!!!!!!!!!!!!!!!!!!
Merci.

:wink:

en fait la fonction n'est pas terrible car elle retourne le pointeur sur le dernier caractère, celui qu'on vient de mettre à '\0'... ça ne sert pas à grand chose, ce serait plus malin de retourner le pointeur d'origine...

Bonjour @J-M-L,

En fait, j'essaye de comprendre. Pouvez-vous me dire si mon analyse de la boucle while est correcte ?

*destination = *(source + beg); :

*destination : déréférencement du pointeur destination soit destination[0] , donc contenu pointé du premier élément du tableau destination.

source : pointeur constant sur le premier élément du tableau source, c’est donc une adresse mémoire du tableau, la première.

beg : entier qui détermine le premier élément du tableau source à extraire.

Source + beg : adresse qui contient le premier élément du tableau source à extraire.

*(source + beg) : déréférencement ou contenu de l’adresse du premier élément du tableau source à extraire.

destination++ : incrémentation de l’adresse qui pointe vers le tableau destination
source++ : incrémentation de l’adresse qui pointe vers le tableau source

*destination = *(source + beg);
    destination++;
    source++;

En incrémentant les adresse des tableaux destination et source, on parcourt donc leurs adresses en mémoire (en partant de la première pour destination et de beg pour source). Par le biais du déréférencement, le contenu de chaque adresse mémoire du tableau destination est égal au contenu de chaque adresse mémoire du tableau source à partir de beg (+ beg).
Lorsqu' on a extrait nos n éléments, le dernier élément de destination est égal à ‘\0’ :
*destination = '\0';

Voilà, il me semble que cette boucle remplit le tableau char destination[25]; élément par élément en commençant par le premier et en le terminant bien évidemment par le zéro terminal.

Lorsque vous m'aurez répondu, Je réfléchirai au fait que :

Merci par avance pour votre patience.
Bonne journée.

oui, et donc cette boucle modifie la valeur du pointeur destination, que la fonction retourne (donc on retourne le pointeur pointant sur le dernier caractère et non pas le pointeur qu'on a reçu au départ sur le début du buffer destination).

ça peut servir si on veut faire de la concaténation, mais c'est contre intuitif si ce n'est pas documenté et c'est le piège dans lequel vous êtes tombé en faisant un print direct de ce que retournait la fonction

oui, ok, tout ça c'est bon.
Mais la fonction retourne le pointeur destination, comme s'il pointait toujours sur le début de la zone de destination.
Mais non.
Ce pointeur a été incrémenté, et au moment de sortie de la fonction, il pointe maintenant vers le '\0' final. La fonction renvoie donc un pointeur vers une chaîne vide.
Mais le travail a été fait. Du point de vue de l'appelant, la sous-chaîne se trouve bien à l'adresse de destination passée à la fonction.

aaargh, grillé par J-M-L !