See the question and my original answer on StackOverflow

If you want memory to survive a function scope, and don't want to use global variables, then you must allocate it from elsewhere than the stack (local variable).

You can use any allocator, you can use one that .NET already knows, or if you use another one specific to C++ or your platform, then you must also provide another P/Invokable function for deallocation, something like this:

C++

int* read_array_adapter()
{
    int nCount[5] = {0, 1, 2, 3, 4};    
    return AllocateAndCopy(...);
}

void free_array_adapter(int *) // or a generic pointer of course...
{
    Free(...);
}

C#

static void Main(string[] args)
{
    var ptr = read_array_adapter();
    var result = new int[5];
    Marshal.Copy(ptr, result, 0, 5);
    free_array_adapter(ptr);
}

[DllImport(@"/sharedlibrary.so",CallingConvention = CallingConvention.Cdecl)]                                
public extern static IntPtr read_array_adapter();

[DllImport(@"/sharedlibrary.so",CallingConvention = CallingConvention.Cdecl)]                                
public extern static void free_array_adapter(IntPtr ptr);

You can also use a known allocator between .NET and C/C++, but this depends on the platform (Windows, linux, etc.): https://www.mono-project.com/docs/advanced/pinvoke/

Here is a sample implementation with C's malloc/free duo:

int* read_array_adapter()
{
    int nCount[5] = { 0, 1, 2, 3, 4 };
    int* p = (int*)malloc(5 * 4);
    memcpy(p, nCount, 5 * 4);
    return p;
}

void free_array_adapter(void * ptr)
{
    free(ptr);
}