Make a File Pointer Read/Write to an In-Memory Location

Make a file pointer read/write to an in-memory location

If your operating system provides fmemopen,
probably it will meet your purpose.

Read and write to a memory location

This is throwing a segment violation (SEGFAULT), as it should, as you don't know what is put in that address. Most likely, that is kernel space, and the hosting environment doesn't want you willy-nilly writing to another application's memory. You should only ever write to memory that you KNOW your program has access to, or you will have inexplicable crashes at runtime.

Storing and accessing data in memory using pointers from txt file

First off, your prof apparently wants you to become familiar with walking a pointer through a collection of both strings (the labels) and numbers (the floating-point values) using pointer arithmetic without using array indexing. A solid pointer familiarity assignment.

To handle the labels you can use a pointer to pointer to type char (a double pointer) as each pointer will point to an array of chars. You can declare and allocate pointers for labels as follows. (this assumes you have already read the rows and cols values from the input file)

    char buf[MAXC] = "",    /* temporary line buffer    */
**labels = NULL, /* collection of labels */
**lp = NULL; /* pointers to walk labels */
...
/* allocate & validate cols char* pointers */
if (!(labels = calloc (rows, sizeof *labels))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}

You can do the same thing for your pointer values, except you only need a pointer to type double as you will simply need to allocate for a collection of doubles.

    double  *mtrx = NULL,   /* collection of numbers    */
*p; /* pointers to walk numbers */
...
nptrs = rows * cols; /* set number of poiners required */

/* allocate & validate nptrs doubles */
if (!(mtrx = calloc (nptrs, sizeof *mtrx))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}

The use of the pointers lp and p are crucial because you cannot increment either labels or mtrx (without saving the original address) because doing so will lose the pointer to the start of the memory allocated to each, immediately causing a memory leak (you have no way to free the block) and preventing you from ever being able to access the beginning again. Each time you need to walk over labels or mtrx just assign the start address to the pointer, e.g.

    p  = mtrx;   /* set pointer p to mtrx */
lp = labels; /* set poiners lp to labels */

Now you are free to read and parse the lines in any manner you choose, but I would strongly recommend using line-oriented-input functions to read each line into a temporary line buffer, and then parse the values you need using sscanf. This has many advantages to reading with fscanf alone. After you read each line, you can parse/validate each value before allocating space for the strings and assigning the values.

(note: I cheat below with a single sscanf call, where you should actually assign a char* pointer to buf, read the label, then loop cols number of times (perhaps using strtok/strtod) checking each value and assigning to mtrx, -- that is left to you)

    /* read each remaining line, allocate/fill pointers */
