malloc allocates memory. It does not call a constructor. So this will result in allocated memory for a "list_type" but it will contain uninitialized garbage.
Node *current = (Node *)malloc(sizeof(Node));
But that is okay for now, because you follow it up by initializing everything.
current->data = data;
current->next = list->head;
[deleted section where I lecture you about using an object for encapsulation for maintainability]
Now let's look at your list type:
struct List
{
Node *head = 0;
};
Default values in the class (struct) declaration are applied in any constructor. But malloc is not a constructor, nor does it call a constructor. It just gets the memory needed. So if you malloc sizeof(List) you will NOT get anything initialized.
However, you don't even do that. You allocate space for another pointer to a list. So list_create gives you a pointer to some allocated memory for another pointer to random garbage (which is not a List, nor would it be valid if it were allocated). Although I suppose there is a chance that sizeof(List) and sizeof(List*) happen to be the same on your compiler, but it's UB if you just got lucky like that.
return (List *)malloc(sizeof(List *));
You meant to do
List * list = (List *)malloc(sizeof(List));
So now you have something the size of a list, and you would then have to add this (because you didn't use a constructor, default or otherwise):
list->head = nullptr; // we're supposed to use "nullptr" now for type safety
return list;
but you don't have that. You just return the invalid pointer. So those are the two errors in that function.
[deleted section where I lecture you on using an object. You can get that from Samed Kahyaoglu's answer. But basically you have to go through a constructor or default constructor in order to use that default value in the header declaration of a class.]
To demonstrate that head was initialized:
int main() {
List * list = list_create();
list_add(list, nullptr);
std::cout << list->head->next << std::endl;
}
output before fix:
CDCDCDCD
output after fix:
00000000
My compiler initializes malloc'ed data with CDCDCDCD in debug builds to make it easy to spot uninitialized data. Release builds would probably just have whatever random junk was there before.