1

I presently trying to learn how to write in memory in various ways, i.e. I'm playing.

I've created a struct as follows:

struct strProjectile
{
    int Damage;
    int ParticlesPerSecond;
    short Fire;
    short Plasma;
};

I've then created an array as follows:

strProjectile Projectiles[40];

From that array, I've then created a pointer to point at the beginning of the array:

strProjectile *ptrProjectiles;
ptrProjectiles = Projectiles;

Using that pointer, you can set the values in the variables like so (I know this can be done directly not using the pointer too using the . notation):

ptrProjectiles->Damage = 0;
ptrProjectiles->ParticlesPerSecond = 0;
ptrProjectiles->Fire = 0;
ptrProjectiles->Plasma = 0;

I have also found I can set the values in the variables by creating a pointer to each of the variables in each struct of the array. For example, the following would set Damage to 0:

int *Damage = (int *)ptrProjectiles;
*Damage = 0;

My question is can I use the pointer to the array directly to set each variable within the struct directly in memory as I did using the separate pointer for damage above? Using something like:

*ptryProjectiles = 0;

then skipping 4 bytes of memory to get to the next variable in the struct (in this case, ParticlesPerSecond), set that, then skip 4 bytes more and set the next variable (in this case, Fire), then skip the next 2 bytes and set the next variable (in this case, Plasma), then skip 2 more bytes to get the next object in the array and then repeat....

I cant see a reason I need to do this, but just want to know if it can be done for my full understanding....

Thanks in advance!

