Using function to find max value from a file - c

I am fairly new to programming so bear with me.
I'm trying to create some code that would read a text file that contains 3 numbers. I want to use a created function to find the max number. I get no errors when compiling but when I run the code the program crashes (no message recieved or anything, just simply file.exe has stopped working).
I would greatly appreciate help in tackling this problem.
Also I would like to avoid using arrays.
#include <stdio.h>
#include <stdlib.h>
int max(int a,int b,int c);
int main()
{
FILE *fpointer;
int a, b, c;
int maxNumber = max(a,b,c);
fpointer = fopen("marks.txt","r");
while(fscanf(fpointer,"%d %d %d",a,b,c)!=EOF) {
printf("%d",max(a,b,c));
}
fclose(fpointer);
return 0;
}
int max(int a,int b,int c){
if((a>b)&&(a>c))
return a;
if((b>a)&&(b>c))
return b;
if((c>a)&&(c>b))
return c;
}

I am fairly new to programming so bear with me.
OK, and we will, but no matter how hard we try, we cannot fix the Undefined Behavior you invoke with:
int maxNumber = max(a,b,c);
The values of a, b & c have not been initialized at the time you call max. This invokes Undefined Behavior. (attempting to access the value of an uninitialized object).
Second, also easily leading to Undefined Behavior is the failure to validate that fopen succeeds, and failing to validate that fscanf succeeds. Testing that fscanf (...) != EOF does not tell you anything about whether valid conversions actually took place. The return for fscanf is the successful number of conversions that took place -- based upon the number of conversion specifiers present in the format string (e.g. "%d %d %d" contains 3 conversion specifiers). So to validate that a, b & c all contain values, you must compare fscanf (...) == 3.
Putting those pieces together, you could do something similar to the following:
#include <stdio.h>
int max (int a, int b, int c);
int main (int argc, char **argv) {
int a, b, c, n = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fscanf (fp, "%d %d %d", &a, &b, &c) == 3)
printf ("line[%2d] : %d\n", n++, max (a, b, c));
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
int max (int a, int b, int c)
{
int x = a > b ? a : b,
y = a > c ? a : c;
return x > y ? x : y;
}
Example Input
$ cat int3x20.txt
21 61 78
94 7 87
74 1 86
79 80 50
35 8 96
17 82 42
83 40 61
78 71 88
62 20 51
58 2 11
32 23 73
42 18 80
61 92 14
79 3 26
30 70 67
26 88 49
1 3 89
62 81 93
50 75 13
33 33 47
Example Use/Output
$ ./bin/maxof3 <dat/int3x20.txt
line[ 0] : 78
line[ 1] : 94
line[ 2] : 86
line[ 3] : 80
line[ 4] : 96
line[ 5] : 82
line[ 6] : 83
line[ 7] : 88
line[ 8] : 62
line[ 9] : 58
line[10] : 73
line[11] : 80
line[12] : 92
line[13] : 79
line[14] : 70
line[15] : 88
line[16] : 89
line[17] : 93
line[18] : 75
line[19] : 47
Look things over and let me know if you have further questions.

