C - Structure's pointer - c

I'm having trouble understanding pointers in general I think.
I can't seem to follow the logic of this code:
typedef struct StackRecord
{
int Capacity;
int TopOfStack;
int* Array;
}*Stack;
In the following structure, *Stack was declared to receive addresses of StackRecord structure type via simply stating Stack due to typedef
BUT code below the return another receiver of addresss of StackRecord structure type. Why isn't it returning the address? But rather return same type of pointer to itself?
Stack CreateStack(int MaxElements)
{
Stack S;
if (MaxElements < MinStackSize)
{
printf("Error : Stack size is too small");
return 0;
}
S = (Stack)malloc(sizeof(struct StackRecord));
if (S == NULL)
{
printf("FatalError : Out of Space!!!");
return 0;
}
S->Array = (int*)malloc(sizeof(char)* MaxElements);
if (S->Array == NULL)
{
printf("FatalError : Out of Space!!!");
return 0;
}
S->Capacity = MaxElements;
MakeEmpty(S);
return S;
}

Getting rid of the typedef may make things a little clearer, believe it or not:
struct StackRecord
{
int Capacity;
int TopOfStack;
int* Array;
};
/**
* Return a pointer to a new, dynamically allocated instance
* of struct StackRecord
*/
struct StackRecord *CreateStack(int MaxElements)
{
struct StackRecord *S;
if (MaxElements < MinStackSize)
{
printf("Error : Stack size is too small");
return 0;
}
S = malloc(sizeof *S); // no need for cast, sizeof *S is same as sizeof (struct StackRecord)
if (S == NULL)
{
printf("FatalError : Out of Space!!!");
return 0;
}
/**
* Allocate the memory for the Array member of
* the new stack record instance.
*/
S->Array = malloc( sizeof *S->Array * MaxElements );
if (S->Array == NULL)
{
printf("FatalError : Out of Space!!!");
return 0;
}
S->Capacity = MaxElements;
MakeEmpty(S);
return S;
}
In the code you posted, Stack is basically a synonym for struct StackRecord *. The function creates a new instance of struct StackRecord using malloc, initializes the contents of that record, and returns a pointer to that new instance.
A note on the malloc calls - in C, you do not need to cast the result of malloc, and doing so is generally considered bad practice1. Also, the operand to sizeof doesn't have to be a type name - it can be an expression of the type you want to allocate. IOW, given a declaration like
T *p;
both sizeof (T) and sizeof *p do the same thing - the expression *p has type T. So the general form of a malloc call can be written as
T *p = malloc( sizeof *p * N );
or
T *p;
...
p = malloc( sizeof *p * N );
That's simpler to write and easier to maintain than
p = (T *) malloc( sizeof (T) * N );
<rant>
Hiding the pointer-ness of a type behind a typedef is bad juju, especially when the user of that type has to be aware that he or she is dealing with a pointer type. Assigning the result of malloc to S means that S must be a pointer type. Using the -> to access members of S means that S must be a pointer to a struct or union type. Since you have to be aware that S is a pointer, it makes no sense to hide that pointerness behind the typedef. Similarly, if the user has to be aware of the struct-ness of the type, you shouldn't hide that struct-ness behind a typedef either.
Abstraction is a powerful tool, but partial (leaky) abstractions like the original code just make life more confusing for everyone (as you have discovered for yourself).
</rant>
This is not true for C++, because C++ doesn't allow implicit conversions between void * and other pointer types the way C does. But, if you're writing C++, you shouldn't be using malloc anyway.

In the typedef, the type identifier Stack is a pointer to a struct. The function prototype for CreateStack() specifies a return value of type Stack, which is a pointer to a StackRecord struct. S is declared to be of type Stack in the function body, so the function does return a pointer to a StackRecord struct.