Gavzooka
  • 57
  • 1
  • 9
  • If the struct contains only integer types, you could use: `memset(ptrProjectiles, 0, sizeof(strProjectile));`. See [memset](http://www.cplusplus.com/reference/cstring/memset/). – Adrian Dec 29 '14 at 19:50
  • that would only (as in many of the OPs examples) only effect the first instance of the struct strProjectile in the array Projectiles. The correct syntax would be Projectiles[x].Damage = 0; Projectiles[x].ParticlesPerSecond = 0; Projectiles[x].Fire = 0; Projectiles[x].Plasma = 0; perhaps in a loop incrementing 'x' from 0 to <40 or to set everything at once to 0 use: memset(Projectiles, 0x00, sizeof(Projectiles) ); – user3629249 Dec 30 '14 at 04:14

5 Answers5

1

In general, you address arrays of structures directly through the array as follows:

strProjectiles[2].Damage=0;

If you want an individual element:

Projectile * p= &strProjectiles[3];
p->Damage=0;
(*p).Fire=1;
gbronner
  • 1,907
  • 24
  • 39
  • Thanks, appreciate this as said in my op. I want to directly address memory without using . notation or the -> notation. Probably using casting of some type I would imagine but cant see how. – Gavzooka Dec 29 '14 at 19:52
  • There are no guarantees on struct packing, so that isn't portable. In general, you could do strProjectiles + n * sizeof(Projectile) + (int) (void *) ((&strProjectiles.Damage) - &(strProjectiles.Plasma)) but stuff like this tends to break between compilers. – gbronner Dec 29 '14 at 19:55
  • Thanks, appreciate it's prone to portability issues and potentially future bugs relating to this method of doing this but its a 'in theory' question and not something I would put to practical use...I'm just trying to understand how to do it as C should be able to address any memory directly if I understand correctly. – Gavzooka Dec 29 '14 at 20:09
  • It is, though using syntactic sugar such as (&strProjectiles[3].Fire) rather than fixed offsets is far more common. The only place where I've actually seen this done regularly is in bit-packed named unions, and it tends to confuse other programmers. – gbronner Dec 29 '14 at 20:18
0

With this statement, you're assuming int Damage; will always be the first field:

int *Damage = (int *)ptrProjectiles;

This is prone to future bugs and should not be used. If you want to set only the int Damage; field in each structure, you use use a loop:

strProjectile Projectiles[MAX_PROJECTILES];
strProjectile *ptrProjectiles = Projectiles;

for(i = 0; i < MAX_PROJECTILES; i++)
    (ptrProjectiles + i)->Damage = 0;

Update

It is possible, though dangerous, to calculate the address of each structure's first field like this:

int *pDamage = (int *)Projectiles;

for(i = 0; i < MAX_PROJECTILES; i++)
{
    *pDamage = 0;
    pDamage = (void *)pDamage + sizeof(Projectiles[0]);
}
Fiddling Bits
  • 8,712
  • 3
  • 28
  • 46
  • Thanks. Appreciate its prone to bugs and cross compilation issues. This is an 'in theory' question which is to help my understanding. I know that C should be able to address any of the memory space, I just want to understand how it applies in this situation – Gavzooka Dec 29 '14 at 20:10
0

TLDR: Yes, but you probably shouldn't.

You can totally do this in C. You could even take in a bytestream and typecast it to your struct and have all of the data members filled out immediately.

The nuance to this is that it is not maintainable code, so... probably not a good idea IRL. Another point to note is that once you've done this type of accessing/setting of variables, you're married to your data structure. You can't really change it, move variables around, add additional data members or anything really without reworking all of your code.

Rubix Rechvin
  • 571
  • 3
  • 16
  • Thanks. Appreciate this isnt good practice but its an 'in theory' question. I'm trying to understand how to do this if I such desired...not that I would use it. Its to help my understanding. – Gavzooka Dec 29 '14 at 20:11
0

OK, thanks for the tips and advice but figured out what I was trying to do....

Firstly, to skip forward between each entry in the array I need to use:

ptrProjectiles = (strProjectile *)((char *)ptrProjectiles + sizeof(strProjectile));

which basically makes it skip 12 bytes at a time.

Assuming its packed correctly with consistent padding of the struct, then the following can be used to set the value of each variable directly in memory:

*(int *)ptrProjectiles = 0;
*(int *)((char *)ptrProjectiles + 4) = 0;
*(short *)((char *)ptrProjectiles + 8) = 0;
*(short *)((char *)ptrProjectiles + 10) = 0;

This does what I was looking for. Bringing it all together, I could then 0 each of the entries in the array with:

int lenProjectiles = sizeof(Projectiles);

for (int i = 0; i < lenProjectiles; i = i + sizeof(strProjectile))
{
    ptrProjectiles = (strProjectile *)((char *)ptrProjectiles + sizeof(strProjectile));
    *(int *)ptrProjectiles = 0;
    *(int *)((char *)ptrProjectiles + 4) = 0;
    *(short *)((char *)ptrProjectiles + 8) = 0;
    *(short *)((char *)ptrProjectiles + 10) = 0;
}
Gavzooka
  • 57
  • 1
  • 9
  • Why do this `ptrProjectiles = (strProjectile *)((char *)ptrProjectiles + sizeof(strProjectile));` when you can just do this `ptrProjectiles++`? – Fiddling Bits Dec 29 '14 at 22:01
  • Fair point. No idea why I did it that way to be honest. Its carry over from another method of achieving this that I was working on but used at a different point in the code. My thought was that I wanted to move 12 bytes and I was obviously thinking that literally. – Gavzooka Dec 29 '14 at 22:07
0

Pretty much everything you wrote is correct, however, there are some important details:

It is perfectly valid code to convert a struct pointer to a pointer to its first element, as in your line

int *Damage = (int *)ptrProjectiles;

This fact is used heavily in object oriented programming, where the first member of a struct (read class) is a variable that contains the base class subobject (another struct). That way a pointer to the subclass can directly be converted into a pointer to the base class and vice versa.

However, it is invalid to try to access the second element of the struct in such a way, i. e.

int* ParticlesPerSecond = (int*)ptrProjectiles + 1;    //Wrong!

is not guaranteed to yield the same result as

int* ParticlesPerSecond = &ptrProjectiles->ParticlesPerSecond;    //Correct.

The reason is alignment: The compiler is free to add padding between elements of a struct which is forbidden in an array.

For the other two members, the situation is even worse: They have a different type than the first member, so they may have different alignment requirements. That makes their offsets pretty unpredictable. Nevertheless, you can compute their offsets with this code:

size_t FireOffset = (size_t)&((struct strProjectile*)0)->Fire;
size_t PlasmaOffset = (size_t)&((struct strProjectile*)0)->Plasma;

This trick has frequently been used in the implementation of the offsetof() macro (offsetof in wikipedia), which is the more canonical way to get the offsets:

#include <stddef.h>

size_t FireOffset = offsetof(struct strProjectile, Fire);
size_t PlasmaOffset = offsetof(struct strProjectile, Plasma);

With this knowledge, you can produce the correct member pointers yourself:

short* Fire = (short*)((char*)ptrProjectiles + FireOffset);
short* Plasma = (short*)((char*)ptrProjectiles + PlasmaOffset);

I hope I don't have to mention that you shouldn't be doing this unless you have very good reason to do so. Studying purposes qualify for almost anything, though...

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • Thank you. That expands my code slightly further to be more correct as far as correct can go. This is purely for study purposes and I wouldn't use in practice. – Gavzooka Dec 29 '14 at 21:42
  • [Dereferencing null pointer causes undefined behaviour](http://stackoverflow.com/questions/26906621/does-struct-name-null-b-cause-undefined-behaviour-in-c11). You could fix that by writing `FireOffset = offsetof(struct strProjectile, Fire);` and so on. – M.M Dec 29 '14 at 23:34
  • @MattMcNabb That's precisely why `FireOffset = offsetof(struct strProjectile, Fire);` appears in the following code block of my answer... – cmaster - reinstate monica Dec 30 '14 at 00:25
  • @cmaster my point is that `offsetof` is correct and the null-dereferencing version isn't, it'd be better to just go straight to your introduction of `offsetof` or perhaps use this method (which is correct): `(char *)&ptrProjectiles->Fire - (char *)ptrProjectiles`, assuming that pointer points to a valid instance. – M.M Dec 30 '14 at 00:31
  • @user3629249 But the question is tagged C, not C++ !?! – cmaster - reinstate monica Dec 30 '14 at 09:12