I have the following code in a header file:
#ifndef BUFFER_H
#define BUFFER_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct c_buff
{
void *buffer; // data buffer
void *buffer_end; // end of data buffer
size_t capacity; // maximum number of items in the buffer
size_t count; // number of items in the buffer
size_t sz; // size of each item in the buffer
void *head; // pointer to head
void *tail; // pointer to tail
};
void cb_init(c_buff *cb, size_t capacity, size_t sz)
{
cb->buffer = malloc(capacity * sz);
if(cb->buffer == NULL) {
// handle error
}
cb->buffer_end = (char *)cb->buffer + capacity * sz;
cb->capacity = capacity;
cb->count = 0;
cb->sz = sz;
cb->head = cb->buffer;
cb->tail = cb->buffer;
}
#endif
And the following c file
#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <common.h>
#include <usart.h>
#include <buffer.h>
struct c_buff usart_buffer;
struct c_buff *usart_buffer_ptr;
cb_init(usart_buffer_ptr, USART_BUFFER_SIZE, sizeof(char));
void initUSART(void) {
SETBIT(UCSR0A, UDRE0);
//SETBIT(UCSR0A, U2X0);
SETBIT(UCSR0C, UCSZ01);
SETBIT(UCSR0C, UCSZ00);
UBRR0 = 25;
SETBIT(UCSR0B, RXCIE0);
SETBIT(UCSR0B, TXCIE0);
SETBIT(UCSR0B, RXEN0);
SETBIT(UCSR0B, TXEN0);
}
ISR(USART_RX_vect) {
char data;
data = UDR0;
UDR0 = data;
}
ISR(USART_TX_vect) {
}
When I try to compile this I get an error that points to this line:
cb_init(usart_buffer_ptr, USART_BUFFER_SIZE, sizeof(char));
And it just says "error: expected ')' before numeric constant".
Google tells me it's some kind of preprocessor error. But I don't see how that could be the case.
I'm new to C so I apologize if it is something totally obvious.
You can't have a naked function call at the top level.
cb_init(usart_buffer_ptr, USART_BUFFER_SIZE, sizeof(char));
is a naked function call. Move that inside main().
You can't run function in global scope. It has to be done in main:
int main(int argc, char *argv[] {
cb_init(usart_buffer_ptr, USART_BUFFER_SIZE, sizeof(char));
}
The problem is that you're attempting to execute a method at the file level.
cb_init(usart_buffer_ptr, USART_BUFFER_SIZE, sizeof(char));
The C language only allows declarations / definitions at this level not actual executed statements. This call needs to be moved into a function definition.
Related
So I'm currently trying to work through a book called "Crafting Interpreters" and it goes step by step through making a language. I'm writing this language in C and already running into issues. I have a couple header files with respective c files and then the main driver code. When I try to compile the code, I get this error:
compiler error msg
I'm not sure what's going wrong here as I have other projects using header files and they are able to compile correctly.
//main
#include "common.h"
#include "chunk.h"
int main(int argc, const char* argv[]) {
Chunk chunk;
initChunk(&chunk);
writeChunk(&chunk, OP_RETURN);
freeChunk(&chunk);
return 0;
}
//common.h
#ifndef clox_common_h
#define clox_common_h
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#endif
//chunk.h
#ifndef clox_chunk_h
#define clox_chunk_h
#include "common.h"
typedef enum {
OP_RETURN,
} OpCode;
typedef struct {
int count;
int capacity;
uint8_t* code;
} Chunk;
void initChunk(Chunk* chunk);
void freeChunk(Chunk* chunk);
void writeChunk(Chunk* chunk, uint8_t byte);
#endif
//chunk.c
#include <stdlib.h>
#include "chunk.h"
#include "memory.h"
void initChunk(Chunk* chunk) {
chunk->count = 0;
chunk->capacity = 0;
chunk->code = NULL;
}
void freeChunk(Chunk* chunk) {
FREE_ARRAY(uint8_t, chunk->code, chunk->capacity);
initChunk(chunk);
}
void writeChunk(Chunk* chunk, uint8_t byte) {
if (chunk->capacity < chunk->count) {
int oldCapacity = chunk->capacity;
chunk->capacity = GROW_CAPACITY(oldCapacity);
chunk->code = GROW_ARRAY(uint8_t, chunk->code,
oldCapacity, chunk->capacity);
}
chunk->code[chunk->count] = byte;
chunk->count++;
}
//memory.h
#ifndef clox_memory_h
#define clox_memory_h
#include "common.h"
#define GROW_CAPACITY(capacity) \
((capacity) < 8 ? 8 : (capacity) * 2)
#define GROW_ARRAY(type, pointer, oldCount, newCount) \
(type*)reallocate(pointer, sizeof(type) * (oldCount), \
sizeof(type) * (newCount))
#define FREE_ARRAY(type, pointer, oldCount) \
reallocate(pointer, sizeof(type) * (oldCount), 0)
void* reallocate(void* pointer, size_t oldSize, size_t newSize);
#endif
//memory.c
#include <stdlib.h>
#include "memory.h"
void* reallocate(void* pointer, size_t oldSize, size_t newSize) {
if (newSize == 0) {
free(pointer);
return NULL;
}
void* result = realloc(pointer, newSize);
if (result == NULL) exit(1);
return result;
}
Any guidance would be appreciated
My arraylist implementation stop working after appending 32754 elements. I think it is very weird that this problem only occurs after appending so many elements and 32000 is still not too high to reach I know I am not checking for NULL pointer and that my test program is a infinite loop. I am using a old version to reduce the code complexity.
output:
32752
32753
32754
zsh: segmentation fault ./acl
array.c:
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
union arraylist_meta {
double dummy_double;
long double dummy_long_double;
long long dummy_long_long;
void *dummy_ptr;
void (*dummy_func_ptr)(void);
struct {
size_t len;
size_t cap;
size_t sizeof_one_element;
};
};
void* acl_arraylist_create(size_t array_size, size_t sizeof_one_element) {
union arraylist_meta *arraylist_new = malloc(array_size * sizeof_one_element + sizeof*arraylist_new);
arraylist_new->len = array_size;
arraylist_new->cap = array_size;
arraylist_new->sizeof_one_element = sizeof_one_element;
return arraylist_new+1;
}
void* acl_arraylist_append(void *arraylist_void, void *element) {
union arraylist_meta *arraylist = arraylist_void;
--arraylist;
if(arraylist->len == arraylist->cap) {
arraylist->cap = arraylist->len + 10;
arraylist = realloc(arraylist, arraylist->cap * arraylist->sizeof_one_element + sizeof *arraylist);
}
memcpy((char*)(arraylist + 1) + arraylist->sizeof_one_element * arraylist->len, element, arraylist->sizeof_one_element);
++arraylist->len;
return arraylist+1;
}
array.h:
#ifndef array_h
#define array_h
#include <stddef.h>
void* acl_arraylist_create(size_t array_size, size_t sizeof_one_element);
void* acl_arraylist_append(void *arraylist_void, void *element_void);
#endif
a simple test programm:
#include <acl/array.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
int *num = acl_arraylist_create(0, sizeof *num);
for(int i = 0;;++i) {
num = acl_arraylist_append(num, &i);
printf("%d\n", i);
}
}
Edit:
I changed the of the executable a while ago. By reverting a few commits back my build script was using the old name again, but executed the executable with name. This means that the problem I describe above does not with code above. It only occurs when using the code below:
array.c:
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <acl/array.h>
size_t acl_arraylist_len(void *arraylist);
void acl_arraylist_free(void *arraylist);
static inline void* acl_arraylist_resize(union acl_arraylist_meta *arraylist, int64_t relativLen) {
size_t cap = arraylist->cap + relativLen;
arraylist = realloc(arraylist, cap * arraylist->sizeof_one_element + sizeof *arraylist);
if(arraylist != NULL) {
arraylist->cap = cap;
}
return arraylist;
}
void* acl_arraylist_create(size_t array_size, size_t sizeof_one_element) {
union acl_arraylist_meta *arraylist_new = malloc(array_size * sizeof_one_element + sizeof*arraylist_new);
if(arraylist_new == NULL) return NULL;
arraylist_new->len = array_size;
arraylist_new->cap = array_size;
arraylist_new->sizeof_one_element = sizeof_one_element;
return arraylist_new+1;
}
void* acl_arraylist_append(void *arraylist_void, void *element) {
void *element_append;
union acl_arraylist_meta *arraylist = acl_arraylist_append_ptr(arraylist_void, &element_append);
if(arraylist == NULL) return NULL;
--arraylist;
memcpy(element_append, element, arraylist->sizeof_one_element);
return arraylist + 1;
}
void* acl_arraylist_append_ptr(void *arraylist_void, void **append_element) {
union acl_arraylist_meta *arraylist = arraylist_void;
--arraylist;
if(arraylist->len == arraylist->cap) {
acl_arraylist_resize(arraylist, 10);
if(arraylist == NULL) return NULL;
}
*append_element = (char*)(arraylist + 1) + arraylist->sizeof_one_element * arraylist->len;
++arraylist->len;
return arraylist + 1;
}
void* acl_arraylist_remove(void *arraylist_void, size_t index) {
union acl_arraylist_meta *arraylist = (union acl_arraylist_meta*)arraylist_void - 1;
char *arraylist_char = arraylist_void;
if(index != arraylist->len - 1) {
memcpy(arraylist_char + arraylist->sizeof_one_element * index, arraylist_char + arraylist->sizeof_one_element * (arraylist->len - 1), arraylist->sizeof_one_element);
}
--arraylist->len;
if(arraylist->len < arraylist->cap - 20) {
void* arraylistTmp = acl_arraylist_resize(arraylist, -10);
if(arraylistTmp != NULL) arraylist = arraylistTmp;
}
return arraylist + 1;
}
array.h:
#ifndef _acl_array_h
#define _acl_array_h
#include <stddef.h>
#include <stdlib.h>
union acl_arraylist_meta {
double dummy_double;
long double dummy_long_double;
long long dummy_long_long;
void *dummy_ptr;
void (*dummy_func_ptr)(void);
struct {
size_t len;
size_t cap;
size_t sizeof_one_element;
};
};
inline size_t acl_arraylist_len(void *arraylist) {
return ((union acl_arraylist_meta*)arraylist - 1)->len;
}
inline void acl_arraylist_free(void *arraylist) {
free((union acl_arraylist_meta*)arraylist-1);
}
void* acl_arraylist_remove(void *arraylist_void, size_t index);
void* acl_arraylist_create(size_t array_size, size_t sizeof_one_element);
void* acl_arraylist_append(void *arraylist_void, void *element);
void* acl_arraylist_append_ptr(void *arraylist_void, void **append_element);
#endif
a simple test programm:
#include <acl/array.h>
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
void *num = acl_arraylist_create(100, sizeof(int));
for (int i = 0; i < 65536; ++i)
{
num = acl_arraylist_append(num, &i);
printf("%d\n", i);
}
}
It's worrying that your array.c source file does not include the header (acl/array.h) that declares the services that the source file defines. It means there is no cross-checking. The headers provide the glue that holds C programs together, providing cross-checking to ensure that the code using the services provided agrees with the code providing those services.
Also: Your sample program doesn't create a list — your code should not compile because num is not defined.
When resequenced a bit, the code does compile cleanly. When I added a call:
void *num = acl_arraylist_create(100, sizeof(int));
before the loop in main() and ran the code (source code acl23.c, program acl23), I got to iteration 150 before the Mac OS library said:
acl23(54767,0x10d41b5c0) malloc: *** error for object 0x7f8c40c02bb0: pointer being realloc'd was not allocated
acl23(54767,0x10d41b5c0) malloc: *** set a breakpoint in malloc_error_break to debug.
If you've got Valgrind available to you, use it.
I think your code is playing with fire (and you're getting burnt) because you're trying to combine the union arraylist_meta structure and the array data.
However, the primary problem is that when the memory is reallocated, you are not using the new value returned by acl_arraylist_append(). Change the line in the loop to:
new = acl_arraylist_append(num, &i);
and the code runs up to 65535 for me. I set the loop to stop then, rather than imposing no limit.
for (int i = 0; i < 65536; ++i).
It isn't clear how the user of your array list is going to access elements of the array. Presumably, you plan to have them convert the void * (num in the example) to an appropriate typed pointer (int *array = num;) and they can then index into the array. It's also not clear how they determine the size of the array — what the maximum index is. You've also not provided a function to free the array. The user can't do that — the pointer they have is not the one returned by one of the allocation functions (malloc(), realloc(), etc). None of these are immediately critical; we can safely assume that they were omitted from the
MCVE (Minimal, Complete, Verifiable Example — or MRE or whatever name SO now uses) you provided.
Here's my working code derived from yours — all in a single file. The changes are actually quite minor.
/*array.h:*/
#ifndef array_h
#define array_h
#include <stddef.h>
void *acl_arraylist_create(size_t array_size, size_t sizeof_one_element);
void *acl_arraylist_append(void *arraylist_void, void *element_void);
#endif
/*array.c:*/
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
/*#include <acl/array.h>*/
union arraylist_meta
{
double dummy_double;
long double dummy_long_double;
long long dummy_long_long;
void *dummy_ptr;
void (*dummy_func_ptr)(void);
struct
{
size_t len;
size_t cap;
size_t sizeof_one_element;
};
};
void *acl_arraylist_create(size_t array_size, size_t sizeof_one_element)
{
union arraylist_meta *arraylist_new = malloc(array_size * sizeof_one_element + sizeof *arraylist_new);
arraylist_new->len = array_size;
arraylist_new->cap = array_size;
arraylist_new->sizeof_one_element = sizeof_one_element;
return arraylist_new + 1;
}
void *acl_arraylist_append(void *arraylist_void, void *element)
{
union arraylist_meta *arraylist = arraylist_void;
--arraylist;
if (arraylist->len == arraylist->cap)
{
arraylist->cap = arraylist->len + 10;
arraylist = realloc(arraylist, arraylist->cap * arraylist->sizeof_one_element + sizeof *arraylist);
}
memcpy((char *)(arraylist + 1) + arraylist->sizeof_one_element * arraylist->len, element, arraylist->sizeof_one_element);
++arraylist->len;
return arraylist + 1;
}
/*a simple test programm:*/
/*#include <acl/array.h>*/
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
void *num = acl_arraylist_create(100, sizeof(int));
for (int i = 0; i < 65536; ++i)
{
num = acl_arraylist_append(num, &i);
printf("%d\n", i);
}
}
I'm not going to show the output; the numbers from 1 to 65535 are not exciting.
I distrust void * as the handle type for your array. The user could provide any pointer of their choosing as a handle and there's no way to know that it's the wrong type of pointer. Provide an opaque type instead; in the header, define:
typedef struct acl_arraylist acl_arraylist;
Then have the functions take and return an acl_arraylist *. The client code doesn't need to know what's in it. Your code in array.c might wrap the union arraylist_meta value into a structure:
struct acl_arraylist
{
union arraylist_meta array;
};
You can then play in much the same way you did before. But the user has to work to pass an arbitrary pointer to the functions — sufficiently hard that they won't get it wrong.
The new pointer returned from acl_arraylist_resize is ignored in acl_arraylist_append_ptr.
modified code:
void* acl_arraylist_append_ptr(void *arraylist_void, void **append_element) {
union acl_arraylist_meta *arraylist = arraylist_void;
--arraylist;
if(arraylist->len == arraylist->cap) {
arraylist = acl_arraylist_resize(arraylist, 10);// this line was modified
if(arraylist == NULL) return NULL;
}
*append_element = (char*)(arraylist + 1) + arraylist->sizeof_one_element * arraylist->len;
++arraylist->len;
return arraylist + 1;
}
I'm getting this error:
list.c list.h types.h
list.c: In function 'List_push':
list.c:11:23: error: invalid initializer
--- void *values[len] = ls->values);
EDIT:
Now, with my current code (I've done undos/redos that removed somethings), I get this error instead:
Why?
Basically I've a List structure which declares an variable-length array, containing void pointers (what I want is pointers to any data type). You can see it below at the list.h file.
I've tried a mix of changes in list.c (i.e., *values[pos++] = ..., etc...), but doing these changes it only results in worse gcc errors.
wscom.c
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
#include "types.h"
int main() {
List ls;
// TEST: Put a value pointer at index 0
uint8 value = 0x41;
List_push(&ls, 1, &value);
printf("%c",
*(char*) List_getindex(&ls, 0)
);
return 0;
}
types.h
#ifndef hydroTrackerTypesH
#define hydroTrackerTypesH
typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned long long uint32;
#endif
list.h (Declarations)
#ifndef hydroTrackerListH
#define hydroTrackerListH
#include "types.h"
typedef struct {
uint32 length;
void *values[];
} List;
void List_push(List *ls, uint8 count, ...);
void *List_getindex(List *ls, uint32 i);
void List_setindex(List *ls, uint32 i, void *v);
#endif
list.c (Defns.)
#include "list.h"
#include "types.h"
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
static size_t PointerSize =
sizeof(void*);
void List_push(List *ls, uint8 count, ...) {
uint32 len = ls->length;
void *values[len] = ls->values;
uint32 sum = len + count;
realloc(&values, sum * PointerSize);
ls->length = sum;
va_list newVals;
va_start(newVals, count);
uint8 pos = len;
while(count--)
values[pos++] = va_arg(newVals, void*);
va_end(newVals);
}
void *List_getindex(List *ls, uint32 i) {
return (void *)(ls->values[i]);
}
//void List_setindex(List *ls, uint32 i, void *v);
This is a little bit long for a comment.
Thus, I make it an answer.
I try to show you how pointers and arrays are related to each other:
#include <stdlib.h>
#include <stdio.h>
int main()
{
/* The compiler allocates space for "Hello" and '\0' (5 + 1 chars)
* and stores the address in aString1.
*/
const char *aString1 = "Hello";
/* The compiler allocates 10 chars and initializes
* it with "World" (and the '\0' for terminator).
*/
const char aString2[10] = "World";
/* The compiler determines length of initializer "I'm here."
* (9 + 1) and allocates the array of appropriate size.
*/
const char aString3[] = "I'm here.";
/* allocate storage for array (3 const char*) */
#if 0 /* the usual way */
const char **array = malloc(3 * sizeof (const char*));
#else /* how Matheus wants to do it */
const char **array = NULL;
array = realloc(array, 3 * sizeof (const char*));
#endif /* 0 */
/* assign contents (using it like an array) */
array[0] = aString1;
array[1] = aString2;
array[2] = aString3;
/* apply array to another variable array2 */
const char **array2 = array; /* assigns the address only */
/* use it: */
printf("array2[0]: '%s', array2[1]: '%s', array2[2]: '%s'\n",
array2[0], array2[1], array2[2]);
/* throw away storage of array (and array2) */
free(array);
/* Attention! array, array2 become wild pointers at this point
* and may not be accessed (except new, valid addresses are assigned).
* However, aString1, aString2, aString3 are still intact.
*/
printf("aString1: '%s', aString2: '%s', aString3: '%s'\n",
aString1, aString2, aString3);
/* done */
return 0;
}
The sample can be tested on ideone.com.
The sample output is:
array2[0]: 'Hello', array2[1]: 'World', array2[2]: 'I'm here.'
aString1: 'Hello', aString2: 'World', aString3: 'I'm here.'
Update:
So, I finally looked again on to the question & answer of Matheus and tried to fix it according to his intention (or how I understood it). I based it on Matheus' implementation and remarked modified codes by comments:
list.h:
#ifndef LIST_H
#define LIST_H
#if 0 /* not necessary to define these types */
#include "types.h"
#else /* they are already available in a (better) portable manner: */
#include <stdint.h>
/* Btw. I had to change:
* uint8 -> uint8_t
* uint32 -> uint32_t
*/
#endif /* 0 */
typedef struct {
uint32_t length;
#if 0 /* gcc ERROR: */
/* list.c:17:3: error: invalid use of flexible array member
* ls->values = NULL;
*/
void *values[];
#else /* (not) 0 */
void **values;
#endif /* 0 */
} List;
void List_init(List *ls);
void List_push(List *ls, uint8_t count, ...);
void* List_getindex(List *ls, uint32_t i);
void List_setindex(List *ls, uint32_t i, void *v);
#endif /* LIST_H */
list.c:
#include "list.h"
#include <stdarg.h>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
#if 0 /* no need for a variable (with storage */
static size_t PointerSize = sizeof(void*);
#else /* use enum instead (constant) */
enum { PointerSize = sizeof(void*) };
#endif /* 0 */
void List_init(List *ls)
{
ls->length = 0;
/* This is important: */
ls->values = NULL;
/* or 1st realloc() in List_push() may have Undefined Behavior.) */
}
void List_push(List *ls, uint8_t count, ...)
{
uint32_t len = ls->length;
uint32_t sum = len + count;
void **values = realloc(ls->values, sum * PointerSize);
if (!values) {
/* realloc() failed! Bail out before destroying the existing data. */
return;
}
ls->length = sum;
ls->values = values;
/* assign new contents */
va_list newVals;
va_start(newVals, count);
#if 1 /* the readable way: */
int pos = len;
while (count--) values[pos++] = va_arg(newVals, void*);
#else /* the hackish C style way: */
values += len;
while (count--) *values++ = va_arg(newVals, void*);
#endif /* 1 */
va_end(newVals);
}
void* List_getindex(List *ls, uint32_t i)
{
return ls->values[i];
}
wscom.c:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "list.h"
int main()
{
List ls;
/* Put a value pointers at indices 0, 1, 2 */
uint8_t value1 = 0x41, value2 = 0x42;
uint8_t value3[3] = { 0x43, 0x44, 0x45 };
List_init(&ls);
List_push(&ls, 3, &value1, &value2, value3);
/* Check whether list contents can be retrieved again */
if ((*(uint8_t*)List_getindex(&ls, 0)) == 0x41) {
printf("List entry 0 is correct.\n");
}
if ((*(uint8_t*)List_getindex(&ls, 1)) == 0x42) {
printf("List entry 1 is correct.\n");
}
{ uint8_t *values = List_getindex(&ls, 2);
if (values[0] == 0x43
&& values[1] == 0x44
&& values[2] == 0x45) {
printf("List entry 2 is correct.\n");
}
}
/* Done. */
return 0;
}
In one of my comments, I stated that void *values[]; in struct List might be OK. Ahem, I was wrong. gcc remarks this as error when I tried to use it in list.c. So, actually, it is OK but not for what I intend it to use.
Finally, my sample session (using gcc in cygwin on Windows 10):
$ gcc -std=c11 -o wscom wscom.c list.c
$ ./wscom
List entry 0 is correct.
List entry 1 is correct.
List entry 2 is correct.
$
2nd Update:
(I believe) I realized the missing piece of Matheus (considering his Javascript background):
There are no dynamic arrays in C (in opposition to Javascript).
Instead, there are arrays with variable size which may be used only in specific situations:
In C:
Definition of arrays with variable size in global variables is prohibited. (The compiler needs to know how many bytes to allocate for storage.) This does not exclude something like e.g.
int array[] = { 1, 2, 3 };
because the compiler determines the size from the initializer (on the right hand side of =).
Declaration of global arrays without explicit size is possible. (The definition with proper size might/must be done somewhere else. The linker will fail if no proper storage definition can be found.)
A local variable (inside a function, storage class auto but not static or extern) might be declared as array with size determined at runtime (from a variable). This feature was introduced in C99 but not (yet) in C++ (at least not until C++11 incl.)
A function parameter might be declared as array with unknown (or any) size. (This is equal to declaring it as a pointer.)
I found a nice answer about this in SO: Dynamic array allocation on stack in C (which I used to prove my own statements above).
The only supported way to have "dynamic arrays" in C is the usage of the standard library functions malloc()/realloc()/free(). However this is better called "dynamic memory" allocation because this applies to any C type (not only arrays).
Disclaimer:
I apologize if I wrote something rubbish about Javascript. I'm the total newbie in Javascript with very less practical experience...
file stack.h
typedef struct
{
void *elems;
int elem_size;
int log_len;
int alloc_len;
void (*free_fn)(void *);
} stack;
void stack_new(stack *s, int elem_size, void (*free_fn)(void *));
void stack_dispose(stack *s);
void stack_push(stack *s, void *value);
void stack_pop(stack *s, void *address);
and the implementation file stack.c
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#define assert(condition) if(!condition) printf("assert fail\n");exit(0)
void strfree(void *elem);
int main()
{
stack s;
int i;
char *copy, *top;
const char *friends[] = {"joe", "castiel", "lily"};
stack_new(&s, sizeof(char *), strfree);
for(i=0; i<3; i++)
{
copy = strdup(friends[i]);
stack_push(&s, &cp);
}
for(i=0; i<=3; i++)
{
stack_pop(&s, &top);
printf("%s\n", top);
}
stack_dispose(&s);
return 1;
}
void strfree(void *elem)
{
free(*(char **)elem);
}
void stack_new(stack *s, int elem_size, void (*free_fn)(void *))
{
assert(elem_size > 0);
s->alloc_len = 4;
s->free_fn = free_fn;
s->log_len = 0;
s->elem_size = elem_size;
s->elems = malloc(s->alloc_len * s->elem_size);
assert(s->elems != NULL);
}
void stack_dispose(stack *s)
{
int i;
if(s->free_fn)
{
for(i=0; i<s->log_len; i++)
{
s->free_fn((char *)s->elems + i * s->elem_size);
}
}
free(s->elems);
}
void stack_push(stack *s, void *v)
{
if(s->log_len == s->alloc_len)
{
s->alloc_len *= 2;
s->elems = realloc(s->elems, s->alloc_len*s->elem_size);
assert(s->elems != NULL);
}
memcpy((char *)s->elems+s->log_len*s->elem_size, v, s->elem_size);
s->log_len++;
}
void stack_pop(stack *s, void *address)
{
assert(s->log_len > 0);
void *source = (char *)s->elems + (s->log_len - 1) * s->elem_size;
memcpy(address, source, s->elem_size);
s->log_len--;
}
So it compiles but it doesn't run.
It has a warning about comparison between pointer and integer which comes from the code
assert(s->elems != NULL);
It is broken somewhere, it will not print out the three names defined here
const char *friends[] = {"joe", "castiel", "lily"};
I know the code is bit of too much, but I really wish to get some help, I'm at my wits end here.
One problem is your assert macro:
#define assert(condition) if(!condition) printf("assert fail\n");exit(0)
The exit(0); is executed regardless of whether the condition is true or false (look at the generated code again). If you want assertions, use the standard #include <assert.h>.
Your first identified problem is with:
assert(s->elems != NULL);
Given the definition, this is equivalent to:
if (!s->elems != NULL)
printf("assert fail\n");
exit(0);
The !s->elems is an integer, either 0 or 1, compared with a null pointer constant. Hence the warning. When writing macros, enclose arguments in parentheses. At minimum:
#define assert(condition) if(!(condition)){printf("assert fail\n");exit(1);}
This still isn't a good macro, but at least it will get rid of the first compilation error, and your stack_new() won't exit when it is called just because it is called. Note that it is conventional to exit with a non-zero status when there is a problem — exiting with zero indicates success.
Run your code in a debugger using GDB to see what it is doing line by line. Google "gdb cheat sheet" to get started and compile your code with -g.
I'm have a bit of a problem with C and XML.
Basically, the code I'm using is:
#include <stdio.h>
#include <curl/curl.h>
#include <libxml/tree.h>
#include <string.h>
#include <stdlib.h>
#define WEBPAGE_URL "http://gdata.youtube.com/feeds/api/videos/mKxLmdBzS10"
typedef struct {
char *contents;
int size;
} data;
/*Curl uses this function to write the contents of a webpage to a file/stdout*/
size_t write_data( void *ptr, size_t size, size_t nmeb, void *stream)
{
data *curl_output = (data *)stream;
int curl_output_size = size * nmeb;
curl_output->contents = (char *) realloc(curl_output->contents, curl_output->size + curl_output_size + 1);
if (curl_output->contents) {
memcpy(curl_output->contents, ptr, curl_output_size); /*Copying the contents*/
curl_output->size += curl_output_size;
curl_output->contents[curl_output->size] = 0;
return curl_output->size;
}
}
int main()
{
data webpage;
webpage.contents = malloc(1);
webpage.size = 1;
CURL *handle = curl_easy_init();
curl_easy_setopt(handle,CURLOPT_URL,WEBPAGE_URL); /*Using the http protocol*/
curl_easy_setopt(handle,CURLOPT_WRITEFUNCTION, write_data); /*Setting up the function meant to copy data*/
curl_easy_setopt(handle,CURLOPT_WRITEDATA, &webpage); /*The data pointer to copy the data*/
curl_easy_perform(handle);
curl_easy_cleanup(handle);
printf("Contents: %s",webpage.contents);
int i;
}
I'm mean't to be getting this XML back: http://gdata.youtube.com/feeds/api/videos/mKxLmdBzS10.
But currently, I only get arbitrary amounts back, sometimes a third sometimes a half and other times just a quater.
Anybody know what I'm doing wrong?
The problem is in your write_data function.
This line copies the new data to the start of your array, rather than the current end.
memcpy(curl_output->contents, ptr, curl_output_size); /*Copying the contents*/
You need to offset your pointer:
memcpy(curl_output->contents + curl_output->size, ptr, curl_output_size); /* Copying the contents */
Additionally, your return value is bad -- it should be return(curl_output_size); to indicate success, via the number of bytes actually processed on the call -- and below the bracket, return(0); to show an error.
You might also find it clarifies things if instead of having curl_output->size and curl_output_size you pick more distinct names...perhaps curl_output->len?