Как правильно указать на член структуры в C? - PullRequest
0 голосов
/ 15 января 2019

Я создал очень простую функцию "csvread" в C, которая будет читать из файлов CSV, как подразумевается.(В этом случае для тестирования я немного отредактировал его, чтобы я мог записать фиктивные данные в файл и затем прочитать его).Я создал структуру для хранения сложных данных.Однако мой файл psuedo-csv содержит только реальную часть данных, которые мне нужны.Я хотел бы сохранить эти данные в массиве "data.real".Тем не менее, я не могу получить правильный синтаксис.(Хотя, по общему признанию это, вероятно, больше проблема понимания указателей полностью, а не только синтаксиса).Мы будем благодарны за любую помощь!

В приведенном ниже коде я знаю, что проблема заключается в следующем вызове функции:

 csvread("test.txt", &data->real);

Однако я пробовал несколько вариантов второго аргумента иэто единственное, что я могу придумать для этой компиляции.

Я получил свой код для работы, когда данные не являются структурой.Например, если данные были объявлены:

double data[10];

Итак, как вы можете (надеюсь) увидеть, у меня возникли проблемы с пониманием указателей на элементы структуры.

Вот мой код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SIZE 10

typedef struct Complex
{
   double real;
   double imag;
}complex;

void csvread(char *filename, double *data_out);

int main(void)
{
   complex *data;
   csvread("test.txt", &data->real);
   for(int i = 0; i<SIZE; i++)
   {
       printf("%f\n", data[i].real);
   }
}  

// This function reads csv files
void csvread(char *filename, double *data_out)
{
    FILE *file;
    char *no_commas; // character buffer to store strings without comma parse
    double *buffer; // character data converted to double type
    const char comma[2] = ",";
    char *csv;
    char *token;
    int file_size;
    int i = 0;

    // Read CSV file
    file = fopen(filename,"w+"); // change to "r" if read only
    if(file == NULL)
    {
        printf("\n The file requested cannot be found.");
        exit(1);
    }
    fprintf(file, "%s", "1.18493,0.68594,-7.65962,9.84941,10.34054,7.86571,0.04500,11.49505,-8.49410,-0.54901"); 
    fseek(file, 0, SEEK_SET); // return to beginning of the file

    // Find the file size in bytes 
    fseek(file, 0, SEEK_END); // go to end of file
    file_size = ftell(file);
    fseek(file, 0, SEEK_SET); //  return to beginning of file

    // Allocate buffer memory
    no_commas = malloc((file_size) * sizeof(char));
    buffer = malloc((file_size) * sizeof(double));

    if (no_commas == NULL || buffer == NULL)
    {
        printf("Failed to allocate memory when reading %s.\n\n", filename);
        exit(1);
    }

    while ((csv = fgets(no_commas, (file_size + 1), file)) != NULL) // fgets is used as file has no newline characters
    {
        // Remove Commas from string
        token = strtok(csv, comma);
        while (token != NULL)
        {
            //printf("%s\n", token); 
            buffer[i] = atof(strdup(token));
            token = strtok(NULL, comma);
            //printf("%f\n", buffer[i]); 
            i++;
        }
    }
    data_out = buffer;
    fclose(file);
    free(no_commas);
    free(buffer);
}

Вывод:

0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000
0.000000

Ожидаемый вывод:

 1.18493
 0.68594
-7.65962
 9.84941
10.34054
 7.86571
 0.04500
11.49505
-8.49410
-0.54901

РЕДАКТИРОВАНИЕ Спасибо всем за ваши комментарии и помощь!Я отметил ответ Джонни Моппа как наиболее полезный.Этот вопрос развился в большей степени о выделении памяти, чем предполагалось, что оказалось очень информативным и полезным.

Ответы [ 2 ]

0 голосов
/ 15 января 2019

Вы не можете выделить только часть real.Вам нужно выделить всю структуру - даже если вы просто используете часть real.Если бы вы использовали массив, он бы выглядел так:

complex data[10];
data[0].real = 1.0;
data[1].real = 2.0;
// etc..

Но вам придется динамически выделять массив complex, так как количество элементов в файле заранее неизвестно (я полагаю,).Вы можете выделять 1 complex объект за один раз, изменяя размер массива по мере продвижения.

// Prototype has changed to pointer-to-pointer complex
// Return value is the number of items read
int csvread(char *filename, complex **data);

