2

I am still very new to the c language and I am playing with reading files for the first time. I had similar code to this code which used to run perfectly fine but now I am running into issues. I keep getting the error Segmentation fault (core dumped) every time I try to run this program.

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

struct student {
    char first[30];
    char last[30];
    char ssn[9];
};

void make_arrays() {
    FILE *fp = fopen("students.db", "r");
    fseek(fp, 0, SEEK_END);
    long size = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    long num_students = size / sizeof(struct student);
    printf("There are %ld students in the file", num_students);
    fclose(fp);
}

int main(int argc, char **argv[]) {
    make_arrays();
    return 0;
}
18
  • 6
    Check the return values of fopen, fseek Commented May 7, 2019 at 21:44
  • 1
    Seems like a good time to learn how to debug your programs. Especially how to use a debugger to catch crashes "in action" to see where in your code they happen, and then examine relevant variables and their values. Commented May 7, 2019 at 21:49
  • 3
    @Edenia What about it? But the file should be opened in binary mode. Commented May 7, 2019 at 21:56
  • 1
    It has already been said. Many times: Check return values of all functions, especially fopen. Here every function can return error. Commented May 7, 2019 at 21:58
  • 2
    That's a bad solution. Read e.g. this fopen reference, especially about the difference between the "r" and "a" options, and what happens if the file doesn't exist. That should help you understand why using the append option make it seem to work. Commented May 7, 2019 at 22:12

2 Answers 2

3

The segmentation fault might be caused by fopen failing to open the file.

You should always test for such failures and exit with an informative message.

Also note that, if the file is indeed binary, it should be open in binary mode to avoid end of line translation:

FILE *fp = fopen("students.db", "rb");

Also change the prototype for main to int main(int argc, char *argv[]) or simply int main(). There are too many stars in char **argv[].

Sign up to request clarification or add additional context in comments.

Comments

2

You don't have to mark my answer as accepted, just want to inspire people to write code so that it is readable and safe. Don't be lazy to write code like this where quality is a factor.

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

#include <sys/stat.h>

struct student /* Magic numbers everywhere */
{
    char first[30];
    char last[30];
    char ssn[9];

};




void* handle_nullptr_error (void* ptr, char *action, int code)
{
    if(ptr == NULL)
    {
        fprintf(stderr, "Failed to %s\n", action);

        exit(code);
    }

    printf("Succeeded to %s\n", action);

    return ptr;
}

int handle_nonzero_error (int val, char *action, int code)
{
    if(val != 0)
    {
        fprintf(stderr, "Failed to %s\n", action);

        exit(code);
    }

    printf("Succeeded to %s\n", action);

    return val;
}

int handle_negval_error (int val, char *action, int code)
{
    if(val < 0)
    {
        fprintf(stderr, "Failed to %s\n", action);

        exit(code);
    }

    printf("Succeeded to %s\n", action);

    return val;
}




/** This function is not guaranteed to be portable and work (but it will at least fail properly),
 * because not all systems and/or library implementations support `SEEK_END` in files
 * opened in text mode, as specified by @mode
 * Moreover, in binary mode it will behave in an undefined manner, because different systems
 * may store files data completely differently. In most cases it will succeed, just don't
 * write code that crashes if not.
 */
long int get_file_charcount (const char *filename, char* mode)
{
    FILE*       fp      = NULL;
    long int    fpSize  = 0L;

    /* Alignment for complicated function calls (for e.g where functions are passed as arguments) */
    fp = handle_nullptr_error       (fopen(filename, mode),     "open file.",           1);

    (void)handle_nonzero_error      (fseek(fp, 0, SEEK_END),    "seek end position.",   2);

    fpSize = handle_negval_error    (ftell(fp),                 "tell position.",       3);

    fclose(fp); /* - May fail, as well */

    return fpSize;
}

/** This function depends on POSIX headers and it is unix-conformant, although there are still
 * some exceptions.
 *
 * Note that the value returned is the length of the contents of the symbolic link,
 * and does not count any trailing null pads. The value is ought to be system-specific.
 */
_off64_t get_file_size (const char *filename)
{
    struct stat st = {0};

    (void)handle_negval_error(stat(filename, &st), "get file size.", (-1));

    return st.st_size;
}

/** A validation function should first determine whether file's size is
 * actually dividable by `sizeof(struct STUDENT_DESCRIPTION);`.
 *
 * Further more you can use `get_file_size()` as an alternative to
 * `get_file_charcount()`. In the latter case, make sure you to specify the
 * appropriate mode, "r" for text files and "rb" for binary files.
 */
void make_arrays ()
{
    long int    size            = get_file_charcount("myfile.txt", "r");
    long int    num_students    = size / sizeof(struct STUDENT_DESCRIPTION);

    printf("size of file: %ld\n", size);

    printf("There are %ld students in the file", num_students);
}




int main (void)
{
    make_arrays();

    return EXIT_SUCCESS;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.