Custom Memory Mapped Allocator SEGFAULTs on allocate after freeing - c

So I needed a simple allocator to allocate (on occasion with zeroing) and later free 4K blocks from a pool of mapped memory. However, after implementing this, while testing I found that after freeing a block or two, if I tried to allocate a block, the program would SEGFAULT.
Curiously enough, when I free multiple blocks in a row, nothing seems to break.
Some important definitions collected from other files:
#define xmattr_constant __attribute__((const))
#define xmattr_malloc __attribute__((malloc))
#define xmattr_pure __attribute__((pure))
#define xm_likely(x) __builtin_expect(!!(x), 1)
#define xm_unlikely(x) __builtin_expect(!!(x), 0)
#define ABLKLEN 4096 // 4K pagesize
typedef struct {
uint8_t magic[16]; // "sfDB5" "vX.XXXXXXX" '\0'
uint8_t *freelist;
uint64_t size;
uint64_t bounds;
} arenaheader;
Allocation code:
void *pd_arena;
void pd_init (size_t len, uint8_t *map) {
int x;
size_t const block = len / 256; // arena physical size
size_t const size = (block / ABLKLEN) * ABLKLEN; // arena useable size
arenaheader *header;
for (x = 0; x < 256; x++) {
header = (void *) &(map[x * block]);
header->freelist = NULL; // no free blocks because all are free
header->size = size; // useable size
header->bounds = ABLKLEN; // current bounds
}
return;
}
xmattr_malloc void *pd_mallocBK (void) {
arenaheader *header = pd_arena;
uint8_t *ptr;
if (xm_unlikely (header->freelist)) { // there's a sitting free block
ptr = header->freelist; // return the free block
void **next = ptr;
header->freelist = *next; // update the free list
} else if (xm_likely (header->bounds < header->size)) { // no free blocks
ptr = pd_arena;
ptr += header->size;
header->size += ABLKLEN;
} else { // no more blocks
ptr = NULL;
}
return ptr;
}
xmattr_malloc void *pd_callocBK (void) {
void *ptr = pd_mallocBK ();
if (xm_likely (ptr)) // allocation was successful
memset (ptr, 0, ABLKLEN);
return ptr;
}
void pd_freeBK (void *ptr) {
arenaheader *header = pd_arena;
if (xm_likely (ptr)) { // non-NULL ptr
void *next = header->freelist; // get current top of stack
void **this = ptr;
*this = next; // move address of current top of stack to ptr
header->freelist = ptr; // push ptr to stack
}
return;
}
Test code:
#define F_LEN (1024 * 1024 * 1024) // 1 GB
#define A_LEN (F_LEN / 256)
int main (int argc, char **argv) {
int x, y;
// setup
int fd;
uint8_t *map;
assert (fd = open ("./pd_single.testout", O_CREAT | O_RDWR | O_EXCL));
if (ftruncate (fd, F_LEN)) {
perror ("ftruncate failed: ");
return 1;
}
assert (map = mmap (NULL, F_LEN, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0));
uint8_t *arena[256];
for (x = 0; x < 256; x++)
arena[x] = map + (x * A_LEN);
// test
volatile int *var;
void *list[512];
int lcnt = 0;
pd_init (F_LEN, map);
// per arena test
for (x = 0; x < 256; x++) {
pd_arena = arena[x];
// allocate and write a few times
for (y = 0; y < 256; y++) {
assert ((list[lcnt] = pd_mallocBK ()));
var = list[lcnt];
*var = (x + 1) * (y + 1);
}
// free some but not all
for (y = 0; y < 64; y++)
pd_freeBK (list[lcnt]);
// now reallocate some and write some
for (y = 0; y < 16; y++) {
assert ((list[lcnt] = pd_mallocBK()));
var = list[lcnt];
*var = 16;
}
}
// cleanup
munmap (map, F_LEN);
close (fd);
return 0;
}
After running the program through gdb, I found that it SEGFAULTs within pd_mallocBK(); specifically, on this line:
header->freelist = *next; // update the free list
However, I can't seem to understand what is wrong with that line and/or how to fix it.
So, two questions, really (in order of importance, most to least):
What is wrong with the selected line and how can I fix it?
Are there any other allocators to which I can simply assign a region of mapped memory to use instead of having to implement this?

