Terugkerende string van C-functie

Ik heb C al meer dan 3 jaar niet gebruikt, ik ben nogal roestig in veel dingen.

Ik weet dat dit misschien stom lijkt, maar ik kan op dit moment geen string van een functie retourneren. Ga er alsjeblieft van uit dat: ik string.hhiervoor niet kan gebruiken.

Hier is mijn code:

#include <ncurses.h>
char * getStr(int length)
{   
    char word[length];
    for (int i = 0; i < length; i++)
    {
        word[i] = getch();
    }
    word[i] = '\0';
    return word;
}
int main()
{
    char wordd[10];
    initscr();
    *wordd = getStr(10);
    printw("The string is:\n");
    printw("%s\n",*wordd);
    getch();
    endwin();
    return 0;
}

Ik kan de string vastleggen (met mijn functie getStr) maar ik krijg hem niet correct weergegeven (ik krijg rommel).

Hulp wordt op prijs gesteld.


Antwoord 1, autoriteit 100%

Wijs de string toe aan de stapel aan de kant van de beller en geef deze door aan uw functie:

void getStr(char *wordd, int length) {
    ...
}
int main(void) {
    char wordd[10 + 1];
    getStr(wordd, sizeof(wordd) - 1);
    ...
}

Of maak de string statisch in getStr:

char *getStr(void) {
    static char wordd[10 + 1];
    ...
    return wordd;
}

Of wijs de string toe aan de heap:

char *getStr(int length) {
    char *wordd = malloc(length + 1);
    ...
    return wordd;
}

Antwoord 2, autoriteit 18%

char word[length];
char *rtnPtr = word;
...
return rtnPtr;

Dit is niet goed. U retourneert een aanwijzer naar een automatische (scoped) variabele, die wordt vernietigd wanneer de functie terugkeert. De aanwijzer blijft wijzen naar een vernietigde variabele, wat vrijwel zeker “vreemde” resultaten zal opleveren (ongedefinieerd gedrag).

U zou de string moeten toewijzen met malloc(bijv. char *rtnPtr = malloc(length)), en deze later freein main.


Antwoord 3, autoriteit 6%

U wijst uw tekenreeks toe aan de stapel en stuurt er vervolgens een aanwijzer naar terug. Wanneer uw functie terugkeert, worden alle stapeltoewijzingen ongeldig; de aanwijzer wijst nu naar een regio op de stapel die waarschijnlijk zal worden overschreven de volgende keer dat een functie wordt aangeroepen.

Om te doen wat je probeert te doen, moet je een van de volgende dingen doen:

  1. Wijs geheugen toe aan de heap met behulp van mallocof iets dergelijks, en retourneer die aanwijzer. De beller moet dan freebellen als het klaar is met het geheugen.
  2. Wijs de tekenreeks toe aan de stapel in de aanroepende functie (degene die de tekenreeks zal gebruiken) en geef een aanwijzer door aan de functie waarin de tekenreeks moet worden geplaatst. Tijdens de hele aanroep van de aanroepende functie zijn de gegevens op de stapel geldig; het is pas als je de aan de stapel toegewezen ruimte teruggeeft, wordt gebruikt door iets anders.

Antwoord 4, autoriteit 4%

Je aanwijzer wijst naar de lokale variabele van de functie. Dus zodra u terugkeert van de functie, wordt het geheugen vrijgegeven. U moet geheugen op heap toewijzen om het in andere functies te gebruiken.

In plaats daarvan
char *rtnPtr = word;

doe dit
char *rtnPtr = malloc(length);

Zodat het beschikbaar is in de hoofdfunctie. Nadat het is gebruikt, maakt u het geheugen vrij.


Antwoord 5

wordstaat op de stapel en gaat buiten bereik zodra getStr()terugkeert. U roept ongedefinieerd gedrag op.


Antwoord 6

Zoals anderen al zeiden, kun je een niet-constante tekenreeks niet op een nuttige manier retourneren zonder deze op de heap toe te wijzen (bijvoorbeeld door strdup te gebruiken). Maar in alle recente versies van de C-standaard (C89 en later als ik me niet vergis) kun je kunteen struct retourneren. Het is niet nodig voor de beller om de toewijzing van het resultaat ongedaan te maken, omdat het niet op de heap staat. En het is draadveilig.

#include <stdio.h>
struct stringbuf
{
  char buf[40];
};
struct stringbuf getanswer(int i)
{
  struct stringbuf result = { 0 };
  snprintf(result.buf, sizeof(result.buf), "The answer is %d", i);
  return result;
}
int main(int argc, char **argv)
{
  /*
   * Remember to pass the .buf member, not the struct, to functions
   * such as printf which expect a character pointer as argument!
   * Passing the result of getanswer in the next line without .buf
   * appended, will likely crash the program because the program
   * will put the entire struct on the stack, not a character
   * pointer, and will make printf interpret the first few bytes
   * of the string as a pointer. That would be bad.
   */
  printf("How many arguments did I get? %s\n", getanswer(argc).buf);
  return 0;
}

