Home > Blockchain >  C vs C placing structs in unsigned char buffer
C vs C placing structs in unsigned char buffer

Time:01-16

Does C have anything similar to C where one can place structs in an unsigned char buffer as is done in C as shown in the standard sec. 6.7.2

template<typename ...T>
struct AlignedUnion {
  alignas(T...) unsigned char data[max(sizeof(T)...)];
};
int f() {
  AlignedUnion<int, char> au;
  int *p = new (au.data) int;           // OK, au.data provides storage
  char *c = new (au.data) char();       // OK, ends lifetime of *p
  char *d = new (au.data   1) char();
  return *c   *d;                       // OK
}

In C I can certainly memcpy a struct of things(or int as shown above) into an unsigned char buffer, but then using a pointer to this struct one runs into strict aliasing violations; the buffer has different declared type.

So suppose one would want to replicate the second line in f the C above in C. One would do something like this

#include<string.h>
#include<stdio.h>
struct Buffer {
  unsigned char data[sizeof(int)];
};

int main()
{ 
  struct Buffer b;
  int n = 5;
  int* p = memcpy(&b.data,&n,sizeof(int));
  printf("%d",*p);  // aliasing violation here as unsigned char is accessed as int
  return 0;
}

Unions are often suggested i.e. union Buffer {int i;unsigned char b[sizeof(int)]}; but this is not quite as nice if the aim of the buffer is to act as storage (i.e. placing different sized types in there, by advancing a pointer into the buffer to the free part potenially some more for proper alignment).

CodePudding user response:

Have you tried using a union?

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

union Buffer {
    int int_;
    double double_;
    long double long_double_;
    unsigned char data[1];
};

int main() {
    union Buffer b;
    int n = 5;
    int *p = memcpy(&b.data, &n, sizeof(int));
    printf("%d", *p);  // aliasing violation here as unsigned char is accessed as int
    return 0;
}

The Buffer aligns data member according the type with the greatest alignment requirement.

CodePudding user response:

Yes, because of strict aliasing rule it is just not possible. As it is not possible to write a standard compliant malloc().

Your buffer is not aligned - alignas(int) from stdalign.h needs to be added.

If you want to protect against compiler optimizations, either:

  • just cast the pointer and access it and compile with -fno-strict-aliasing, or use volatile
  • or move the accessor to the buffer to another file that is compiled without LTO so that compiler just is not able to optimize it.

// mybuffer.c
#include <stdalign.h>
alignas(int) unsigned char buffer[sizeof(int)];
void *getbuffer() { return buffer; }


// main.c
#include <string.h>
#include <stdio.h>
#include "mybuffer.h"
int main() {
  void *data = getbuffer();
  // int *p = new (au.data) int;           // OK, au.data provides storage
  int *p = data;
  // char *c = new (au.data) char();       // OK, ends lifetime of *p
  char *c = data;
  *c = 0;
  // char *d = new (au.data   1) char();
  char *d = (char*)data   1;
  *d = 0;

  return *c   *d;
}
  •  Tags:  
  • Related