I tried to write a vector in c using memory operations.Compiler shows no errors but if I try to print an element from the vector it simply crashes. And whenever I try to print destination variable (printf((int) destination)) the program crashes again.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct{
void* elemList;
int elemSize;
int maxSize;
int curSize;
}myvector;
void initVec(myvector * vec, int typeSize){
vec->curSize = 0;
vec->maxSize = 10;
vec->elemSize =typeSize;
vec->elemList = malloc(10*sizeof(typeSize));
}
void add(myvector * vec, void* elem){
if(vec->curSize >= vec->maxSize){
vec->elemList = realloc(vec->elemList, vec->maxSize*2);
}
memcpy(&vec->elemList[vec->curSize],elem,vec->elemSize);
}
void get(myvector * vec, int index, void* destination){
if(index > vec->curSize || index < 0){
printf("Invalid Index");
return;
}
destination = malloc(vec->elemSize);
memcpy(destination,&vec->elemList[index], vec->elemSize);
}
int main()
{
myvector newVec;
initVec(&newVec,sizeof(int));
int a = 5;
add(&newVec,&a);
int* b;
get(&newVec,0,b);
printf(*b);//this is where the program crashes
return 0;
}
Basically the pointer in the get is not handled correctly. It's being passed by value so a copy of the pointer is made, the copy is modified (memory allocation is done for this copy), but the original pointer once you quit the get method is not pointing to a valid memory. You have to pass the address of the pointer. Following is a modified code (note the double ** in the destination in the get method). Basically I pass the address of the "destination" pointer instead of the pointer itself. Additionally I fixed the line sizeof(typeSize) .. it should be typeSize only since you are already calling the initVec method with sizeof operator.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct{
void* elemList;
int elemSize;
int maxSize;
int curSize;
}myvector;
void initVec(myvector * vec, int typeSize){
vec->curSize = 0;
vec->maxSize = 10;
vec->elemSize = typeSize;
vec->elemList = malloc(vec->maxSize*typeSize);
}
void add(myvector * vec, void* elem){
if(vec->curSize >= vec->maxSize)
{
vec->elemList = realloc(vec->elemList, vec->maxSize*2);
}
memcpy(&vec->elemList[vec->curSize], elem, vec->elemSize);
vec->curSize++;
}
void get(myvector * vec, int index, void** destination){
if(index > vec->curSize || index < 0)
{
printf("Invalid Index");
return;
}
*destination = malloc(vec->elemSize);
memcpy(*destination, &vec->elemList[index], vec->elemSize);
}
int main()
{
myvector newVec;
initVec(&newVec,sizeof(int));
int a = 5;
add(&newVec,&a);
int* b;
get(&newVec, 0, &b);
printf("value of b is %d\n", *b); // This works correctly now
return 0;
}
A couple of issues with the code :
vec->elemList = malloc(10*sizeof(typeSize)); should be vec->elemList = malloc(10*typeSize);
If you would like get to create a pointer to int I would recommend either defining it like int* get(myvector * vec, int index) and return a newly allocated pointer to int or in the main function use :
int b;
get(&newVec, 0, &b);
the latter will also avoid memory leaks.
printf(*b); is wrong as you are passing an int and it expects a char* use either printf("%d", b); if b is an int or printf("%d", b);if b is aint`
you are using malloc a lot but no free. In this particular program you don't get memory leaks as the OS will reclaim all memory when main returns. But think early about a function to clear your vector and.
*b shouldn't be a valid pointer to string, so it will cause crash.
Try printing it by printf("%d",*b);
To make it better, you should free the buffer that are allocated by malloc.
UPDATE
The get function is wrong since it throws away the buffer allocated to destination
get function and main function should be like this:
void get(myvector * vec, int index, void** destination){
if(index > vec->curSize || index < 0){
printf("Invalid Index");
return;
}
*destination = malloc(vec->elemSize);
memcpy(*destination,&vec->elemList[index], vec->elemSize);
}
int main()
{
myvector newVec;
initVec(&newVec,sizeof(int));
int a = 5;
add(&newVec,&a);
int* b;
get(&newVec,0,&b);
printf("%d",*b);//this is where the program crashes
return 0;
}
But this still gives me Segmentation Fault. I'm working on.
UPDATE 2
You should think about the size of each elements.
You also forget the size information in add function.
This code should work if we don't care about memory leak.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
typedef struct{
void* elemList;
int elemSize;
int maxSize;
int curSize;
}myvector;
void initVec(myvector * vec, int typeSize){
vec->curSize = 0;
vec->maxSize = 10;
vec->elemSize =typeSize;
vec->elemList = malloc(vec->maxSize*vec->elemSize);
}
void add(myvector * vec, void* elem){
if(vec->curSize >= vec->maxSize){
vec->elemList = realloc(vec->elemList, vec->elemSize * vec->maxSize*2);
vec->maxSize *= 2;
}
memcpy(vec->elemList + vec->curSize * vec->elemSize,elem,vec->elemSize);
vec->curSize++;
}
void get(myvector * vec, int index, void** destination){
if(index >= vec->curSize || index < 0){
printf("Invalid Index");
return;
}
*destination = malloc(vec->elemSize);
memcpy(*destination,vec->elemList + index * vec->elemSize, vec->elemSize);
}
int main()
{
myvector newVec;
initVec(&newVec,sizeof(int));
int a = 5;
add(&newVec,&a);
int* b;
get(&newVec,0,(void**)&b);
printf("%d",*b);
return 0;
}
Related
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;
}
This question already has answers here:
Changing address contained by pointer using function
(5 answers)
Closed 2 years ago.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void *alloc_init(void *ptr, size_t size, void *val)
{
ptr = malloc(size);
memcpy(ptr, val, size);
return ptr;
}
int main()
{
int *a;
int val = 5;
int b = 5;
alloc_init(a, 4, &val);
printf("%d\n", *a);
return 0;
}
It's a very simple program - I wanted to test the alloc_init function. I expect 5 to be printed, but it's always 1. The function should be allocating memory for a variable(in this case) and assigning it a value passed to the function by copying it's bytes. What am I doing wrong?
Two ways to do this (both untested):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void alloc_init(void **ptr, size_t size, void *val)
{
*ptr = malloc(size);
memcpy(*ptr, val, size);
}
int main()
{
int *a;
int val = 5;
int b = 5;
alloc_init(&a, sizeof *a, &val);
printf("%d\n", *a);
return 0;
}
or
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void *alloc_init(size_t size, void *val)
{
void *ptr = malloc(size);
memcpy(ptr, val, size);
return ptr;
}
int main()
{
int *a;
int val = 5;
int b = 5;
a = alloc_init(sizeof *a, &val);
printf("%d\n", *a);
return 0;
}
You should probably prefer the 2nd approach. Passing an int** where a void** is expected is probably UB.
Yesterday I had a test on C where I coudn't figure out the last question:
We were given two arrays of two types of arrays: arrays including consecutive elements that are equal(eg: {"stack","heap","heap"}) and arrays of where no consecutive elements where equal (eg: {1,2,3,4,5,6,7,8,9}).
We were then asked to find one function that returned 1 or 0 if the given array contained doubles or not. So this function had to work with both integer arrays and char * arrays.
This is what I came up with today (but it keeps giving the wrong answer and crashing afterwards or a segmentation fault when comparing the strings)
Edit: correct code (thanks to #BLUEPIXY !)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int contains_dup(void *array, size_t size, size_t sizeoftype, int (*cmp)(const void*, const void*)){
//array != NULL, size != 0
char *obj = array;
size_t i;
for(i = 0; i < size-1; ++i){
if(cmp(obj + sizeoftype * i, obj + sizeoftype * (i+1)))
return 1;
}
return 0;
}
int eqi(const void *a, const void *b){
int x = *(const int *)a;
int y = *(const int *)b;
return x == y;
}
int eqs(const void *a, const void *b){
return strcmp(a, b) == 0;
}
#define TEST(name, cmp)\
do{\
int test;\
puts(#name ":");\
test = contains_dup(name, sizeof(name)/sizeof(*name), sizeof(*name), cmp);\
test ? puts("doubles? Yes\n") : puts("doubles? No\n");\
}while(0)\
/**/
int main(void){
int ints_yes[] = {0,1,2,2,2,3,4,4,5};
int ints_no[] = {0,1,2,3,4,5,6,7,8};
char *strings_yes[]={"heap","stack","stack","overflow"};
char *strings_no[] ={"heap","stack","heap","stack","overflow"};
puts("test:");
TEST(ints_yes, eqi);
TEST(ints_no, eqi);
TEST(strings_yes, eqs);
TEST(strings_no, eqs);
return 0;
}
Wrong old code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int array_contains_doubles(void ** array, int size, int sizeoftype){
int i;
char **out =(char**) malloc(size * sizeof(char*));
for(i=0;i<size;i++){ //trying to convert the array of ints to an
out[i] = array+i*sizeoftype; //array of char * eg: {1,2} ->{"1","2"}
// *out[i] +='a';
printf("%c\n",*out[i]);
}
out[i]= NULL;
while(*(out+1)!=NULL){
if(strcmp(*out,*(out++))==0){ //<- where i get the segmentation error
return 1;
}
}
return 0;
}
int main(void){
int i;
int ints_yes[] = {0,1,2,2,2,3,4,4,5};
int ints_no[]={0,1,2,3,4,5,6,7,8};
char * strings_yes[]={"heap","stack","stack","overflow"};
char * strings_no[]={"heap","stack","heap","stack","overflow"};
int test = array_contains_doubles((void **) ints_no,
sizeof(ints_no)/sizeof(ints_no[0]), sizeof(int));
(test) ? (printf("doubles? Yes")) : (printf("doubles? No"));
}
Sorry for any spelling mistakes, english is not my native language.
What your teacher is likely fishing for, is for you to implement a "functor" similar to the function pointer passed to bsearch (study this function). Something along the lines of this:
typedef int comp_func_t (const void*, const void*);
bool equal (const void* obj1, const void* obj2, comp_func_t* comp)
{
return comp(obj1, obj2)==0;
}
You call equal from your application with a pointer to the objects to compare, no matter what kind of objects they are. The function pointer specifies how objects of this type should be compared. You then implement the comparison functions for each type:
int comp_int (const void* obj1, const void* obj2)
{
int a = *(const int*)obj1;
int b = *(const int*)obj2;
if(a < b)
{
return -1;
}
else if(a > b)
{
return 1;
}
else // a == b
{
return 0;
}
}
int comp_str (const void* obj1, const void* obj2)
{
...
}
Typical use could be:
int x;
int y;
...
if(equal(&x, &y, comp_int))
{
...
}
Now this only compares two objects, so you'll have to expand this for an array by 1) sorting the array and 2) calling it for every two adjacent items in the sorted array, to find out if any are equal.
The above is the old, "de facto standard" way to implement type-specific behavior in C. In newer versions of the language, more elegant ways are available through the _Generic keyword, but this would probably not be addressed on a beginner-level class.
Take in mind the following piece of code:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int a;
int b;
int c;
}A;
A *test;
void init(A* a)
{
a->a = 3;
a->b = 2;
a->c = 1;
}
int main()
{
test = malloc(sizeof(A));
init(test);
printf("%d\n", test->a);
return 0;
}
It runs fine! Now imagine that I want to use the malloc function outside the main itself without returning a pointer to the struct. I would put malloc inside init and pass test adress. But this doesnt seem to work.
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int a;
int b;
int c;
}A;
A *test;
void init(A** a)
{
*a = malloc(sizeof(A));
*a->a = 3;
*a->b = 2;
*a->c = 1;
}
int main()
{
init(&test);
printf("%d\n", test->a);
return 0;
}
It keeps telling me that int a(or b/c) is not a member of the struct A when I use the pointer.
Your problem is operator precedence. The -> operator has higher precedence than the * (dereference) operator, so *a->a is read as if it is *(a->a). Change *a->a to (*a)->a:
#include <stdio.h>
#include <stdlib.h>
typedef struct
{
int a;
int b;
int c;
}A;
A *test;
void init(A** a)
{
*a = malloc(sizeof(A));
(*a)->a = 3;
(*a)->b = 2;
(*a)->c = 1;
}
int main()
{
init(&test);
printf("%d\n", test->a);
return 0;
}
You must add parenthesis:
void init(A **a)
{
*a = malloc(sizeof(A)); // bad you don't verify the return of malloc
(*a)->a = 3;
(*a)->b = 2;
(*a)->c = 1;
}
But it's good practice to do this:
void init(A **a)
{
A *ret = malloc(sizeof *ret); // we want the size that is referenced by ret
if (ret != NULL) { // you should check the return of malloc
ret->a = 3;
ret->b = 2;
ret->c = 1;
}
*a = ret;
}
You need to write (*a)->a = 3; for reasons of precedence.
Even though it's not a direct answer to your question, since we're in the vicinity of initialization I'd like to point out that C11 gives you a nicer syntax to initialize structs:
void init(A **a)
{
A *ret = malloc(sizeof *ret); // we want the size that is referenced by ret
if (ret != NULL) { // you should check the return of malloc
*ret = (A) {3, 2, 1};
// or
*ret = (A) { .a = 3, .b = 2, .c = 1 };
}
*a = ret;
}
Another advantage is that any uninitialized members are zeroed.
I keep geeting a segfault in the main function. I create a pointer to a struct I created and I pass it's reference to another function which allocates and reallocates memory. Then accessing it in the main function (where it was originally defined) causes a segfault.
Here is my test code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct {
char desc[20];
int nr;
} Kap;
void read(Kap *k);
int main(void) {
Kap *k = NULL;
read(k);
printf("__%s %d\n", k[4].desc, k[4].nr);
return 0;
}
void read(Kap *k) {
int size = 3;
k = (Kap*) calloc(size, sizeof(Kap));
k[0].nr = 1;
k[1].nr = 2;
k[2].nr = 3;
strcpy(k[0].desc, "hello0");
strcpy(k[1].desc, "hello1");
strcpy(k[2].desc, "hello2");
size *= 2;
k = (Kap*) realloc(k, sizeof(Kap)*size);
if(k == NULL) {
printf("ERROR\n");
exit(EXIT_FAILURE);
}
k[3].nr = 4;
k[4].nr = 5;
k[5].nr = 6;
strcpy(k[3].desc, "hello3");
strcpy(k[4].desc, "hello4");
strcpy(k[5].desc, "hello5");
}
What am I doing wrong?
Here's a simplified version of the problem you are having:
#include <stdio.h>
void func(int x)
{
x = 10;
}
int main()
{
int x = 5;
printf("x = %d\n", x);
func(x);
printf("x = %d\n", x);
}
The same reason x does not change is the reason that k does not change in your program. A simple way to fix it is this:
Kap *read()
{
Kap *k = calloc(...);
...
k = realloc(k, ...);
...
return k;
}
int main()
{
Kap *k = read();
...
}
The problem is you're not passing the pointer back to main(). Try something like this instead:
Kap * read();
int main(void) {
Kap *k = read();
printf("__%s %d\n", k[4].desc, k[4].nr);
return 0;
}
Kap * read() {
... everything else you're already doing ...
return k;
}
The code you showed passes a pointer by value into read(). The subroutine can use that pointer (though it's kind of useless since its local copy is immediately changed), however changes made within read() do not bubble back to its caller.
My suggestion is one method of allowing read() to send the new pointer back up, and it's probably the right method to choose. Another method is to change read()s signature to be void read(Kap **);, where it will receive a pointer to a pointer -- allowing it to modify the caller's pointer (due to being passed by reference).