In comments on #DavidBowling's answer you express this apparent misconception:
Stack is a pointer to StackRecord which means pointer must contain another address to which it is pointing to.
The typedef declares the identifier Stack to be an alias for the type struct StackRecord *. That would perhaps be clearer if it were rewritten in this wholly equivalent form:
struct StackRecord
{
int Capacity;
int TopOfStack;
int* Array;
};
typedef struct StackRecord *Stack;
No object of type struct StackRecord is declared, only that type itself and type Stack.
When function CreateStack() allocates memory sufficient for a struct StackRecord ...
malloc(sizeof(struct StackRecord));
... it is perfectly reasonable to convert the resulting pointer to type struct StackRecord *. Indeed, type Stack is exactly the same type as struct StackRecord *, so that's precisely what the code in fact does. The converted pointer still points to the same memory, and when that pointer is returned, the return value also points to the same memory.

Related

Why does this pointer declaration work with malloc()?

I am new to C and have a question about a specific pointer declaration:
Here is a program i wrote:
#include <stdlib.h>
struct n {
int x;
int y;
};
int main()
{
struct n **p = malloc(sizeof(struct n));
return 0;
}
The declaration here is not be correct but why not?
Here is my thought process:
The man page of malloc specifies that it returns a pointer:
The malloc() and calloc() functions return a pointer to the
allocated memory, which is suitably aligned for any built-in
type.
The type of p is struct n** aka a pointer to another pointer.
But shouldn't this declaration work in theory because:
malloc returns type struct n* (a pointer)
and p points to the pointer that malloc returns
so it is essentially a pointer to another pointer
so the type of p is fulfilled
Sorry if this is a dumb question but i am genuinely confused about why this does not work. Thanks for any help in advance.
The return type of malloc is not struct n *, regardless of how it is called. The return type of malloc is void *.
Initializing a struct n ** object with a value of type void * implicitly converts it to struct n **. This implicit conversion is allowed, because the rules for initialization follow the rules for assignment in C 2018 6.5.16.1 1, which state one of the allowed assignments is:
… the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object type, and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;…
and p points to the pointer that malloc returns
No, the value p is initialized to the value that malloc returns. Then p points to the memory malloc allocated.
It is a mistake for this code to allocate enough space for a struct n (using malloc(sizeof(struct n))) but assign the address of that space to the struct n ** that is p. To point to a struct n, use struct n *p = malloc(sizeof (struct n)); or, preferably, struct n *p = malloc(sizeof *p);.
To pointer to a pointer to a struct n, first create some pointer to a struct n, as with the above struct n *p = malloc(sizeof *p);. Then a pointer to that pointer would be struct n **pp = &p;.
If you wanted to allocate space for those pointers-to-pointers, you could do it with struct n **pp = malloc(sizeof *pp);, after which you could fill in the pointer to the struct n with *pp = malloc(sizeof **pp);. However, you should not add this additional layer of allocation without good reason.
Note that the form MyPointer = malloc(sizeof *MyPointer); is often preferred to MyPointer = malloc(sizeof (SomeType)); because the former automatically uses the type that MyPointer points to. The latter is prone to errors, such as somebody misintepreting the type of MyPointer and not setting SomeType correctly or somebody later changing the declaration of MyPointer but omitting to make the corresponding change to SomeType in the malloc call.
This doesn't really work. In this example, malloc is returning a void *, which points to a newly allocated place in the heap that is large enough to hold a struct n.
Note that malloc() returns type void *, which is basically a pointer to any potential type, malloc() DOES NOT return type struct n * (a pointer to your declared struct type). void * has the special property of being able to be cast to any other type of pointer, and back again.
All together this means that your code doesn't actually work, since the first dereference of your struct n** would be a struct n, not a struct n*. If you tried to dereference twice, you would most likely get an invalid memory reference and crash.
The reason that your code can compile and run without crashing is because:
The compiler automatically casts void * to struct n **
You never attempt to actually dereference your pointer, which means you never attempt an invalid memory reference.
Simplify the problem and understanding may come:
int main()
{
struct n data;
struct n *pData = &data; // skipped in your version
struct n **ppData = &pData;
// struct n **ppData = &data; // Will not compile
// struct n **ppData = (struct n **)&data; // Casting but wrong!
return 0;
}
Because malloc() returns a void ptr, you are free to store the pointer into anything that is a pointer datatype. On your head if you put it into the wrong datatype...

Type punning and malloc'ed memory

