char * containing all kind of types

K

Keith Thompson

Nick Keighley said:
I think you're stuffed [English: in a bad position] then. If you read
a 4 byte integer (we'll assume a byte is 8 bits for this discussion)
it *must* be correctly aligned (and endianed) if you want to read it
out of your container as a C int type (I'm including long and short in
"int type").

Then you mean "integer type", not "int type"; "int" is one of several
integer types.

[...]
 
S

Seebs

His folks, is this safe code?
No.

int main(void)
{
char c = 'A'; int i = 1; float f = -1.01; double d =
1000000.99999;
char *pc; int *pi; float *pf; double *pd;
char *p, *data;

p = data = malloc(sizeof(char) + sizeof(int) + sizeof(float) +
sizeof(double));
if (data == NULL) {
fprintf(stderr, "out of mem\n");
exit(1);
}

memcpy(data, &c, sizeof(c));
data += sizeof(c);
memcpy(data, &i, sizeof(i));
data += sizeof(i);
memcpy(data, &f, sizeof(f));
data += sizeof(f);
memcpy(data, &d, sizeof(d));
data = p;

This part is fine.
pc = (char *)data;
printf("%c\n", *pc);
data += sizeof(c);
Fine.

pi = (int *)data;
printf("%d\n", *pi);
data += sizeof(i);

Not fine. You have no guarantee that 'pi' is correctly aligned. You could
do this with:
memcpy(&i, data, sizeof(i));

-s
 
S

Seebs

Not safe; data may be improperly aligned for an int* .
[...]

Thanks, Ike

"unsigned char *" can fix this?

No. Why on earth would you think it would?

There is no difference in size between char and unsigned char, and neither
of them guarantees alignment.

-s
 
B

Ben Bacarisse

David RF said:
You're very kind
Despite binary file, here in ascii, I have a file named data.csv and
another named data.def

Ah, the code explained a lot! Neither of these files is a binary file
(you read single "type indicator" characters from one and use fscanf to
read and convert data from the other).

Your problem is not about packing or unpacking binary data but about
storing data whose type you don't know until run-time. What you are
doing below works fine (by the way, you are clearly a competent C
programmer) but you want to avoid wasting the space used by having a
union for each cell.

It is true that you might end up using some odd unpacking code but that
comes from the fact that you store the data in rows rather than columns.
I'd switch to having the columns store the data since the types are
associated with columns rather than rows. That way, all the data can be
properly aligned and the access will be simpler (and probably faster).

The trick for doing this is to use a union to get the alignment but then
lie when you allocate and access the column:

typedef union TData {
char as_chr;
int as_int;
float as_flt;
double as_dbl;
} Data;

enum {type_chr, type_int, type_flt, type_dbl};

typedef struct TCol {
struct TTable *table;
struct TCol *prev;
struct TCol *next;
int id;
int type;
Data data[]; /* Flexible array member */
} Col;

To allocate a column of type 'type' you use:

Col *col = malloc(sizeof *col + nrows * type_size[type]);

type_size is an array that maps type numbers to sizes:

size_t type_size[] = {
[type_chr] = sizeof(char), [type_int] = sizeof(int),
/* and so on. [x] = y is C99's designated initialiser *.
};

This does mean that you need to know how many rows there are and there
won't be an actual representation of a row. The first is easy: read
the file twice; once to count the rows and once to read the actual
data. The second may or may not be a problem -- it depends on how use
this structure.

To access this column data you need to lie about the data member:

Data get_cell(size_t row, Col *col)
{
switch (col->type) {
case type_char:
return (Data){ .as_char = ((char *)col->data)[row] };
case type_int:
return (Data){ .as_char = ((int *)col->data)[row] };
/* etc... */
}
}

(I am using C99's compound literals since you seem happy with using C99
features).

If you can't switch to storing columns, then just store and access the
element using memcpy as you are planning to do. You'll need to store
(in the Col structure) the byte offset of each column. The data member
can then just be a char (or unsigned char) array.

<snip explanatory code>
 
D

David RF

It is true that you might end up using some odd unpacking code but that
comes from the fact that you store the data in rows rather than columns.
I'd switch to having the columns store the data since the types are
associated with columns rather than rows.  That way, all the data can be
properly aligned and the access will be simpler (and probably faster).

Ok, gonna try this, thanks again Ben!!
 
D

David RF

Ok, gonna try this, thanks again Ben!!

KISS principle!!!

Works like a charm, that's finally what I wan't:
(All suggestions are welcome)

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

enum {type_char, type_int, type_float, type_double};

#define FILE_LINE_SIZE 100

typedef struct TTable {
struct TCol *col_first;
struct TCol *col_last;
int cols;
int rows;
} Table;

typedef struct TCol {
struct TTable *table;
struct TCol *prev;
struct TCol *next;
int id;
int type;
void *cell;
char *as_char;
int *as_int;
float *as_float;
double *as_double;
} Col;

static Table *table_create()
{
Table *new;

new = malloc(sizeof(Table));
if (new == NULL) {
fprintf(stderr, "Nos hemos quedado sin memoria.\n");
exit(EXIT_FAILURE);
}
new->col_first = NULL;
new->col_last = NULL;
new->cols = 0;
new->rows = 0;
return new;
}

static Col *table_add_col(Table *table, int type)
{
Col *new;

new = malloc(sizeof(Col));
if (new == NULL) {
fprintf(stderr, "Nos hemos quedado sin memoria.\n");
exit(EXIT_FAILURE);
}
new->table = table;
new->id = table->cols++;
new->type = type;
switch (new->type) {
case type_char:
new->cell = new->as_char = malloc(table->rows * sizeof(char));
break;
case type_int:
new->cell = new->as_int = malloc(table->rows * sizeof(int));
break;
case type_float:
new->cell = new->as_float = malloc(table->rows * sizeof(float));
break;
case type_double:
new->cell = new->as_double = malloc(table->rows * sizeof(double));
break;
}
if (table->col_last) {
table->col_last->next = new;
new->prev = table->col_last;
} else {
table->col_first = new;
new->prev = NULL;
}
new->next = NULL;
table->col_last = new;
return new;
}

static void table_free(Table *table)
{
Col *col;

while (table->col_first) {
col = table->col_first->next;
free(table->col_first->cell);
free(table->col_first);
table->col_first = col;
}
free(table);
}

static void table_raise_error(Table *table, const char *err)
{
table_free(table);
fprintf(stderr, "%s", err);
exit(EXIT_FAILURE);
}

static void table_print(Table *table)
{
int i;
Col *col;

for (i = 0; i < table->rows; i++) {
printf("-\n");
for (col = table->col_first; col; col = col->next) {
switch (col->type) {
case type_char:
printf("\tCampo %d = %c\n", col->id, ((char *)col->cell));
break;
case type_int:
printf("\tCampo %d = %d\n", col->id, ((int *)col->cell));
break;
case type_float:
printf("\tCampo %d = %f\n", col->id, ((float *)col->cell));
break;
case type_double:
printf("\tCampo %d = %f\n", col->id, ((double *)col->cell));
break;
}
}
}
}

int main(void)
{
Table *table;
Col *col;
FILE *file;
char c, s[FILE_LINE_SIZE], *p;
int i = 0;

table = table_create();
/* Cuento lineas en data.csv */
file = fopen("data.csv", "r");
if (file == NULL) {
table_raise_error(table, "No se puede abrir el archivo data.csv en
modo lectura.\n");
}
while (fgets(s, FILE_LINE_SIZE, file) != NULL) {
table->rows++;
}
fclose(file);
if (table->rows == 0) {
table_raise_error(table, "Sin datos no puedo hacer mucho!!!\n");
}
file = fopen("data.def", "r");
if (file == NULL) {
table_raise_error(table, "No se puede abrir el archivo data.def en
modo lectura.\n");
}
/* Por cada tipo declarado en data.def ... */
while ((c = fgetc(file))) {
if ((c < '0') || (c > '3')) break;
/* ... creo una columna */
table_add_col(table, c - '0');
};
fclose(file);
if (table->cols == 0) {
table_raise_error(table, "Sin campos no puedo hacer mucho!!!\n");
}
/* Leo data.csv cargando la estructura */
file = fopen("data.csv", "r");
if (file == NULL) {
table_raise_error(table, "No se puede abrir el archivo data.csv en
modo lectura.\n");
}
while ((p = fgets(s, FILE_LINE_SIZE, file)) != NULL) {
col = table->col_first;
while (col) {
switch (col->type) {
case type_char:
sscanf(p, "%c", &((char *)col->cell));
break;
case type_int:
sscanf(p, "%d", &((int *)col->cell));
break;
case type_float:
sscanf(p, "%f", &((float *)col->cell));
break;
case type_double:
sscanf(p, "%lf", &((double *)col->cell));
break;
}
if ((col = col->next)) {
while (*++p != ',');
p++;
}
}
i++;
}
fclose(file);
table_print(table);
table_free(table);
return 0;
}

Thanks to all
 
B

Ben Bacarisse

David RF said:
KISS principle!!!

By removing the flexible array member, you've switched from one malloc
to two. That is probably simpler but it's not obviously simpler!
Works like a charm, that's finally what I wan't:
(All suggestions are welcome)

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

enum {type_char, type_int, type_float, type_double};

#define FILE_LINE_SIZE 100

typedef struct TTable {
struct TCol *col_first;
struct TCol *col_last;
int cols;
int rows;
} Table;

typedef struct TCol {
struct TTable *table;
struct TCol *prev;
struct TCol *next;
int id;
int type;
void *cell;
char *as_char;
int *as_int;
float *as_float;
double *as_double;
} Col;

Obviously you prefer to avoid the casts in my suggestion. That's fine
(I don't like them either) but I'd write this:

typedef struct TCol {
struct TTable *table;
struct TCol *prev;
struct TCol *next;
int id;
int type;
void *cells;
union {
char *as_char;
int *as_int;
float *as_float;
double *as_double;
} data;
} Col;

i.e. a union of pointer types with only the void *outside. You could do
away with this extra pointer but that brings up an issue when freeing
(I'll say more below).
static Col *table_add_col(Table *table, int type)
{
Col *new;

new = malloc(sizeof(Col));
if (new == NULL) {
fprintf(stderr, "Nos hemos quedado sin memoria.\n");
exit(EXIT_FAILURE);
}
new->table = table;
new->id = table->cols++;
new->type = type;
switch (new->type) {
case type_char:
new->cell = new->as_char = malloc(table->rows * sizeof(char));
break;
case type_int:
new->cell = new->as_int = malloc(table->rows * sizeof(int));
break;
case type_float:
new->cell = new->as_float = malloc(table->rows * sizeof(float));
break;
case type_double:
new->cell = new->as_double = malloc(table->rows * sizeof(double));
break;
}

Here I'd rather not repeat the malloc calls and I'd put the sizes in a
table.

table->cells = malloc(table->rows * type_size[table->type]);
switch (table->type) {
case type_char: new->data.as_char = table->cells; break;
case type_int: new->data.as_int = table->cells; break;
case type_float: new->data.as_float = table->cells; break;
case type_double: new->data.as_double = table->cells; break;
}

I'd be tempted to add a default case with an error message. If you do
away with the cells member, just use a local variable to hold the malloc
result.
if (table->col_last) {
table->col_last->next = new;
new->prev = table->col_last;
} else {
table->col_first = new;
new->prev = NULL;
}
new->next = NULL;
table->col_last = new;
return new;
}
<snip>

If you choose to do away with the void * member you'd have to switch on
the type and free the appropriate as_xxx member of the union. Not a
serious complication but its probably better to keep two pointers to the
cell data.
free(table->col_first);
table->col_first = col;
}
free(table);
}

static void table_print(Table *table)
{
int i;
Col *col;

for (i = 0; i < table->rows; i++) {
printf("-\n");
for (col = table->col_first; col; col = col->next) {
switch (col->type) {
case type_char:
printf("\tCampo %d = %c\n", col->id, ((char *)col->cell));
break;
case type_int:
printf("\tCampo %d = %d\n", col->id, ((int *)col->cell));
break;
case type_float:
printf("\tCampo %d = %f\n", col->id, ((float *)col->cell));
break;
case type_double:
printf("\tCampo %d = %f\n", col->id, ((double *)col->cell));
break;
}


Oh! Now I though the point of your multiple pointer types was to avoid
using a cast in places like this. You could have written:

case type_int:
printf("\tCampo %d = %d\n", col->id, col->as_int);
break;

and something almost identical with my suggested union of pointer types.
}
}
}

int main(void)
{
Table *table;
Col *col;
FILE *file;
char c, s[FILE_LINE_SIZE], *p;
int i = 0;

table = table_create();
/* Cuento lineas en data.csv */
file = fopen("data.csv", "r");
if (file == NULL) {
table_raise_error(table, "No se puede abrir el archivo data.csv en
modo lectura.\n");
}
while (fgets(s, FILE_LINE_SIZE, file) != NULL) {
table->rows++;
}

I might just count '\n' characters rather than reading the lines:

while ((c = fgetc(file)) != EOF)
if (c == '\n')
table->rows++;
fclose(file);
if (table->rows == 0) {
table_raise_error(table, "Sin datos no puedo hacer mucho!!!\n");
}
file = fopen("data.def", "r");
if (file == NULL) {
table_raise_error(table, "No se puede abrir el archivo data.def en
modo lectura.\n");
}
/* Por cada tipo declarado en data.def ... */
while ((c = fgetc(file))) {
if ((c < '0') || (c > '3')) break;
/* ... creo una columna */
table_add_col(table, c - '0');
};
fclose(file);
if (table->cols == 0) {
table_raise_error(table, "Sin campos no puedo hacer mucho!!!\n");
}
/* Leo data.csv cargando la estructura */
file = fopen("data.csv", "r");
if (file == NULL) {
table_raise_error(table, "No se puede abrir el archivo data.csv en
modo lectura.\n");
}
while ((p = fgets(s, FILE_LINE_SIZE, file)) != NULL) {
col = table->col_first;

If you use a for loop here you can make it simpler to check that i starts
at zero. I had to look up and check! More importantly, you need to
check that there are no more rows now than when you counted them:

for (i = 0; i said:
while (col) {
switch (col->type) {
case type_char:
sscanf(p, "%c", &((char *)col->cell));
break;
case type_int:
sscanf(p, "%d", &((int *)col->cell));
break;
case type_float:
sscanf(p, "%f", &((float *)col->cell));
break;
case type_double:
sscanf(p, "%lf", &((double *)col->cell));
break;


and again, here, I though you planned to avoid the casts. If you don't
mind them, you can do away with all the extra pointer types.
 
D

David RF

Obviously you prefer to avoid the casts in my suggestion.  That's fine
(I don't like them either) but I'd write this:

  typedef struct TCol {
        struct TTable *table;
        struct TCol *prev;
        struct TCol *next;
        int id;
        int type;
        void *cells;
        union {
            char   *as_char;
            int    *as_int;
            float  *as_float;
            double *as_double;
        } data;
  } Col;

i.e. a union of pointer types with only the void *outside.  You could do
away with this extra pointer but that brings up an issue when freeing
(I'll say more below).

oops, it's true, I'm so stupid :(

Thank for all suggestions!
 
D

David RF

[...]
fixed!!
grrr, I'll have to put in the credits of the program before me

If is useful for someone, the final version:

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

enum {type_char, type_int, type_float, type_double};
static size_t type_size[] = {sizeof(char), sizeof(int), sizeof(float),
sizeof(double)};

#define FILE_LINE_SIZE 100

typedef struct TTable {
struct TCol *col_first;
struct TCol *col_last;
int cols;
int rows;
} Table;

typedef struct TCol {
struct TTable *table;
struct TCol *prev;
struct TCol *next;
int id;
int type;
void *cell;
union {
char *as_char;
int *as_int;
float *as_float;
double *as_double;
} data;
} Col;

static Table *table_create()
{
Table *new;

new = malloc(sizeof(Table));
if (new == NULL) {
fprintf(stderr, "Nos hemos quedado sin memoria.\n");
exit(EXIT_FAILURE);
}
new->col_first = NULL;
new->col_last = NULL;
new->cols = 0;
new->rows = 0;
return new;
}

static void table_free(Table *table)
{
Col *col;

while (table->col_first) {
col = table->col_first->next;
free(table->col_first->cell);
free(table->col_first);
table->col_first = col;
}
free(table);
}

static void table_raise_error(Table *table, const char *err)
{
table_free(table);
fprintf(stderr, "%s", err);
exit(EXIT_FAILURE);
}

static Col *table_add_col(Table *table, int type)
{
Col *new;

new = malloc(sizeof(Col));
if (new == NULL) {
table_raise_error(table, "Nos hemos quedado sin memoria.\n");
}
new->table = table;
new->id = table->cols++;
new->type = type;
new->cell = malloc(table->rows * type_size[type]);
switch (new->type) {
case type_char:
new->data.as_char = new->cell;
break;
case type_int:
new->data.as_int = new->cell;
break;
case type_float:
new->data.as_float = new->cell;
break;
case type_double:
new->data.as_double = new->cell;
break;
}
if (table->col_last) {
table->col_last->next = new;
new->prev = table->col_last;
} else {
table->col_first = new;
new->prev = NULL;
}
new->next = NULL;
table->col_last = new;
return new;
}

static void table_print(Table *table)
{
int i;
Col *col;

for (i = 0; i < table->rows; i++) {
printf("-\n");
for (col = table->col_first; col; col = col->next) {
switch (col->type) {
case type_char:
printf("\tCampo %d = %c\n", col->id, col->data.as_char);
break;
case type_int:
printf("\tCampo %d = %d\n", col->id, col->data.as_int);
break;
case type_float:
printf("\tCampo %d = %f\n", col->id, col->data.as_float);
break;
case type_double:
printf("\tCampo %d = %f\n", col->id, col->data.as_double);
break;
}
}
}
}

int main(void)
{
Table *table;
Col *col;
FILE *file;
char c, s[FILE_LINE_SIZE], *p;
int i;

table = table_create();
/*
* Cuento lineas en data.csv
* Es necesario saber cuantos registros hay para poder reservar
memoria
* en bloque para cada columna.
*/
file = fopen("data.csv", "r");
if (file == NULL) {
table_raise_error(table, "No se puede abrir el archivo data.csv
en modo lectura.\n");
}
while ((c = fgetc(file)) != EOF) {
if (c == '\n') table->rows++;
}
fclose(file);
if (table->rows == 0) {
table_raise_error(table, "Sin datos no puedo hacer mucho!!!\n");
}
/* Leo data.def */
file = fopen("data.def", "r");
if (file == NULL) {
table_raise_error(table, "No se puede abrir el archivo data.def
en modo lectura.\n");
}
/* Por cada tipo declarado en data.def ... */
while ((c = fgetc(file))) {
if ((c < '0') || (c > '3')) break;
/* ... creo una columna */
table_add_col(table, c - '0');
};
fclose(file);
if (table->cols == 0) {
table_raise_error(table, "Sin campos no puedo hacer mucho!!!\n");
}
/* Leo data.csv cargando la estructura */
file = fopen("data.csv", "r");
if (file == NULL) {
table_raise_error(table, "No se puede abrir el archivo data.csv
en modo lectura.\n");
}
for (i = 0; (i < table->rows) && (p = fgets(s, FILE_LINE_SIZE,
file)); i++) {
col = table->col_first;
while (col) {
switch (col->type) {
case type_char:
sscanf(p, "%c", &(col->data.as_char));
break;
case type_int:
sscanf(p, "%d", &(col->data.as_int));
break;
case type_float:
sscanf(p, "%f", &(col->data.as_float));
break;
case type_double:
sscanf(p, "%lf", &(col->data.as_double));
break;
}
if ((col = col->next)) {
while (*++p != ',');
p++;
}
}
}
fclose(file);
table_print(table);
table_free(table);
return 0;
}

*/ End */

A last question, just for better understand

What about this, is safe code?

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

typedef union TCell {
char as_char;
int as_int;
float as_float;
double as_double;
} Cell;

int main(void)
{
char *row;
Cell *cell;
size_t map[4], mapsiz;

map[0] = 0;
map[1] = map[0] + sizeof(char);
map[2] = map[1] + sizeof(int);
map[3] = map[2] + sizeof(float);
mapsiz = map[3] + sizeof(double);

row = malloc(mapsiz);

cell = (Cell *)(row + map[0]);
cell->as_char = 'A';
cell = (Cell *)(row + map[1]);
cell->as_int = 1;
cell = (Cell *)(row + map[2]);
cell->as_float = 1.9;
cell = (Cell *)(row + map[3]);
cell->as_double = 111111.999999;

cell = (Cell *)(row + map[0]);
printf("%c\n", cell->as_char);
cell = (Cell *)(row + map[1]);
printf("%d\n", cell->as_int);
cell = (Cell *)(row + map[2]);
printf("%f\n", cell->as_float);
cell = (Cell *)(row + map[3]);
printf("%f\n", cell->as_double);

free(row);

return 0;
}

Thanks
 
B

Ben Bacarisse

David RF said:
On 26 mayo, 18:04, David RF <[email protected]> wrote:
sscanf(p, "%c", &(col->data.as_char));


There is nothing at all wrong with this, but I prefer to use minimal
brackets in most cases. All post-fix operators bind tighter than all
prefix operators so you can just write

&col->data.as_char

Some people say the () help readability but I disagree. All the other
possible bracket positions have something illogical (or at least odd)
about them so someone who is unsure of the precedence should not be
unsure for long.

It is, though, very much a matter of style and a minor one at that.
 
D

David RF

David RF said:
On 26 mayo, 18:04, David RF <[email protected]> wrote:
                               sscanf(p, "%c", &(col->data.as_char));


There is nothing at all wrong with this, but I prefer to use minimal
brackets in most cases.  All post-fix operators bind tighter than all
prefix operators so you can just write

  &col->data.as_char

Some people say the () help readability but I disagree.  All the other
possible bracket positions have something illogical (or at least odd)
about them so someone who is unsure of the precedence should not be
unsure for long.

It is, though, very much a matter of style and a minor one at that.


I have learned a lot in this post, you're a master

but? hey!! what about the last question in my last post? :p
 
B

Ben Bacarisse

David RF said:
I have learned a lot in this post, you're a master

but? hey!! what about the last question in my last post? :p

Do you mean:

| grrr, I'll have to put in the credits of the program before me

? It doesn't matter to me if you credit me or not. I don't want to
sound churlish -- it is nice to be thanked (as you have done) but I am
not bothered about credit. I doubt that anyone else here feels
significantly different (and I hope they will say if they do) though it
would be a different matter if the assistance was significant -- say a
new algorithm or a neat way to represent some data structure.

I did once ask not to be credited -- the code was so bad that I could
not stand the thought of having my name associated with it in any way!

The exchanges about your code have been considerably more good natured
and productive than the norm for this group and that benefits everyone;
so thank you for posting your question.
 
D

David RF

Do you mean:

| grrr, I'll have to put in the credits of the program before me

?  It doesn't matter to me if you credit me or not.  I don't want to
sound churlish -- it is nice to be thanked (as you have done) but I am
not bothered about credit.  I doubt that anyone else here feels
significantly different (and I hope they will say if they do) though it
would be a different matter if the assistance was significant -- say a
new algorithm or a neat way to represent some data structure.

I did once ask not to be credited -- the code was so bad that I could
not stand the thought of having my name associated with it in any way!

The exchanges about your code have been considerably more good natured
and productive than the norm for this group and that benefits everyone;
so thank you for posting your question.

jajaja, that was just a joke, 27 may 14:02, at the end

*/ End */

A last question, just for better understand

What about this, is safe code?

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

typedef union TCell {
char as_char;
int as_int;
float as_float;
double as_double;

} Cell;

int main(void)
{
char *row;
Cell *cell;
size_t map[4], mapsiz;

map[0] = 0;
map[1] = map[0] + sizeof(char);
map[2] = map[1] + sizeof(int);
map[3] = map[2] + sizeof(float);
mapsiz = map[3] + sizeof(double);

row = malloc(mapsiz);

cell = (Cell *)(row + map[0]);
cell->as_char = 'A';
cell = (Cell *)(row + map[1]);
cell->as_int = 1;
cell = (Cell *)(row + map[2]);
cell->as_float = 1.9;
cell = (Cell *)(row + map[3]);
cell->as_double = 111111.999999;

cell = (Cell *)(row + map[0]);
printf("%c\n", cell->as_char);
cell = (Cell *)(row + map[1]);
printf("%d\n", cell->as_int);
cell = (Cell *)(row + map[2]);
printf("%f\n", cell->as_float);
cell = (Cell *)(row + map[3]);
printf("%f\n", cell->as_double);

free(row);

return 0;

}
 
B

Ben Bacarisse

David RF said:
jajaja, that was just a joke, 27 may 14:02, at the end

The best way to refer to a post is by using the message ID. I usually
just copy the whole reader. You are referring to this post:

Message-ID: <3d99e4b7-8016-4a96-862b-baa393e85a3d@f14g2000vbn.googlegroups.com>

I missed the question at the end.
A last question, just for better understand

What about this, is safe code?

It is not more "safe" than the code in your original post. The
trouble is the term "safe": there are
lots of machine that will accept it with no problem but I think you mean
is it safe on a wide variety of hardware. I.e. is it portable? No, it
isn't. For example, the pointer...
#include <stdio.h>
#include <stdlib.h>

typedef union TCell {
char as_char;
int as_int;
float as_float;
double as_double;

} Cell;

int main(void)
{
char *row;
Cell *cell;
size_t map[4], mapsiz;

map[0] = 0;
map[1] = map[0] + sizeof(char);
map[2] = map[1] + sizeof(int);
map[3] = map[2] + sizeof(float);
mapsiz = map[3] + sizeof(double);

row = malloc(mapsiz);

cell = (Cell *)(row + map[0]);
cell->as_char = 'A';
cell = (Cell *)(row + map[1]);
cell->as_int = 1;

.... cell need not be properly aligned for an int.

<snip similar>
 
E

Eric Sosman

[...]
What about this, is safe code?

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

typedef union TCell {
char as_char;
int as_int;
float as_float;
double as_double;

} Cell;

int main(void)
{
char *row;
Cell *cell;
size_t map[4], mapsiz;

map[0] = 0;
map[1] = map[0] + sizeof(char);
map[2] = map[1] + sizeof(int);
map[3] = map[2] + sizeof(float);
mapsiz = map[3] + sizeof(double);

row = malloc(mapsiz);

if (row == NULL) ...
cell = (Cell *)(row + map[0]);
cell->as_char = 'A';
cell = (Cell *)(row + map[1]);

Bzzzt! The fact that a union can hold values of many types
(one at a time) does not imply that the union has no alignment
requirement. It actually implies the opposite: The union as a
whole must be aligned at least as strictly as its strictest
element.

The memory you obtain from malloc() -- if malloc() is able
to provide any -- is aligned so as to be able to hold any data
object of any type whatsoever. It's therefore aligned properly
for all of the union's elements, and also for the union itself.
But the address one byte further along is not necessarily a spot
where a union can begin.

I suggest you ponder Questions 2.12, 2.13, and 16.7 in the
FAQ before you go much further.
 
D

David RF

The best way to refer to a post is by using the message ID.  I usually
just copy the whole reader.  You are referring to this post:

Message-ID: <3d99e4b7-8016-4a96-862b-baa393e85a3d@f14g2000vbn.googlegroups.com>
Pointed


I missed the question at the end.
A last question, just for better understand
What about this, is safe code?

It is not more "safe" than the code in your original post.  The
trouble is the term "safe": there are
lots of machine that will accept it with no problem but I think you mean
is it safe on a wide variety of hardware.  I.e. is it portable?  No, it
isn't.  For example, the pointer...


#include <stdio.h>
#include <stdlib.h>
typedef union TCell {
        char as_char;
        int as_int;
        float as_float;
        double as_double;
int main(void)
{
        char *row;
        Cell *cell;
        size_t map[4], mapsiz;
        map[0] = 0;
        map[1] = map[0] + sizeof(char);
        map[2] = map[1] + sizeof(int);
        map[3] = map[2] + sizeof(float);
        mapsiz = map[3] + sizeof(double);
        row = malloc(mapsiz);
        cell = (Cell *)(row + map[0]);
        cell->as_char = 'A';
        cell = (Cell *)(row + map[1]);
        cell->as_int = 1;

... cell need not be properly aligned for an int.

OK!!
and stay quiet, not gonna put you in the credits :) thanks again
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,780
Messages
2,569,608
Members
45,252
Latest member
MeredithPl

Latest Threads

Top