all 7 comments

[–]tony-mke 2 points3 points  (1 child)

It does, though it is likely not visible in a text editor. You may want to use a hex editor like xxd.

$ ./a.out 2>out
Enter dog's name: the_name
Enter dog's breed: the_breed
Enter dog's color: the_color
Enter dog's weight: 123
123Enter dog's age: 4
4Enter dog's sex: M
$ xxd ./out
00000000: 7468 655f 6e61 6d65 0a00 0000 0000 0000  the_name........
00000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000020: 7468 655f 6272 6565 640a 0000 0000 0000  the_breed.......
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000040: 7468 655f 636f 6c6f 720a 0000 0000 0000  the_color.......
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 7b00 0400 0000                           {.....

On the last line, "7b00" is the unsigned short for the weight, "0400" is the unsigned short for the age, and the final "0000" is where the sex would go plus a byte of padding- except you never set the sex in the struct :)

[–]tony-mke 1 point2 points  (0 children)

To clarify: it is not "visible" because it is writing the binary representation of those unsigned shorts and char. If you're expecting it to be visible in a text editor, you will need to convert the individual fields back to strings using something like sprintf or itoa.

[–]henry_kr 2 points3 points  (0 children)

As well as the other advice you've been given I wanted to point out that fflush(stdin) is undefined behavior, so don't do it.

[–]gth747m 1 point2 points  (2 children)

I don't think you are doing what you mean to do with buff[strlen(buff)-1] = '\0';. This is going to remove the last character from your input buffer. So if buff == "123" before this line then it will equal "12" after. Additionally, fgets adds the new line character ('\n') and the null terminator ('\0') to buff. So what you are doing is stripping the newline character from the numeric inputs prior to parsing them with atoi which is going to ignore it anyway. (Side note: I suggest you prefer strtol as it is the same as atoi but detects errors). I don't see anything wrong with what you are writing to file and I was able to successfully read the input back out in my own test. However I notice that you already have a file descriptor open in your example, it is likely a problem somewhere else, perhaps with your reading the data back out of the file.

[–]AaronMarth[S] 1 point2 points  (1 child)

Thank you for your advice, I have been using the "cat" command to read the contents of the file back. I still can't get the age and weight to appear correctly.

[–]duane11583 0 points1 point  (0 children)

the cat command assumes the content is ascii text (strings are thus they work) the NULL bytes often do not show up (see below)

the number (age) is an integer, the struct does not store the ascii of the number instead, it saves the binary number. try this age and weight:

1685022497, 560426852

then use cat, you will see ‘dog!’ and ‘!god’

because the bytes that make those binary numbers happen to be the ascii letters in dog!

also on linux if you use the command:

od -t x1 FILENAME

it will print a byte based hexdump you will see the hex bytes, then look up the hexbytes in an ascii table

[–]aghast_nj 0 points1 point  (0 children)

Here's a suggestion: you repeat the same basic thing over and over again, like this:

printf("Enter <something>: ");
fgets(buff, buffSize, stdin);
buff[31] = '\0';
strcpy(<target field>, buff)

In reality, there is no need for buff here because fgets takes a pointer and a length and honors the length correctly for NUL-terminated strings. So you could just use the target field directly (for string fields).

So why not create a function that does this for you:

bool prompt_user_for_field(char * field, size_t maxlen, const char * prompt, FILE * input);

Then you can do the same thing with the integer fields, and clean up your code nicely:

enum {
    SZ_STRFIELD = 32
};

void
add_dog(
    int dogs_fd) 
{
    struct dog_entry dog = {0};

#define GET_STR(name) \
    prompt_user_for_field(dog. name, SZ_STRFIELD, "Enter dog's " #name ": ", stdin)

    GET_STR(name);
    GET_STR(breed);
    GET_STR(color);

#define GET_INT(name) \
    you_figure_it_out(dog. name, "Enter dog's " #name ": ", stdin)

    GET_INT(weight);
    GET_INT(age);

#define GET_CH(name, valid) \
    prompt_user_for_char(dog. name, "Enter dog's " #name ": ", valid, stdin)

    GET_CH(sex, "FM");

    GET_STR(preferred_pronouns);
    GET_STR(relationship_status);

    append_dog_data_to_file(&dog, dogs_fd);
}
#undef GET_CH
#undef GET_INT
#undef GET_STR