The following code works better than the original, but still crashes eventually when starting to work on the last arena.
#include <assert.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>
#define xmattr_malloc __attribute__((malloc))
#define xm_likely(x) __builtin_expect(!!(x), 1)
#define xm_unlikely(x) __builtin_expect(!!(x), 0)
enum { ABLKLEN = 4096 };
void pd_freeBK(void *ptr);
xmattr_malloc void *pd_callocBK(void);
xmattr_malloc void *pd_mallocBK(void);
void pd_init(size_t len, uint8_t *map);
typedef struct {
uint8_t magic[16]; // "sfDB5" "vX.XXXXXXX" '\0'
uint8_t *freelist;
uint64_t size;
uint64_t bounds;
} arenaheader;
static void *pd_arena;
static void pd_dump_arena(FILE *fp, const char *tag, const arenaheader *arena)
{
assert(arena != NULL);
fprintf(fp, "Arena: 0x%.8" PRIXPTR " - %s\n", (uintptr_t)arena, tag);
fprintf(fp, "Size: %.8" PRIu64 ", Bounds: %.8" PRIu64 ", Freelist: 0x%.8" PRIXPTR "\n",
arena->size, arena->bounds, (uintptr_t)arena->freelist);
}
void pd_init(size_t len, uint8_t *map)
{
size_t const block = len / 256; // arena physical size
size_t const size = (block / ABLKLEN) * ABLKLEN; // arena useable size
arenaheader *header;
for (int x = 0; x < 256; x++)
{
header = (void *) &(map[x * block]);
header->freelist = NULL; // no free blocks because all are free
header->size = size; // useable size
header->bounds = ABLKLEN; // current bounds
}
for (int x = 0; x < 256; x++)
{
char buffer[32];
sprintf(buffer, "arena %.3d", x);
pd_dump_arena(stdout, buffer, (arenaheader *)&map[x * block]);
}
}
xmattr_malloc void *pd_mallocBK(void)
{
arenaheader *header = pd_arena;
void *ptr;
if (xm_unlikely(header->freelist)) // there's a sitting free block
{
ptr = header->freelist; // return the free block
void **next = ptr;
header->freelist = *next; // update the free list
}
else if (xm_likely(header->bounds < header->size)) // no free blocks
{
ptr = pd_arena;
ptr = (uint8_t *)ptr + header->size;
header->size += ABLKLEN;
}
else // no more blocks
{
ptr = NULL;
}
return ptr;
}
xmattr_malloc void *pd_callocBK(void)
{
void *ptr = pd_mallocBK();
if (xm_likely(ptr)) // allocation was successful
memset(ptr, 0, ABLKLEN);
return ptr;
}
void pd_freeBK(void *ptr)
{
arenaheader *header = pd_arena;
if (xm_likely(ptr)) // non-NULL ptr
{
void *next = header->freelist; // get current top of stack
void **this = ptr;
*this = next; // move address of current top of stack to ptr
header->freelist = ptr; // push ptr to stack
}
}
enum { NUM_ARENAS = 256 };
#define F_LEN (1024 * 1024 * 1024) // 1 GB
#define A_LEN (F_LEN / NUM_ARENAS)
int main(void)
{
const char filename[] = "./pd_single.testout";
// setup
//int fd = open(filename, O_CREAT | O_RDWR | O_EXCL, 0444);
int fd = open(filename, O_CREAT | O_RDWR, 0600);
assert(fd >= 0);
if (ftruncate(fd, F_LEN))
{
unlink(filename);
perror("ftruncate failed: ");
return 1;
}
uint8_t *map = mmap(NULL, F_LEN, PROT_READ | PROT_WRITE, MAP_FILE | MAP_SHARED, fd, 0);
assert(map != MAP_FAILED);
uint8_t *arena[NUM_ARENAS];
for (int x = 0; x < NUM_ARENAS; x++)
arena[x] = map + (x * A_LEN);
pd_init(F_LEN, map);
// test
void *list[512];
// per arena test
for (int x = 0; x < NUM_ARENAS; x++)
{
int lcnt = 0;
pd_arena = arena[x];
printf("Arena[%.3d] = 0x%.8" PRIXPTR "\n", x, (uintptr_t)pd_arena);
// allocate and write a few times
for (int y = 0; y < 256; y++)
{
assert((list[lcnt] = pd_mallocBK()));
int *var = list[lcnt];
*var = (x + 1) * (y + 1);
printf("[%.3d] data 0x%.8" PRIXPTR " = %d\n", y, (uintptr_t)list[lcnt], *var);
lcnt++;
}
// free some but not all
lcnt = 0;
for (int y = 0; y < 64; y++)
{
printf("[%.3d] free 0x%.8" PRIXPTR " = %d\n", y, (uintptr_t)list[lcnt], *(int *)list[lcnt]);
pd_freeBK(list[lcnt]);
lcnt++;
}
// now reallocate some and write some
lcnt = 0;
for (int y = 0; y < 16; y++)
{
assert((list[lcnt] = pd_mallocBK()));
int *var = list[lcnt];
*var = 16;
printf("[%.3d] data 0x%.8" PRIXPTR " = %d\n", y, (uintptr_t)list[lcnt], *var);
lcnt++;
}
}
// cleanup
munmap(map, F_LEN);
close(fd);
unlink(filename);
return 0;
}
I've not yet tracked down the residual bug. Note the diagnostic printing (verbose) and the different handling of lcnt in main(). You were busy freeing the same memory multiple times, but not detecting that in your pd_freeBK() code. You were also leaking memory because you were not incrementing lcnt in main().