fscanf uses pointer arguments and you are passing the variable values. When the function tries to access the address a (which is in fact an uninitialized variable), it causes a segmentation fault (you are trying to access an invalid memory address) and your program crashes and exits.
You should instead pass the variable addresses to the pointer argument (e.g. &a - the address of variable a) , thus it will access a valid memory address.
while(fscanf(fpointer,"%d %d %d",&a,&b,&c)!=EOF) {
Other undefined behaviors might be avoided like initializing the variables and checking the return values correctly as the answer of #DavidC.Rankin describes in details.

All need to make it work is adding an & at this line...
while(fscanf(fpointer,"%d %d %d",&a,&b,&c)!=EOF) { ... }

Related

Why does my c code not add the correct null zero at the end like it is supposed to and keeps printing out code?

I do not know why my code does not seem to be working properly. I am reading from a file, and grabbing each line and from there I am using my own function to try and break down each of the lines and add them to character arrays in a structure and then add those structures to an array. But for whatever reason, when I am trying to indivudually print out the individual values for all of the information it keeps printing out all of it. From what I am seeing, for whatever reason even though my function strsub is supposed to add a '\0' at the end, it does not seem to be doing that. So every time I pass in the pointer to the begging of each of the character variables it does not stop until the end of the whole structure so it starts by printing out the whole string and then prints out less and less. Is that the problem that I really have or am I missing something else?
This is my code so far. I first just tried creating a struct and filling the array with each pass, but unfortunantly I had the same issue.
#define _CRT_SECURE_NO_WARNINGS // Since I want to strictly use ANSI C and not Microsoft C without getting the warning message, I'm adding this line of code before I include header files.
#include <stdio.h> // "#include" includes the contents of another file, commonly called header file, into the source code file.
#include <string.h>
#define MAX 100
FILE *fp, *csit;
void strsub(char buf[], char sub[], int start, int end);
void printArray(struct trainCartrain[]);
struct trainCar {
char car[10];
char type[2];
char weight[6];
char length[3];
char horsepower[3];
char numberInTrain[4];
};
int main() {
struct trainCar ar[7];
struct trainCar train;
// test and open input file and output file.;
if (!(fp = fopen("train.txt", "r"))) {
printf("train.txt could not be opened for input.");
exit(1);
}
if (!(csit = fopen("csit.txt", "w"))) {
printf("csit.txt could not be opened for output.");
exit(1);
}
int i = 0;
char buf[MAX];
while (!feof(fp)) {
fgets(buf, MAX, fp);
strsub(buf, train.car, 0, 9);
strsub(buf, train.type, 10, 11);
strsub(buf, train.weight, 12, 17);
strsub(buf, train.length, 18, 20);
strsub(buf, train.horsepower, 21, 23);
strsub(buf, train.numberInTrain, 24, 27);
printf("%s", train.car);
printf("%s", train.type);
ar[i] = train;
i++;
}
printArray(ar);
fclose(csit);
fclose(fp);
return 0;
}
void strsub(char buf[], char sub[], int start, int end) { //strsub () grabs a substring, sub, from a string, buf, given the start and end index within the string.
int i, j;
for (j = 0, i = start; i <= end; i++, j++) {
sub[j] = buf[i];
}
sub[j] = '\0';
//end with the null terminator character that signifies the end of a string.
}
My file is small and simple, textfile
Boxcar D 44000 55 16 45
Hopper B 23000 62 18 33
Tanker G 15000 45 30 12
Autocar A 30000 37 23 6
Livestock L 56500 50 18 19
Coalcar C 49300 53 22 100
Flatcar F 18000 66 15 25
and what it prints out is
Boxcar D 44000 55 16 45
D 44000 55 16 45
44000 55 16 45
55 16 45
16 45
45
Hopper B 23000 62 18 33
B 23000 62 18 33
23000 62 18 33
62 18 33
18 33
33
Tanker G 15000 45 30 12
G 15000 45 30 12
15000 45 30 12
45 30 12
30 12
12
Autocar A 30000 37 23 6
A 30000 37 23 6
30000 37 23 6
37 23 6
23 6
6
Livestock L 56500 50 18 19
L 56500 50 18 19
56500 50 18 19
50 18 19
18 19
19
Coalcar C 49300 53 22 100
Flatcar F 18000 66 15 25C 49300 53 22 100
Flatcar F 18000 66 15 2549300 53 22 100
Flatcar F 18000 66 15 2553 22 100
Flatcar F 18000 66 15 2522 100
Flatcar F 18000 66 15 25100
Flatcar F 18000 66 15 25Flatcar F 18000 66 15 25F 18000 66 15 2518000 66 15 2566 15 2515 2525
can someone please explain what I am doing wrong? I do have to use this function strsub for my class too.
I am just trying to get it to print out the individual charachter data and not the whole string each time. I think it is an issue with the terminating zero at the end and when I tried debugging it does not seem to be adding that for some reason. I don't know why though, if that is the problem.
strsub(buf, train.car, 0, 9); accesses train.car with index 0 till 9 in the loop and then index 10 outside, but that's already out of bounds for a char car[10];.
Solution:
Increase the size of all of your arrays by 1 to have space for the 0-terminator of the string.
Also have a look at Why is “while( !feof(file) )” always wrong? . It is not related to your problem, but you might run into that problem in the next minutes.
Instead of
while (!feof(fp)) {
fgets(buf, MAX, fp);
....
}
use
while (fgets(buf, MAX, fp)) {
....
}
You missed a space in void printArray(struct trainCartrain[]);. It should be void printArray(struct trainCar train[]); and moved to after the definition of struct trainCar.
You also have to #include <stdlib.h> to use exit(1);

First function in code executing, but code following the first function is not executed

My code executes the first function, but anything after the first function is not executed. I compiled using gcc -o -Wall and received no error messages.
when I tried to execute the code, I receive the execution at the bottom. I am not sure why the program will not execute more of the code. In the first function I can add more to the function to make work but it won't touch the other functions.
1 #include <stdio.h>
2 //functions prototypes
3 void compute_discount(void);
4 int print_results(void);
5
6
7 //defined Gloabal var
8 double Mdisc;
9 double Cost_of_purchase;
10 double DiscTot;
11 double Sales_tax;
12 double Total_price;
13 char military;
14
15 int main (void) {
16 //declare variables
17
18 //Cost of purchase
19 printf("Cost of purchase?\t\t$");
20 scanf ("%lf",&Cost_of_purchase);
21
22 //Military?
23 printf("In military (y or n)?\t\t\n");
24 scanf("%s",&military);
25
26 }
27
28
29 //function to compute discount
30 void compute_discount(void){
31
32 //compute military discount
33 switch(military){
34 case 'y':
35 case 'Y':
36 if(Cost_of_purchase > 150) {
37 Mdisc = .15 * Cost_of_purchase;
38 } else if (Cost_of_purchase < 150) {
39 Mdisc = .10 * Cost_of_purchase;
40 }
41 break;
42 case 'n':
43 case 'N':
44 Mdisc = 0;
45 break;
46 default:
47 printf("Error:bad input");
48 }
49
50 //cost minus military discount
51 DiscTot = Cost_of_purchase - Mdisc;
52 //sales tax
53 Sales_tax = .05 * DiscTot;
54 //Total Calculated
55 Total_price = DiscTot + Sales_tax;
56
57 printf("maybe this is the problem%f",Mdisc);
58 }
59
60 //function to print results
61 int print_results(void){
62
63 //if input is n N y Y then use below, this is not dependant on if military only if the letter is accepted
64 switch(military){
65 case 'y':
66 case 'Y':
67 case 'n':
68 case 'N':
69 printf("Military discount (15%%): \t\t$%f", Mdisc);
70 printf("Discounted total: \t\t$%f", DiscTot);
71 printf("Sales tax (5%%): \t\t$%f", Sales_tax);
72 printf("Total: \t\t$%f", Total_price);
73 break;
74 }
75 return(0);
76 }
Result of execution:
[p18d541#csci112 lab1]$ gcc -o lab1 -Wall lab1.c
[p18d541#csci112 lab1]$ ./lab1
Cost of purchase? $500
In military (y or n)?
y
[p18d541#csci112 lab1]$
I am wondering what more needs to be done to fix this issue, or what ways I can use to make the program execute those functions other than int main().
On this line:
scanf("%s",&military);
You're passing the address of a single char. While the %s format specifier expects a char * parameter which is what is being passed, it also expects it to point to the first byte of an array so that it may store a string. Because of this, the function attempts to write more than one byte when only one byte is available. Writing past the bounds of an object triggers undefined behavior.
This can be fixed by using the %c format specifier which is for reading a single char. You'll also need to add a space before it to consume the newline that the prior scanf left in the input buffer:
scanf(" %c",&military);
You also never called compute_discount and print_results, so you need to do that after reading the user's input.
int main (void)
{
printf("Cost of purchase?\t\t$");
scanf ("%lf",&Cost_of_purchase);
//Military?
printf("In military (y or n)?\t\t\n");
scanf(" %c",&military);
compute_discount();
print_results();
return 0;
}
You defined functions you want to use, but you are not calling these functions at all. Imagine a scenario, where you wanted to define a function to sum two numbers, int a and int b, and then call the function to count the sum. It would be:
int sum(int a, int b) {
return a + b;
}
int main() {
int a = 5;
int b = 10;
printf("result: %d\n", sum(a, b));
// ^^^^^^^^^
// call sum function
}
It means that after defining the function, you need to use it, so your main would be:
int main (void) {
printf("Cost of purchase?\t\t$");
scanf ("%lf",&Cost_of_purchase);
printf("In military (y or n)?\t\t\n");
scanf("%s",&military);
// Finally call your defined functions:
compute_discount();
print_results();
}
The other functions (besides main) are not executed because they are never called. main is called automatically when you run the program, but any other functions must be explicitly called. Add calls to compute_discount and print_results at the bottom of main, following the second scanf.
BTW, your second scanfis particularly dangerous, because you are using a "%s" format to read a single character... what happens if the user types in "yes"... where do the 'e' and the 's' go?

Having issues iterating through machine code

I'm attempting to recreate the wc command in c and having issues getting the proper number of words in any file containing machine code (core files or compiled c). The number of logged words always comes up around 90% short of the amount returned by wc.
For reference here is the project info
Compile statement
gcc -ggdb wordCount.c -o wordCount -std=c99
wordCount.c
/*
* Author(s) - Colin McGrath
* Description - Lab 3 - WC LINUX
* Date - January 28, 2015
*/
#include<stdio.h>
#include<string.h>
#include<dirent.h>
#include<sys/stat.h>
#include<ctype.h>
struct counterStruct {
int newlines;
int words;
int bt;
};
typedef struct counterStruct ct;
ct totals = {0};
struct stat st;
void wc(ct counter, char *arg)
{
printf("%6lu %6lu %6lu %s\n", counter.newlines, counter.words, counter.bt, arg);
}
void process(char *arg)
{
lstat(arg, &st);
if (S_ISDIR(st.st_mode))
{
char message[4056] = "wc: ";
strcat(message, arg);
strcat(message, ": Is a directory\n");
printf(message);
ct counter = {0};
wc(counter, arg);
}
else if (S_ISREG(st.st_mode))
{
FILE *file;
file = fopen(arg, "r");
ct currentCount = {0};
if (file != NULL)
{
char holder[65536];
while (fgets(holder, 65536, file) != NULL)
{
totals.newlines++;
currentCount.newlines++;
int c = 0;
for (int i=0; i<strlen(holder); i++)
{
if (isspace(holder[i]))
{
if (c != 0)
{
totals.words++;
currentCount.words++;
c = 0;
}
}
else
c = 1;
}
}
}
currentCount.bt = st.st_size;
totals.bt = totals.bt + st.st_size;
wc(currentCount, arg);
}
}
int main(int argc, char *argv[])
{
if (argc > 1)
{
for (int i=1; i<argc; i++)
{
//printf("%s\n", argv[i]);
process(argv[i]);
}
}
wc(totals, "total");
return 0;
}
Sample wc output:
135 742 360448 /home/cpmcgrat/53/labs/lab-2/core.22321
231 1189 192512 /home/cpmcgrat/53/labs/lab-2/core.26554
5372 40960 365441 /home/cpmcgrat/53/labs/lab-2/file
24 224 12494 /home/cpmcgrat/53/labs/lab-2/frequency
45 116 869 /home/cpmcgrat/53/labs/lab-2/frequency.c
5372 40960 365441 /home/cpmcgrat/53/labs/lab-2/lineIn
12 50 1013 /home/cpmcgrat/53/labs/lab-2/lineIn2
0 0 0 /home/cpmcgrat/53/labs/lab-2/lineOut
39 247 11225 /home/cpmcgrat/53/labs/lab-2/parseURL
138 318 2151 /home/cpmcgrat/53/labs/lab-2/parseURL.c
41 230 10942 /home/cpmcgrat/53/labs/lab-2/roman
66 162 1164 /home/cpmcgrat/53/labs/lab-2/roman.c
13 13 83 /home/cpmcgrat/53/labs/lab-2/romanIn
13 39 169 /home/cpmcgrat/53/labs/lab-2/romanOut
7 6 287 /home/cpmcgrat/53/labs/lab-2/URLs
11508 85256 1324239 total
Sample rebuild output (./wordCount):
139 76 360448 /home/cpmcgrat/53/labs/lab-2/core.22321
233 493 192512 /home/cpmcgrat/53/labs/lab-2/core.26554
5372 40960 365441 /home/cpmcgrat/53/labs/lab-2/file
25 3 12494 /home/cpmcgrat/53/labs/lab-2/frequency
45 116 869 /home/cpmcgrat/53/labs/lab-2/frequency.c
5372 40960 365441 /home/cpmcgrat/53/labs/lab-2/lineIn
12 50 1013 /home/cpmcgrat/53/labs/lab-2/lineIn2
0 0 0 /home/cpmcgrat/53/labs/lab-2/lineOut
40 6 11225 /home/cpmcgrat/53/labs/lab-2/parseURL
138 318 2151 /home/cpmcgrat/53/labs/lab-2/parseURL.c
42 3 10942 /home/cpmcgrat/53/labs/lab-2/roman
66 162 1164 /home/cpmcgrat/53/labs/lab-2/roman.c
13 13 83 /home/cpmcgrat/53/labs/lab-2/romanIn
13 39 169 /home/cpmcgrat/53/labs/lab-2/romanOut
7 6 287 /home/cpmcgrat/53/labs/lab-2/URLs
11517 83205 1324239 total
Notice the difference in the word count (second int) from the first two files (core files) as well as the roman file and parseURL files (machine code, no extension).
C strings do not store their length. They are terminated by a single NUL (0) byte.
Consequently, strlen needs to scan the entire string, character by character, until it reaches the NUL. That makes this:
for (int i=0; i<strlen(holder); i++)
desperately inefficient: for every character in holder, it needs to count all the characters in holder in order to test whether i is still in range. That transforms a simple linear Θ(N) algorithm into an Θ(N2) cycle-burner.
But in this case, it also produces the wrong result, since binary files typically include lots of NUL characters. Since strlen will actually tell you where the first NUL is, rather than how long the "line" is, you'll end up skipping a lot of bytes in the file. (On the bright side, that makes the scan quadratically faster, but computing the wrong result more rapidly is not really a win.)
You cannot use fgets to read binary files because the fgets interface doesn't tell you how much it read. You can use the Posix 2008 getline interface instead, or you can do binary input with fread, which is more efficient but will force you to count newlines yourself. (Not the worst thing in the world; you seem to be getting that count wrong, too.)
Or, of course, you could read the file one character at a time with fgetc. For a school exercise, that's not a bad solution; the resulting code is easy to write and understand, and typical implementations of fgetc are more efficient than the FUD would indicate.

bufbomb stack overflow failed

I'm using bufbomb.c to do some buffer overflow attack experimenting.
I successfully used gdb to debug the code. Howeverer; when I run the program directly, I get a "Segmentation fault (core dumped)" when I enter the characters to try the attack.
I used gcc (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1. to build the following.
//bufbomb.c
/* Bomb program that is solved using a buffer overflow attack */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
/* Like gets, except that characters are typed as pairs of hex digits.
Nondigit characters are ignored. Stops when encounters newline */
char *getxs(char *dest)
{
int c;
int even =1; /* Have read even number of digits */
int otherd =0; /* Other hex digit of pair */
char*sp = dest;
while ((c = getchar()) != EOF && c !='\n') {
if (isxdigit(c)) {
int val;
if ('0'<= c && c <='9')
val = c -'0';
else if ('A'<= c && c <='F')
val = c -'A'+10;
else
val = c -'a'+10;
if (even) {
otherd = val;
even =0;
}
else {
*sp++= otherd *16+ val;
even =1;
}
}
}
*sp++='\0';
return dest;
}
/* $begin getbuf-c */
int getbuf()
{
char buf[12];
getxs(buf);
return 1;
}
void test()
{
int val;
printf("Type Hex string:");
val = getbuf();
printf("getbuf returned 0x%x\n", val);
}
/* $end getbuf-c */
int main()
{
int buf[16];
/* This little hack is an attempt to get the stack to be in a
stable position
*/
int offset = (((int) buf) &0xFFF);
int*space = (int*) alloca(offset);
*space =0; /* So that don't get complaint of unused variable */
test();
return 0;
}
Then I executed it under gdb:
...> gdb ./bugbomb
...
..run
Type Hex string:30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 d8 bf ff ff 9f 85 04 08 b0 86 04 08 30 31 32 33 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 ef be ad de
getbuf returned 0xdeadbeef
[Inferior 1 (process 13530) exited normally]
And then without gdb::
./bufbomb
Type Hex string:30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 d8 bf ff ff 9f 85 04 08 b0 86 04 08 30 31 32 33 30 31 32 33 34 35 36 37 38 39 30 31 32 33 34 35 36 37 38 39 ef be ad de
Segmentation fault (core dumped)
I am looking for some help to resolve the seg-fault.
Run it under gdb with a bigger buffer to see which address it's trying to access to guess the stack offset of the return address used by getbuf().
To bear with small differences in memory offsets that arise from the use of gdb, use a NOP-sled. Your attack buffer should look like this:
|RET ADDRESS x 30 | NOPS (0x90) x 1000 | SHELLCODE|.
The return address should point to the middle of the NOP-sled.
If the execution jumps anywhere in the sled, it will slide to the shellcode.
You are accessing memory that your process doesn't "own".
When you run gdb, the compiler adds stuff (like extra debug info).
You can bypass the segmentation fault by expanding the stack before you attempt the buffer overflow:
int expand_stack(int n_bytes)
{
char buf[n_bytes];
return buf[n_bytes-1]; // access the memory to make sure the optimiser doesn't remove buf.
}
And in main, add a call to expand_stack before you call test:
int main()
{
int buf[16];
/* This little hack is an attempt to get the stack to be in a
stable position
*/
int offset = (((int) buf) &0xFFF);
int*space = (int*) alloca(offset);
*space = expand_stack(200);
test();
return 0;
}
Note that your code still invokes undefined behaviour.
Note2: If your compiler doesn't support variable length arrays, just use a fixed array, buf[200].

Fread binary file dynamic size string

I've been working on this assignment, where I need to read in "records" and write them to a file, and then have the ability to read/find them later. On each run of the program, the user can decide to write a new record, or read an old record (either by Name or #)
The file is binary, here is its definition:
typedef struct{
char * name;
char * address;
short addressLength, nameLength;
int phoneNumber;
}employeeRecord;
employeeRecord record;
The way the program works, it will store the structure, then the name, then the address. Name and address are dynamically allocated, which is why it is necessary to read the structure first to find the size of the name and address, allocate memory for them, then read them into that memory.
For debugging purposes I have two programs at the moment. I have my file writing program, and file reading.
My actual problem is this, when I read a file I have written, i read in the structure, print out the phone # to make sure it works (which works fine), and then fread the name (now being able to use record.nameLength which reports the proper value too).
Fread however, does not return a usable name, it returns blank.
I see two problems, either I haven't written the name to the file correctly, or I haven't read it in correctly.
Here is how i write to the file: where fp is the file pointer. record.name is a proper value, so is record.nameLength. Also i am writing the name including the null terminator. (e.g. 'Jack\0')
fwrite(&record,sizeof record,1,fp);
fwrite(record.name,sizeof(char),record.nameLength,fp);
fwrite(record.address,sizeof(char),record.addressLength,fp);
And i then close the file.
here is how i read the file:
fp = fopen("employeeRecord","r");
fread(&record,sizeof record,1,fp);
printf("Number: %d\n",record.phoneNumber);
char *nameString = malloc(sizeof(char)*record.nameLength);
printf("\nName Length: %d",record.nameLength);
fread(nameString,sizeof(char),record.nameLength,fp);
printf("\nName: %s",nameString);
Notice there is some debug stuff in there (name length and number, both of which are correct). So i know the file opened properly, and I can use the name length fine. Why then is my output blank, or a newline, or something like that? (The output is just Name: with nothing after it, and program finishes just fine)
Thanks for the help.
I tried your code and it worked fine. In order, here is the output, a hexdump of the file, and your source made to compile.
Update: Updated code to read name and address from stdin or command-line arguments.
prompt$ g++ -g -Wall -o test_records test_records.cpp
prompt$ echo -e "Test User\nSomeplace, Somewhere" | ./test_records
sizeof(employeeRecord) = 24
Number: 5551212
Name Length: 9
Name: Test User
prompt$ hexdump -C employeeRecord
00000000 90 f7 bf 5f ff 7f 00 00 70 f7 bf 5f ff 7f 00 00 |..._....p.._....|
00000010 14 00 09 00 6c b4 54 00 54 65 73 74 20 55 73 65 |....l.T.Test Use|
00000020 72 53 6f 6d 65 70 6c 61 63 65 2c 20 53 6f 6d 65 |rSomeplace, Some|
00000030 77 68 65 72 65 |where|
00000035
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct{
char * name;
char * address;
short addressLength, nameLength;
int phoneNumber;
}employeeRecord;
int main(int argc, char *argv[])
{
employeeRecord record;
#if 0
// Commmand line arguments
if (argc < 3)
return 1;
record.nameLength = strlen(argv[1]);
record.name = (char *)malloc(sizeof(char)*(record.nameLength + 1));
strncpy(record.name, argv[1], record.nameLength + 1);
record.addressLength = strlen(argv[2]);
record.address = (char *)malloc(sizeof(char)*(record.addressLength + 1));
strncpy(record.address, argv[2], record.addressLength + 1);
#else
// stdin
char input[1024];
fgets(input, sizeof(input), stdin);
record.nameLength = strlen(input);
record.name = (char *)malloc(sizeof(char)*(record.nameLength + 1));
strncpy(record.name, input, record.nameLength + 1);
fgets(input, sizeof(input), stdin);
record.addressLength = strlen(input);
record.address = (char *)malloc(sizeof(char)*(record.addressLength + 1));
strncpy(record.address, input, record.addressLength + 1);
#endif
record.phoneNumber = 5551212;
FILE *fp = NULL;
printf("sizeof(employeeRecord) = %lu\n", sizeof(employeeRecord));
// Write
fp = fopen("employeeRecord","w");
fwrite(&record,sizeof(employeeRecord),1,fp);
// Note: we're not including terminating NULLs.
fwrite(record.name,sizeof(char),record.nameLength,fp);
fwrite(record.address,sizeof(char),record.addressLength,fp);
fclose(fp);
// Read
fp = fopen("employeeRecord","r");
fread(&record,sizeof(employeeRecord),1,fp);
printf("Number: %d\n",record.phoneNumber);
char *nameString = (char *)malloc(sizeof(char)*(record.nameLength + 1));
printf("\nName Length: %d",record.nameLength);
fread(nameString,sizeof(char),record.nameLength,fp);
nameString[record.nameLength] = '\0';
printf("\nName: %s",nameString);
printf("\n");
fclose(fp);
return 0;
}
I would like to add my input....since you are dumping the memory structure to disk, the pointer addresses used to hold the data would most certainly be valid prior to dumping, but when reading from them, the pointer addresses could be invalid....which would explain why the character pointer is not showing the name...
Firstly, a nitpick: you never need sizeof (char) - it's 1, always, by definition.
As for the blank name output: do you perhaps need a newline after the %s to flush the output? I've seen weird behaviour when you leave this out, and you don't state which platform you are using. If the platform's printf() is implemented bizarrely enough, you could have the format string printed and flushed, but the name itself stuck in the C library's buffers when your program exits.
And I'm never happy about reading or writing blobs of binary data like a struct to and from files. Realise that by doing so you're promising your program that it will only ever read what it wrote on the same platform. You couldn't write a file on, say, a 64-bit host and read the file back in on a 16-bit microwave oven controller.

Resources