Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

[Debutant] Passer une chaine de C++ à C

7 réponses
Avatar
ZeGilooo
Bonjour,
j'ai un problème pour passer une chaine de caractère d'une fonction en C++ à
une autre en langage C.
Dans mon fichier en C j'appelle ma fonction qui est dans un fichier en CPP :
-----------------------
fichier1.c :
*returnValueEnC = mycppFunction(Attribut);
-----------------------
fichier2.cpp :
je déclare bien ma fonction avec extern "C" et ça fonctionne bien pour
toutes les autres fonctions.
extern "C" char mycppFunction (char * Attribut);
-----------------------
et voici la fonction qui pose problème :

char mycppFunction (char * Attribut) {

char *returnValue = "Erreur";
long returnValue_len;

// je fais quelque chose pour obtenir une chaine de caractère dans
returnValue, avec succès.

returnValue_len = strlen(returnValue);

returnValue[returnValue_len]='\0';

return *returnValue;
}

Je constate, dans le débogueur, que la fonction C++ renvoi la bonne valeur
(return *returnValue;)
Mais quand je la reçoit dans mon fichier en C, *returnValueEnC renvoi la
première lettre qui est bonne (hasard ?) suivie d'une infinité de caractères
"ì" (code ascii = 147) ...

Que faut-il faire pour obtenir la bonne chaine ?

Merci beaucoup de votre aide,
Gilles.

7 réponses

Avatar
Fabien LE LEZ
On Fri, 29 Oct 2004 16:49:40 +0200, "ZeGilooo" :

j'ai un problème pour passer une chaine de caractère d'une fonction en C++ à
une autre en langage C.


Tu parles... contrairement à ce qui se passe en C++, les chaînes de
caractères sont assez pénibles à utiliser en C.

Commençons par le plus simple : une fonction en C++.

std::string f_CPP()
{
std::string reponse= "Hello ";
reponse+= "world!";
return reponse;
}

std::string, le type "chaîne de caractères" de base en C++ (même si on
utilise parfois des classes différentes mais globalement
équivalentes), est un type normal, qui s'utilise aussi simplement
qu'un int.

int main()
{
std::string chaine= f_CPP();
cout << chaine << endl;
}

Maintenant, attaquons-nous à l'interfaçage avec un programme C.
J'avoue que je ne suis pas au courant du support de "const" par le
langage C ; tu peux éventuellement enlever "const" si ça gêne.

En C, il n'y a pas de type "chaîne de caractères". Il y a juste des
séquences d'octets contigus en mémoire, généralement terminées par
'' (qui est l'octet dont tous les bits sont à zéro).
Pour manipuler les chaînes de caractères, on utilise généralement un
pointeur vers le premier caractère -- un "char*" (ou "char const*").

Logiquement, si tu veux renvoyer une chaîne de caractères, tu dois
donc renvoyer un "char*". Sauf que c'est un pointeur, et il doit
pointer vers une zone mémoire qui n'a pas été invalidée.

Ainsi, prends le code suivant :

int f_C_int()
{
int i= 42;
return i; // OK
}

Ici, le "return" prend une copie de "i", qu'il fournira à la fonction
appelante, puis "i" disparaît, étant local à la fonction.



char* f_C()
{
size_t const TAILLE_BUF= 50;
char buf [TAILLE_BUF];
/* ici, on met ce qu'on veut dans "buf", en faisant attention à la
taille limite */
return buf; /* Ce serait trop beau... */
}

Ici, "return" prend une copie du pointeur "buf", puis détruit cette
variable locale, tout en libérant les 50 octets de mémoire. Du coup,
la fonction appelante se retrouve avec un pointeur pointant sur...
rien.
Il y a des méthodes en C pour retourner une chaîne de caractères, mais
ce n'est pas le propos du présent forum -- d'autant que je ne connais
pas de méthode vraiment géniale, juste des compromis. Je t'invite à
acheter un bouquin de C sur le sujet, et/ou à consulter
fr.comp.lang.c.

Notes :
- l'expression "truc" (avec les guillemets) représente une chaîne de 5
octets, et est équivalente à { 't', 'r', 'u', 'c', '' }
- la fonction mycppFunction() renvoie un char, i.e. un seul caractère.
Ensuite, tu prends un pointeur sur ce caractère, ce qui ne te donne
absolument pas la chaîne d'origine -- juste ce caractère, sans ''
après, d'où les caractères bizarres qui apparaissent (ce sont des
octets qui sont en mémoire à cet endroit, plus ou moins par hasard --
et tu as de la chance, il y a [toujours par hasard] un '' un peu
plus loin, qui dit à l'afficheur de s'arrêter.


--
;-)

Avatar
ZeGilooo
Un grand merci pour ces explications, ça fait du bien d'avoir une idée de ce
qu'il se passe.

Votre message vaut plus que tous les tutoriaux sur la question.

Maintenant, il me reste à trouver une solution ...

Gilles.
Avatar
Fabien LE LEZ
On Fri, 29 Oct 2004 17:41:52 +0200, "ZeGilooo" :

Votre message vaut plus que tous les tutoriaux sur la question.


J'espère que non -- normalement, un bon bouquin de C s'étend
longuement sur ce genre de questions.
Un bouquin d'introduction au C++, par contre, risque fort de passer ce
genre de détails sous silence.

--
;-)

Avatar
drkm
Fabien LE LEZ writes:

char* f_C()
{
size_t const TAILLE_BUF= 50;
char buf [TAILLE_BUF];
/* ici, on met ce qu'on veut dans "buf", en faisant attention
à la taille limite */
return buf; /* Ce serait trop beau... */
}

Ici, "return" prend une copie du pointeur "buf",