Related

ansi c read and write to void* memory block

I'm interested in how you can write data in a particular structure to a pre-allocated memory block.
The idea is to write down the render commands and then read them in a loop. The problem is that allocating and deleting memory every cycle, which is 60 times per second is bad and I want to ask how can I write and read different types of data to the buffer.
example:
struct cmd_viewport {
int x, y, w, h;
};
struct cmd_line {
int x0, y0, x1, y1;
unsigned int width;
};
void* buffer_alloc(void* buffer, size_t offset, size_t size) {
...
}
void* buffer_get_ptr(void* buffer, size_t offset) {
...
}
int main(int argc, char* argv) {
void* cmd_buffer;
struct cmd_viewport* viewport;
struct cmd_line* line;
struct cmd_line* line2;
cmd_buffer = malloc(1024);
if (cmd_buffer == NULL) {
return EXIT_FAILURE;
}
viewport = (struct cmd_viewport*)buffer_alloc(cmd_buffer, 0, sizeof(struct cmd_viewport));
line = (struct cmd_line*)buffer_alloc(cmd_buffer, sizeof(struct cmd_viewport), sizeof(struct cmd_line));
/* it must be a direct write to the cmd_buffer memory location */
viewport->x = 0;
viewport->y = 0;
viewport->w = 800;
viewport->h = 600;
line->x0 = 100;
line->y0 = 100;
line->x1 = 300;
line->y1 = 400;
line->width = 2;
line2 = (struct cmd_line*)buffer_get_ptr(cmd_buffer, sizeof(struct cmd_viewport));
printf("line->width = %d\n", line2->width);
free(cmd_buffer);
return EXIT_SUCCESS;
}
How can you make a void* buffer cast from a specific offset to any data type ?

