0

Assum I have some pure C code like this.

It is not the real code, just for an example.

struct Param {
  struct InParam {
    int a;
    int b;
  } in; 
  struct OutParam {
    int *c;
  } out;
};

void MakeArray(struct Param *param) {
  // just for filling the output param
  for (int i = 0; i < param->in.a; ++i) {
    param->out.c[i] = param->in.b;
  }
}

int main() {
  int *p = NULL;
  special_memory_alloc(&p, sizeof(int) * 3));  // assume never failed

  struct Param param = { {3, 4}, {p} };  
  MakeArray(&param);

  special_memory_free(p);
}

The function MakeArray could within an independent module. I need the caller (eg. main in this example) allocate the memories in out param for me.

So is there some way to hint users to do the allocation for the output param?

Xingx1
  • 127
  • 7
  • 1
    You could change the function declaration to `void MakeArray(struct Param *param, int *memory)` and then copy the `memory` pointer into the structure. Start the function with `assert(memory != NULL)` so that the user can't pass a NULL pointer. – user3386109 Jan 21 '22 at 09:04
  • @user3386109 Checking if parameters passed to a function is NULL is not recommended practice in C since it leads to bad performance. Instead, error handling should be left to the caller. You should document through source code comments what the function expects. If it says that it needs a pointer to allocated memory and someone passes a null pointer anyway, it's not the library's problem. This is an universally accepted way to design C programs, since the spirit of C has always been performance over safety. – Lundin Jan 21 '22 at 09:08
  • (Though on very modern compilers you can do `void func(size_t n, int memory [static n])` to at least get compiler errors if someone tries to pass a null pointer at compile-time.) – Lundin Jan 21 '22 at 09:10
  • @Lundin what's wrong with `assert(memory != NULL)`? That's only for debug builds. – Jabberwocky Jan 21 '22 at 09:15
  • 1
    @Jabberwocky If you can guarantee that asserts become no-ops in release, then sure it can be used. I'm just saying that in general, this code should not be inside the function. There's absolutely no reason why the check shouldn't be placed on the caller side, ideally as close to the point where the pointer is assigned as possible. If you suspect that it can be NULL for whatever the reason, then the error check should be at the location where it risks getting set to NULL and not inside some completely unrelated function. – Lundin Jan 21 '22 at 09:22

1 Answers1

2

This design seems a bit confused about allocation overall. The struct you pass to MakeArray is passed by value, so it's just a local copy stored on the stack and not the memory allocated. Though as it happens, it has a copy of the pointer c pointing at the allocated heap memory.

Some rules of thumb you should follow:

  • Never pass structs by value. Always by reference through a pointer.
  • The one who allocates memory for an item is also the one responsible for freeing it. Don't "outsource" allocation/clean-up to the user of your library, that's very bad design.
  • Memory allocation should ideally be encapsulated with the functions handling the struct. This could be referred to as an "abstract data type" (ADT) or a "class" if you prefer OO terminology - it boils down to similar things. A "constructor" function and a "destructor" function. Since C doesn't support automatic calls of constructors/destructors (RAII), they have to be called manually. But C programmers are used to expect such, so it's not a problem.
  • Keep in mind that if there's a need to implement a destructor, there's likely also a need to implement some manner of copy function. Similar to the C++ "rule of three" if that's familiar.

The professional way to implement this would be through so-called opaque type. Examples of how to do that can be found here: How to do private encapsulation in C?

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • sorry it is a typo or a careless mistake – Xingx1 Jan 21 '22 at 13:51
  • In my view, let users do the allocation is not an absolutely very bad design all the time. Because the memories could from some special memory modules -- not from the system memory pool. The library could hand over allocations in that cases. – Xingx1 Jan 21 '22 at 14:06
  • @Xingx1 Yes, relying completely on caller allocation is often the way to go, but then you won't get private encapsulation. – Lundin Jan 21 '22 at 14:07