while (ndx < rows && fgets (buf, MAXC, fp)) {
if (*buf == '\n') continue; /* skip empty lines */
char label[MAXC] = ""; /* temp storage for labels */
double val[cols]; /* temp storage for numbers */
if (sscanf (buf, "%s %lf %lf %lf %lf", /* parse line */
label, &val[0], &val[1], &val[2], &val[3]) ==
(int)(cols + 1)) {
*lp++ = strdup (label); /* alloc/copy label */
for (i = 0; i < cols; i++) /* alloc/copy numbers */
*p++ = val[i];
ndx++; /* increment index */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */

Then it is simply a matter of looping over the values again, using or outputting as needed, and then freeing the memory you allocated. You could do that with something similar to:

    p  = mtrx;                     /* reset pointer p to mtrx */
lp = labels; /* reset poiners lp to labels */

for (i = 0; i < rows; i++) {
printf (" %-10s", *lp);
free (*lp++);
for (j = 0; j < cols; j++)
printf (" %7.2lf", *p++);
putchar ('\n');
}
free (mtrx); /* free pointers */
free (labels);

That's basically one of many approaches. Putting it all together, you could do:

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

enum { MAXC = 512 }; /* constants for max chars per-line */

int main (int argc, char **argv) {

char buf[MAXC] = "", /* temporary line buffer */
**labels = NULL, /* collection of labels */
**lp = NULL; /* pointers to walk labels */
double *mtrx = NULL, /* collection of numbers */
*p; /* pointers to walk numbers */
size_t i, j, ndx = 0, rows = 0, cols = 0, nptrs = 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 (fgets (buf, MAXC, fp)) /* get rows, ignore blank lines */
if (sscanf (buf, "%zu", &rows) == 1)
break;

while (fgets (buf, MAXC, fp)) /* get cols, ignore blank lines */
if (sscanf (buf, "%zu", &cols) == 1)
break;

if (!rows || !cols) { /* validate rows & cols > 0 */
fprintf (stderr, "error: rows and cols values not found.\n");
return 1;
}
nptrs = rows * cols; /* set number of poiners required */

/* allocate & validate nptrs doubles */
if (!(mtrx = calloc (nptrs, sizeof *mtrx))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}

/* allocate & validate rows char* pointers */
if (!(labels = calloc (rows, sizeof *labels))) {
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}

p = mtrx; /* set pointer p to mtrx */
lp = labels; /* set poiners lp to labels */

/* read each remaining line, allocate/fill pointers */
while (ndx < rows && fgets (buf, MAXC, fp)) {
if (*buf == '\n') continue; /* skip empty lines */
char label[MAXC] = ""; /* temp storage for labels */
double val[cols]; /* temp storage for numbers */
if (sscanf (buf, "%s %lf %lf %lf %lf", /* parse line */
label, &val[0], &val[1], &val[2], &val[3]) ==
(int)(cols + 1)) {
*lp++ = strdup (label); /* alloc/copy label */
for (i = 0; i < cols; i++) /* alloc/copy numbers */
*p++ = val[i];
ndx++; /* increment index */
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */

p = mtrx; /* reset pointer p to mtrx */
lp = labels; /* reset poiners lp to labels */

for (i = 0; i < rows; i++) {
printf (" %-10s", *lp);
free (*lp++);
for (j = 0; j < cols; j++)
printf (" %7.2lf", *p++);
putchar ('\n');
}
free (mtrx); /* free pointers */
free (labels);

return 0;
}

Example Input File Used

$ cat dat/arrinpt.txt
3

4

abc123 8.55 5 0 10

cdef123 83.50 10.5 10 55

hig123 7.30 6 0 1.9

Example Use/Output

$ ./bin/arrayptrs <dat/arrinpt.txt
abc123 8.55 5.00 0.00 10.00
cdef123 83.50 10.50 10.00 55.00
hig123 7.30 6.00 0.00 1.90

Memory Use/Error Check

In any code your write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.

It is imperative that you use a memory error checking program to insure you haven't written beyond/outside your allocated block of memory, attempted to read or base a jump on an uninitialized value and finally to confirm that you have freed all the memory you have allocated. For Linux valgrind is the normal choice.

$ valgrind ./bin/arrayptrs <dat/arrinpt.txt
==22210== Memcheck, a memory error detector
==22210== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==22210== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==22210== Command: ./bin/arrayptrs
==22210==
abc123 8.55 5.00 0.00 10.00
cdef123 83.50 10.50 10.00 55.00
hig123 7.30 6.00 0.00 1.90
==22210==
==22210== HEAP SUMMARY:
==22210== in use at exit: 0 bytes in 0 blocks
==22210== total heap usage: 5 allocs, 5 frees, 142 bytes allocated
==22210==
==22210== All heap blocks were freed -- no leaks are possible
==22210==
==22210== For counts of detected and suppressed errors, rerun with: -v
==22210== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 1 from 1)

Always confirm All heap blocks were freed -- no leaks are possible and equally important ERROR SUMMARY: 0 errors from 0 contexts. Note: some OS's do not provide adequate leak and error suppression files (the file that excludes system and OS memory from being reported as in use) which will cause valgrind to report that some memory has not yet been freed (despite the fact you have done your job and freed all blocks you allocated and under your control).

Look things over and let me know if you have any questions.

Read Write to Memory space

Your problem is here:

    *((unsigned char *) (weights+0x00))  = conv.lw;   // Writing into the mempory
SysMem= *((unsigned char *) (weights+0x00)); // Reading it out from the memory

You're casting your (void *) weights as a (unsigned char *), then storing the value of conv.lw at that pointer location.

But by doing that type-cast, you've explicitly told your compiler that you only want to write a single unsigned char, so it quite happily does that with the least-significant byte of conv.lw.

Similarly, when you read it back, you again cast weights as an (unsigned char *) and so you're only reading a singe byte form that location.

If you instead did something like:

    *((unsigned long *) (weights+0x00))  = conv.lw;   // Writing into the mempory
SysMem= *((unsigned long *) (weights+0x00)); // Reading it out from the memory

you'd be writing and reading all the bytes of conv.lw.

There are also a few reasons which make what you're trying to do non-portable, including: unsigned long is only typically only 4 bytes on a 32-bit architecture, and dereferencing pointers cast from other types is (at least sometimes) undefined behavior.

Memory and file pointers

The latter, no data is read from the file until needed, i.e. when you call fread() or some other I/O function.

Of course the underlying operating system might decide to speculatively read data when the file is opened, to save time later, but that's outside your control so in effect it doesn't matter. I mean that it doesn't matter because any memory used by such speculative buffering will need to be immediately made available to applications on demand.

That said, it's not as if any practical system will let the fopen() spend the time needed to read 100 MB though, that would be very bad engineering.

Also note that there might be limits on how many files a single process can open in parallel. 100 should be fine for most modern systems, though.

Different Seek Pointer for read\write

Both processes are using the same file description (i.e. the same open file).

They are accessing the same file pointer because it's part of the same file description. When one of them writes some characters, the file pointer advances for all of them, because it's the same file pointer.

You can get the file pointer by calling lseek(fd, 0, SEEK_CUR). If you print the file pointer before starting the child program, and after waiting for it, you should see that it has changed.



Related Topics



Leave a reply



Submit