Отправка строкового указателя на функцию

#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);