Reputation: 17268
My original question is here, which no one seems interested in.
I've decided to break that tedious question down and to ask the following:
char* shared_memory;
shared_memory = (char*) shmat (segment_id, 0, 0);
Do we typically get the pointer to the shared memory like the example above? In other words, should we always cast the pointer to char*
or whatever better fits our needs?
Upvotes: 2
Views: 2805
Reputation: 83404
I don't know exactly about shmat
, but I have some experience with the WinAPI equivalent (MapViewOfFile, so I will give a more general answer.
Because you tied your two questions together, and the other question is about C++ objects in shared memory, I'll handle both C and C++ cases here. I invite you to add the [c++] tag to your question.
Whatever the API, you'll end up getting a void *
because it is what it is: An address to some memory zone (shared or not).
That "allocation API" (either malloc, shmat, MapViewOfFile, etc.) has no idea what you'll do with that memory, what abstractions you'll use (C structs, C++ objects, even C macros or built-in types), so the only thing that API can do is to give you:
void *
The problem is: What you'll do with that memory?
There's no way you can access the content of that memory through the void *
, because you can't dereference a void *
(it is, after all, a pointer to void
...). A void *
only contains an address. Nothing more, nothing less.
char
abstractionThe first abstraction you'll find, the easiest one, is the char
abstraction. There is no byte
type in C and C++, and the char
(or unsigned char
) type fills the role. So, if you want to access that memory as an array of bytes, you cast the return of that memory into char
:
/* C code */
char * pc = p ; /* p being the void * pointer */
// C++ code
char * pc = static_cast<char *>(p) ; // p being the void * pointer
struct
abstractionThe second abstraction is to assume the shared memory is a struct (or perhaps even an array of struct), so you should cast the pointer into a pointer to that struct:
/* C code */
typedef struct S { /* etc. */ } S ;
S * ps = p ; /* p being the void * pointer */
// C++ code
struct S { /* etc. */ } ;
S * ps = static_cast<S *>(p) ; // p being the void * pointer
Thus, you can access that shared memory through the struct.
Worst case: Two processes will working simultaneously. So if you don't use synchronization primitives (like an interprocess mutex), you'll fall into race conditions (one process writing a value, while the other is reading it, potentially leading to corrupted read data)
Usually, shared memory is used to be shared between processes. Usually, this means that the API can (and will probably) return for the same shared memory different addresses for each process.
The reasons are somewhat complicated (and should be their own question), but here it is: Two processes with pointers to the same memory won't have the same addresses on their respective pointers.
/* C code */
/* process A */
void * p = getSharedMemory("ABCD") ;
/* p could have the value 0x1234 */
/* process B */
void * p = getSharedMemory("ABCD") ;
/* p could have the value 0x56789 */
Addresses are useless in shared memory. If you put a valid pointer address from the process A in a shared memory, that pointer address will be invalid in the process B. So, never put addresses.
What you can put in shared memory is indices. For example, you could have the struct:
/* C code */
typedef struct S
{
size_t index ;
double value[1000] ;
} S ;
With this struct, you could set a value 3.1415 in the value
array, at the index 42:
/* C code - process A */
S * s = p ; /* p being the pointer to the shared memory */
s->index = 42 ;
s->value[42] = 3.1415 ;
And then retrieve it in the other process:
/* C code - process B */
S * s = p ; /* p being the pointer to the shared memory */
size_t index = s->index ;
double value = s->value[index] ;
This shared memory thing is an API problem, not a C or C++ specific problem.
In your original question, you mentioned C++ objects in shared memory, so I'll detail some differences in C and C++ that have been reported despite being outside the true scope of your question.
In C, it is legal to implicitly cast any void *
pointer into any kind of pointer T *
. In C++, you need a static cast for that:
/* valid C code */
T * t = p ; /* p being a void * pointer */
T * t = (T *) p ; /* useless but authorized cast */
// valid C++ code ;
T * t = static_cast<T *>(p) ; // p being a void * pointer
T * t = (T *) p ; // considered bad style for multiple reasons
So usually, to produce C/C++ compatible code, most people will use the C-style cast which is common to the two languages, with invariably incurring language lawyers's remarks (I am guilty of that).
Despite heated debates, the truth is that each language is right because despite their strong similarities and common grounds, they are different in one major domain: C is a weakly typed language, whereas C++ is a strongly typed language.
Remember the part where I wrote that you should not put pointers in shared memory?
This is true for C and C++: The moment you have a pointer instead of a relative index in shared memory, you have a probable problem (i.e. a probable bug).
So, if you put in shared memory a struct with a pointer member containing an address in process A, that address will be invalid in process B.
C++ objects offer a strong abstraction, meaning they are easy and safe to use (there's no memory leaks risks when using std::string
or std::vector<std::string> objects
despite the amount of memory allocation involved, for example). But this strong abstraction only hides the fact that inside, you still do have pointers...
The second difficulty with C++ objects in shared memory is that construction and destruction must be handled manually (using placement new and explicit destructor call).
Conclusion: Unless you know the object you're using can handle it, and you used that object correctly, writing the following cast:
// C++ code
struct MyObject { /* constructors, destructors, etc. */ } ;
MyObject * myObject = static_cast<MyObject*>(p) ; // p being void *
will not work correctly with pointers to shared memory.
Upvotes: 5
Reputation: 213827
Yes. Although the cast is superfluous in C,
char *shared_memory = shmat(segment_id, 0, 0);
It is generally preferred these days to use the newer shared memory objects (shm_open
/ mmap
).
You can put any type of data in a shared memory segment,
struct s { int x; char s[16]; float z; };
struct s *shared_memory = shmat(segment_id, 0, 0);
Note: If you put pointers in a shared memory segment, note that the data they point to might not be shared, and if it is, might have a different address in different processes.
Upvotes: 4
Reputation: 43558
shmat
, like malloc
, returns void *
, which can be casted (better implicitly) to any other pointer type.
In general a void *
pointer cannot be safely casted to any other pointer type because of alignment issues, but if it is already aligned on a sufficiently large boundary (as is the case with the returned pointers by shmat
, malloc
, mmap
, etc.) you safely cast it to another pointer type (even implicitly) and safely dereference it.
Upvotes: 1