accessing a double structure pointer, segmentation fault occured - c

typedef struct
{
int member;
} mystruct;
void myfunc(mystruct **data)
{
mystruct *const *p;
for(p = data; *p !=NULL; p++)
{
printf("hello\n");
}
}
void main(int argc, char *argv[])
{
myfunc(NULL);
}
tried with the above code am getting segmentation fault, its mainly something wrong in that for loop, how to just remove this segmentation fault in that for loop.... actually am learning this double pointer stuff, so i may be a little stupid in asking few question.... thanks in advance

The *p in the for loop dereferences the first pointer.
But that pointer is NULL, as you call your function with myfunc(NULL);.

Whenever you dereference a nullpointer, you invoke undefined behaviour (e.g. a segmentation fault).
typedef struct
{
int member;
} mystruct;
void myfunc(mystruct **data)
{
mystruct *const *p;
// this loop assumes data to be a valid pointer
// to a NULL-terminated array!
for(p = data; *p != NULL; p++)
{
printf("hello\n");
}
}
int main()
{
mystruct s;
mystruct *arr[2];
arr[0] = &s; // arr[0] points to s
arr[1] = NULL; // null-terminator
s.member = 13;
myfunc(arr);
// myfunc(NULL); // undefined behaviour
return 0;
}
you can fix this by checking data in myfunc:
void myfunc(mystruct **data)
{
mystruct *const *p;
// first check data
if(data != NULL)
{
// loop still assumes data to be a NULL-terminated array!
for(p = data; *p != NULL; ++p)
{
printf("hello\n");
}
}
}
...
myfunc(NULL); // well defined as the pointer will be checked
if you just want to iterate over a range, consider the standard approach:
void myfunc_range(mystruct *begin, mystruct *end)
{
mystruct const *it;
for(it = begin; it != end; ++it)
{
printf("hello %d\n", it->member);
}
}
int main()
{
mystruct s;
mystruct arr[2];
s.member = 42;
myfunc_range(&s, &s + 1); // iterate over a single element
arr[0].member = 13;
arr[1].member = 37;
myfunc_range(arr, arr + sizeof(arr) / sizeof(*arr)); // iterate over the whole array
myfunc_range(arr + i, arr + i + k); // iterate over elements arr[i..i+k-1]
myfunc_range(NULL, NULL); // well defined as NULL == NULL (an empty range)
// myfunc(arr, &s); // undefined behaviour as s is not part of the array arr
return 0;
}

Thanks for all your answers, exactly now while accessing the NULL value in the condition part of the for loop is leading to the segmentation fault.

Related

Returning NULL pointer

I have function that deallocates struct:
typedef struct matrix_t {
int r; // num of rows
int c; // num of cols
int *values;
} matrix_t;
function that does that is:
void deallocate_matrix(matrix_t **ptr) {
if (ptr != NULL && *ptr != NULL) {
free((*ptr)->values);
(*ptr)->values = NULL;
free(*ptr);
*ptr = NULL;
}
}
Then my main:
int main(int argc, char *argv[]) {
matrix_t *M[4] = {NULL};
char op[2] = {0};
int op_ret[2] = {0};
M[0] = matrix_input();
scanf(" %c", &op[0]);
M[1] = matrix_input();
M[2] = calc(M[0], M[1],op[0]);
matrix_print(M[2]);
deallocate_matrix((matrix_t **)&M[1]);
assert(M[1] == NULL); //this looks ok, M[1] is NULL
return 0;
}
But if I omit deallocate_matrix((matrix_t **)&M[1]); and deallocate with function calc like this:
matrix_t *calc(matrix_t *A, matrix_t *B, char op) {
matrix_t *M = NULL;
//some code to compute M
deallocate_matrix((matrix_t **)&A);
deallocate_matrix((matrix_t **)&B);
return M;
}
the assertion M[1]==NULL would fail, so I am clearly doing something wrong in the calc function, but I am not sure what.
Your code is correct. The assert is wrong. Get rid of it and you won't have an issue. You have freed the matrix.
What's happening is that calc takes A and B by value. So it can't change the value of A or B in the caller. So they won't be NULL after the call.
M[2] = calc(M[0], M[1],op[0]);
Here, we pass the values of M[0] and M[1] to calc. So calc can't change the value of M[0] or M[1] because it doesn't get a pointer to them. That's fine. There no reason it should.
Please just get out of the habit of senselessly setting pointers to NULL when you deallocate what they point to. It causes lots of pain the more complex your code gets.
You see the way free works, right? Your functions should work the same way for sanity, so:
void deallocate_matrix(matrix_t *ptr) {
if (ptr != NULL) {
free(ptr->values);
free(ptr);
}
}
Simple and easy to use.

Double pointer as function parameter

