MISRA Discussion Forums

Full Version: Function returning pointer to array - Rule 17.4
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
I have a function returning a pointer to an array, not an uncommon situation I would think.
How can I reference elements in that array without violating Rule 17.4 ?.
1. Indexing is not compliant as the return type is not an array.
2. Pointer arithmetic is not compliant.
So it looks like returning pointers to arrays is effectively banned by this rule, is that the case?.
As I pointed out here:

http://www.misra.org.uk/forum/viewtopic.php?f=73&t=641

17.4 doesn't make sense from a safety point-of-view. MISRA agrees that it doesn't, but insists on keeping the rule for what seems to be code style reasons.
An expression like

x[3] = 0;

is by ISO C's definition equivalent with

*(x+3) = 0;

And the compiler will translate any [] expression to a *() expression. This is the reason why this horrible code is perfectly valid C:

3[x] = 0;

Because it will be translated to *(3 + x) = 0;
All these three syntax cases will yield exactly the same machine code and they will all merrily access an array out of bounds if given the chance. There is no safety argument for rule 17.4, it is just about coding style.

---

However, why would you ever want to return a pointer to an array? I always boldly state that no such case exists in a good program design, and so far nobody has been able to show me a single case where it makes sense to return pointers from functions.

A pointer returned from a function can point at the following kind of variables:

- Local variables. This case is always a bug in the program.

- The same data as one of the parameters of the function. This is poor programming, as the same parameter will be used twice in the function, taking up unnecessary stack space when the function is called. The C standard contains such functions, but then the C standard contains countless cases of poor programming practice, hence the need of subsets like MISRA-C.

- Dynamically allocated variables. Returning pointers to variables that were dynamically allocated inside the function is one of the most common causes for memory leaks in C programs. Instead, leave allocation to the caller. (Dynamic memory is banned by MISRA-C anyhow.)

- Global variables. This is a bug in a multi-process environment, as it might make the function unsafe for multiple processes. The function will also become sensitive to the order of evaluation, in case the function is called twice in the same expression (ie implementation-defined behavior, which you aren't allowed to rely on in MISRA-C).

- Static variables. This is a bug in a multi-process environment, as it might make the function unsafe for multiple processes. The function will also become sensitive to the order of evaluation, in case the function is called twice in the same expression (ie implementation-defined behavior, which you aren't allowed to rely on in MISRA-C). This is also a flaw in the program design, as it breaks the encapsulation of private variables.


So please tell me of a case where it makes sense to return a pointer from a function.
Quote:So please tell me of a case where it makes sense to return a pointer from a function.

How about when parsing a linked list that is statically declared.
William Forbes Wrote:
Quote:So please tell me of a case where it makes sense to return a pointer from a function.

How about when parsing a linked list that is statically declared.

Ok this is drifting off topic... I apologize for that.

A simple version of such code would be:

Code:
BOOL find_node (Node* linked_list, Data key, Node** result)
{
  BOOL found = FALSE;
  Node* iterator;

  for(iterator = linked_list; iterator != NULL; iterator = iterator->next)
  {
    if(iterator->data == key)
    {
      *result = iterator;
      found = TRUE;
      break;
    }
  }
  
  return found;
}


This code has the big advantage that it won't touch any pointers in the caller function unless it found the node.
Had you returned a pointer, you would overwrite variables in the caller function even though you found nothing:

Code:
/* Bad practice example */
Node* find_node (Node* linked_list, Data key);
...

Node* node = fail_safe_data;
...
node = find_node (list, key);
/* oops, node is overwritten to NULL or some such if function fails */

This is particularly poor practice in safety-critical applications, where the design should be so that functions preserve data if they fail.

However, I think the main issues with returning a pointer to static are the mentioned issues: order of evaulation, broken encapsulation, and multi-threading. Consider this:

Code:
if (find_node(list, this) == find_node(list, that)) /* Ask for trouble. Possibly violates a number of MISRA rules. */

Dangerous code, as you can't know whether the left or right function call will be evaluated first. If find_none() alters something in the list, the above code will contain a subtle bug that would either break the whole program or make it non-portable. Had you returned the pointers through the parameter list, you would never end up with that order of evaluation issue, as it would rule out the dumb idea of calling the functions in the same expression:

Code:
Node* result1;
Node* result2;
find_node(list, this, &result1);
find_node(list, that, &result2);
if(result1 == result2)
OK, you win!
The MISRA C working group agrees that this is a problem with the current wording for Rule 17.4. The problem is not limited to returns from functions.

For example:
Code:
int_32 * select_row (int_32 arr[3][2], uint_32 index)
{
  return arr[index];
}

int_32 array[3][2] = {{1, 2}, {3, 4}, {5, 6}};
uint_32 i;

for ( i = 0U; i < 3U; i++)
{
   int_32 *row = select_row(array, i);

   /* or int_32 *row = array[i];  */
  
   ... row[0] ....  /* violates 17.4 */
  
}
In many cases, returning a pointer to an array is best avoided as suggested by a previous response to this question. However, it is appreciated that slicing an array may be required and it may be more readable to use a pointer to the slice. For this reason, the next version of the MISRA C guidelines to likely to allow such indexing.

Indexing such slices is non-compliant in this version of MISRA C and will need a deviation.