This is one of the most fundamental things you must do in any language:
- read data from a file, and
 
- parse that data into needed information.
 
In your case you have whitespace separate names and an ID in your input file. While you can use fscanf directly, it is horribly fragile. If a single line does not match your format string, your read will fail with a matching failure, character extraction from the stream ceases, and you are then left with the remainder of the line in your input buffer to deal with before you can move forward.
For that reason, a better approach is the read each line into a buffer with fgets and a sufficiently sized buffer (or using POSIX getline) to consume an entire line of input with every read. The you can parse the needed information from the line stored in the buffer without affecting your read operation. This also provides the benefit of being able to independently validate your (1) read, and (2) the parse of information.
There are many ways to parse the needed information from the buffer. You can use sscanf reading from the buffer (much like you would have used fscanf on the input itself), you can walk-a-pair-of-pointers down the buffer, bracketing each word and then memcpy and nul-terminate, you can use strtok (but it modifies the original buffer), or you can use a combination of strspn and strcspn to bracket each word similar to walking the pointers.
In your case let's just use sscanf since for a fixed format, it is just as easy. To store your 3-strings worth of name, last, id, create a struct with those members, then you can create an array of struct (we will leave the dynamic array, or linked-list for later), and you can store all names and IDs you read, for example:
#include <stdio.h>
#define MAXID  16   /* if you need a constant, #define one (or more) */
#define MAXNM  32
#define MAXPN 128
#define MAXC 1024
typedef struct {
    char name[MAXNM],
         last[MAXNM],
         id[MAXID];
} typeperson;
You now have a struct (with a convenient typedef to typeperson you can use to create an array of struct (with each array initialized all zero), e.g.
int main (int argc, char **argv) {
    char buf[MAXC];
    size_t n = 0;
    typeperson person[MAXPN] = {{"", "", ""}};
You now have an array of MAXPN (128) person to fill. Now simply open your file using the name provided as the first argument to your program (or read from stdin by default if no argument is given) and validate the file is open for reading:
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }
With your file open and validated, you can now read each line into buf and then parse name, last, id from buf using sscanf (all conversion specifiers except for "%c" and "%[..]" (and technically "%n", but that doesn't extract from the buffer) skip all leading whitespace allowing you to separate your name, last, id regardless of the amount of whitespace between them:
    /* protect array bounds and read each line into struct */
    while (n < MAXPN && fgets (buf, MAXC, fp)) {
        if (sscanf (buf, "%s %s %s", 
                    person[n].name, person[n].last, person[n].id) == 3)
        n++;
    }
(note: the test of n < MAXPN that protects your array bounds and prevents you from writing more elements than you have storage for)
What happens if the line has the wrong format? How do you recover? Simple. By consuming a line with each read, any line that doesn't match your sscanf format string is quietly ignore and does not cause you any problem.
All that remains is closing the file and using your data in any way you need. Putting it together in a short example, you could do:
#include <stdio.h>
#define MAXID  16   /* if you need a constant, #define one (or more) */
#define MAXNM  32
#define MAXPN 128
#define MAXC 1024
typedef struct {
    char name[MAXNM],
         last[MAXNM],
         id[MAXID];
} typeperson;
int main (int argc, char **argv) {
    char buf[MAXC];
    size_t n = 0;
    typeperson person[MAXPN] = {{"", "", ""}};
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }
    /* protect array bounds and read each line into struct */
    while (n < MAXPN && fgets (buf, MAXC, fp)) {
        if (sscanf (buf, "%s %s %s", 
                    person[n].name, person[n].last, person[n].id) == 3)
        n++;
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */
    for (size_t i = 0; i < n; i++)  /* output the resutls */
        printf ("person[%3zu] : %-20s %-20s %s\n",
                i, person[i].name, person[i].last, person[i].id);
}
Example Input File
With an intentional line that does not match the format (e.g. "..."):
$ cat dat/peopleid.txt
George      Washington          1
John        Adams               2
Thomas      Jefferson           3
James       Madison             4
...
Royal       Embarrasment        45
Example Use/Output
$ ./bin/struct_person < dat/peopleid.txt
person[  0] : George               Washington           1
person[  1] : John                 Adams                2
person[  2] : Thomas               Jefferson            3
person[  3] : James                Madison              4
person[  4] : Royal                Embarrasment         45
Look things over and let me know if you have any further questions.