I was reading a page of "Understanding and Using C Pointers" when this function appeared:
void safeFree(void **pp) {
if (pp != NULL && *pp!= NULL) {
free(*pp);
*pp = NULL;
}
}
and an example code from it:
int main(int argc, char **argv) {
int* pi = (int*)malloc(sizeof(int));
*pi = 5;
safeFree((void**)&pi);
return EXIT_SUCCESS;
}
My point is, checking pp != NULL in the if condition in this scenario is useless, right? Because according to the way this code is written this condition will never be false. But there is a scenario in which this condition will be true, assuming **pp expects a memory address and (assumed by me) a memory address of a variable can never be NULL? Or did the writer did that checkup in case someone did something like this?
int main(int argc, char **argv) {
int **pi = NULL;
safeFree((void**)pi);
return EXIT_SUCCESS;
}
Thanks.
Having said that safeFree is just confusing and does not provide safety. Because the code assumes that the API user will always pass a particularly constructed pointer. Consider these:
tricky 0 (screws up a)
#include <stdio.h>
#include <stdlib.h>
void safeFree(void **pp) {
if (pp != NULL && *pp!= NULL) {
free(*pp);
*pp = NULL;
}
}
int main() {
int *a;
int **p;
a = malloc(5 * sizeof(int));
p = malloc(1 * sizeof(a));
a[0] = 10;
p[0] = a;
fprintf(stderr, "%d\n", a[0]);
safeFree((void **)p); /* grrrr */
fprintf(stderr, "%d\n", a[0]);
}
tricky 1 (crashes)
int main() {
int a[] = { };
int b[] = { 1 };
int c[] = { 2 };
int **p = malloc(3 * sizeof(int *));
p[0] = a, p[1] = b, p[2] = c;
safeFree((void **)p); /* grrrr */
}
The function checks for NULL mainly for cases like your second. If an actual variable address is passed that check will not fail but never trust your caller to be sane.

Dynamically resizing a struct in C

