I am working on an embedded platform which has a block of battery-backed
RAM. I need to store various types of data in this block of memory -
for example, bitmapped data for control registers, strings for logging,
and structures for data points.
I want to use one function to read data from this block and one function
to write data, for example:
sram_read(OBJECT_IDENTIFIER) would return a pointer to the appriate
object and
sram_write(OBJECT_IDENTIFIER, &object) would write the object to the memory
I have to do this in a way that is portable across architectures - we
support two platforms, one is ARM based and the other is x86 based.
Sometimes the object might be a string, sometimes a char, sometimes a
structure.....
What is the correct way to declare these functions?
Also, and a somewhat more difficult problem (for me at least) is the
actual storage mechanism....
One of the objects I have to store is a structure like this:
struct AnalogPoint {
float value;
float alarm_hi, alarm_low;
float average, avg_weight;
int log_freq;
int hyst_timer;
char ctrl_reg;
};
I've been kicking around various ways to store this structure. Figure
that I have to store 16 of these. I could store an array of 16
structures and return pointers to them. But then I see potential
problems; if the software is upgraded the newly compiled versions may
not put the same data in the same place, so I'd have garbage when the
new software tried to read the data saved by the older software.
I could 'unpack' the structure and store all of the elements as linear
arrays and build a structure on the fly and return a pointer to it, but
then I have problems with the data area being overwritten on the next call.
So.... Any suggestions? Stability is more important than speed.
Thanks,
--Yan 9 2377
CptDondo wrote: I am working on an embedded platform which has a block of battery-backed RAM. I need to store various types of data in this block of memory - for example, bitmapped data for control registers, strings for logging, and structures for data points.
I want to use one function to read data from this block and one function to write data, for example:
sram_read(OBJECT_IDENTIFIER) would return a pointer to the appriate object and
sram_write(OBJECT_IDENTIFIER, &object) would write the object to the memory
I have to do this in a way that is portable across architectures - we support two platforms, one is ARM based and the other is x86 based.
Sometimes the object might be a string, sometimes a char, sometimes a structure.....
What is the correct way to declare these functions?
My suggestion is to make the storage module ignorant of the data type
being stored. As I understand, you want to assign handles to different
data items, then retrieve the last stored data, given the handle.
I would pass sram_write the identifier, pointer, and length:
typedef unsigned char pdtype; /* persistent data type identifier */
int /* 0= OK, 1=out of memory */
sram_write (
pdtype type, /* type of data */
const void* pd, /* data to be stored */
size_t ldata /* length of data in bytes */
};
const void* /* ptr to data */
sram_read (
pdtype type /* data type to retrieve */
);
Your module should do its own allocation of memory. I assume here that
you only have a few data types and don't need to delete data, but you
will do periodic overwrites. If the length of the data items vary, then
you need to handle new memory allocation and garbage collection to
maintain integrity over the long run.
Internally you can have an array that gives an address pointer and
length for each item, indexed by pdtype.
To provide protection against database corruption due to power loss, you
need to do updates in steps so that if you stop executing at any point,
you can, on restart, either finish the transaction or cancel it. You
may also want to provide a separate call to start an update and complete
an update. All calls to sram_write that occur after the start are
nullified if the completion call didn't occur. That way you can
guarantee a consistent state on powerup. Don't forget that garbage
collection must be restartable.
Also, and a somewhat more difficult problem (for me at least) is the actual storage mechanism....
One of the objects I have to store is a structure like this:
struct AnalogPoint { float value; float alarm_hi, alarm_low; float average, avg_weight; int log_freq; int hyst_timer; char ctrl_reg; };
I've been kicking around various ways to store this structure. Figure that I have to store 16 of these. I could store an array of 16 structures and return pointers to them.
As suggested earlier, give each struct variable a separate handle.
Internally each one will have its own pointer and length.
But then I see potential problems; if the software is upgraded the newly compiled versions may not put the same data in the same place, so I'd have garbage when the new software tried to read the data saved by the older software.
That's a different problem, but you eliminate it by storing the version
number identifying the data format used to store the data. On startup,
compare the stored data format version with the current version. If
less, do an explicit conversion of the data and restore, then update the
data format version number. The conversion, for example, could read the
old data structure and write the data with the new data structure. The
lengths could be different. Again, to be robust, you need to make the
procedure restartable in case of power loss. The easiest way to do
this, if you have sufficient memory, is to keep the old data stored
until all the data has been updated, then delete the older version.
I could 'unpack' the structure and store all of the elements as linear arrays and build a structure on the fly and return a pointer to it, but then I have problems with the data area being overwritten on the next call.
That is too complicated, IMO. The storage module shouldn't know what
the data is, only where it is and how long each item is (as specified by
the sram_write call). The cost is the pointer and length storage per
item, which may be a structure.
If you need to store several long arrays, it would suggest adding
mechanisms to create flexible arrays in persistent storage. I have
implemented something along the lines I mentioned for non-volatile flash
storage (without a separate file system).
--
Thad
CptDondo wrote: I am working on an embedded platform which has a block of battery-backed RAM. I need to store various types of data in this block of memory - for example, bitmapped data for control registers, strings for logging, and structures for data points.
I want to use one function to read data from this block and one function to write data, for example:
sram_read(OBJECT_IDENTIFIER) would return a pointer to the appriate object and
sram_write(OBJECT_IDENTIFIER, &object) would write the object to the memory
I have to do this in a way that is portable across architectures - we support two platforms, one is ARM based and the other is x86 based.
Sometimes the object might be a string, sometimes a char, sometimes a structure.....
What is the correct way to declare these functions?
The most straightforward is probably
void *sram_read(int objid);
void sram_write(int objid, const void *objptr);
Variations: The first argument might be something other than
an `int', sram_read() might return a `const void*' instead of
a plain `void*', ... There are lots of possibilities, but all
that I can think of share the same advantage and the same
disadvantage:
Pro: The scheme is general and extensible. A `void*' can
point to absolutely any C data type, so if you someday decide
you need the functions to handle `double complex' or something
even weirder, the function declarations can remain the same.
Con: Since a `void*' can point to anything at all, it's up
to the caller and the functions to deduce the "real" data type
and make sure it's correct. The compiler will be of no help
here: if you call sram_read() to fetch an `int' datum and
carelessly store the returned pointer in a `double*', your
error will not be detected until run-time, and then only by
the fact that demons start flying from your nose.
Also, and a somewhat more difficult problem (for me at least) is the actual storage mechanism....
One of the objects I have to store is a structure like this:
struct AnalogPoint { float value; float alarm_hi, alarm_low; float average, avg_weight; int log_freq; int hyst_timer; char ctrl_reg; };
I've been kicking around various ways to store this structure. Figure that I have to store 16 of these. I could store an array of 16 structures and return pointers to them. But then I see potential problems; if the software is upgraded the newly compiled versions may not put the same data in the same place, so I'd have garbage when the new software tried to read the data saved by the older software.
I could 'unpack' the structure and store all of the elements as linear arrays and build a structure on the fly and return a pointer to it, but then I have problems with the data area being overwritten on the next call.
I fear I do not completely understand what worries you.
Specific pointer values are, in general, not portable from one
execution of a program to the next; they are only valid within a
single execution. The situation may be different with a chunk of
special storage that has been laid out by other (non-portable)
means -- but why are you worried about pointer values anyhow? Your
functions use the first argument to designate the data to read or
write, not a pointer value. If you stick to the object IDs and
stop worrying about the pointer values, you may be better off.
As for storing a struct -- well, why not simply store it as
it is? Something is clearly bothering you about this (and the
"something" is probably a reasonable concern), but I don't yet
know the nature of your difficulty.
-- Er*********@sun.com
On Fri, 28 Oct 2005 22:45:42 -0400, Eric Sosman wrote: I've been kicking around various ways to store this structure. Figure that I have to store 16 of these. I could store an array of 16 structures and return pointers to them. But then I see potential problems; if the software is upgraded the newly compiled versions may not put the same data in the same place, so I'd have garbage when the new software tried to read the data saved by the older software.
I could 'unpack' the structure and store all of the elements as linear arrays and build a structure on the fly and return a pointer to it, but then I have problems with the data area being overwritten on the next call.
I fear I do not completely understand what worries you. Specific pointer values are, in general, not portable from one execution of a program to the next; they are only valid within a single execution. The situation may be different with a chunk of special storage that has been laid out by other (non-portable) means -- but why are you worried about pointer values anyhow? Your functions use the first argument to designate the data to read or write, not a pointer value. If you stick to the object IDs and stop worrying about the pointer values, you may be better off.
As for storing a struct -- well, why not simply store it as it is? Something is clearly bothering you about this (and the "something" is probably a reasonable concern), but I don't yet know the nature of your difficulty.
Thak you both for the comments. By way of history, our products have a
field life of 20 years, so it is likely that this code will be maintained
by someone else at some point. Our current product is based on a
proprietary, closed controller running Basic, with limited memory. Over
the years, as features have been added, variable names and all legibility
has been sacrificed, until the system is now a nearly unmaintainable mess.
The initial decision to use this product made sense; it just turned out to
be unmaintainable over the long run. Unfortunately, I am now responsible
for maintaining this beast.... So, I am *very determined* to do things
right, in a cross-platform portable way just so my successor won't hunt
me down in my retirement.... ;-)
Now back to my issue....
My concern with storage of structures is this:
Assume that I compile version 1 with plain old compiler 3.3.3; it stores
structures as declared: if I declare int a, b, c; it will allocate a total
of 12 bytes and store a in the first 4, b in the next 4, and c in the last
4.
Now I recompile the code with optimizing compiler version 4.4.4; it
'optimizes' the storage locations and stores the variables as follows: c
in the first 4, b in the next, and a in the last 4.
Is this a legitimate concern? Perhaps not. I don't know if c
guarantess a particular memory order for storage in a structure. Storing
the structure as a block of bytes pretty much assumes on retrieval that
the receiving structure has the same internal representation as the
sending structure.
But field upgrades will be done by people with little to no computer
skills, or even by an automated process, and it is essential that the data
remain intact over software upgrades.
I could build an 'upgrader', something that would suck the data out into a
text file using the old version, then put it back with the new version of
the software, but I'd rather build stability into the data storage
mechanism.
--Yan
Captain Dondo wrote: [...] My concern with storage of structures is this:
Assume that I compile version 1 with plain old compiler 3.3.3; it stores structures as declared: if I declare int a, b, c; it will allocate a total of 12 bytes and store a in the first 4, b in the next 4, and c in the last 4.
Now I recompile the code with optimizing compiler version 4.4.4; it 'optimizes' the storage locations and stores the variables as follows: c in the first 4, b in the next, and a in the last 4.
Is this a legitimate concern? Perhaps not. I don't know if c guarantess a particular memory order for storage in a structure. Storing the structure as a block of bytes pretty much assumes on retrieval that the receiving structure has the same internal representation as the sending structure.
The compiler cannot change the order of the elements
within a struct. The first element must start at the address
of the struct itself, the next at a higher address, and so on.
HOWEVER, the compiler is allowed to insert unnamed padding
bytes after any element of the struct. The total size of the
struct can therefore be greater than the sum of the sizes of
its elements; also, different compilers may use different
amounts of padding and thus disagree about the relative offsets
of struct elements other than the first (which is necessarily
at offset zero).
The usual reason to insert padding bytes is to satisfy
alignment requirements, so it would be very strange to find
padding between two adjacent struct elements of the same type.
Even in the absence of inter-element padding, you might still
find padding at the end of the struct -- maybe the compiler
expands the struct by an extra `int'-worth so it can use some
machine's sixteen-byte-aligned instructions to manipulate it.
Such shenanigans are strange but not unheard-of -- and the
compiler is not forbidden to be strange.
If you want to be absolutely sure there's no padding, use
an array of three elements instead of a struct with three
elements. If the rest of the program really wants to deal with
a struct, just do element-by-element copying as you pass the
data to and from your storage functions.
But field upgrades will be done by people with little to no computer skills, or even by an automated process, and it is essential that the data remain intact over software upgrades.
I could build an 'upgrader', something that would suck the data out into a text file using the old version, then put it back with the new version of the software, but I'd rather build stability into the data storage mechanism.
I'd suggest that you fix the format of the stored data,
making it independent of the compiler's whims. Then you can
write "adapter" functions that convert the stored format to
and from whatever the current compiler happens to give you:
let your program deal with structs in their native form, and
convert between "native" and "stored" on the way in and out of
your storage system. A few hints:
- An `int' is not necessarily four bytes long. It will
have at least sixteen bits, but could have more. Also,
a `char' (the basic unit of size in C) will have at
least eight bits but could be wider. You may or may not
need to worry about such things; it depends on the set of
platforms your code needs to run on.
- Different machines arrange the individual bytes of multi-
byte values in different ways. Two popular arrangements
are "Big-Endian" (the most significant byte appears at
the lowest memory address) and "Little-Endian" (the
reverse). At least one machine used a variant sometimes
called "Middle-Endian." Even if you don't need to deal
with strange sizes, you're likely to need to cope with
variations in "Endianness" if you need to exchange stored
data between dissimilar machines.
- The offsetof() macro in <stddef.h> can tell you where the
compiler has chosen to place each element of a struct. You
can use it to build an array that describes the types and
relative positions of struct elements; this can let you
write just a few wrapper functions instead of many.
--
Eric Sosman es*****@acm-dot-org.invalid
On Sat, 29 Oct 2005 06:41:08 -0700, Captain Dondo wrote: On Fri, 28 Oct 2005 22:45:42 -0400, Eric Sosman wrote:
[...] I fear I do not completely understand what worries you. Specific pointer values are, in general, not portable from one execution of a program to the next; they are only valid within a single execution. The situation may be different with a chunk of special storage that has been laid out by other (non-portable) means
<OT>
From a separate posting in comp.os.linux.misc I gather that Captain Dondo
is using the POSIX mmap() function to return a pointer to a block of
memory representing the persistent RAM device. For comp.lang.c purposes
we might treat it as a call to malloc() which happens to return memory
already initialised with data. If Captain Dondo wants to clarify this
equivalence he should probably ask on comp.unix.programmer.
</OT>
-- but why are you worried about pointer values anyhow? Your functions use the first argument to designate the data to read or write, not a pointer value. If you stick to the object IDs and stop worrying about the pointer values, you may be better off.
It seems that the OP is doing his own memory management on a block of
memory.
If he wants help with that part of his task, he could provide more
information on his constraints:
* Are there a fixed number of objects with fixed OBJECT_IDENTIFIERS, or
can objects and OBJECT_IDENTIFIERS be dynamically added/removed?
* What is the relationship between an OBJECT_IDENTIFIER and the object it
identifies?
As for storing a struct -- well, why not simply store it as it is? Something is clearly bothering you about this (and the "something" is probably a reasonable concern), but I don't yet know the nature of your difficulty.
[...] My concern with storage of structures is [the use of different versions of the compiler to generate the program accessing the same data] I don't know if c guarantess a particular memory order for storage in a structure.
It does. What it doesn't guarantee is that the new compiler version won't
change the amount of padding between members (it does guarantee that there
is no initial padding).
Storing the structure as a block of bytes pretty much assumes on retrieval that the receiving structure has the same internal representation as the sending structure.
One other potential problem is alignment. The initial pointer to the
memory block (when equated to a returned pointer from malloc()) is
suitably aligned for any data type. But in standard C, reading from and
writing to that memory block at a location other than the initial one
using an indirected pointer of type other than a char pointer could fail
due to incorrect alignment.
But field upgrades will be done by people with little to no computer skills, or even by an automated process, and it is essential that the data remain intact over software upgrades.
I could build an 'upgrader', something that would suck the data out into a text file using the old version, then put it back with the new version of the software, but I'd rather build stability into the data storage mechanism.
One portable way of dealing with this type of situation (and one that I've
successfully used) is for your sram_read and sram_write functions to
decode the objects and read/write them into the memory block in a portable
representation of your devising using char * access only.
sram_read(OBJECT_IDENTIFIER) would either take an additional pointer to an
object of appropriate type that it would fill in after interpreting the
object's representation in the memory block, or it would malloc the memory
itself and return it.
Likewise, sram_write(OBJECT_IDENTIFIER, &object) would interpret the
object pointed to by &object and store it using char * (i.e. byte by byte)
into the memory block in a portable format of your own devising.
The downside is that you need to treat each structure separately, but with
some helper functions for the basic types (int, double, etc) it's not that
much work to add support for a new structure.
This doesn't deal with how you determine _where_ in the memory block to
store those objects, which as I said above you need to provide further
information about if you wish assistance in that regard, although it may
be borderline w.r.t. topicality.
I see that Eric Sosman has just responded, and my advice parallels his.
-- http://members.dodo.com.au/~netocrat
On Sat, 29 Oct 2005 14:58:56 +0000, Netocrat wrote: On Sat, 29 Oct 2005 06:41:08 -0700, Captain Dondo wrote: On Fri, 28 Oct 2005 22:45:42 -0400, Eric Sosman wrote: [...] I fear I do not completely understand what worries you. Specific pointer values are, in general, not portable from one execution of a program to the next; they are only valid within a single execution. The situation may be different with a chunk of special storage that has been laid out by other (non-portable) means <OT> From a separate posting in comp.os.linux.misc I gather that Captain Dondo is using the POSIX mmap() function to return a pointer to a block of memory representing the persistent RAM device. For comp.lang.c purposes we might treat it as a call to malloc() which happens to return memory already initialised with data. If Captain Dondo wants to clarify this equivalence he should probably ask on comp.unix.programmer.
There is another issue that I am dealing with - mmap and the whole
POSIX/SYS V/BSD memory mapping... I am totaly clueless (but trying to
learn).... But that's OT for this group.
</OT> -- but why are you worried about pointer values anyhow? Your functions use the first argument to designate the data to read or write, not a pointer value. If you stick to the object IDs and stop worrying about the pointer values, you may be better off.
It seems that the OP is doing his own memory management on a block of memory.
If he wants help with that part of his task, he could provide more information on his constraints: * Are there a fixed number of objects with fixed OBJECT_IDENTIFIERS, or can objects and OBJECT_IDENTIFIERS be dynamically added/removed? * What is the relationship between an OBJECT_IDENTIFIER and the object it identifies?
OK, I realize that this is getting somewhat OT, but here's what I'm trying
to do:
I have a block of 512k of battery backed ram. I need to partition this
into roughly three areas:
one area contains three fixed length ring buffers for logging purposes
(these are pretty easy, just a pointer at the head of the buffer, and each
message being \n terminated)
one for config file storage (again, easy, a fixed length text area holding
a single \0 terminated string)
one for binary data storage (this is the one that's giving me fits)
The amount of binary data will be fixed - there will be a set number of
data points, control registers, etc. So I could for example lay out a map
that puts 16 data point structures first, then the control registers, then
the bus maps (16 bytes of bit-mapped data) and so on. Most of my data is
bitmapped, so I can use char for that. It's really the data points that I
am worried about. Each data point has alarm setpoints, and if the sensor
goes above or below that we need to stop the machine. This is the data I
really need to preserve.
The data storage is not for 'general purpose', it is very specific to my
uses. We can assume that sram_read and sram_write can have complete
knowledge of the type of data being stored.
From the discussion, it may be easier to unroll my data points into
arrays, and then fill a client-supplied struct to match. Since the type
and amount of data I have to deal with is fixed, I should be able to
assign blocks of memory to specific arrays to hold the unrolled structures.
Things like sizeof(float) should not change unless we change
architectures... In which case, I'm not going to be worried about
preserving data. The new hardware would be factory-programmed with the
correct data.
thus, on the client side,
struct DataPoint point;
sram_read(DATAPOINT5,&point);
and within sram_read() I do something like this:
point->alarm_hi = mmap_ptr_to_alarm_hi_array + sizeof(float) * 5;
...
sram_read and sram_write would have to have enough intelligence to know
the type of the second argument from the identifier, but that's easily
done.
This doesn't deal with how you determine _where_ in the memory block to store those objects, which as I said above you need to provide further information about if you wish assistance in that regard, although it may be borderline w.r.t. topicality.
I think I can pretty much figure that part out... I just don't know
enough about the very low level stuff I have to deal with here....
--
o__
,>/'_ o__
(_)\(_) ,>/'_ o__
Yan Seiner, PE (_)\(_) ,>/'_ o__
Certified Personal Trainer (_)\(_) ,>/'_ o__
Licensed Professional Engineer (_)\(_) ,>/'_
Who says engineers have to be pencil necked geeks? (_)\(_)
On Sat, 29 Oct 2005 09:07:06 -0700, Captain Dondo wrote:
[...] The amount of binary data will be fixed - there will be a set number of data points, control registers, etc.
Well that makes things easier. :-)
[...] From the discussion, it may be easier to unroll my data points into arrays, and then fill a client-supplied struct to match. Since the type and amount of data I have to deal with is fixed, I should be able to assign blocks of memory to specific arrays to hold the unrolled structures.
I'm not sure what you mean by "unrolling into arrays". Are you saying
that all of the members in your datapoint struct are of equal size and you
can conceptualise the struct as an array of "binary data" members?
Things like sizeof(float) should not change unless we change architectures... In which case, I'm not going to be worried about preserving data. The new hardware would be factory-programmed with the correct data.
Representation as well as size is an issue, but as you say it's likely to
be largely hardware-dependent and unrelated to your compiler version (you
should probably verify that before deciding to use native float format
though).
It seems to me that the likeliest change (and it's somewhat remote unless
you use different compiler options) is structure padding.
thus, on the client side,
struct DataPoint point;
sram_read(DATAPOINT5,&point);
and within sram_read() I do something like this:
point->alarm_hi = mmap_ptr_to_alarm_hi_array + sizeof(float) * 5; ...
That still has alignment issues unless you are guaranteed that
mmap_ptr_to_alarm_hi_array is suitably aligned for a float. Of course you
may not have alignment problems on your particular platform(s) and be able
to safely write non-portable code, but that's beyond the scope of this
group.
To do it portably, you would copy the float using memcpy() or a loop with
char * pointers.
[...]
-- http://members.dodo.com.au/~netocrat
On Sat, 29 Oct 2005 16:52:57 +0000, Netocrat wrote: I'm not sure what you mean by "unrolling into arrays". Are you saying that all of the members in your datapoint struct are of equal size and you can conceptualise the struct as an array of "binary data" members?
The other way... I have 16 structures, each of which has float a, b,
c,.... as members. I am thinking I should store these as the equivalent
of
float a[16], b[16], .... which should get me around the padding issue, and
then reassemble the structure as requested. point->alarm_hi = mmap_ptr_to_alarm_hi_array + sizeof(float) * 5; ...
That still has alignment issues unless you are guaranteed that mmap_ptr_to_alarm_hi_array is suitably aligned for a float. Of course you may not have alignment problems on your particular platform(s) and be able to safely write non-portable code, but that's beyond the scope of this group.
Good point. I have to make sure I start each array pointer on a multiple
of sizeof(float)....
--
o__
,>/'_ o__
(_)\(_) ,>/'_ o__
Yan Seiner, PE (_)\(_) ,>/'_ o__
Certified Personal Trainer (_)\(_) ,>/'_ o__
Licensed Professional Engineer (_)\(_) ,>/'_
Who says engineers have to be pencil necked geeks? (_)\(_)
Captain Dondo wrote: I have a block of 512k of battery backed ram. I need to partition this into roughly three areas:
one area contains three fixed length ring buffers for logging purposes (these are pretty easy, just a pointer at the head of the buffer, and each message being \n terminated)
one for config file storage (again, easy, a fixed length text area holding a single \0 terminated string)
one for binary data storage (this is the one that's giving me fits) The amount of binary data will be fixed - there will be a set number of data points, control registers, etc.
You say fixed size, but if you are concerned about software upgrades, it
is a reasonable possibility that the size may change in a newer version
due to additional variables to store. If so, think in terms of number
of items and item size changing infrequently.
The data storage is not for 'general purpose', it is very specific to my uses. We can assume that sram_read and sram_write can have complete knowledge of the type of data being stored.
In my earlier post I recommended making the persistent storage routines
ignorant of the type and size of data items. Let me amplify on that point.
I would recommend multiple layers of code to support your persistent data:
1. persistent storage routines, such as the versions of sram_read and
sram_write I recommended earlier.
2. access routines, which call the storage routines, that know about
data types.
The application code can then call the access routines, such as
pstore_meas (measurement_t *m) and
gstore_meas (measurement_t *m).
The access routines know about the format of the individual data item.
They, in turn, call the storage routines to put and get copies.
I recommend that only the storage routines move data to and from the
storage area, since they may need to shuffle data items in memory as
part of the data recovery strategy. The storage routines leave the
interpretation of the data to the upper layers.
... thus, on the client side,
struct DataPoint point;
sram_read(DATAPOINT5,&point);
and within sram_read() I do something like this:
point->alarm_hi = mmap_ptr_to_alarm_hi_array + sizeof(float) * 5; ...
sram_read and sram_write would have to have enough intelligence to know the type of the second argument from the identifier, but that's easily done.
Again, I recommend that knowledge about data types be placed in an upper
layer, not the persistence logic, unless you really don't need to
provide for consistent data recovery in an environment where the power
may be lost at any time. Your data recovery can be robust if you design
it that way.
--
Thad This thread has been closed and replies have been disabled. Please start a new discussion. Similar topics
by: maniac |
last post by:
Hey guys, I'm new here, just a simple question.
I'm learning to Program in C, and I was recommended a book called,
"Mastering C Pointers", just asking if any of you have read it,
and if it's...
|
by: Gerald |
last post by:
I have a problem with an array of pointers.
In a program I'm writing, I have to read a file, containing thousands
of short lines. The content of another file will be compared against
each line...
|
by: wonderboy |
last post by:
Hey guys,
I have a simple question. Suppose we have the following
functions:-
//-----My code starts here
char* f1(char* s)
{
char* temp="Hi";
return temp;
|
by: santosh |
last post by:
In following code
char str = "asdf" ;
str = 's' ;
is possible.
But char *str = "asdf" ;
str = 's' ;
is an run-time error.
What i understand is *str = "asdf" is stored in Read-only-Memory....
|
by: Martin Jørgensen |
last post by:
Hi,
I'm relatively new with C-programming and even though I've read about
pointers and arrays many times, it's a topic that is a little confusing
to me - at least at this moment:
----
1)...
|
by: kleary00 |
last post by:
Hi,
I am writing a function that needs to return an array of strings and I
am having some trouble getting it right. I need some help.
Here is what I consider an array of 100 strings:
char...
|
by: J Caesar |
last post by:
In C you can compare two pointers, p<q, as long as they come from the
same array or the same malloc()ated block. Otherwise you can't.
What I'd like to do is write a function
int comparable(void...
|
by: Alisha |
last post by:
A pointer is a variable which stores the address of another
variable. There are two important operators when working with pointers
in C++: the address of (&) operator and the value of (*)...
|
by: raphfrk |
last post by:
Is this valid?
int a;
void *b;
b = (void *)a; // b points to a
b += 5*sizeof(*a); // b points to a
a = 100;
|
by: lllomh |
last post by:
Define the method first
this.state = {
buttonBackgroundColor: 'green',
isBlinking: false, // A new status is added to identify whether the button is blinking or not
}
autoStart=()=>{
|
by: DJRhino |
last post by:
Was curious if anyone else was having this same issue or not....
I was just Up/Down graded to windows 11 and now my access combo boxes are not acting right. With win 10 I could start typing...
|
by: isladogs |
last post by:
The next Access Europe meeting will be on Wednesday 4 Oct 2023 starting at 18:00 UK time (6PM UTC+1) and finishing at about 19:15 (7.15PM)
The start time is equivalent to 19:00 (7PM) in Central...
|
by: Aliciasmith |
last post by:
In an age dominated by smartphones, having a mobile app for your business is no longer an option; it's a necessity. Whether you're a startup or an established enterprise, finding the right mobile app...
|
by: tracyyun |
last post by:
Hello everyone,
I have a question and would like some advice on network connectivity. I have one computer connected to my router via WiFi, but I have two other computers that I want to be able to...
|
by: giovanniandrean |
last post by:
The energy model is structured as follows and uses excel sheets to give input data:
1-Utility.py contains all the functions needed to calculate the variables and other minor things (mentions...
|
by: NeoPa |
last post by:
Hello everyone.
I find myself stuck trying to find the VBA way to get Access to create a PDF of the currently-selected (and open) object (Form or Report).
I know it can be done by selecting :...
|
by: Teri B |
last post by:
Hi, I have created a sub-form Roles. In my course form the user selects the roles assigned to the course.
0ne-to-many. One course many roles.
Then I created a report based on the Course form and...
|
by: GKJR |
last post by:
Does anyone have a recommendation to build a standalone application to replace an Access database? I have my bookkeeping software I developed in Access that I would like to make available to other...
| |