Copy structure to buffer

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <math.h>
#include <time.h>
#define RAND(lower,upper) (rand()%(upper-lower+1))+lower
int power(int base, int exp)
{
int result=1;
while (exp != 0)
{
result *= base;
--exp;
}
return result;
}
int isKthBitSet(int n, int k)//from right, 1<=k<=n
{
int new_num = n >> (k - 1);
// if it results to '1' then bit is set,
// else it results to '0' bit is unset
return (new_num & 1);
}
struct Astructure{
uint16_t bitmap;
uint32_t a;
uint32_t b;
char str[10];
uint16_t d;
}__attribute__((packed));
int main()
{
struct Astructure abitmap;
abitmap.bitmap = 0;
char buffer[(sizeof(struct Astructure))];
abitmap.a = 52;
abitmap.b = 16;
char c[10]={"ramya"};
strcpy(abitmap.str, c);
abitmap.d = 59;
char ch;
srand(time(0));
for(uint8_t position =1;position<5;position++)
{
int random10 = RAND(0,1);
if(random10==1)
{
int value = power(2,position-1);
abitmap.bitmap = abitmap.bitmap | value;
}
}
memcpy(buffer, (char *)&abitmap.bitmap, sizeof(abitmap.bitmap)+1);
uint8_t temp4 = isKthBitSet(abitmap.bitmap,4);
uint8_t temp3 = isKthBitSet(abitmap.bitmap,3);
uint8_t temp2 = isKthBitSet(abitmap.bitmap,2);
uint8_t temp1 = isKthBitSet(abitmap.bitmap,1);
uint8_t previousLength = sizeof(abitmap.bitmap);
if(temp4){
//add a to buffer
memcpy(buffer + previousLength, (void *)&abitmap.a, sizeof(abitmap.a)+1);
previousLength += sizeof(abitmap.a);
}
if(temp3){
//add b to buffer
memcpy(buffer + previousLength, (void *)&abitmap.b, sizeof(abitmap.b)+1);
previousLength += sizeof(abitmap.b);
}
if(temp2){
//add c to buffer
memcpy(buffer + previousLength, (void *)&abitmap.str, sizeof(abitmap.str)+1);
previousLength += sizeof(abitmap.str);
}
if(temp1){
//add d to buffer
memcpy(buffer + previousLength, (char *)&abitmap.d, sizeof(abitmap.d)+1);
previousLength += sizeof(abitmap.d);
}
//SHOW BUFFER
previousLength = sizeof(abitmap.bitmap);
uint32_t a;
uint32_t b;
char str[10];
uint16_t d;
if(temp4){
memcpy(&a , (void *) buffer+previousLength , sizeof(abitmap.a)+1);//
printf("a = %d\t",a);
previousLength += sizeof(a);
}
if(temp3){
memcpy(&b , (void *) buffer+previousLength , sizeof(abitmap.b)+1);
printf("b = %d\t",b);
previousLength += sizeof(b);
}
if(temp2){
memcpy(str , (void *) buffer+previousLength , sizeof(abitmap.str)+1);//memccpy
printf("string = %s\t",str);
previousLength += sizeof(str);
}
if(temp1){
memcpy(&d , (void *) buffer+previousLength , sizeof(abitmap.d)+1);
printf("d = %d\t",d);
previousLength += sizeof(d);
}
printf("\n");
}
I trying to copy some variables to my buffer. The variables I want to add in the buffer is picked at random. Here I gave the size of my buffer before hand to the size of my structure. But as per my program I may not be adding all my variables in the buffer. So its a waste.
So How am I supposed to declare my buffer here.
if my replace this
char buffer[(sizeof(struct Astructure))];
to
char *buffer;
this
I get below error.
a = 52 string = ramya
*** stack smashing detected ***: terminated
Aborted (core dumped)
I dont want to waste the size of my buffer in here, giving the whole size of structure. so what do i do.
If you want to allocate only the required memory:
typedef struct
{
size_t length;
unsigned char data[];
}buff_t;
buff_t *addtobuffer(buff_t *buff, const void *data, size_t size)
{
size_t pos = buff ? buff -> length : 0;
buff = realloc(buff, pos + size + sizeof(*buff));
if(buff)
{
memcpy(buff -> data + pos, data, size);
buff -> length = pos + size;
}
return buff;
}
and example usage:
buff_t *buffer = NULL;
/* ... */
if(temp4)
{
buff_t *tmp = addtobuffer(buffer, &abitmap.a, sizeof(abitmap.a)+1);
if(!tmp) { /* error handling */}
buffer = tmp;
/*...*/
}

Aborted (core dumped) in C in the last row