Opmerking: om de voorbeeldcode zo eenvoudig en gericht mogelijk te houden, heb ik eenvoudig een struct-type gedeclareerd zonder typedef. U kunt uzelf veel typen besparen door typedef te gebruiken en het gedefinieerde type terug te geven.

Er zijn (misschien) enkele nadelen:

  • Een functie die een struct retourneert, kan NULL niet retourneren.
  • De grootte van de buffer in de struct is vast omdat de compiler de grootte van het retourtype moet weten tijdens het compileren.
  • Het resultaat van een functie die een struct retourneert, wordt waarschijnlijk op de stapel opgeslagen; dit kan een probleem zijn in kleine systemen (zoals microcontrollers) die niet veel stackruimte hebben.
  • In tegenstelling tot tekenreeksen, is een instantie van een struct geen bruikbare alias voor de tekenreeks die erin is opgeslagen. Met andere woorden, terwijl u een array van tekens kunt maken en de naam ervan kunt gebruiken als een aanwijzer naar het eerste teken, kunt u de naam van een struct niet als een aanwijzer gebruiken.

Dat laatste punt is belangrijk omdat je er rekening mee moet houden dat een struct met een karakterarray niet hetzelfde is als de array zelf. Dus als je een stringfunctie wilt aanroepen, moet je de string member-variabele doorgeven, niet een pointer naar de struct. Dit is vooral belangrijk voor functies met variadische argumenten zoals printf en vrienden, waar een compiler je misschien niet waarschuwt als je het verkeerd doet: als je een struct doorgeeft, wordt de hele struct op de stapel geplaatst, niet alleen een verwijzing naar het eerste teken. Printf zal de eerste paar karakters in de struct interpreteren als een karakteraanwijzer, die zeker ongeldig zal zijn.

Ja, het is mogelijk om een pointer naar een struct naar een char * te casten en deze door te geven aan een tekenreeksfunctie (inclusief printf) en dat zal correct werken, maar ik zou zeggen dat het een slechte gewoonte is om dit te doen: Als u ( of iemand anders) ooit besluit om een andere lidvariabele in de struct-declaratie voor de stringbuffer te plaatsen, zou elk gebruik van een typecast-aanwijzer naar een struct die ervan uitgaat dat de stringbuffer begint waar de struct begint, stilem>mislukken. U wilt dit waarschijnlijk vermijden, dus gebruik een aanwijzer naar de string-lidvariabele, zelfs als dit enigszins onhandig is.

===Jac


Antwoord 7

Nog eenvoudiger: retourneer een pointer naar een string die is malloc’d met strdup.

#include <ncurses.h>
char * getStr(int length)
{   
    char word[length];
    for (int i = 0; i < length; i++)
    {
        word[i] = getch();
    }
    word[i] = '\0';
    return strdup(&word[0]);
}
int main()
{
    char wordd[10];
    initscr();
    *wordd = getStr(10);
    printw("The string is:\n");
    printw("%s\n",*wordd);
    getch();
    endwin();
    return 0;
}

Antwoord 8

Ik kwam deze draad tegen tijdens het werken aan mijn begrip van Cython. Mijn extensie naar de oorspronkelijke vraag kan van nut zijn voor anderen die werken bij de C / Cython-interface. Dit is dus de uitbreiding van de oorspronkelijke vraag: hoe retourneer ik een tekenreeks van een C-functie, waardoor het beschikbaar is voor Cython & AMP; Dus naar Python?

Voor degenen die er niet mee vertrouwd zijn, kun je de Python-code stellen die je moet versnellen. Dus het proces is, geniet van het schrijven van Python :), vind het een beetje traag ergens, profiel het, kalf een functie of twee en cythoniseer ze. Wauw. Dicht bij C-snelheid (het compileert tot c) vast. Yay. Het andere gebruik importeert C-functies of bibliotheken in Python zoals hier gedaan.

Hiermee drukt u een tekenreeks af en retourneert dezelfde of een andere reeks naar Python. Er zijn 3 bestanden, de C-bestand C_HELLO.C, het Cython-bestand Sayhello.pyx en het Cython Setup-bestand Sayhello.pyx. Wanneer ze worden gecompileerd met python setup.py build_ext --inplacegenereren ze een gedeelde bibliotheekbestand dat in Python of Ipython en de functie Sayhello.hello Run kan worden geïmporteerd.

c_hello.c

#include <stdio.h>
char *c_hello() {
  char *mystr = "Hello World!\n";
  return mystr;
  // return "this string";  // alterative
}

sayhello.pyx

cdef extern from "c_hello.c":
    cdef char* c_hello()
def hello():
    return c_hello()

setup.py

from setuptools import setup
from setuptools.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize
ext_modules = cythonize([Extension("sayhello", ["sayhello.pyx"])])
setup(
name = 'Hello world app',
cmdclass = {'build_ext': build_ext},
ext_modules = ext_modules
)

Other episodes