Are multiple variable-length arrays in a structure in C possible? - c

Is something like this possible in C? It would be really nice to have both of these.
typedef struct {
int r;
char a0[0];
char a1[0];
}

No. Rather use dynamically allocated memory. Something like:
typedef struct {
int *data1;
size_t len1
int *data;
size_t len2;
} sometype;
sometype *alloc_sometype(size_t len1, size_t len2) {
sometype *n = malloc(sizeof(sometype));
if (!n) return NULL;
n->len1 = len1;
n->len2 = len2;
n->data1 = malloc(sizeof(int) * len1);
n->data2 = malloc(sizeof(int) * len2);
// Error handling if those malloc calls fail
return n;
}

The flexible array must be the last member in the struct so you can't have two. However, if both arrays are supposed to have the same size, you could combine them:
#include <stdio.h>
#include <stdlib.h>
typedef struct { // a struct to keep one a0 a1 pair
char a0;
char a1;
} A;
typedef struct {
size_t len;
A a[]; // flexible array of a0 a1 pairs
} data;
data *data_create(size_t len) {
data *d = malloc(sizeof *d + len * sizeof *d->a);
if(d) d->len = len;
return d;
}
int main() {
data *d = data_create(10);
for(size_t i = 0; i < d->len; ++i) {
d->a[i].a0 = i;
d->a[i].a1 = i+1;
}
free(d);
}

Related

errors with structure save and load function