I am using cygwin, I am getting after the output in the last raw aborted (dumped core) I uploaded an image of the output this is my code in the code. I have Array.h, Array.c, Table.c, Table.h and main.c.
Code: table header file Table.h:
#ifndef EX4_TABLE_H
#define EX4_TABLE_H
#include "Array.h"
typedef struct Table *TableP;
TableP TableCreate();
void TableDestroy(TableP p);
char* TableRowName(TableP p, int i);
char* TableColumnName(TableP p, int i);
int TableNumColumns(TableP p);
int TableNumRows(TableP p);
Element TableGet(TableP p, int i, int j);
void TableSet(TableP p, int i, int j, Element val);
void TableAddRow(TableP p, char *name);
void TableAddColumn(TableP p, char *name, Element (*cpy)(Element), void(*fre)(Element));
#endif
Table C file Table.c:
#include "Table.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
struct Table {
Array *arr;
int numOfCul;
char **name_cul;
int rowSize;
char **name_row;
};
TableP TableCreate() {
TableP p = (TableP)malloc(sizeof(struct Table));
if (!p) {
fprintf(stderr, "%s/%u: failed to allocate %lu bytes. Aborting\n\n", __FILE__, __LINE__, sizeof(struct Table));
exit(0);
}
p->name_cul = NULL;
p->name_row = NULL;
p->arr = NULL;
p->numOfCul = 0;
p->rowSize = 0;
return p;
}
void TableDestroy(TableP p) {
for (int i = 0; i < p->numOfCul; i++) {
ArrayDestroy(p->arr[i]);
free(p->name_cul[i]);
for (int j = 0; j < p->rowSize; j++) {
free(p->name_row[j]);
}
}
free(p);
}
void TableAddRow(TableP p, char *name) {
p->rowSize = p->rowSize + 1;
p->name_row = (char **)realloc(p->name_row, sizeof(char*) * p->rowSize);
p->name_row[p->rowSize - 1] = (char *)malloc(sizeof(char) * 8);
strcpy(p->name_row[p->rowSize-1], name);
for (int i = 0; i < p->numOfCul; i++) {
ArrayResize(p->arr[i], p->rowSize);
}
}
void TableAddColumn(TableP p, char *name, Element (*cpy)(Element), void(*fre)(Element)) {
p->numOfCul = p->numOfCul + 1;
p->name_cul = (char **)realloc(p->name_cul, sizeof(char*) * p->numOfCul);
p->name_cul[p->numOfCul - 1] = (char *)malloc(sizeof(char) * 8);
strcpy(p->name_cul[p->numOfCul - 1], name);
p->arr = (Array *)realloc(p->arr, sizeof(Array));
p->arr[p->numOfCul - 1] = ArrayCreate(p->rowSize, cpy, free);
}
void TableSet(TableP p, int i, int j, Element val) {
if (i > p->numOfCul || j > p->rowSize) {
printf("number not in range \n");
exit(0);
}
ArraySet(p->arr[i], j, val);
}
Element TableGet(TableP p, int i, int j) {
if (i > p->numOfCul || j > p->rowSize) {
printf("number not in range \n");
exit(0);
}
return ArrayGet(p->arr[i], j);
}
int TableNumRows(TableP p) {
return p->rowSize;
}
int TableNumColumns(TableP p) {
return p->numOfCul;
}
char *TableRowName(TableP p, int i) {
if (p->rowSize < i) {
printf("num row is wrong\n");
exit(1);
}
return p->name_row[i];
}
char *TableColumnName(TableP p, int i) {
if (p->rowSize < i) {
printf("num row is wrong\n");
exit(1);
}
return p->name_cul[i];
}
File Array.h:
#ifndef EX4_ARRAY_H
#define EX4_ARRAY_H
typedef void *Element;
typedef struct Array *Array;
/*
* Array constructor, expects to get the size of the array, a copy function for the type of elements to be kept
* and a free function. Array is initialized with NULL values
*/
Array ArrayCreate(unsigned int n, Element (*cpy)(Element), void(*fre)(Element));
/*
* Array destractor
*/
void ArrayDestroy(Array a);
/*
* Keeps a copy of element e in index i. Will print an error message and terminate the program if i is out of range.
* The element that was kept in index i (if there is any) will be deleted
*/
void ArraySet(Array a, unsigned int i, Element e);
/*
* Returns the element in index i (NULL if there is no element stored). Will print an error message and terminate the
* program if i is out of range.
*/
Element ArrayGet(Array a, unsigned int i);
/*
* Returns the size of the array
*/
unsigned int ArraySize(Array a);
/*
* Changes the size of the array. If the new size if hiigher than current size then empty cells will be added.
* If the new size is smaller, then the items at the end of the array will be deleted.
* newn can be 0 (resulting with an empty array).
*/
void ArrayResize(Array a, unsigned int newn);
#endif
File Array.c:
#include <stdio.h>
#include <stdlib.h>
#include "Array.h"
/***********************************************************************************************************************/
struct Array {
Element *elements;
unsigned int num_elements;
Element (*cpy)(Element);
void (*fre)(Element);
};
/***********************************************************************************************************************/
Array ArrayCreate(unsigned int n, Element (*cpy)(Element), void(*fre)(Element)) {
Array array = (Array)calloc(1, sizeof(struct Array));
if (!array) {
fprintf(stderr, "%s/%u: failed to allocate %lu bytes. Aborting\n\n", __FILE__, __LINE__, sizeof(struct Array));
exit(-1);
}
array->num_elements = n;
array->elements = (Element)calloc(array->num_elements, sizeof(Element));
if (!array->elements) {
fprintf(stderr, "%s/%u: failed to allocate %lu bytes. Aborting\n\n", __FILE__, __LINE__, sizeof(Element)*array->num_elements);
exit(-1);
}
array->cpy = cpy;
array->fre = fre;
return array;
}
/***********************************************************************************************************************/
void ArrayDestroy(Array a) {
ArrayResize(a, 0);
free(a->elements);
free(a);
}
/***********************************************************************************************************************/
void ArraySet(Array a, unsigned int i, Element e) {
if (i >= a->num_elements) {
fprintf(stderr, "\nError, %s: tried to set an element in index %u, array size is %u. Aborting\n\n", __FUNCTION__, i, a->num_elements);
exit(-1);
}
if (a->elements[i])
a->fre(a->elements[i]);
a->elements[i] = a->cpy(e);
}
/***********************************************************************************************************************/
Element ArrayGet(Array a, unsigned int i) {
if (i >= a->num_elements) {
fprintf(stderr, "\nError, %s: tried to get an element from index %u, array size is %u. Aborting\n\n", __FUNCTION__, i, a->num_elements);
exit(-1);
}
return a->elements[i];
}
/***********************************************************************************************************************/
unsigned int ArraySize(Array a) {
return a->num_elements;
}
/***********************************************************************************************************************/
void ArrayResize(Array a, unsigned int newn) {
// If the new size is smaller than the older one then remove the extra elements
if (newn < a->num_elements) {
for (;a->num_elements > newn; a->num_elements--) {
a->fre(a->elements[a->num_elements - 1]);
}
}
// If the new size is 0 then free a->elements and leave
if (newn == 0) {
free(a->elements);
a->elements = NULL;
return;
}
a->elements = (Element)realloc(a->elements, sizeof(Element) * newn);
if (!a->elements) {
fprintf(stderr, "%s/%u: failed to allocate %lu bytes. Aborting\n\n", __FILE__, __LINE__, sizeof(Element)*newn);
exit(-1);
}
// If the new size is larger than the old size - fill the newly added cells with NULL
if (newn > a->num_elements) {
for (; a->num_elements < newn; a->num_elements++)
a->elements[a->num_elements] = NULL;
}
}
File main.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Table.h"
/***********************************************************************************************************************/
Element str_cpy(Element e) {
char *s = (char *)malloc(sizeof(char) * (strlen((char*)e) + 1));
if (!s) {
fprintf(stderr, "\n%s/%u: failed to allocate %lu bytes. Aborting\n\n", __FILE__, __LINE__, sizeof(char)*(strlen((char*)e)+1));
exit(-1);
}
strcpy(s, (char*)e);
return (Element)s;
}
/***********************************************************************************************************************/
Element int_cpy(Element e) {
int *i = (int *)malloc(sizeof(int));
if (!i) {
fprintf(stderr, "\n%s/%u: failed to allocate %lu bytes. Aborting\n\n", __FILE__, __LINE__, sizeof(int));
exit(-1);
}
*i = *((int *)e);
return (Element)i;
}
/***********************************************************************************************************************/
int main() {
TableP t = TableCreate();
TableAddColumn(t, "Int1", int_cpy, free);
TableAddColumn(t, "Char1", str_cpy, free);
TableAddColumn(t, "Int2", int_cpy, free);
TableAddColumn(t, "Char2", str_cpy, free);
srand(42);
for (int i = 0; i < 5; i++) {
int i1 = 500 + i, i2 = i1 * i1;
char str[32];
sprintf(str, "r%d", i);
TableAddRow(t, str);
TableSet(t, 0, i, &i1);
sprintf(str, "Hello_%d", i1);
TableSet(t, 1, i, str);
TableSet(t, 2, i, &i2);
sprintf(str, "Bye_%d", i2);
TableSet(t, 3, i, str);
}
for (int i = 0; i < TableNumColumns(t); i++) {
printf("\t%s", TableColumnName(t, i));
}
printf("\n");
for (int r = 0; r < TableNumRows(t); r++) {
printf("%s", TableRowName(t, r));
for (int c = 0; c < TableNumColumns(t); c++) {
if (c == 0 || c == 2)
printf("\t%d", *(int *)(TableGet(t, c, r)));
else
printf("\t%s", (const char *)(TableGet(t, c, r)));
}
printf("\n");
}
TableDestroy(t);
return 0;
}
output:
There are multiple problems in the code:
you test for malloc failures but not everywhere, nor do you check for realloc failures. You should write wrappers or macros for these functions that perform the test and exit gracefully upon failure. This would make the calling code more readable.
in TableDestroy, the 2 for loops should not be nested. This causes multiple free for the same pointers.
when setting column and row names, you should use strdup() instead of allocating a harcoded length of 8 and using strcpy without checking that the name fits.
in TableAddColumn the array pointed to by p->arr is not resized to the new number of entries. You should have:
p->arr = (Array *)realloc(p->arr, sizeof(Array) * p->numOfCul);
the test in TableSet and TableGet is incorrect: you should use >= instead of >, report the error to stderr and exit with a non zero status:
if (i >= p->numOfCul || j >= p->rowSize) {
fprintf(stderr, "table coordinates out of range: %d,%d\n", i, j);
exit(1);
}
same problem in TableRowName and TableColumnName
hiding pointers behind typedefs is confusing and error prone. Especially confusing is the typedef typedef struct Array *Array;

