I've been working on a custom malloc where the test code is the following.
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include "../tst.h"
#include "../brk.h"
#include <time.h>
/* returns an array of arrays of char*, all of which NULL */
char ***alloc_matrix(unsigned rows, unsigned columns) {
char ***matrix = malloc(rows * sizeof(char **));
unsigned row = 0;
unsigned column = 0;
if (!matrix) abort();
for (row = 0; row < rows; row++) {
matrix[row] = calloc(columns, sizeof(char *));
if (!matrix[row]) abort();
for (column = 0; column < columns; column++) {
matrix[row][column] = NULL;
}
}
return matrix;
}
/* deallocates an array of arrays of char*, calling free() on each */
void free_matrix(char ***matrix, unsigned rows, unsigned columns) {
unsigned row = 0;
unsigned column = 0;
for (row = 0; row < rows; row++) {
for (column = 0; column < columns; column++) {
/* printf("column %d row %d\n", column, row);*/
free(matrix[row][column]);
}
free(matrix[row]);
}
free(matrix);
}
int main(int agrc, char **argv) {
int i;
srand(time(NULL));
int randomnumber;
int size = 1024;
void *p[size];
for (i = 0; i < size; i++) {
randomnumber = rand() % 10;
p[i] = malloc(1024 * 1024 * randomnumber);
}
for (i = size-1; i >= 0; i--) {
free(p[i]);
}
int x = 1024;
char *** matrix = alloc_matrix(x, x);
free_matrix(matrix, x, x);
return (0);
}
The code compiles and runs cleanly.
developer@1604:~/proj/openmalloc/overhead$ gcc gigtest.c
developer@1604:~/proj/openmalloc/overhead$ ./a.out
But how can I improve it? I would like to make it easier to test the thing (my next work should be to add multithreading to the malloc implementation).
Should I use a testing framework instead?
1 Answer 1
This code is very easy to understand, which is great! I like that you used full words for the variable names rather than abbreviating everything to single character names.
Don't Abort
In alloc_matrix()
, if there is a failure to allocate memory, it calls abort()
. This is going to make it very difficult to test! If it attempts to allocate and fails, it aborts the test. Usually a test will return success or failure. This is an important distinction because you may write tests that should fail to test the failure paths of your code.
Proper Types
I would change the types of the argument to this:
char*** alloc_matrix(const size_t rows, const size_t columns);
I made them all const
because the function doesn't change their values. I also made them size_t
which is the appropriate type to use when you want to represent a size.
More Tests
You should add some more tests. As mentioned above, you need to test cases that should fail, such as allocating a block that's the maximum 64-bit value (for example malloc(ULLONG_MAX);
).
You should also test things like alignment. Many platforms have strict byte alignment requirements. For example, pointers can usually not have an odd start address, and often must be 4-byte, 8-byte, or 16-byte aligned. You may need to check the specifics of the platform you're testing on.
-
1\$\begingroup\$ You misunderstood the purpose of this program. The OP wrote an allocator (not shown here) and the code shown here is supposed to test that allocator. \$\endgroup\$Roland Illig– Roland Illig2017年07月20日 05:29:27 +00:00Commented Jul 20, 2017 at 5:29
-
2\$\begingroup\$ Eh, maybe you're right. I removed the irrelevant stuff. Clearly from the comments to the question, I'm not the only one who was confused! \$\endgroup\$user1118321– user11183212017年07月20日 05:46:29 +00:00Commented Jul 20, 2017 at 5:46
-
\$\begingroup\$ @user1118321 Thanks for the review. I do have more tests. it's it the file
RUN_TESTS
in the repository. I inherited some of the code and tests and I write the code in the question in order to leran how to write tests and test that I actually could overrider ("shadow") the system's builtin malloc. \$\endgroup\$Niklas Rosencrantz– Niklas Rosencrantz2017年07月23日 04:51:40 +00:00Commented Jul 23, 2017 at 4:51 -
\$\begingroup\$ There's no need to add
const
to thesize_t
types as they're passed by value anyway. If they were passed by reference, it would be a different story. \$\endgroup\$S.S. Anne– S.S. Anne2019年06月08日 03:21:34 +00:00Commented Jun 8, 2019 at 3:21 -
\$\begingroup\$ They're there as an annotation to the someone reading the code who might call it. Not strictly necessary, but a clue to how they're used in the function. \$\endgroup\$user1118321– user11183212019年06月08日 05:08:40 +00:00Commented Jun 8, 2019 at 5:08
Explore related questions
See similar questions with these tags.
void*
? \$\endgroup\$../tst.h
and../brk.h
are for. \$\endgroup\$char ***alloc_matrix()
does not return anarray of arrays of char*
It returns a pointer to pointer to pointer to char. The key issue is does the calling code ofalloc_matrix()
needs to swap rows, re-size allocations or are all the pointers mean to be constant untilfree_matrix()
? If the latter, then code should use a singlemalloc()
\$\endgroup\$