#include <stdio.h>
#include <stdlib.h>
struct birdhome{
int area;
int heightcm;
int feederquantity;
char hasNest[6];
};
struct bird{
char isRinged[6];
char nameSpecies[50];
int birdAgeMonths;
struct BirdHome *hom;
char gender[7];
};
int save(char * filename, struct bird *st, int n);
int load(char * filename);
int main(void)
{
char * filename = "birds.dat";
struct bird birds[] = { "True","sparrow",3,10,20,2,"False","Male","False","crane",24,50,100,6,"True","Female","False","False","griffin",10,100,80,1,"False","Male" };
int n = sizeof(struct bird) / sizeof(birds[0]);
save(filename, birds, n);
load(filename);
return 0;
}
int save(char * filename, struct bird * st, int n)
{
FILE * fp;
char *c;
int size = n * sizeof(struct bird);
if ((fp = fopen(filename, "wb")) == NULL)
{
perror("Error occured while opening file");
return 1;
}
c = (char *)&n;
for (int i = 0; i<sizeof(int); i++)
{
putc(*c++, fp);
}
c = (char *)st;
for (int i = 0; i < size; i++)
{
putc(*c, fp);
c++;
}
fclose(fp);
return 0;
}
int load(char * filename){
FILE * fp;
char *c;
int m = sizeof(int);
int n, i;
int *pti = (int *)malloc(m);
if ((fp = fopen(filename, "r")) == NULL)
{
perror("Error occured while opening file");
return 1;
}
c = (char *)pti;
while (m>0)
{
i = getc(fp);
if (i == EOF) break;
*c = i;
c++;
m--;
}
n = *pti;
struct bird * ptr = (struct bird *) malloc(n * sizeof(struct bird));
c = (char *)ptr;
while ((i= getc(fp))!=EOF)
{
*c = i;
c++;
}
printf("\n%d birds in the file stored\n\n", n);
for (int k = 0; k<n; k++)
{
printf("%-10d %-6s %-50s %-24d %-100d %-100d %-10d %-10s %-10s \n", k + 1, (ptr + k)->isRinged, (ptr + k)->nameSpecies,(ptr + k)->birdAgeMonths,(ptr + k)->hom.area,(ptr + k)->hom.heightcm,(ptr + k)->hom.feederquantity,(ptr + k)->hom.hasNest,(ptr + k)->gender);
}
Well, the program is theoretically running. The problem is with the printf inside the load function.
The error says that all the structure types that come in struct Birdhome is a
pointer and that I should use -> instead of . in it.
But when I do this it says that I should change the . to ->.
The problem is that bird.hom is a pointer. Saving a pointer to a file is not a useful thing to do, because memory addresses change from one process to another. It's also not saving the contents of the BirdHome structure. And your initialization of birds doesn't work, because you can't initialize members of an indirect structure as part of the main structure.
You should declare it as an embedded structure rather than a pointer.
struct bird{
char isRinged[6];
char nameSpecies[50];
int birdAgeMonths;
struct BirdHome hom;
char gender[7];
};
Declaring it as a pointer would be useful if you wanted a dynamically-sized array of homes, or you wanted to allow multiple birds to reference the same BirdHome structure. If that's what you really need, you'll need to redesign your save and load functions so they dereference the pointer and save what it points to. And if you have a dynamically-sized array of BirdHome, you need to include the array size in bird.
Other errors in your code:
You have an extra "False" in the initialization list of birds. It should be:
struct bird birds[] = { "True","sparrow",3,10,20,2,"False","Male","False","crane",24,50,100,6,"True","Female","False","griffin",10,100,80,1,"False","Male" };
Your calculation of n is incorrect. It should be:
int n = sizeof(birds) / sizeof(birds[0]);

function to clear malloc, and make pointer to null

in my last question, I've asked how to use function to free an malloc'ed array, I wanted to improve my code so that the function won't just free the memory but also will set the pointer to NULL once it finishes the clearing.
Also I want a single function to do both - setting and clearing, depending on the command I'm passing, this is what I've done so far:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint-gcc.h>
char **set_arr(int number, char *command);
int command_read(char *command);
void clear_arr(char *arr[], int size);
char set[] = "set";
char clear[] = "clear";
int main() {
int num = // get number from user;
char** my_arr = NULL;
my_arr = set_arr(num, set);
// so far the code works as excepted
set_arr((size_t)&my_arr, clear);
return 0;
}
int command_read(char *command) {
if (strcmp(command, set) == 0)
return 'S';
if (strcmp(command, clear) == 0)
return 'C';
}
char **set_arr(int number, char *command) {
static char **arr = NULL;
static int size;
switch (command_read(command)) {
case 'S':
size = (int)number;
arr = malloc((size + 1) * sizeof(char *));
for (int i = 0; i <= size; i++) {
arr[i] = NULL;
if (i == size)
break;
arr[i] = malloc((string_len) * sizeof(char));
}
break;
case 'C':
clear_arr(arr, size);
free(arr);
uintptr_t value = number;
uint64_t *temp = (void *)value;
*temp = 0x0;
break;
}
return arr;
}
void clear_arr(char *arr[], int size) {
for (int i = 0; i < size; i++) {
free(arr[i]);
arr[i] = NULL;
}
}
I know that there is better methods to clear (and allocate memory?) but my primary question is, did I free all the memory I allocated for the array, and after the clearing, does the pointer my_arr is set correctly to NULL?
Writing a generic function to achieve your goal is not possible in Standard C because pointers to different types of objects may have a different representation so you cannot pass the address of a pointer and expect the function to handle it in a generic manner.
Yet this provision in the C Standard is not used on most current systems today. In particular, the POSIX standard mandates that all pointers have the same representation. Hence your generic function can work on these systems, with some precautions to avoid compilation warnings:
// free an array of allocated things
void free_array(void ***p, size_t count) {
void **array = *p;
for (size_t i = 0; i < count; i++) {
free(array[i]);
array[i] = NULL; // for safety
}
free(array);
*p = NULL;
}
// deal with the non portable conversion with macros
#define FREE_ARRAY(p, n) free_array((void ***)(void *)&(p), n)
// allocate an array of pointers to allocated things of size `size`.
// return a pointer to the array or `NULL` if any allocation failed
void **malloc_array(size_t count, size_t size) {
void **array = malloc(count * sizeof(*array));
if (array) {
for (size_t i = 0; i < count; i++) {
array[i] = calloc(size, 1); // allocate and initialize to all bits zero
if (array[i] == NULL) {
while (i-- > 0) {
free(array[i]);
array[i] = NULL;
}
return NULL;
}
}
}
return array;
}
#define MALLOC_ARRAY(n, type) ((type **)(void *)malloc_array(n, sizeof(type)))
#define MALLOC_2D_ARRAY(n1, n2, type) ((type **)(void *)malloc_array(n1, (n2) * sizeof(type)))
Passing the command as a string is very inefficient. You should use an int or an enum for the command, but you can use the above macros and code in your program this way:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint-gcc.h>
int main() {
int string_len = 100;
int num = 10; // get number from user;
char **my_arr = MALLOC_2D_ARRAY(num, string_len, char);
FREE_ARRAY(my_arr, num);
return 0;
}

Using max_align_t to store a chunk of bytes

In this thread I was suggested to use max_align_t in order to get an address properly aligned for any type, I end up creating this implementation of a dynamic array:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>
struct vector {
size_t capacity;
size_t typesize;
size_t size;
max_align_t data[];
};
#define VECTOR(v) ((struct vector *)((unsigned char *)v - offsetof(struct vector, data)))
static void *valloc(size_t typesize, size_t size)
{
struct vector *vector;
vector = calloc(1, sizeof(*vector) + typesize * size);
if (vector == NULL) {
return NULL;
}
vector->typesize = typesize;
vector->capacity = size;
vector->size = 0;
return vector->data;
}
static void vfree(void *data, void (*func)(void *))
{
struct vector *vector = VECTOR(data);
if (func != NULL) {
for (size_t iter = 0; iter < vector->size; iter++) {
func((unsigned char *)vector->data + vector->typesize * iter);
}
}
free(vector);
}
static void *vadd(void *data)
{
struct vector *vector = VECTOR(data);
struct vector *new;
size_t capacity;
if (vector->size >= vector->capacity) {
capacity = vector->capacity * 2;
new = realloc(vector, sizeof(*vector) + vector->typesize * capacity);
if (new == NULL) {
return NULL;
}
new->capacity = capacity;
new->size++;
return new->data;
}
vector->size++;
return vector->data;
}
static size_t vsize(void *data)
{
return VECTOR(data)->size;
}
static void vsort(void *data, int (*comp)(const void *, const void *))
{
struct vector *vector = VECTOR(data);
if (vector->size > 1) {
qsort(vector->data, vector->size, vector->typesize, comp);
}
}
static char *vgetline(FILE *file)
{
char *data = valloc(sizeof(char), 32);
size_t i = 0;
int c;
while (((c = fgetc(file)) != '\n') && (c != EOF)) {
data = vadd(data);
data[i++] = (char)c;
}
data = vadd(data);
data[i] = '\0';
return data;
}
struct data {
int key;
char *value;
};
static int comp_data(const void *pa, const void *pb)
{
const struct data *a = pa;
const struct data *b = pb;
return strcmp(a->value, b->value);
}
static void free_data(void *ptr)
{
struct data *data = ptr;
vfree(data->value, NULL);
}
int main(void)
{
struct data *data;
data = valloc(sizeof(struct data), 1);
if (data == NULL) {
perror("valloc");
exit(EXIT_FAILURE);
}
for (size_t i = 0; i < 5; i++) {
data = vadd(data);
if (data == NULL) {
perror("vadd");
exit(EXIT_FAILURE);
}
data[i].value = vgetline(stdin);
data[i].key = (int)vsize(data[i].value);
}
vsort(data, comp_data);
for (size_t i = 0; i < vsize(data); i++) {
printf("%d %s\n", data[i].key, data[i].value);
}
vfree(data, free_data);
return 0;
}
But I'm not sure if I can use max_align_t to store a chunk of bytes:
struct vector {
size_t capacity;
size_t typesize;
size_t size;
max_align_t data[]; // Used to store any array,
// for example an array of 127 chars
};
Does it break the one past the last element of an array rule?
Does it break the one past the last element of an array rule?
No.
Using max_align_t to store a chunk of bytes
OP's issue is not special because it uses a flexible array member.
As a special case, the last element of a structure ... have an incomplete array type; this is called a flexible array member. ... However, when a . (or ->) operator has a left operand that is (a pointer to) a structure with a flexible array member and the right operand names that member, it behaves as if that member were replaced with the longest array (with the same element type) ...
It is the same issue as accessing any allocated memory or array of one type as if it was another type.
The conversion from max_align_t * to char * to void * is well defined when alignment is done right.
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. C11dr ยง6.3.2.3 7
All reviewed accessing in code do not attempt to access outside the "as if" array.

General-purpose char buffer used as array of structs with flexible array member

You can't have arrays of structures with flexible array members.
This is the TL;DR of this question. And thinking about it, it makes perfect sense.
However, may one simulate an array of structures with flexible array member - let's call them swfam - of fixed size as below :
#include <assert.h>
#include <stdlib.h>
typedef struct {
int foo;
float bar[];
} swfam_t; // struct with FAM
typedef struct { // this one also has a FAM but we could have used a char array instead
size_t size, // element count in substruct
count; // element count in this struct
char data[];
} swfam_array_t;
#define sizeof_swfam(size) (sizeof(swfam_t) + (size_t)(size) * sizeof(float))
swfam_array_t *swfam_array_alloc(size_t size, size_t count) {
swfam_array_t *a = malloc(sizeof(swfam_array_t) + count * sizeof_swfam(size));
if (a) {
a->size = size;
a->count = count;
}
return a;
}
size_t swfam_array_index(swfam_array_t *a, size_t index) {
assert(index < a->count && "index out of bounds");
return index * sizeof_swfam(a->size);
}
swfam_t *swfam_array_at(swfam_array_t *a, size_t index) {
return (swfam_t *)&a->data[swfam_array_index(a, index)];
}
int main(int argc, char *argv[]) {
swfam_array_t *a = swfam_array_alloc(100, 1000);
assert(a && "allocation failed");
swfam_t *s = swfam_array_at(a, 42);
s->foo = -18; // do random stuff..
for (int i = 0; i < 1000; ++i)
s->bar[i] = (i * 3.141592f) / s->foo;
free(a);
return 0;
}
Is the trick valid C99 / C11 ? Am I lurking towards undefined behaviour ?
One way to do this would be to use a pointer member instead of a flexible array. You would then have to manually allocate its size via malloc() et. al. [] is typically used only when the array is initialized on declaration, which is not possible with struct, which is essentially a definition, not a declaration. The ability to immediately declare an instance of the struct type does not change the nature of the definition, it's just for convenience.
typedef struct {
int foo;
float* bar; } swfam_t; // struct with FAM

Quicksorting an array of pointers to structs

I'm currently attempting to use the built-in quicksort provided by C in order to sort an array of pointers to structs. I want to sort each element based on a name element within the struct.
Although my debug output of the entire array each time through the comparison function shows me that the function is indeed shifting elements, the end result is not the correct sorted order. Is there something I'm just not seeing here?
typedef struct // The custom data type.
{
char *name;
} Person;
----------------------------
Person **people; // A dynamically allocated array of Person pointers.
int numPeople; // The logical index of people.
int maxPeople; // The current maximum capacity of people.
int compare(const void *a, const void *b) // The comparison function for determining
{ // alphabetic ordering.
const Person *const *p1 = a;
const Person *const *p2 = b;
return strcmp((*p1)->name, (*p2)->name); // Compare alphabetically, return result.
}
void SomeFunction(void)
{
qsort(people, numPeople, sizeof(Person *), compare); // Perform the sort.
}
Thanks for help with this.
I have tested your code and it looks working OK. Here is the code I compiled with gcc 4.5.2:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct // The custom data type.
{
char *name;
} Person;
Person **people; // A dynamically allocated array of Person pointers.
int numPeople; // The logical index of people.
int maxPeople; // The current maximum capacity of people.
int compare(const void *a, const void *b) // The comparison function for determining
{ // alphabetic ordering.
const Person *const *p1 = a;
const Person *const *p2 = b;
return strcmp((*p1)->name, (*p2)->name); // Compare alphabetically, return result.
}
void SomeFunction(void)
{
qsort(people, numPeople, sizeof(Person *), compare); // Perform the sort.
}
int main()
{
int iCnt;
maxPeople = 4;
numPeople = 4;
people = calloc(1, sizeof(Person *) * maxPeople);
people[0] = calloc(1, sizeof(Person));
people[1] = calloc(1, sizeof(Person));
people[2] = calloc(1, sizeof(Person));
people[3] = calloc(1, sizeof(Person));
people[0]->name = strdup("Tanya");
people[1]->name = strdup("Alfred");
people[2]->name = strdup("Harry");
people[3]->name = strdup("Oakley");
for(iCnt = 0; iCnt < numPeople; iCnt ++)
printf("[%d] %s\n", iCnt, people[iCnt]->name);
SomeFunction();
for(iCnt = 0; iCnt < numPeople; iCnt ++)
printf("[%d] %s\n", iCnt, people[iCnt]->name);
return 0;
}
The code looks legit and I'm not sure what's wrong. Could you try compiling the code I tested and see if it works?
Can you try with this
int compare(const void *a, const void *b) // The comparison function for determining
{ // alphabetic ordering.
const Person *p1 = *(const Person**)a;
const Person *p2 = *(const Person**)b;
return strcmp((p1)->name, (p2)->name); // Compare alphabetically, return result.
}

Resources