Pass a structure to a function and store values in the structure's element

I want to pass a structure to a function and store value in the structure's element. Here is my code.
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef struct {
uint32_t len;
uint16_t *arr;
} seq;
void write_seq(seq *q, int val)
{
// How to implement this function?
uint16_t *tmp;
tmp = (uint16_t *)malloc(sizeof(uint16_t));
*tmp = val;
*(q->arr + q->len) = *tmp;
q->len += 1;
}
int main(int argc, char *argv[])
{
seq q;
q.len = 0;
q.arr = NULL;
int i;
for (i = 0; i < 10; i++) {
write_seq(&q, i);
}
printf("length is %d\n", q.len);
for (i = 0; i < q.len; i++) {
printf("%d\n", *(q.arr+i));
}
return 0;
}
I want to write 0 to 9 to a memory block which q.arr points to.
I don't want to use malloc in main(), since I don't know how many bytes I need before I call write_seq. I want to locate new memory every time when write_seq is called. Here's how output should look like.
length is 10
0
1
2
3
4
5
6
7
8
9
My implementation of write_seq() would cause core dumped. I don't know how to fix it. Thank you.
To add members to the array when you don't know in advance the size of that array, you'd need to use realloc() to increase the size on demand. However it's inefficient to do that for every change to the array size, so it would be more usual to allow for a certain amount of headroom (whether that be a fixed increment or a percentage based amount) in the buffer.
The corollary of that is you need to store the current capacity of the buffer as well as the offset to the amount currently used.
This also means that there'll be a certain amount of memory wastage, but that's the trade off you have to make.
My approach would look something like this, abstracting the operations that you might want to perform on a seq.
typedef struct {
size_t capacity;
size_t offset;
uint16_t *arr;
} seq;
static const size_t SEQ_INITIAL = 8;
static const size_t SEQ_INCREMENT = 8;
int seq_init(seq *seq) {
assert(seq != NULL); /* catch null seq */
assert(seq->arr == NULL); /* error to call on already init'd seq */
seq->capacity = SEQ_INITIAL;
seq->offset = 0;
seq->arr = malloc(seq->capacity * sizeof(seq->arr[0]));
return seq->arr == NULL ? -1 : 0;
}
static int seq_grow(seq *seq) { /* private implementation detail */
size_t new_capacity = seq->capacity + SEQ_INCREMENT;
void *p = realloc(seq->arr, new_capacity * sizeof(seq->arr[0]));
if (p == NULL) { /* realloc failed, seq unmodified */
return -1;
}
seq->arr = p;
seq->capacity = new_capacity;
return 0;
}
int seq_write(seq *seq, uint16_t value) {
assert(seq != NULL); /* catch null seq */
if ((seq->offset == seq->capacity) && seq_grow(seq) < 0) {
return -1; /* write failed */
}
assert(seq->arr != NULL); /* catch bad seq */
assert(seq->offset < seq->capacity); /* ensure seq really has room */
seq->arr[seq->offset++] = value;
return 0;
}
void seq_free(seq *seq) {
if (seq != NULL) {
free(seq->arr);
seq->arr = NULL;
}
}
You need to use realloc(), not malloc(), like this:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef struct {
int len;
int *arr;
} seq;
void write_seq(seq *q)
{
q->arr = realloc (q->arr, (q->len + 1) * sizeof(int));
q->arr[q->len] = q->len;
q->len++;
}
int main(void)
{
seq q;
q.len = 0;
q.arr = NULL;
for(int i = 0; i < 10; ++i)
write_seq(&q);
printf("length is %d\n", q.len);
for (int i = 0; i < q.len; i++) {
printf("%d\n", q.arr[i]);
}
free(q.arr);
return 0;
}
Output:
length is 10
0
1
2
3
4
5
6
7
8
9