I'm fiddling around with Object oriented programming in C (note! Not C++ or C# - just plain ol' C). Right now, I'm trying to dynamically resize a struct (I'm playing with writing a simple String class). The code builds okay:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct TestClass
{
char *s;
size_t size;
size_t b_size;
void (*CreateString) (struct TestClass*,char*);
};
void TestClassCreateString(struct TestClass *m, char* str)
{
char *buf;
m->size = strlen(str);
if (!m->size)
{
free(m->s);
m->s = malloc(16);
}
else
{
buf = realloc(m->s, m->size);
if (buf) m->s = buf;
}
}
struct TestClass* TestClassCreate()
{
struct TestClass* m = malloc((sizeof(struct TestClass)));
m->CreateString = TestClassCreateString;
return m;
}
int main()
{
struct TestClass* fizz = TestClassCreate();
fizz->CreateString(fizz,"Hello World");
free(fizz);
return 0;
}
…But on running it I get the following error:
malloc: *** error for object 0x5000000000000000: pointer being realloc'd was not allocated
*** set a breakpoint in malloc_error_break to debug
Is anyone able to identify where I've gone wrong? Thanks in advance!
malloc does not zero its memory; it returns garbage, so you get an invalid pointer inside this struct:
struct TestClass* m = malloc((sizeof(struct TestClass)));
When creating a struct TestClass in TestClassCreate() the code misses to properly initialise the freshly allocated struct.
So calling
free(m->s);
tries to free memory at a random address, which invokes undefined behaviour and typically crashes the program.
To fix this modify the code as follows
struct TestClass* TestClassCreate()
{
struct TestClass* m = ...
...
m->s = NULL;
m->size = 0;
m->b_size = 0;
return m;
}
To make things better also add some error checking:
struct TestClass* TestClassCreate()
{
struct TestClass * m = malloc((sizeof(struct TestClass)));
if (NULL != m)
{
m->CreateString = TestClassCreateString;
m->s = NULL;
m->size = 0;
m->b_size = 0;
}
return m;
}
To make the code even more fail-safe apply these last changes:
struct TestClass* TestClassCreate(void)
{
struct TestClass * m = malloc(sizeof *m);
...
Further more the code misses to allocate memory for the C-"string"'s 0-terminator here:
void TestClassCreateString(struct TestClass *m, char* str)
{
...
else
{
buf = realloc(m->s, m->size + 1); /* allocate 1 byte more for the trailing
`0` marking the end of a C-"string". */
...
You are short by 1-byte. You need to add 1 to m->size for the null-terminator if you intend to copy str to m->s. E.g.:
void TestClassCreateString(struct TestClass *m, char* str)
{
char *buf;
m->size = strlen(str);
if (!m->size)
{
free(m->s);
m->s = malloc(16);
}
else
{
buf = realloc(m->s, m->size + 1);
if (buf) m->s = buf;
strncpy (m->s, str, m->size + 1);
}
}
Then you can do something like:
int main()
{
struct TestClass* fizz = TestClassCreate();
fizz->CreateString(fizz,"Hello World");
printf ("\n fizz->s : %s\n\n", fizz->s);
free(fizz);
return 0;
}
and get:
$ ./bin/oo_struct
fizz->s : Hello World

C pointer address changes without assignment

I am working on a Uni assignment here, and I've run into a problem. I am attempting to store a string input at a point inside a struct using a for-loop. Later on I intend to use the pointer to the place where the data was stored to fetch the string. Now the problem is, as I move on inside my for-loop, the address of the point changes as well. This code:
printf("B: %p\n", txt->point);
for(i = 0; i < input_sz; i++)
{
txt->point[i] = input[i];
}
printf("A: %p\n", txt->point);
gives the output:
B: 0x7fc111803200
A: 0x7fc111803265
where B is before-value and A is after-copying value.
Any help debugging this would be very appreciated!
EDIT: Here's some more code:
The struct:
struct text_storage {
char* start;
char* point;
char* end;
} typedef text_t;
Initialization function:
text_t* text_init(void *memory, size_t size)
{
text_t* to_return;
if(size < sizeof(text_t))
{
return NULL;
}
to_return = (text_t*) memory;
to_return->start = to_return;
to_return->end = to_return->start + size;
to_return->point = to_return->start;
printf("Start: %p, point: %p, end: %p, end-start: %d\n", to_return->start, to_return->point, to_return->end, (to_return->end - to_return->start));
return to_return;
}
The text-store method in which the error occurs:
int text_store_entry(text_t *txt, const char *input, size_t input_sz)
{
int to_return;
char* begin = txt->point;
int i;
if(input_sz > (txt->end - txt->point))
{
return -1;
}
printf("Start: %p, point: %p, end: %p, end-start: %d\n", txt->start, txt->point, txt->end, (txt->end - txt->start));
printf("B: %p\n", txt->point);
for(i = 0; i < input_sz; i++)
{
txt->point[i] = input[i];
}
printf("A: %p\n", txt->point);
}
Main-function (testing purposes only):
int main(int argc, char* argv[])
{
void* memory = malloc(10000);
char* a = "hei pa deg din trekkbasun";
text_t* txt;
int memoverwritten;
txt = text_init(memory, 10000);
memoverwritten = text_store_entry(txt, a, (size_t)26);
printf("got through\n");
return 0;
}
The problem most probably is due to the initialization of structures of type struct text_storage. Such structures contain three pointers to text. Each pointer should be initialized, possibly with a malloc. Your text_init function does not do that properly.
In fact, the place where the start pointer is stored overlaps with the first bytes of the memory that you want to use.
I'm guessing that you need a structure like this:
typedef struct text_storage {
char* start;
char* point;
char* end;
char* data;
} text_t;
initialized with a function like this:
text_t text_init(void *memory, size_t size)
{
text_t to_return;
to_return.data = (char *) memory;
to_return.start = to_return.data;
to_return.end = to_return.start + size;
to_return.point = to_return.start;
return to_return;
}
Print txt->point in the loop and see the point at which it changes. I'm guessing it changes when assigning to txt->point[0]. I'm not fully familiar with printf, so I'm not sure what it's printing out for you, but the name of an array references the first location. If printf is printing out a pointer, txt->point[i] is always a char pointer, and printf may be dereferencing txt->point, which will get it the first entry, and then showing the address there, which you do assign when you change the point to input[i].

Setting a double pointer array

I know there are a lot of double pointer questions, but I couldn't find one that pertained to starting an array.
In the code below, I can set pointers in main by ptrs[0] = &array[0];, but the code halts when enqueue() calls *queue[i] = p;. Why is that? I don't know if it matters, but ptrs[] is not initialized.
#define QUEUE_LEN 5
int *ptrs[5];
int array[5] = {1,2,3,4,5};
void enqueue(int *p, int **queue) {
int i = 0;
int *tmp;
// Find correct slot
while (*queue && *queue[i] >= *p) {
i++;
}
// Error no free slots
if (i == QUEUE_LEN) {
printf("No free slots.\r\n");
return;
}
// Insert process
if (!*queue) {
*queue[i] = p;
return;
}
else {
tmp = *queue[i];
*queue[i] = p;
}
// Increment the other processes
return;
}
int main(int argc, char** argv) {
int i;
for (i=0; i<5; i++) {
enqueue(&array[i], ptrs);
}
for (i=0; i<QUEUE_LEN; i++)
printf("%d\n", *(ptrs[i]));
return 0;
}
After first loop, i will remain zero. Here:
if (!*queue) {
*queue[i] = p;
return;
}
You check, that *queue is 0 and dereference it as well. It is UB.
PS. Btw, this:
*queue[i] = p;
Will not compiles, since *queue[i] has type int, but p has type int*.
// Find correct slot
while (*queue && *queue[i] >= *p) {
i++;
}
This will access some random memory address taken from uninitialized ptrs value.
Your check for *queue != 0 is not enough, you need to initialize array with zeores as:
int *ptrs[5] = {0};
And you still need to allocate memory you are attempting to write later when inserting.

Resources