Jeg skrev denne funksjonen for å lese en linje fra en fil:
const char *readLine(FILE *file) {
if (file == NULL) {
printf("Error: file pointer is null.");
exit(1);
}
int maximumLineLength = 128;
char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);
if (lineBuffer == NULL) {
printf("Error allocating memory for line buffer.");
exit(1);
}
char ch = getc(file);
int count = 0;
while ((ch != '\n') && (ch != EOF)) {
if (count == maximumLineLength) {
maximumLineLength += 128;
lineBuffer = realloc(lineBuffer, maximumLineLength);
if (lineBuffer == NULL) {
printf("Error reallocating space for line buffer.");
exit(1);
}
}
lineBuffer[count] = ch;
count++;
ch = getc(file);
}
lineBuffer[count] = '\0';
char line[count + 1];
strncpy(line, lineBuffer, (count + 1));
free(lineBuffer);
const char *constLine = line;
return constLine;
}
Funksjonen leser filen riktig, og ved hjelp av printf ser jeg at constLine-strengen også ble lest riktig.
Men hvis jeg bruker funksjonen f.eks. som dette:
while (!feof(myFile)) {
const char *line = readLine(myFile);
printf("%s\n", line);
}
gir printf ut volapyk. Hvorfor er det slik?
I readLine
-funksjonen returnerer du en peker til line
-arrayet (strengt tatt en peker til det første tegnet, men forskjellen er irrelevant her). Siden det er en automatisk variabel (dvs. det er "på stabelen"), blir minnet gjenvunnet når funksjonen returnerer. Du ser gibberish fordi printf
har lagt sine egne ting på stabelen.
Du må returnere en dynamisk allokert buffer fra funksjonen. Du har allerede en, det er lineBuffer
; alt du trenger å gjøre er å avkorte den til ønsket lengde.
lineBuffer[count] = '\0';
realloc(lineBuffer, count + 1);
return lineBuffer;
}
ADDED (svar på oppfølgingsspørsmål i kommentar): readLinereturnerer en peker til tegnene som utgjør linjen. Denne pekeren er det du trenger for å arbeide med innholdet i linjen. Det er også det du må sende til
freenår du er ferdig med å bruke minnet som er tatt av disse tegnene. Slik kan du bruke funksjonen
readLine`:
char *line = readLine(file);
printf("LOG: read a line: %s\n", line);
if (strchr(line, 'a')) { puts("The line contains an a"); }
/* etc. */
free(line);
/* After this point, the memory allocated for the line has been reclaimed.
You can't use the value of `line` again (though you can assign a new value
to the `line` variable if you want). */
readLine()` returnerer peker til lokal variabel, noe som forårsaker udefinert oppførsel.
For å komme rundt kan du:
readLine()
.line
ved hjelp av malloc()
- i dette tilfellet vil line
være persistent.const char *readLine(FILE *file, char* line) {
if (file == NULL) {
printf("Error: file pointer is null.");
exit(1);
}
int maximumLineLength = 128;
char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);
if (lineBuffer == NULL) {
printf("Error allocating memory for line buffer.");
exit(1);
}
char ch = getc(file);
int count = 0;
while ((ch != '\n') && (ch != EOF)) {
if (count == maximumLineLength) {
maximumLineLength += 128;
lineBuffer = realloc(lineBuffer, maximumLineLength);
if (lineBuffer == NULL) {
printf("Error reallocating space for line buffer.");
exit(1);
}
}
lineBuffer[count] = ch;
count++;
ch = getc(file);
}
lineBuffer[count] = '\0';
char line[count + 1];
strncpy(line, lineBuffer, (count + 1));
free(lineBuffer);
return line;
}
char linebuffer[256];
while (!feof(myFile)) {
const char *line = readLine(myFile, linebuffer);
printf("%s\n", line);
}
Merk at variabelen 'line' er deklarert i anropsfunksjonen og deretter sendt, slik at readLine
-funksjonen din fyller forhåndsdefinert buffer og bare returnerer den. Dette er måten de fleste C-biblioteker fungerer på.
Det er andre måter, som jeg er klar over:
char line[]
som statisk
(static char linje[MAX_LINE_LENGTH]
-> det vil holde verdien ETTER retur fra funksjonen). -> dårlig,
funksjonen er ikke reentrant, og
rasebetingelse kan oppstå -> hvis du
kaller det to ganger fra to tråder, det
vil det overskrive det' s resultatermalloc()
ing av char-linjen[], og
frigjøre det i å kalle funksjoner ->
for mange dyre malloc
er, og,
delegere ansvaret for å frigjøre bufferen til en annen funksjon (den mest elegante løsningen er å kalle malloc
og free
på alle buffere i samme funksjon).btw, 'eksplisitt' casting fra char*
til const char*
er overflødig.
btw2, det er ikke nødvendig å malloc()
lineBuffer, bare definere det char lineBuffer[128]
, slik at du ikke trenger å frigjøre den
btw3 ikke bruk 'dynamic sized stack arrays' (definere arrayet som char arrayName[some_nonconstant_variable]
), hvis du ikke vet nøyaktig hva du gjør, fungerer det bare i C99.