Main Content

MISRA C++:2023 Rule 7.11.2

An array passed as a function argument shall not decay to a pointer

Since R2024b

Description

Rule Definition

An array passed as a function argument shall not decay to a pointer.

Rationale

When you pass an array as an argument to a function, the array decays to a pointer in the function scope. After this decay, the bounds of the array are no longer known in the function scope and bounds checking is not possible. For instance, consider this code:

void bar(int array[10]);

void foo() {
	int array[8];
	bar(array); //array decays to pointer
}
When bar() is called from the function foo(), its argument immediately decays to a pointer. Calling bar() with a nine-element array compiles without any errors. The current definition of bar() cannot check the bounds of the input array, which can result in an array index out of bounds error and undefined behavior.

Avoid passing arrays as function parameters without preserving their bounds. You can use several strategies to preserve the dimensionality of a passed pointer:

  • Declare functions to accepts arrays of specific dimensions:

    void bar(int (&array)[10]);
    //...
    int array1[10];
    int array2[11];
    //...
    bar(array1);
    bar(array2); //Compile fail
    Here, bar() expects a reference to an array of 11 elements. With this definition, calling bar() with an array of incorrect size causes a compilation fail.

  • Implement functions as templates that accepts arrays of different size. The dimensionality of the array is preserved in the function scope while maintaining the flexibility of calling the function with arrays of arbitrary size.

    template<size_t numel>
    void bar(int (&array)[numel]); //array size automatically deduced
    
    //...
    int array1[10];
    int array2 [11];
    //...
    bar(array1); 
    bar(array2);

  • Instead of using a C-style array, consider using containers from the standard template library (STL). These containers preserve their bounds when passed as arguments to functions.

  • If you are using C++20, consider wrapping the array in an std::span object. Otherwise, consider wrapping the array in a class similar to std::span. See std::span.

As an exception, it is not a violation of this rule to pass string literals to functions with a char* parameter. String literals are null-terminated and the null-termination allows for bounds checking when string literals are passed to functions as char* arguments.

Polyspace Implementation

Polyspace® reports a violation of this rule if a C-style array is passed to a function. Polyspace does not report a violation if you pass string literals to functions.

Troubleshooting

If you expect a rule violation but Polyspace does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

In this example, the function sum() is calculates the sum of an 11 element integer array. Because sum() accepts an array that can decay to a pointer in the function scope, it is possible to call this function with arrays of any size. For instance, foo() calls sum() with a 5-element array. Because the function sum() cannot check the bounds of the input array, this code can result in unexpected return value or array index out of bounds errors. In this example, the first assert statement fails. Polyspace reports a violation of this rule.

#include<iostream>
#include <cassert>

int sum(int array[10]) {
	int val;
	for(int i = 0; i < 10; ++i) {
		val += array[i];
	}
	return val;
}


template<size_t numel>
int sum_fixed(int (&array)[numel]){
	int val=0;
	for(int i = 0; i < numel; ++i) {
		val += array[i];
	}
	return val;
}

void foo() {
	int array[] = {1, 2, 3, 4, 5};
	assert(sum(array) == 15); //Noncompliant
	assert(sum_fixed(array) == 15); //Compliant
}

When designing function a that accepts an array, preserve the bounds of the array in the function scope. The template sum_fixed deduces the length of the array and preserves the bounds of the input array in the function scope. Using this technique allows you to pass arrays of arbitrary lengths to the template while avoiding unexpected errors. Polyspace does not report a violation when you pass array to sum_fixed.

If you are using C++20, consider using std::span to pass arrays to functions. Modify the function sum() to accept a std::span argument:

int sum(std::span<int> array) {
    int val = 0;
    for (auto& num : array) {
        val += num;
    }
    return val;
}
This implementation preserves the bounds of the input array and avoids unexpected errors.

In this example, the character array returned by ex.what() is passed to the operator+ which expects a std::string. The bounds of the character array is not preserved and Polyspace reports a violation. To avoid the violation, create a temporary std::string from the return value of ex.what() before passing the std::string to operator+.

#include <cctype>
#include <iostream>
#include <string>
#include <stdexcept>

int main()
{

	try {
		float f = std::stof("(3.14)");
	}
	catch(std::invalid_argument const &ex) {
		std::string s(std::string("Error: "
		            + std::string("exception raised calling ")
		            + ex.what())); // Noncompliant
		std::cout << s << '\n';
	}
}

Check Information

Group: Standard Conversions
Category: Required

Version History

Introduced in R2024b