Je suppose que c'est une simplification pour les besoins de
l'explication. Mais je pense important d'être précis, trop de
confusion existe à ce propos.

« buf » n'est pas un pointeur. Il s'agit d'un tableau, de 50 char,
local à la fonction f_C(). Cette fonction en retourne l'adresse,
obtenue par conversion implicite du tableau en l'adresse de son
premier élément. Le return ci-dessus est équivalent à :

return static_cast< char * >( buf ) ;

puis détruit cette
variable locale, tout en libérant les 50 octets de mémoire. Du coup,
la fonction appelante se retrouve avec un pointeur pointant sur...
rien.


Pire. L'objet sur lequel il pointe, buf, a été détruit. Il se peut
que le programme fonctionne correctement, passe tous les tests, etc.
Jusqu'au jour où ça ne marche plus. Ou seulement une fois sur deux.
Et ça c'est pas cool.

--drkm

Avatar
Fabien LE LEZ
On Sat, 30 Oct 2004 16:31:21 +0200, drkm :

Je suppose que c'est une simplification pour les besoins de
l'explication.


J'avoue que même avec des simplifications, mon explication était
passablement embrouillée. Ça mérite largement un long chapitre dans un
bouquin.


--
;-)

Avatar
joel.chappel
"ZeGilooo" wrote in message news:<41825888$0$302$...
Bonjour,
j'ai un problème pour passer une chaine de caractère d'une fonction en C++ à
une autre en langage C.
Dans mon fichier en C j'appelle ma fonction qui est dans un fichier en CPP :
-----------------------
fichier1.c :
*returnValueEnC = mycppFunction(Attribut);
-----------------------
fichier2.cpp :
je déclare bien ma fonction avec extern "C" et ça fonctionne bien pour
toutes les autres fonctions.
extern "C" char mycppFunction (char * Attribut);
-----------------------
et voici la fonction qui pose problème :

char mycppFunction (char * Attribut) {

char *returnValue = "Erreur";
long returnValue_len;

// je fais quelque chose pour obtenir une chaine de caractère dans
returnValue, avec succès.

returnValue_len = strlen(returnValue);

returnValue[returnValue_len]='';

return *returnValue;
}

Je constate, dans le débogueur, que la fonction C++ renvoi la bonne valeur
(return *returnValue;)
Mais quand je la reçoit dans mon fichier en C, *returnValueEnC renvoi la
première lettre qui est bonne (hasard ?) suivie d'une infinité de caractères
"ì" (code ascii = 147) ...


L'explication a déjà été donnée par Fabien Le Lez...

Vous avez déjà étudié les fonctions de manipulation de chaîne standard
de C (toutes ces fonctions strXXX dans string.h) ? Regardez bien leurs
prototypes : elles ne retournent jamais de char* !!! Ce serait
pourtant bien pratique... Mais non !!! Danger !!! Et c'est exactement
pour cette raison là : libération immédiate de la chaîne dont
l'adresse est retournée à la fin de la fonction.

Une solution concrète consiste à ne pas (JAMAIS) retourner de char*
dans une fonction.

Modifiez votre code de la façon suivante (= passer returnValueEnC en
paramètre) :

fichier1.c :
char returnValueEnC[xxx]; // Déclaration+réservation de la chaîne
manipulée
mycppFunction(Attribut,returnValueEnC);
-----------------------
fichier2.cpp :
extern "C" void mycppFunction (char * Attribut, char* returnValue);
void mycppFunction (char * Attribut, char* returnValue) {

long returnValue_len;

returnValue = "Erreur";

// quelque chose pour obtenir une chaine de caractère dans
returnValue.

returnValue_len = strlen(returnValue);

returnValue[returnValue_len]='';

//return *returnValue;
}

De cette manière, la chaîne returnValueEnC (manipulée sous le nom
returnValue dans la fonction mycppFunction) est déclarée (et la
mémoire nécessaire réservée) dans la fonction appelante. L'espace
mémoire correspondant ne sera libéré qu'à la fin de la fonction
appelante (donc après l'exploitation du résultat).

Avatar
Richard Delorme

Vous avez déjà étudié les fonctions de manipulation de chaîne standard
de C (toutes ces fonctions strXXX dans string.h) ? Regardez bien leurs
prototypes : elles ne retournent jamais de char* !!!


Retournent char* :

char *strcat(char *restrict, const char *restrict);
char *strchr(const char *, int);
char *strcpy(char *restrict, const char *restrict);
char *strerror(int);
char *strncat(char *restrict, const char *restrict, size_t);
char *strncpy(char *restrict, const char *restrict, size_t);
char *strpbrk(const char *, const char *);
char *strrchr(const char *, int);
char *strstr(const char *, const char *);
char *strtok(char *restrict, const char *restrict);

Retournent autre chose :

int strcmp(const char *, const char *);
int strcoll(const char *, const char *);
size_t strcspn(const char *, const char *);
size_t strlen(const char *);
int strncmp(const char *, const char *, size_t);
size_t strspn(const char *, const char *);
size_t strxfrm(char *restrict, const char *restrict, size_t);

Donc 59% des fonctions str*() retourne un char*, ce qui est loin de jamais.

Ce serait
pourtant bien pratique... Mais non !!! Danger !!! Et c'est exactement
pour cette raison là : libération immédiate de la chaîne dont
l'adresse est retournée à la fin de la fonction.


On peut retourner autre chose que chaîne locale à la fonction... c'est
ce que font les fonction de la bibliothèque C. Cela dit la plupart des
fonctions str*() du C sont mal foutue et mieux vaut les éviter quand on
peut (et en C++ on peut facilement).

--
Richard