int main(void)
{
   complex *data = NULL;
   int num_records = csvread("test.txt", &data);
   for(int i = 0; i < num_records; i++)
   {
       printf("%f\n", data[i].real);
   }
   free(data);
}  

// This function reads csv files
int csvread(char *filename, complex **data_out)
{
    // This will be used to avoid always having to dereference
    // the data_out parameter
    complex *array = NULL;
    int num_records = 0;

    while (1) {
        double value = // read a value from the file.....
        // If error or end of file break out of loop

        // Allocate space for another complex object
        array = realloc(array, sizeof(complex) * (num_records + 1));
        if (NULL == array) // handle error

        // Store just the real part
        array[num_records].real = value;
        // Also, you can initialize imag here but not required
        array[num_records].imag = 0;
        num_records += 1;
    }

    // Store and return
    *data_out = array;
    return num_records;
}

На основе обновленных комментариев: Вверху моей головы, вот один из способов обработки нескольких файлов,Сначала создайте 2 функции: одну для чтения всего содержимого файла и одну для замены strtok.Причина, по которой нам нужен второй, заключается в том, что strtok работает, вы можете использовать его только для одной строки за раз, а мы хотим использовать его для двух.Затем измените функцию readcsv на 2 имени файла.Это не проверено и могут иметь ошибки.

// Create a function that just opens and reads a file
char *load_file(const char *path) {
    // TODO:
    // Open the file and read entire contents
    // return string with contents

    // If path is NULL, must return NULL

    // Must return NULL if file does not exist
    // or read error
}

// Use this function instead of strok so you 
// can use on 2 string simultaneously
double get_next_value(char **string)
{
    char *start = *string;
    char *end   = *string;

    // Loop until comma or end of string
    while (*end && *end != ',') end++;
    // If comma, terminate and increment
    if (*end) *end++ = 0;
    // Update for next time
    *string = end;
    return atof(start);
}

// This function reads csv files
int csvread(char *real_filename, char *imag_filename, complex **data_out)
{
    // This will be used to avoid always having to dereference
    // the data_out parameter
    complex *array = NULL;
    int num_records = 0;

    // Load each file into a string. May be NULL
    char *real_data_orig = load_file(real_filename);
    char *imag_data_orig = load_file(imag_filename);

    // Temporary copies of the pointers. Keep the originals
    // to free() later. These will be modified
    char *real_data = real_data_orig;
    char *imag_data = imag_data_orig;

    while (1) {
        // Check for data. Make sure pointer is not
        // NULL and it is still pointing to something
        // that is not '\0'
        bool has_real = real_data && *real_data;
        bool has_imag = imag_data && *imag_data;

        // No data? Done.
        if (!has_real && !has_imag) break;

        // Allocate space for another complex object
        array = realloc(array, sizeof(complex) * (num_records + 1));
        if (NULL == array) // handle error

        // Store the real part (if there is one)
        if (has_real) {
            array[num_records].real = get_next_value(&real_data);
        }
        // Store the imag part (if there is one)
        if (has_imag) {
            array[num_records].imag = get_next_value(&imag_data);
        }
        num_records += 1;
    }

    // Free the file contents
    free(real_data_orig);
    free(imag_data_orig);

    // Store and return
    *data_out = array;
    return num_records;
}
0 голосов
/ 15 января 2019

В Java это будет выглядеть как NullPointerException , вы не получаете такого рода ошибки в C, но это также связано с ответственностью. Отсылка нулевого указателя в C (как вы это сделали) может отвечать разными способами. Как уже было сказано в комментариях, вам нужно выделить указатель data.

complex *data = malloc(sizeof(complex));

И вам также следует освободить память, выделенную с помощью malloc, поэтому в конце кода перед возвратом состояния вашей программы в ОС (чего вы не делали) вы должны сделать:

free(data);

Я не уверен, как работает ваш csvread, но он может не работать, используя не что иное, как массив сложных структур. Приведенный ниже код выделяет 10 комплексных чисел, инициализирующих их все, внутри функции csvread вам, возможно, придется внести некоторые изменения, чтобы перебрать их, поскольку вы показали, что с простыми объявлениями массивов ваш код работает.

complex *data = calloc(10, sizeof(complex));

Приведенный выше код выделяет 10 сложных структур , и указатель указывает на первую выделенную. Для их перебора вы можете индексировать их по номерам (например, data[0].real = 4) или использовать арифметику указателей. У меня сильное чувство, что вам придется изменить способ итерации указателя data_out внутри функции csvread.

...