I am working on a little project and want to make it splint-proof for kicks. I am allocating an array and passing it on to a function that generates values and assigns them to array elements, i.e.
#include <stdio.h>
#include <stdlib.h>
static void generate(int* arr, int len);
int main() {
int length = 10;
int* array = malloc((size_t) (length * sizeof(int)));
if (array == NULL)
return 1;
generate(array, length);
int i;
for (i = 0; i < length; i++)
printf("%d\n", array[i]);
free(array);
return 0;
}
void generate(int* arr, int len) {
int i;
for (i = 0; i < len; i++)
arr[i] = i;
}
splint complains about this:
test.c: (in function main)
test.c:14:18: Passed storage array not completely defined (*array is undefined): generate (array, ...)
Storage derivable from a parameter, return value or global is not defined.
Use /*@out@*/ to denote passed or returned storage which need not be defined.
(Use -compdef to inhibit warning)
test.c:8:58: Storage *array allocated
Fair enough. Returning a default value to avoid having uninitialized storage around is not a bad idea, and I modify the program as follows:
#include <stdio.h>
#include <stdlib.h>
/*@null@*/
static int* generate(int len);
int main(void) {
int length = 10;
int* array = generate(length);
if (array == NULL)
return 1;
int i;
for (i = 0; i < length; i++)
printf("%d\n", array[i]);
free(array);
return 0;
}
int* generate(int len) {
int i;
int* arr = malloc((size_t) (len * sizeof(int)));
if (arr != NULL) {
for (i = 0; i < len; i++)
arr[i] = i;
return arr;
} else
return NULL;
}
I would have thought this gives a good definition to the arr array, and yet running splint again:
testnew.c: (in function empty)
testnew.c:33:16: Returned storage arr not completely defined (*arr is undefined): arr
Storage derivable from a parameter, return value or global is not defined.
Use /*@out@*/ to denote passed or returned storage which need not be defined.
(Use -compdef to inhibit warning)
testnew.c:28:53: Storage *arr allocated
What exactly does "completely defined" mean here then? I don't want to add an /*@out@*/ annotation because I don't understand how the code can be interpreted as anything other than "return an array of 'len' integers or NULL". Is it good practice to use initializer functions instead of calling malloc directly?
Edit: Why not just use calloc?
When using calloc instead of malloc in either case splint produces no warnings anymore, but I'm not sure why that should matter if I'm not reading from the array before initializing. The default values are not necessarily zero, and I want to be able to realloc the arrays. I'm not chasing minuscule performance improvements, but at this point I am simply curious if you can 'completely define' uninitialized memory at runtime.