Reputation: 303
Im attempting the starts of a VM in C and i'm attempting to understand how to read out the binary file ive produced from the assembler. As per instructions of assignment we are to allocate a global memory as an unsigned byte of 1k memory space and and then using a load function are to read this binary file into the memory and then using a fetch function read these bytes into their instructions. The issue im having is part 1, how do i read this binary file into this unsigned int array and then decode it in a way that can be worked with? At the moment what i have prints out a value that isnt at all what is to be expected.
MAIN.c
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
unsigned int memory[1028];
int loads(char *filename){
FILE *file = fopen(filename, "r");
ssize_t read;
if(file == NULL){
exit(-1);
}
while(fread(&memory, sizeof(unsigned int), 1, file) == 1){
printf("%d\n", *memory);
}
fclose(file);
exit(0);
}
int main(int argc, char** argv){
if (argc <= 1){
printf("No file found\n");
return -1;
}
char *filename = argv[1];
loads(filename);
}
inputfile.txt
t@w@# (this is what it shows which is unreadable but when using od -t x1 output.txt | head -5
it prints out to be 0000000 74 40 77 40 11 23
0000006
)
Current Output 1081557108
Desired output 74 40 77 40 11 23
Upvotes: 0
Views: 1711
Reputation: 84579
When reading with fread
, fread
reads bytes. It has no idea about any concept of a line. The byte '\n'
(0xa
) is just a byte like any other byte in the file. The declaration for fread
is:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
Where ptr
is the starting address for a block of memory sufficient to hold size * nmemb
(size * no. of members) values from stream
. Where size
is the element or object size (in your case sizeof (unsigned)
and nmemb
is the number you will read (e.g. 1028
in the case of your unsigned memory[1028];
) See man 3 fread
Where you have problems with your code is your use of &memory
as the pointer. That is incorrect. (and why you get 2-values) &memory
is type unsigned (*)[1028]
(e.g. a pointer-to-array-of unsigned[1028]
) What is the sizeof(unsigned)
? (hint: 4-bytes
) What is the sizeof (a_pointer)
? (hint: 8-bytes
on x86_64). So you are able to store 2-unsigned values in the storage of a pointer while invoking Undefined Behavior with the remainder of the read.
The correct parameter for fread
is simply memory
which, as an array, is converted to a pointer on access subject to the four exceptions listed in C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)
In your case you declare an unsigned array memory
as:
#define MAXU 1028 /* if you need a constant, #define one (or more) */
unsigned memory[MAXU];
(note: you want to avoid the use of global variables unless absolutely necessary. Instead, declare the array in the scope needed, e.g. in main()
and then pass a pointer to any function where it is required)
When handling files, instead of passing a filename as a parameter to a function, instead, open the file and validate it is open in the caller (main()
here), and pass an open FILE*
pointer as a parameter. Unless the file can be opened, there is no need to make the function call and set up the function stack to begin with. So in main()
you can do something similar to the following to pass the filename as the first argument to your program, e.g.
int main(int argc, char **argv) {
int n = 0;
if (argc < 2) { /* validate at least 1 argument given for filename */
fprintf (stderr, "usage: %s filename\n", argv[0]);
return 1; /* do NOT return negative values to the shell */
}
/* use filename provided as 1st argument */
FILE *fp = fopen (argv[1], "r");
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
(note: do NOT return a negative value to the shell. Instead return 1
or EXIT_FAILURE
(which has the value 1
) to indicate error)
Now you want to call your loads
function passing the open file-stream fp
, e.g.
if ((n = loads (fp)) == 0) { /* validate return of loads */
fputs ("error: loads() read zero bytes or error occurred.\n", stderr);
return 1;
}
Your loads()
function reduces to:
int loads (FILE *fp)
{
return fread (memory, sizeof *memory, MAXU, fp);
}
(note: fread
returns the number of members read, which is only equal to the number of bytes read when size == 1
. So by choosing the size of unsigned
it will return the number of unsigned
values read).
A complete example could be:
#include <stdio.h>
#include <stdlib.h>
#define MAXU 1028 /* if you need a constant, #define one (or more) */
unsigned memory[MAXU];
int loads (FILE *fp)
{
return fread (memory, sizeof *memory, MAXU, fp);
}
int main(int argc, char **argv) {
int n = 0;
if (argc < 2) { /* validate at least 1 argument given for filename */
fprintf (stderr, "usage: %s filename\n", argv[0]);
return 1; /* do NOT return negative values to the shell */
}
/* use filename provided as 1st argument */
FILE *fp = fopen (argv[1], "r");
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if ((n = loads (fp)) == 0) { /* validate return of loads */
fputs ("error: loads() read zero bytes or error occurred.\n", stderr);
return 1;
}
for (int i = 0; i < n; i++) { /* loop n times outputting values */
if (i && i % 10 == 0) /* output 10 columns for convenience */
putchar ('\n');
printf (" %4x", memory[i] & 0xffff); /* & 0xffff is for my file */
}
putchar ('\n'); /* tidy up with newline */
}
Example Use/Output
In my file ../dat/100000int.bin
I have 100,000 integer values (both positive and negative in the range of short
, so I have masked the upper 2-bytes in each value with memory[i] & 0xffff
to prevent the sign-extended output as an unsigned value, e.g. 0xffff7d77
, when the stored values is less than zero.
$ ./bin/freadunsignedval ../dat/100000int.bin
7d77 6cad c544 21f8 723f 54d1 8a81 2c6a 1ba9 f95b
1858 7565 f4b 28e4 7fdd 5a92 b5df 7a3f 4e1a 7e19
669 f365 34c0 95e 903 689d 66f2 abf2 1223 1290
372f f9b 7f3d 71eb ce6d 717c 46bc 2712 1de6 6265
d248 363e 57cb 3d03 5f23 57a8 1795 2944 51e7 65af
275d 5851 724a 5c1e 61af 7b4d 44bb 48a2 4f5b 56de
5b32 68b 6679 5a6f 7876 180c 4beb 3f33 3f1f 69d1
2198 6cd7 200f 7963 29da 7f32 510b 4170 2877 22f3
271f 4fd4 84bc 196a 2bf2 5cf3 14b7 70ad 2595 6413
...
6503 b2 f135 15f6 776c b7f3 1ffd 1365 1e4d 129b
23f 6c3e 20c a8c 2ef6 f72b 4d4 793a 1b6b 425
79d5 6bac ba8 6527 6239 17ea 644e 1175 4464 1c88
346d 2967 1d3a 4339 3f5d 14a6 b46 5f5a
(which is 103 lines of output with 10-values per-line and 8 values in the final line)
Look things over and let me know if you have further questions.
Upvotes: 1