So you're right C doesn't return arrays in the sense that you are talking about, however the way we do that in C is by utilizing two ideas together:
Working with memory: By allocating a region of memory and then returning the starting location of that memory region.
The function call to allocate memory is called malloc
and then you have to use the free
call to release that memory region.
Pointer Arithmetic: I'm sure you already know that C has some basic types (char, int, long, float, double, and lately bool); each takes a different amount of memory and so C also provides the convenience of understanding typed pointers (pointers are variables that mark a memory location). A typed pointer understands how "big" it's associated type is in bytes and when you increment its value by 1 in C internally it's incremented by the size of the type.
Combining these ideas we can get arrays:
So for example if you have an array of five integers then you would allocate a region that is 5 * sizeof(int)
; Assuming a modern system the sizeof(int)
is going to be 32-bits (or 4 bytes); so you will get a memory region of 20 bytes back and you can fill it up with the values.
#include <stdio.h>
#include <stdlib.h>
int * get_numbers(size_t count) {
int *data = malloc(count * sizeof(int));
if (!data) {
return NULL; // could not allocate memory
}
// notice that we are increasing the index i by 1
// data[i] shifts properly in memory by 4 bytes
// to store the value of the next input.
for(int i=0; i < count; i++) {
scanf("%d", &data[i]);
}
}
int main() {
int *data = get_numbers(5);
if (data == NULL) {
fprintf(stderr, "Could not allocate memory\n");
exit(-1);
}
for(int i = 0; i < 5; i++) {
printf("data[%d] = %d\n", i, data[i]);
}
free(data);
}
Now in your case you have a bit of a complication (but not really) you want to be able to read in names, which are "strings" which don't really exist in C except as array of characters.
So to rephrase our problem is that want an array of names. Each name in itself is an array of characters. The thing is, C also understands type pointer pointers (yes a pointer to pointers) and can figure out the pointer arithmetic for that.
So we can have a memory region full of different types; the type pointers themselves can have a list of pointers in a memory region; and then make sure that each pointer in that list is pointing to a valid memory region itself.
#include <stdio.h>
#include <stdlib.h>
char *get_one_name() {
char *name = NULL;
size_t len = 0;
printf("Enter a name:");
// getline takes a pointer, which if it's set to NULL
// will perform a malloc internally that's big enough to
// hold the input value.
getline(&name, &len, stdin);
return name;
}
char ** get_names(size_t count) {
// this time we need memory region enough to hold
// pointers to names
char **data = malloc(count * sizeof(char *));
if (!data) {
return NULL; // could not allocate memory
}
for(int i=0; i < count; i++) {
data[i] = get_one_name();
}
return data;
}
void free_names(char **data, size_t count) {
for(int i = 0; i < count) {
if(data[i] != NULL) {
free(data[i]);
}
}
free(data);
}
int main() {
char **data = get_names(5);
if (data == NULL) {
fprintf(stderr, "Could not allocate memory\n");
exit(-1);
}
for(int i = 0; i < 5; i++) {
printf("data[%d] = %d\n", i, data[i]);
}
free_names(data, 5);
}
Hopefully this gives some ideas to you.