1

1) Should a function check each of it's pointer arguments for NULL?

void do_stuff(char** object1, char** object2, ...) {
    if (object1 == NULL) {
        return;
    }
    if (object2 == NULL) {
        return;
    }
    ...

2) When a function creates a dynamic object, should it return a pointer to a newly created abject or should it assign it to it's argument?

void allocate_object(char** object);

or

char** allocate_object(void);

3) When a function is intended to allocate an object and fails to do so, how to notify the caller about the result?

return -1;

or

object = NULL;

4) Who (in general) is responsible (check, free) for a pointer argument: a function or it's caller?

5) Are there any guidelines or resources that answer similar questions?


With C++ and it's classes, each object has it's owner. This, together with RAII and exception handling helps me to answer my questions. In C, I am yet to understand this.

CorellianAle
  • 645
  • 8
  • 16
  • 2
    C and C++ are two different languages with very different practices, please choose. Also please ask only one question at a time. – Jens Gustedt Jan 29 '18 at 13:47
  • I've placed both C and C++ because 1) I've mentioned C++ 2) I understand these concepts in C++ and trying to find corresponding answers for C 3) To better specify my questions by proving an example in C++. – CorellianAle Jan 29 '18 at 13:49
  • Avoid all raw pointers. Rather than test for NULL use pass by reference. – Richard Critten Jan 29 '18 at 13:49
  • @CorellianAle you don't provide any c++ example... – apple apple Jan 29 '18 at 13:51
  • MOST of your questions must be handled by design & specification. F.e. a function does not have to check for a null parameter if the design guarantees it is never passed one. However, durig debugging and development it would be prudent to check it anyway, give a message and abort (and remove the code before production) – Paul Ogilvie Jan 29 '18 at 13:55
  • Your second example should be `void allocate_object(char** object)` – Paul Ogilvie Jan 29 '18 at 13:56
  • @PaulOgilvie Thanks, my mistake. – CorellianAle Jan 29 '18 at 14:07
  • You changed a bit too much. `char** allocate_object(void);` should be `char* allocate_object(void);` or `new_object_type* allocate_object(void);` – Gerhardh Jan 29 '18 at 14:43

3 Answers3

1
  1. Depends on your own API documentation. If you have stated explicitly that the function will accept null pointers, you should check for them. If not, then no check can be assumed.

  2. The first form won't work for obvious reasons, see Dynamic memory access only works inside function. You can either return the data through pointer-to-pointer or through the return value. Either is fine. Generally, the return value of professional API:s is reserved for result/error codes.

  3. According to your own API documentation. The most crude error handling would be to return NULL or similar. More detailed functions with lots of things that can go wrong typically use an enum or some other custom type.

  4. Depends on the purpose of the function. The code that did the memory allocation is responsible for cleaning up. Generally, it is always best to leave allocation to the caller if that's possible, but sometimes it is not, like when designing with "opaque pointers" or some other kind of ADT. Then the ADT should handle the allocation/freeing internally just as in C++. With the difference that C only has "poor man's constructor/destructors" - the functions have to be called explicitly.

  5. Not really, though there's various "de facto" standards. You can study how it is done in large libraries like the Windows API.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Basically everything is implementation-defined and comes from technical specification. In my case, I call POSIX function a lot. is it a good approach to implement thing similar to it? – CorellianAle Jan 29 '18 at 14:05
1

1) Null checks should be used based on the context.

Its always better to keep null checks in functions unless NULL is an expected solution.

2) Its better to return error codes as results like the following function. In case we want to pass some additional information along with the pointer argument, its always better to return an error or status code.

int32_t allocate_object(char* object);// error code type will be int32_t

3) Enums serves this job better than int. Create an enum with various error cases in it.

enum etype_error
{
    E_SUCCESS = 0,
    E_INVALID_INPUT = -1,
    E_FILE_NOT_FOUND = -2, 
    ...
};

4) In c and c++ no one is responsible for the pointer allocation & deallocation, you can do it any where any time. Its only a good practice to deallocate it in the same block where it is allocated.

So, its better to deallocate the pointer by the caller himself.

5) Im not sure about a good reference text books for C language. But for c++, Effective c++ and More Effective C++ by Scott Meyers are good books with lot of guidlines and programming practices. Im sure it will help a lot.

  • Thank you. 5) Yes, I am able to find a lot of "how to do it?" instead of "what is a preferable and professional way?" for C language. – CorellianAle Jan 29 '18 at 14:18
0

Just a complement to Lundin's answer for point 4:

4) Who (in general) is responsible (check, free) for a pointer argument: a function or it's caller?

The best case is when the allocation and de-allocation can occur in same function. In you can know in advance the size of an array, or if you only need to allocate a single complete struct, you can do the allocation in caller and pass the allocated (but un filled memory) to callee. But sometimes the actual size can only be computed in callee function. In that case, the callee is responsible for the allocation, and it returns the allocated bloc either as its return value or in a pointer to pointer (two-starred pointer). Then the caller gets ownership (since the creator function is over) and is responsable for the de-allocation, either directly (with a free call) or by calling a closing function.

An example of that later case is a FILE stream. It is an opaque object that is initialized by fopen and released by fclose

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252