#c
#c
Вопрос:
Я прохожу свой первый курс программирования на C и столкнулся с проблемой, пытаясь написать функцию, которая считывает текстовый файл по одной строке за раз. Вот мой код:
#define LINELENGTH 81
int getLine(char* line, FILE* file) {
if (line == NULL) {
line = malloc(sizeof(char) * LINELENGTH);
}
fgets(line, LINELENGTH, file);
int length = strcspn(line, "n");
if (line[length] == 'n') {
line[length] = '';
line = realloc(line, sizeof(char) * (length 1));
return length;
} else {
char* addThis = NULL;
int addedLength = getLine(addThis, file);
length = addedLength;
line = realloc(line, sizeof(char) * length);
strcat(line, addThis);
free(addThis);
addThis = NULL;
return length;
}
}
int main() {
FILE *text = fopen("test.txt", "r");
char* line = NULL;
getLine(line, text);
printf("The first line is "%s"", line);
fclose(text);
free(line);
return 0;
}
Мой тестовый входной файл прямо сейчас содержит только одну строку «test»
Когда я запускаю программу, я получаю «Первая строка — «(null)»». Не то, на что я надеялся. Когда я выполняю функцию в отладчике, кажется, что внутри getLine все работает нормально. Но когда функция возвращает все, что у меня осталось, равно null.
Любая помощь приветствуется. Спасибо.
Ответ №1:
Вызов to getLine
передает char*
указатель по значению. Присвоение line
внутри этой функции не приводит к возвращению выделенного указателя вызывающей стороне. Объявление функции должно быть:
int getLine(char** line, FILE* file) {...
А затем присвоите результат *line
. И вызов функции должен был бы передать адрес:
getLine( amp;line, text );
Вы также можете использовать локальную переменную для использования внутри функции, а затем присвоить конечный результат *line
до возврата. Это может немного упростить понимание кода. В противном случае при каждом line
использовании было бы необходимо разыменовывать указатель, и он становится (только мое мнение здесь) немного более беспорядочным. Так что, возможно, измените параметр в определении функции на getLine( char** retLine, ... )
. Затем объявите локальную переменную вида char* line;
. Затем перед return
операторами назначьте его:
*retLine = line;
Очень неполный пример:
int getLine( char **retLine, FILE *file ) {
*retLine = NULL; // make sure we don't return garbage if error occurs
char *line = malloc( ... );
// do stuff with line, fill it up, etc.
...
// assign the final result to the output param
*retLine = line;
return length;
}
По сути, это проблема предпочтений. В противном случае необходимо выполнить разыменование. Например,
*line = malloc(...);
int length = strcspn( *line, ... );
и т.д.
Комментарии:
1. Спасибо за быстрый ответ. Мне еще не приходилось иметь дело с указателями на указатели, и я нахожу это немного запутанным. Сейчас я ищу несколько примеров кода…
2. @jobrien929: я добавил немного больше информации, которая может (или не может) помочь прояснить то, что я пытался сказать.
3. Это абсолютно так. Я только что заработал. Спасибо за помощь.
4. Мое предложение состояло бы в том, чтобы не просто заставить его работать. Это не то, почему @MarkWilkins потратил так много времени на его написание. Правильно поймите концепцию. (Игнорируйте это сообщение, если вы не просто использовали ответ, но и получили идею). Я пытался решить эту проблему, не используя указатель на указатель. Надеюсь, это будет то, что вы уже рассмотрели в классе.
Ответ №2:
«line» — это указатель, ваша функция getLine изменяет только значение копирования «line», она сама не изменяет указатель «line». В вашем случае вам следует попробовать
int main() {
// ..
line = malloc(sizeof(char) * LINELENGTH);
getLine(line, text);
// ..
}
Ответ №3:
Вам нужно изменить вашу getLine
функцию и способ ее вызова, чтобы она могла изменять указатель, который вы ей передаете. Прямо сейчас вы изменяете локальную копию только внутри функции, когда выделяете или перераспределяете строку.
Вместо
int getLine(char* line, FILE* file) {
вам нужно
int getLine(char **line, FILE *file) {
Затем внутри getLine
вам нужно разыменовать line
везде, где вы его используете, для работы с указателем, на который он указывает, например:
*line = malloc(sizeof(char) * LINELENGTH);
…и…
(*line)[length] = '';
…и аналогично для других применений line
.
Если вы можете избавиться от рекурсии, и strcat
вы также можете повысить эффективность своей функции, хотя вышеуказанные изменения должны заставить ее работать.
Ответ №4:
похоже, что у вас долгий путь, я бы сделал что-то вроде этого:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char line[1000];
int main()
{
FILE *file;
file = fopen("test.txt", "r");
fgets(line, sizeof(line), file);
printf("First line: %s", line);
memset(line, 0, strlen(line));
fclose(file);
return 0;
}
и вы могли бы включить это в функцию, если это необходимо.
или, если вы хотите, чтобы он отображал весь файл, вы могли бы просто сделать это:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char line[1000];
int main()
{
FILE *file;
file = fopen("test.txt", "r");
while(!feof(file))
{
fgets(line, sizeof(line), file);
printf(line);
memset(line, 0, strlen(line));
}
fclose(file);
return 0;
}
Ответ №5:
Учитывая, что вы не сделали указатель на указатели.
Я бы предложил изменить сигнатуру метода следующим образом:
char* getLine(int *len, FILE* file);
// instead of assigning the value to line like in your case, return it.
Использование было бы:
int main() {
.
.
.
int length;
char* line = NULL;
line = getLine(amp;len, text);
.
.
}
Кроме того, я вижу, что вы не используете возвращаемое значение, то есть длину строки в main() . Таким образом, вы могли бы полностью опустить поле длины и иметь определение метода, подобное этому:
char* getLine(FILE* file);