Why does my pointer arithmetic fail in this array

#include <stdio.h>
#include <stdlib.h>
// Resource
typedef struct _Resource Resource;
// ResourceBuffer
typedef struct _ResourceBuffer ResourceBuffer;
ResourceBuffer *resource_buffer_new(int);
ResourceBuffer *resourceBuffer;
void resource_buffer_test();
struct _ResourceBuffer {
char *id;
int size;
Resource **buffer;
};
struct _Resource {
int id;
char *status;
};
Resource *resource_new(int i) {
Resource *r = malloc(sizeof(*r));
r->status = "groovy";
r->id = i;
return r;
}
void resource_buffer_test() {
printf("buftest has buf as %d\n",resourceBuffer->buffer);
printf("buftest has drefbuf as %d\n",*(resourceBuffer->buffer));
Resource *bp[resourceBuffer->size];
bp[0] = *(resourceBuffer->buffer+0);
bp[1] = *(resourceBuffer->buffer+1);
bp[2] = *(resourceBuffer->buffer+2);
int i = 0;
for (i = 0; i < 3; i++) {
printf("address is %d\n", bp[i]);
}
for (i = 0; i < 3; i++) {
//printf("ptrBuffer r%d is %s\n", i, (*((Resource **)(resourceBuffer->buffer)+i))->status);
printf("Buffer r%d is %s\n", bp[i]->id, bp[i]->status);
}
}
ResourceBuffer *resource_buffer_new(int bufferSize) {
ResourceBuffer *r = malloc(sizeof(*r));
Resource *b[bufferSize];
int i;
for (i = 0; i < bufferSize; i++) {
b[i] = resource_new(i);
}
for (i = 0; i < bufferSize; i++) {
printf("res address is %d\n", b[i]);
printf("pnt address is %d\n", &b[i]);
}
printf("b address is %d\n", b);
r->buffer = b;
printf("buffer set to %d\n", r->buffer);
r->size = bufferSize;
r->id = "foo";
return r;
}
int main(int argc, char** argv) {
// initialize buffer
resourceBuffer = resource_buffer_new(3);
resource_buffer_test();
return (EXIT_SUCCESS);
}
The output is:
res address is 36585520
pnt address is 783569984
res address is 36585552
pnt address is 783569992
res address is 36585584
pnt address is 783570000
b address is 783569984
buffer set to 783569984
buftest has buf as 783569984
buftest has drefbuf as 36585520
address is 36585520
address is 36585552
address is 36585520
Buffer r0 is groovy
Buffer r1 is groovy
Buffer r0 is groovy
What confuses me is the last 6 lines... Why in resource_buffer_test() do b[0] and b[2] end up pointing to the Resource struct r0?
For some reason this part fails:
bp[0] = *(resourceBuffer->buffer+0);
bp[1] = *(resourceBuffer->buffer+1);
bp[2] = *(resourceBuffer->buffer+2);
Where *(resourceBuffer->buffer+2) somehow ends up pointing back to the first element in the array, rather than the third.
Why does this happen? What causes the C pointer arithmetic to reset *(resourceBuffer->buffer+2) back to *(resourceBuffer->buffer+0)?
What is really odd (to me anyway)... is that when I change the resource_buffer_new function to look like this:
ResourceBuffer *resource_buffer_new(int bufferSize) {
ResourceBuffer *r = malloc(sizeof(*r));
Resource *b[bufferSize+1];
(note the *b[bufferSize+1]) -- then it works as expected..
Why do I need to have extra room in my buffer array for the pointer arithmetic to work?
r->buffer = b; is your problem. b is a local variable that goes out of scope once you return from resource_buffer_new(). If you made b static or allocated it, it would be ok.

Resources