Hi, i'm just mixing file handling and struct types and pointers to learn, but i'm a little stuck on this.
I might be doing something awfull i can tell, sorry for that. Can anyone help? What's the best way to pass

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

struct persona {
    char nombre[15];
    char apellido[15];
    unsigned edad;
};

int edadMedia(struct persona *);

int main()
{
    char *filename = "C:\\Usuarios\\Administrador\\Escritorio\\C\\test.txt";
    printf("Accediendo a %s:\n", filename);
    FILE *file = fopen(filename, "r");

    struct persona nombres[10];
    int counter = 0, contador = 0;
    char *linea;
    linea = malloc(15 * sizeof(char));

    while((fgets(linea, 15, file)) && *linea != EOF)
    {
        printf("Reading line %i -> %s", counter, linea);
        if(linea[0] == '#') continue;
        switch (counter)
        {
            case 0:
                strcpy(nombres[contador].nombre, linea);
                counter++;
                break;
            case 1:
                strcpy(nombres[contador].apellido, linea);
                counter++;
                break;
            case 2:
                nombres[contador].edad = (unsigned int) atoi(linea);
                counter = 0;
                contador++;
                break;
        }
    }
    printf("\n\n\t[i] Detectados %i miembros!\n", contador);
    printf("\t[i] Edad media %i aƱos", edadMedia(nombres));
    return 0;
}

int edadMedia(struct persona * nombres) { //code still to go here }

Use nombres the same way in edadMedia as you did in main (ie. as an array). However, I strongly recommend that you pass the size of the array as well so that you can avoid bogus indexes in edadMedia.

A few notes:

>printf("Accediendo a %s:\n", filename);
>FILE *file = fopen(filename, "r");

This is valid only in C99.

>char *linea;
>linea = malloc(15 * sizeof(char));

You can combine this into a single line, just like you did with fopen. Also note that sizeof(char) is absolutely guaranteed to be 1 in all cases. You can safely remove that, or replace it with sizeof *linea to make upgrades from char to wchar_t easier:

char *linea = malloc(15); /* Okay for char */
char *linea = malloc(15 * sizeof *linea); /* Future-proofed*/

>while((fgets(linea, 15, file)) && *linea != EOF)
Good luck getting a character with the value of EOF out of a call to fgets. I'd recommend changing that condition to *linea != '\n' . My guess is that you're looking for an empty line, but if fgets succeeds (the first part of your condition), there's going to be at least one character present (the newline).

Use nombres the same way in edadMedia as you did in main (ie. as an array). However, I strongly recommend that you pass the size of the array as well so that you can avoid bogus indexes in edadMedia.

But how can i do that? If i say int edadMedia(struct persona); , i'm not saying it's going to be an array right?
And here int edadMedia(struct persona * nombres) { } i wanted to create a pointer to an array of structs, so i can freely pass it to whatever function i want to (i.e. edadMedia()). And i dont know how to do it.

A few notes:

>printf("Accediendo a %s:\n", filename);
>FILE *file = fopen(filename, "r");

This is valid only in C99.

That's what i'm using (-std=C99), shouldn't i use C99 even being more modern than C89? (that's something ive never understood)

>while((fgets(linea, 15, file)) && *linea != EOF)
Good luck getting a character with the value of EOF out of a call to fgets. I'd recommend changing that condition to *linea != '\n' . My guess is that you're looking for an empty line, but if fgets succeeds (the first part of your condition), there's going to be at least one character present (the newline).

Isn't EOF the standard way to say that's the end of the file?

>And here int edadMedia(struct persona * nombres) { }
>i wanted to create a pointer to an array of structs

That close to what you said, and personally I think it's what you really meant. When you pass an array to a function, it becomes a pointer to the first element of the array. Therefore, an array and a pointer to the first element of an array are equivalent as function parameters.

In summary: nombres is an array. You pass nombres to edadMedia which is a function that expects an array. Therefore, all you need to do is use nombres inside edadMedia as if it were an array and you'll be solid.

>That's what i'm using (-std=C99)
And that possibility is why I didn't say you were wrong. :)

>shouldn't i use C99 even being more modern than C89?
C99 still isn't widely implemented and despite being the current standard, is far less portable than C89. That's why I generally recommend using an intersection of C89 and C99. That way your code is portable under both standards.

>Isn't EOF the standard way to say that's the end of the file?
1) EOF is not a character in the file.
2) fgets never gives you the EOF macro, in any form.

You're thinking of fgetc, which returns EOF when it detects either an error or end-of-file:

int ch;

while ( ( ch = fgetc ( in ) ) != EOF ) {
  /* ... */
}

It's a subtle difference, seeing as how fgetc returns EOF as well as the characters it extracts from the stream. But one is an "error" code and the other is valid data. I actually wrote a super simple stdio implementation a while back to help people see what goes on under the hood. Here's the implementation of fgetc:

int fgetc ( FILE *in )
{
  /* The stream must be open and ready for reading */
  if ( !( in->_flag & _OPEN ) || in->_flag & _WRITE )
    return EOF;

  /* Reset the stream to read mode */
  in->_flag &= ~_WRITE;
  in->_flag |= _READ;

  /* Try to pull from the push back stack first */
  if ( _deque_ready ( in->_unget ) && !_deque_empty ( in->_unget ) )
    return _deque_popf ( in->_unget );
  else {
    /* Always fill for non-buffered and empty streams */
    if ( in->_flag & _NBF || _deque_empty ( in->_buf ) )
    {
      if ( _intern_fill ( in ) != 0 )
        return EOF;
    }

    return _deque_popb ( in->_buf );
  }
}

You really don't need to worry about anything except for the fact that EOF and a character extracted from one of the buffers are separate and distinct.

And here's the implementation of fgets:

static char *_getline ( char *s, int n, FILE *in, int store_nl )
{
  char *p = s;
  int ch;

  while ( --n > 0 ) {
    ch = fgetc ( in );

    if ( ch == EOF )
      break;
    else if ( ch == '\n' ) {
      if ( store_nl )
        *p++ = '\n';
      break;
    }
    else
      *p++ = (char)ch;
  }

  if ( p != s && !ferror ( in ) ) {
    *p = '\0';
    return s;
  }
  else
    return NULL;
}

char *fgets ( char *s, int n, FILE *in )
{
  return _getline ( s, n, in, 1 );
}

Note that EOF never leaves fgets. Any "error" detection is done through one of the following:

  • A NULL return value.
  • errno
  • The feof function.
  • The ferror function.
  • The presence or absence of a newline character in the buffer.
This article has been dead for over six months. Start a new discussion instead.