I originally asked this question: Type Punning with Unions and Heap
And not wanting the question to keep evolving to the point that anyone reading in the future had no idea what the original question was, I have a spin off question.
After reading this site:
https://kristerw.blogspot.com/2016/05/type-based-aliasing-in-c.html
Near the bottom it talks about malloc'd memory. Is it safe to say that casting from one pointer type to another pointer type is safe when memory is on the heap?
Example:
#include <stdio.h>
#include <stdlib.h>
struct test1
{
int a;
char b;
};
struct test2
{
int c;
char d;
};
void printer(const struct test2* value);
int main()
{
struct test1* aQuickTest = malloc(sizeof(struct test1));
aQuickTest->a = 42;
aQuickTest->b = 'a';
printer((struct test2*)aQuickTest); //safe because memory was malloc'd???
return 0;
}
void printer(const struct test2* value)
{
printf("Int: %i Char: %c",value->c, value->d);
}
And guessing it might not be safe. What would be the proper way to do this with memcpy? I will attempt to write an example with a function of what might hopefully work?
struct test2* converter(struct test1* original);
int main()
{
struct test1* aQuickTest = malloc(sizeof(struct test1));
aQuickTest->a = 42;
aQuickTest->b = 'a';
struct test2* newStruct = converter(aQuickTest);
printer(newStruct);
return 0;
}
struct test2* converter(struct test1* original)
{
struct test2* temp;
memcpy(&temp, &original, sizeof(struct test2));
return temp;
}
void *pnt = malloc(sizeof(struct test1));
What type has the memory behind pnt pointer? No type. It is uninitialized (it's value is "indeterminate"). There is just "memory".
Then you do:
struct test1* aQuickTest = malloc(sizeof(struct test1));
You only cast the pointer. Nothing happens here. No assembly is generated. Reading uninitialized memory is undefined behavior tho, so you can't read from aQuickTest->a (yet). But you can assign:
aQuickTest->a = 1;
This writes to an object struct test1 in the memory. This is assignment. You can now read aQuickTest->a, ie. print it.
But the following
printf("%d", ((struct test2*)aQuickTest)->a);
is undefined behavior (although it will/should work). You access the underlying object (ie. struct test1) using a not matching pointer type struct test2*. This is called "strict alias violation". Dereferencing an object (ie. doing -> or *) using a handle of not compatible type results in undefined behavior. It does not matter that struct test1 and struct test2 "look the same". They are different type. The rule is in C11 standard 6.5p7.
In the first code snipped undefined behavior happens on inside printf("Int: %i Char: %c",value->c. The access value-> accesses the underlying memory using incompatible handle.
In the second code snipped the variable temp is only a pointer. Also original is a pointer. Doing memcpy(&temp, &original, sizeof(struct test2)); is invalid, because &temp writes into the temp pointer and &original writes into the original pointer. No to the memory behind pointers. As you write out of bounds into&temppointer and read of bounds from&originalpointer (because most probablysizeof(temp) < sizeof(struct test2)andsizeof(original) < sizeof(struct test2)`), undefined behavior happens.
Anyway even if it were:
struct test1* original = &(some valid struct test1 object).
struct test2 temp;
memcpy(&temp, original, sizeof(struct test2));
printf("%d", temp.a); // undefined behavior
accessing the memory behind temp variable is still invalid. As the original didn't had struct test2 object, it is still invalid. memcpy doesn't change the type of the object in memory.

Strict aliasing in flexible array member?

I'm writing an Arena Allocator and it works, but I feel like it violates strict aliasing rules. I want to know if I'm right or wrong. Here's the relevant part of the code:
typedef struct ArenaNode ArenaNode;
struct ArenaNode {
ArenaNode *next;
size_t dataSize;
u8 data[];
};
typedef struct {
ArenaNode *head;
ArenaNode *current;
size_t currentIndex;
} Arena;
static ArenaNode *ArenaNodeNew(size_t dataSize, ArenaNode *next)
{
ArenaNode *n = malloc(sizeof(ArenaNode) + dataSize);
n->next = NULL;
n->dataSize = dataSize;
return n;
}
void *ArenaAlloc(Arena *a, size_t size)
{
const size_t maxAlign = alignof(max_align_t);
size_t offset = nextHigherMultiplePow2(offsetof(ArenaNode, data), maxAlign) - offsetof(ArenaNode, data);
size_t dataSize = offset + max(size, ARENA_SIZE);
// first time
void *ptr;
if (a->head == NULL) {
ArenaNode *n = ArenaNodeNew(dataSize, NULL);
a->head = n;
a->current = n;
ptr = n->data + offset;
a->currentIndex = nextHigherMultiplePow2(offset + size, maxAlign);
} else {
// enough space
if (a->currentIndex + size <= a->current->dataSize) {
ptr = &a->current->data[a->currentIndex];
a->currentIndex = nextHigherMultiplePow2(a->currentIndex + size, maxAlign);
} else {
ArenaNode *n = ArenaNodeNew(dataSize, NULL);
a->current->next = n;
a->current = n;
ptr = n->data + offset;
a->currentIndex = nextHigherMultiplePow2(offset + size, maxAlign);
}
}
return ptr;
}
The Arena is a linked list of Nodes and a Node is a header followed by data u8 data[]. u8 is unsigned char.
I maintain the next available index (currentIndex) and advance data by this index and return it as void * (ptr = &a->current->data[a->currentIndex]). Does this violate strict aliasing rule because I'm converting a pointer to u8 to something else and using that?
My confusion comes from the fact that memory returned by malloc has no effective type. But since I'm casting the malloc'd pointer to ArenaNode * and setting its data members (next and dataSize) after allocating it (in ArenaNodeNew), the effective type becomes ArenaNode. Or does it? I didn't set data field of that.
Basically, I think the question can be simplified to this: If I malloc a memory region of say, size 10, cast the pointer to struct {int a;} * (assume 4 bytes int), set it a to something, what happens to the rest of the 6 bytes? Does it have any effective type? Does the presence of flexible array member affect this in any way?
The extra bytes that are part of the flexible array member will have the effective type of that member as you write to them.
You can safely declare ptr as u8 * and define your function to return that type as well.
In your example of allocating 10 bytes and treating the first 4 as a struct of the given type, the remaining bytes have no effective type yet. You can use those for any type, assuming the pointer you use is aligned correctly, i.e. you can point a int * to the following bytes but not a long long *. due to alignment.
Does this violate strict aliasing rule because I'm converting a pointer to u8 to something else and using that?
No, you are not violating strict aliasing, but your code might violate the constraints imposed by 7.22.3 Memory management functions, paragraph 1:
The pointer returned if the allocation succeeds is suitably aligned so that it may be assigned to a pointer to any type of object with a fundamental alignment requirement and then used to access such an object or an array of such objects in the space allocated ...
You don't appear to be making sure the memory you use for any object is "suitably aligned" for any object. Given 6.3.2.3 Pointers, paragraph 7's statement:
A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined.
you appear to be risking undefined behavior.
"Suitably aligned" is extremely platform-dependent.

Compiler warnings and notes but dropping & does not work

In the below code snippet I'm getting a gcc compiler warning: "passing argument 1 of initArr from incompatible pointer type initArr(&stack,2);"
and a note: "expected 'struct Arr *' but argument is of type 'struct Arr**'"
which makes perfect sense to me.
As suggested by many a SO post, dropping the ampersand from the call to initArr(&stack, 2) however, results in another warning: "'stack' is used unitialized in this function: init(stack, 2);" and an immediate segfault error when ran.
What am I doing incorrectly?
I also tried using struct Arr in the malloc sizeof call and as expected, no change.
#include<stdio.h>
#include <stdlib.h>
#define TYPE int
struct Arr {
TYPE * data; // Pointer to the data array.
int size; // Number of elements in the array.
int capacity; // Capacity of the array.
};
void initArr(struct Arr * da, int capacity) {
da->data = malloc(sizeof(TYPE) * capacity);
da->size = 0;
da->capacity = capacity;
}
int main() {
struct Arr *stack;
initArr(&stack, 2);
return 0;
}
As haccks answer says,
you need to initialize your pointer, so it points to a real location
pass the pointer (rather than the address of the pointer variable)
int main()
{
struct Arr realArray; // allocate memory for the Arr (on the CPU's stack)
struct Arr *stack; // pointer variable (undefined)
stack = &realArray; // make variable "stack" point to realArray
initArr(stack, 2); // pass the pointer to realArray
}
stack is a pointer and it must be pointing to some valid memory location. Since stack is passed to the function and isn't initialized, you are getting a warning. In the function you are trying to access some random memory location which can cause the program crash or any erroneous behavior of the program, aka undefined behavior.
Change this:
struct Arr *stack;
To this:
struct Arr stack;
First look at the declaration of your initArr function:
void initArr(struct Arr * da, int capacity)
Your first argument is of type 'pointer to a struct'
Now look at your invocation of this function:
struct Arr *stack;
initArr(&stack, 2);
We see that stack is of type 'pointer to a struct', and in the function you are passing the address of stack (.i.e. the type is a pointer to a pointer to a struct). Which is what the gcc error is about, the note actually gives more information about the type that the function expects and the type that you are providing
There are two ways to fix this
leave the declaration of initArr as it is, and then change the function
invocation from initArr(&stack, 2) to initArr(stack, 2). In this case you will need to make sure that you have allocated memory for the structure prior to passing it to the function;
int main()
{
struct Arr* da = NULL;
da = malloc(sizeof(struct Arr));
initArr(da, 2);
}
or equivalently,
int main
{
struct Arr stack;
initArr(&stack, 2);
}
I would probably go for one of the above solutions.
Modify the declaration of initArr to initArray(struct Arr** da, int capacity). You will still need to make sure memory is allocated as before. However, if you change initArr to:
void initArr(struct Arr** da, int capacity)
{
*da = malloc(sizeof(struct Arr));
(*da)->data = malloc(sizeof(TYPE) * capacity);
(*da)->size = 0;
(*da)->capacity = capacity;
}
In this case I am changing the semantics of your initArr function so that I am initializing the structure all in one place. The code in your main function would remain unchanged.
N.B. For clarity, error checking has been omitted in the above code snippets, please don't do this in real code. Always check the return value from malloc to ensure that memory has been allocated.

getting C error: conversion to non-scalar type requested

Hey I am getting this error:
error: conversion to non-scalar type requested
Here are my structs:
typedef struct value_t value;
struct value{
void* x;
int y;
value* next;
value* prev;
};
typedef struct key_t key;
struct key{
int x;
value * values;
key* next;
key* prev;
};
Here is the code that is giving me problems:
struct key new_node = (struct key) calloc(1, sizeof(struct key));
struct key* curr_node = head;
new_node.key = new_key;
struct value head_value = (struct value) calloc(1, sizeof(struct value))
Am I not suppose to use calloc on structs? Also, I have a struct that I have created and then I want to set that to a pointer of that same struct type but getting an error. This is an example of what I am doing:
struct value x;
struct value* y = *x;
this gives me this error
error: invalid type argument of ‘unary *’
When I do y = x, I get this warning:
warning: assignment from incompatible pointer type
You are trying to assign a pointer expression (the return type of malloc() and friends is void*) to a struct type (struct new_node). That is nonsense. Also: the cast is not needed (and possibly dangerous, since it can hide errors)
struct key *new_node = calloc(1, sizeof *new_node);
the same problem with the other malloc() line:
struct value *head_value = calloc(1, sizeof *head_value);
More errors: You are omitting the 'struct' keyword (which is allowed in C++, but nonsense in C):
struct key{
int x;
struct value *values;
struct key *next;
struct key *prev;
};
UPDATE: using structs and pointers to struct.
struct key the_struct;
struct key other_struct;
struct key *the_pointer;
the_pointer = &other_struct; // a pointer should point to something
the_struct.x = 42;
the_pointer->x = the_struct.x;
/* a->b can be seen as shorthand for (*a).b :: */
(*thepointer).x = the_struct.x;
/* and for the pointer members :: */
the_struct.next = the_pointer;
the_pointer->next = malloc (sizeof *the_pointer->next);
I don't think you've correctly understood typedefs.
The common idiom with using typedefs for convenience naming is this:
struct foo {
int something;
};
typedef struct foo foo_t;
Then you use the type foo_t instead of the less convenient struct foo.
For convenience, you can combine the struct declaration and the typedef into one block:
typedef struct {
int something;
} foo_t;
This defines a foo_t just like the above.
The last token on the typedef line is the name you're assigning. I have no idea what the code you wrote is actually doing to your namespace, but I doubt it's what you want.
Now, as for the code itself: calloc returns a pointer, which means both your cast and your storage type should be struct key* (or, if you fix your naming, key_t). The correct line is struct key* new_node = (struct key*)calloc(1, sizeof(struct key));
For your second, independent, issue, the last line should be struct value* y = &x;. You want y to store the address of x, not the thing at address x. The error message indicates this - you are misusing the unary star operator to attempt to dereference a non-pointer variable.
struct key new_node = (struct key) calloc(1, sizeof(struct key));
calloc returns a pointer value (void *), which you are trying to convert and assign to an aggregate (IOW, non-scalar) type (struct key). To fix this, change the type of new_node to struct key * and rewrite your allocation as follows:
struct key *new_node = calloc(1, sizeof *new_node);
Two things to note. First of all, ditch the cast expression. malloc, calloc, and realloc all return void *, which can be assigned to any object pointer type without need for a cast1. In fact, the presence of a cast can potentially mask an error if you forget to include stdlib.h or otherwise don't have a declaration for malloc in scope2.
Secondly, note that I use the expression *new_node as the argument to sizeof, rather than (struct key). sizeof doesn't evaluate it's operator (unless it's a variable array type, which this isn't); it just computes the type of the expression. Since the type of the expression *new_node is struct key, sizeof will return the correct number of bytes to store that object. It can save some maintenance headaches if your code is structured like
T *foo;
... // more than a few lines of code
foo = malloc(sizeof (T))
and you change the type of foo in the declaration, but forget to update the malloc call.
Also, it's not clear what you're trying to accomplish with your typedefs and struct definitions. The code
typedef struct value_t value;
struct value{
void* x;
int y;
value* next;
value* prev;
};
isn't doing what you think it is. You're creating a typedef name value which is a synonym for an as-yet-undefined type struct value_t. This value type is different from the struct value type you create later (typedef names and struct tags live in different namespaces). Rewrite your structs to follow this model:
struct value_t {
void *x;
int y;
struct value_t *next;
struct value_t *prev;
};
typedef struct value_t value;
Also, life will be easier if you write your declarations so that the * is associated with the declarator, not the type specifier3. A declaration like T* p is parsed as though it were written T (*p). This will save you the embarrassment of writing int* a, b; and expecting both a and b to be pointers (b is just a regular int).
1 - This is one area where C and C++ differ; C++ does not allow implicit conversions between void * and other object pointer types, so if you compile this as C++ code, you'll get an error at compile time. Also, before the 1989 standard was adopted, the *alloc functions returned char *, so in those days a cast was required if you were assigning to a different pointer type. This should only be an issue if you're working on a very old system.
2 - Up until the 1999 standard, if the compiler saw a function call without a preceding declaration, it assumed the function returned an int (which is why you still occasionally see examples like
main()
{
...
}
in some tutorials; main is implicitly typed to return int. As of C99, this is no longer allowed). So if you forget to include stdlib.h and call calloc (and you're not compiling as C99), the compiler will assume the function returns an int and generate the machine code accordingly. If you leave the cast off, the compiler will issue a diagnostic to the effect that you're trying to assign an int value to a pointer, which is not allowed. If you leave the cast in, the code will compile but the pointer value may be munged at runtime (conversions of pointers to int and back to pointers again is not guaranteed to be meaningful).
3 - There are some rare instances, limited to C++, where the T* p style can make code a little more clear, but in general you're better off following the T *p style. Yes, that's a personal opinion, but one that's backed up by a non-trivial amount of experience.
calloc(3) returns a pointer to the memory it allocates.
struct key new_node = (struct key) calloc(1, sizeof(struct key));
should be
struct key* new_node = calloc(1, sizeof(struct key));
You should not assign a pointer to a non-pointer variable. Change new_node to be a pointer.
Also, to use the address of variable, you need &, not *, so change it to struct value* y = &x;
Edit: your typedefs are wrong too. reverse them.
For the second problem, you want to use an ampersand & instead of an astrisk "*`. An astrisk dereferences a pointer, an ampersand gives you the pointer from the value.

Resources