J'essaie de renvoyer une chaîne C à partir d'une fonction, mais cela ne fonctionne pas. Voici mon code.
char myFunction()
{
return "My String";
}
Dans main
je l'appelle comme ceci:
int main()
{
printf("%s", myFunction());
}
J'ai également essayé d'autres moyens myFunction
, mais ils ne fonctionnent pas. Par exemple:
char myFunction()
{
char array[] = "my string";
return array;
}
Remarque: je ne suis pas autorisé à utiliser des pointeurs!
Petit historique sur ce problème:
Il y a une fonction qui découvre de quel mois il s'agit. Par exemple, s'il vaut 1, il renvoie janvier, etc.
Alors , quand il va imprimer, ça fait comme ça: printf("Month: %s",calculateMonth(month));
. Maintenant, le problème est de savoir comment renvoyer cette chaîne à partir de la calculateMonth
fonction.
return 0
est implicite par défaut uniquement en C99 (et C ++) mais pas en C90.Réponses:
Votre signature de fonction doit être:
Contexte:
C'est tellement fondamental pour C & C ++, mais peu de discussions supplémentaires devraient être en ordre.
En C (et C ++ d'ailleurs), une chaîne est juste un tableau d'octets terminé par un octet zéro - d'où le terme "chaîne-zéro" est utilisé pour représenter cette saveur particulière de chaîne. Il existe d'autres types de chaînes, mais en C (et C ++), cette saveur est intrinsèquement comprise par le langage lui-même. D'autres langages (Java, Pascal, etc.) utilisent différentes méthodologies pour comprendre "ma chaîne".
Si jamais vous utilisez l'API Windows (qui est en C ++), vous verrez assez régulièrement des paramètres de fonction comme: "LPCSTR lpszName". La partie 'sz' représente cette notion de 'string-zero': un tableau d'octets avec un terminateur nul (/ zéro).
Clarification:
Pour cette «intro», j'utilise les mots «octets» et «caractères» de manière interchangeable, car c'est plus facile à apprendre de cette façon. Sachez qu'il existe d'autres méthodes (caractères larges et systèmes de caractères multi-octets ( mbcs )) qui sont utilisées pour gérer les caractères internationaux. UTF-8 est un exemple de mbcs. Par souci d'intro, je «saute» tranquillement tout cela.
Mémoire:
Cela signifie qu'une chaîne comme "ma chaîne" utilise en fait 9 + 1 (= 10!) Octets. Il est important de savoir quand vous parvenez enfin à allouer des chaînes de manière dynamique.
Donc, sans ce «zéro final», vous n'avez pas de chaîne. Vous avez un tableau de caractères (également appelé tampon) en mémoire.
Longévité des données:
L'utilisation de la fonction de cette façon:
... vous amènera généralement avec des exceptions aléatoires non gérées / des défauts de segment et autres, en particulier `` sur la route ''.
En bref, bien que ma réponse soit correcte - 9 fois sur 10, vous vous retrouverez avec un programme qui plante si vous l'utilisez de cette façon, surtout si vous pensez que c'est une «bonne pratique» de le faire de cette façon. En bref: ce n'est généralement pas le cas.
Par exemple, imaginez dans le futur, la chaîne doit maintenant être manipulée d'une manière ou d'une autre. Généralement, un codeur `` empruntera le chemin facile '' et (essaiera) d'écrire du code comme celui-ci:
Autrement dit, votre programme plantera car le compilateur (peut / peut ne pas) avoir libéré la mémoire utilisée
szBuffer
par le moment où leprintf()
inmain()
est appelé. (Votre compilateur doit également vous avertir de tels problèmes au préalable.)Il existe deux façons de renvoyer des chaînes qui ne barfront pas si facilement.
std::string
) pour gérer la longévité des données (ce qui nécessite de changer la valeur de retour de la fonction), ouNotez qu'il est impossible d'utiliser des chaînes sans utiliser de pointeurs en C. Comme je l'ai montré, ils sont également. Même en C ++ avec des classes de modèle, des tampons (c'est-à-dire des pointeurs) sont toujours utilisés en arrière-plan.
Donc, pour mieux répondre à la (question maintenant modifiée). (Il y aura sûrement une variété d '«autres réponses» qui peuvent être fournies.)
Des réponses plus sûres:
Exemple 1, en utilisant des chaînes allouées statiquement:
Ce que fait le «statique» ici (de nombreux programmeurs n'aiment pas ce type d '«allocation»), c'est que les chaînes sont placées dans le segment de données du programme. Autrement dit, il est alloué en permanence.
Si vous passez au C ++, vous utiliserez des stratégies similaires:
... mais il est probablement plus facile d'utiliser des classes d'assistance, par exemple
std::string
, si vous écrivez le code pour votre propre usage (et ne fait pas partie d'une bibliothèque à partager avec d'autres).Exemple 2, utilisant des tampons définis par l'appelant:
C'est la manière la plus «infaillible» de passer des chaînes. Les données renvoyées ne sont pas sujettes à manipulation par l'appelant. Autrement dit, l'exemple 1 peut facilement être abusé par un appelant et vous exposer à des défauts d'application. De cette façon, c'est beaucoup plus sûr (bien qu'il utilise plus de lignes de code):
Il y a de nombreuses raisons pour lesquelles la deuxième méthode est meilleure, en particulier si vous écrivez une bibliothèque pour être utilisée par d'autres (vous n'avez pas besoin de vous verrouiller dans un schéma d'allocation / désallocation particulier, les tiers ne peuvent pas casser votre code, et vous n'avez pas besoin de créer un lien vers une bibliothèque de gestion de mémoire spécifique), mais comme tout code, c'est à vous de décider de ce que vous préférez. Pour cette raison, la plupart des gens optent pour l'exemple 1 jusqu'à ce qu'ils aient été brûlés tellement de fois qu'ils refusent de l'écrire de cette façon;)
Avertissement:
J'ai pris ma retraite il y a plusieurs années et mon C est un peu rouillé maintenant. Ce code de démonstration devrait tous se compiler correctement avec C (c'est bien pour n'importe quel compilateur C ++).
la source
char *
, car les littéraux de chaîne en C sont de typechar[]
. Cependant, ils ne doivent en aucun cas être modifiés, le retourconst char*
est donc préférable (voir securecoding.cert.org/confluence/x/mwAV ). Un retourchar *
peut être nécessaire si la chaîne sera utilisée dans une fonction de bibliothèque héritée ou externe qui (malheureusement) attend unchar*
argument en tant que, même si elle ne lira qu'à partir de celui-ci. C ++, d'autre part, a des littéraux de chaîne deconst char[]
type (et, depuis C ++ 11, vous pouvez également avoir desstd::string
littéraux).fraught with problems
dans la section «Longévité des données» est en fait parfaitement valide. Les littéraux de chaîne ont des durées de vie statiques en C / C ++. Voir le lien mentionné par Giorgi ci-dessus.La chaîne AC est définie comme un pointeur vers un tableau de caractères.
Si vous ne pouvez pas avoir de pointeurs, par définition, vous ne pouvez pas avoir de chaînes.
la source
void foo( char array[], int length)
. Bien sûr,array
c'est un pointeur sous le capot, mais ce n'est pas "explicitement" un pointeur, et il peut donc être plus intuitif pour quelqu'un qui apprend les tableaux mais qui n'a pas tout à fait appris les pointeurs.Notez cette nouvelle fonction:
J'ai défini «tableau» comme statique. Sinon, lorsque la fonction se termine, la variable (et le pointeur que vous renvoyez) sort de la portée. Depuis que la mémoire est allouée sur la pile, et il sera corrompue. L'inconvénient de cette implémentation est que le code n'est ni réentrant ni threadsafe.
Une autre alternative serait d'utiliser malloc pour allouer la chaîne dans le tas, puis de libérer sur les emplacements corrects de votre code. Ce code sera réentrant et threadsafe.
Comme indiqué dans le commentaire, c'est une très mauvaise pratique, car un attaquant peut alors injecter du code dans votre application (il / elle doit ouvrir le code en utilisant GDB, puis faire un point d'arrêt et modifier la valeur d'une variable retournée pour déborder et le plaisir ne fait que commencer).
Il est beaucoup plus recommandé de laisser l'appelant gérer les allocations de mémoire. Voir ce nouvel exemple:
Notez que le seul contenu qui peut être modifié est celui que l'utilisateur. Un autre effet secondaire - ce code est désormais threadsafe, du moins du point de vue de la bibliothèque. Le programmeur appelant cette méthode doit vérifier que la section mémoire utilisée est threadsafe.
la source
Votre problème concerne le type de retour de la fonction - il doit être:
... et alors votre formulation originale fonctionnera.
Notez que vous ne pouvez pas avoir de chaînes C sans que des pointeurs soient impliqués, quelque part le long de la ligne.
Aussi: augmentez les avertissements de votre compilateur. Il aurait dû vous avertir de la conversion de la ligne de retour a
char *
enchar
sans conversion explicite.la source
Sur la base de votre histoire nouvellement ajoutée avec la question, pourquoi ne pas simplement renvoyer un entier de 1 à 12 pour le mois, et laisser la fonction main () utiliser une instruction switch ou if-else ladder pour décider quoi imprimer? Ce n'est certainement pas la meilleure façon de procéder - char * serait - mais dans le contexte d'une classe comme celle-ci, j'imagine que c'est probablement la plus élégante.
la source
Vous pouvez créer le tableau dans l'appelant, qui est la fonction principale, et passer le tableau à l'appelé qui est votre myFunction (). Ainsi myFunction peut remplir la chaîne dans le tableau. Cependant, vous devez déclarer myFunction () comme
Et dans la fonction principale, myFunction doit être appelée de cette manière:
Cependant, un pointeur est toujours utilisé.
la source
Le type de retour de votre fonction est un seul caractère (
char
). Vous devez renvoyer un pointeur vers le premier élément du tableau de caractères. Si vous ne pouvez pas utiliser de pointeurs, vous êtes foutu. :(la source
Ou que diriez-vous de celui-ci:
Et appelez cela avec le mois que vous calculez ailleurs.
la source
A
char
n'est qu'un seul caractère d'un octet. Il ne peut pas stocker la chaîne de caractères, ni un pointeur (que vous ne pouvez apparemment pas avoir). Par conséquent, vous ne pouvez pas résoudre votre problème sans utiliser des pointeurs (quichar[]
sont du sucre syntaxique pour).la source
Si vous ne pouvez vraiment pas utiliser de pointeurs, faites quelque chose comme ceci:
Le chiffre magique 9 est horrible et ce n'est pas un exemple de bonne programmation. Mais vous obtenez le point. Notez que les pointeurs et les tableaux sont la même chose (en quelque sorte), donc c'est un peu de la triche.
la source
Eh bien, dans votre code, vous essayez de renvoyer un
String
(en C qui n'est rien d'autre qu'un tableau de caractères terminé par null), mais c'est le type de retour de votre fonctionchar
qui vous cause tous les problèmes. Au lieu de cela, vous devriez l'écrire de cette façon:Et il est toujours bon de qualifier votre type avec
const
tout en attribuant des littéraux en C aux pointeurs, car les littéraux en C ne sont pas modifiables.la source
Votre prototype de fonction indique que votre fonction renverra un caractère. Ainsi, vous ne pouvez pas renvoyer une chaîne dans votre fonction.
la source
En C, les littéraux de chaîne sont des tableaux avec la classe de mémoire constante statique, donc renvoyer un pointeur vers ce tableau est sûr. Plus de détails sont dans la question "Life-time" de Stack Overflow d'une chaîne littérale en C
la source
Retourne la chaîne de la fonction
la source