Click here to Skip to main content
Click here to Skip to main content

An overview of interior pointers in C++/CLI

, 28 Nov 2004
Rate this:
Please Sign up or sign in to vote.
Tries to explain the syntax, usage and behavior of interior pointers in C++/CLI

Introduction

One very confusing facet of the now obsolete Managed Extensions to C++ was its pointer usage syntax, where T* could be a native pointer, a managed reference or an interior pointer. In the new C++/CLI syntax, managed references use the ^ punctuator (called hat by Redmondians and mistakenly called cap by me the first time I saw it), thereby avoiding any confusion with a native pointer. With probably the same intentions, Herb Sutter and team have provided us with a far more intuitive template-style syntax for representing interior pointers, similar to the new syntax for representing CLI arrays. In this article, I'll talk about what interior pointers are, where they can be used and how they can be used.

What are interior pointers?

Interior pointers are essentially pointers into the CLI heap (needn't always point to the CLI heap though) that point to managed objects or members of managed objects. The basic problem with using a native pointer to point to a managed object or a member-object of a managed object is that the Garbage Collector might move the object around in the CLI heap during a GC/Memory-Compaction cycle. Thus, a managed object at 0x00BB0010 might end up at 0x00BBC010 after a GC cycle, and you can imagine what'd happen if we had a native pointer p pointing to the managed object before the GC cycle occurs. The pointer p would continue pointing to 0x00BB0010 (the location of the managed object before the GC cycle), but now the managed object is at 0x00BBC010, and 0x00BB0010 is just a random location in the CLI heap that might later be occupied by some other object. If the native pointer p is now used to modify the contents of 0x00BB0010, we are basically corrupting data - most undesirable as you can see!

That's where interior pointers come into play. The CLR knows about interior pointers and every time the Garbage Collector relocates an object pointed to by an interior pointer variable, the CLR automatically updates the value of the interior pointer to reflect the new location. So we can continue using the interior pointer and yet be totally oblivious as to the GC moving our pointed-to object around the CLI heap. Here's some code to see this in action :-

Here's our test class with an int member.

ref class Test
{
public:
    int m_i;
};

Here's a function to try and fill up the CLI heap's Generation-0 memory. [You might have to modify the loop-count (10,000 for me) to other values if you are not getting the desired effect]

void DoLotsOfAllocs()
{
    for(int i=0; i<10000; i++)
    {
        gcnew Test();
    }    
}

Here's the test code.

void _tmain()
{
    DoLotsOfAllocs();
    Test^ t = gcnew Test();
    t->m_i = 99;
    interior_ptr<int> p = &t->m_i;
    printf("%p %d\r\n",p,*p);
    DoLotsOfAllocs();
    printf("%p %d\r\n",p,*p);

Output on my machine [addresses will be different for you]

******* Output ******
00AC9B3C 99
00AA8D18 99

There you go; the same interior pointer variable p which was pointing to 0x00AC9B3C later points to 0x00AA8D18. Had we been using a native pointer, the runtime wouldn't have been able to update its value (the pointed-to address); and just in case you were wondering about trying that out, don't bother, since the compiler won't let you use a native pointer to point to a managed object's member.

Passing by reference using interior pointers

One handy use for interior pointers is when you have a requirement for functions with pass-by-ref arguments. Let's take a simple function that simply squares the passed integer :-

ref class Test
{
public:
    int m_i;
};

void Square(interior_ptr<int> pNum)
{
    *pNum *= *pNum;
}

Now see this code.

void _tmain()
{
    Test^ t = gcnew Test();
    t->m_i = 99;

    interior_ptr<int> p = &t->m_i;
    printf("%d\r\n",*p);
    Square(p);
    printf("%d\r\n",*p);
        
    int a = 10;
    Square(&a);
    printf("%d\r\n",a);

Output :-

******* Output ******
99
9801
100

You can see how we can pass both interior pointers as well as native pointers to the same function; this is because native pointers automatically convert to interior pointers. [Note that interior pointers cannot convert to native pointers]

Now that we've seen how interior pointers can be used for passing values by reference, it should be stated here that the same could be just as easily accomplished using tracking references. See below another function that does the same thing using a tracking reference as parameter.

void Square2(int% pNum)
{
    pNum *= pNum;
}

And the corresponding caller code.

void _tmain()
{
    Test^ t = gcnew Test();
    t->m_i = 99;
    
    printf("%d\r\n",t->m_i);
    Square2(t->m_i);
    printf("%d\r\n",t->m_i);
        
    int a = 10;
    Square2(a);
    printf("%d\r\n",a);

Tracking references and interior pointers are quite similar in nature, though you can do a lot more with interior pointers like pointer arithmetic and pointer comparison.

Pointer arithmetic with interior pointers

Here's some sample code that iterates through and sums an array of ints.

void ArrayStuff()
{
    array<int>^ arr = gcnew array<int> {2,4,6,8,3,5,7};
    interior_ptr<int> p = &arr[0];
    int s = 0;
    while(p != &arr[0] + arr->Length)
    {
        s += *p;
        p++;
    }
    printf("Sum = %d\r\n",s);
}

And here's some code that directly manipulates a System::String

String^ str = "hello";
interior_ptr<Char> ptxt = const_cast< interior_ptr<Char> >(
    PtrToStringChars(str));
for(int i=0; i<str->Length; i++)
    *(ptxt+i) = *(ptxt+i) + 1;
Console::WriteLine(str);

If you are of a masochistic disposition, you could change the for loop to

for(; (*ptxt++)++; *ptxt);

with the same results. It doesn't produce very legible looking code but it does demonstrate that you can do pointer comparison (implicit check for nullptr in the above example) with interior pointers.

Points of interest

You cannot have an interior pointer as a member of a class and I guess this is to maintain language interoperability. I mean you can't have VBers using your class if it's full of interior pointer member objects, can you? (If I guessed wrong, please feel free to correct me)

You cannot use an interior pointer to point to a ref object, though you can point to the handle to a ref object. Thus interior_pointer<System::String> is not allowed but interior_ptr<System::String^> is legal.

All interior pointers are implicitly initialized to nullptr unless you explicitly give it some other default value.

The compiler will emit a modopt to distinguish interior pointers from tracking references. Currently this modopt is Microsoft::VisualC::IsCXXPointerModifier (found in Microsoft.VisualC.dll) but in the final release this will be System::Runtime::CompilerServices::IsExplicitlyDereferenced (found in mscorlib.dll starting with Whidbey).

For value classes, the this pointer is an interior pointer. My guess is that, this is so because value classes can be members of managed classes, and if so, assuming the value class uses the this pointer, it could be fatally dangerous if the pointer was not an interior pointer. (If I guessed wrong, please feel free to correct me)

value class V
{
    void A()
    {
        interior_ptr<V> pV1 = this;
        V* pV2 = this; //won't compile
    }
};

Conclusion

I hope I've given a pretty decent coverage of interior pointers and their usage in C++/CLI. Kindly submit your feedback so that I get a chance to improve on this article. Thank you.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Nish Sivakumar

United States United States
Nish is a real nice guy who has been writing code since 1990 when he first got his hands on an 8088 with 640 KB RAM. Originally from sunny Trivandrum in India, he has been living in various places over the past few years and often thinks it’s time he settled down somewhere.
 
Nish has been a Microsoft Visual C++ MVP since October, 2002 - awfully nice of Microsoft, he thinks. He maintains an MVP tips and tricks web site - www.voidnish.com where you can find a consolidated list of his articles, writings and ideas on VC++, MFC, .NET and C++/CLI. Oh, and you might want to check out his blog on C++/CLI, MFC, .NET and a lot of other stuff - blog.voidnish.com.
 
Nish loves reading Science Fiction, P G Wodehouse and Agatha Christie, and also fancies himself to be a decent writer of sorts. He has authored a romantic comedy Summer Love and Some more Cricket as well as a programming book – Extending MFC applications with the .NET Framework.
 
Nish's latest book C++/CLI in Action published by Manning Publications is now available for purchase. You can read more about the book on his blog.
 
Despite his wife's attempts to get him into cooking, his best effort so far has been a badly done omelette. Some day, he hopes to be a good cook, and to cook a tasty dinner for his wife.

Comments and Discussions

 
Questioninterior pointer to interior pointer or pass interior pointer by reference Pinmemberdave34569-Sep-06 10:29 
AnswerRe: interior pointer to interior pointer or pass interior pointer by reference PinstaffNishant Sivakumar9-Sep-06 10:33 
GeneralMasochism PinmemberRobert Stairs3-Feb-06 9:41 
GeneralRe: Masochism PinmemberGregory YuYe Lin28-Feb-07 7:31 
GeneralAnother good one PinmemberHolger Grund3-Feb-05 0:17 
A very nice description of pointers in the CLR.
 
There's some points I'd like to add, though. Managed pointers can only have automatic storage duration. Unfortunately, this limitation is enforced at the type definition level. That means, you can't have managed pointers as members of a class. I believe the rationale is the efficiency of the GC. For the same reason you can't have managed pointers to managed pointers. This limitation derives directly from the CLR.
 
Value classes have a managed pointer as the this pointer. That makes it possible to call member functions on value types in their unboxed form. VC++ reflects this CLR concept. You can convert that to a native pointer by pinning the object.
 
I'd also like to point out that the concept of managed pointers has two representations in C++. Interior pointers have pointer semantics (need to use * to deref and -> for member access). But there's also managed references which for value types translate to managed pointers. These have C++ reference like semantics (implicitly deref'd, . member access, can't rebind, lvalue semantics)
 
Therefore VC++ imposes the same limitations for V% than for interior_ptr.
GeneralRe: Another good one PinstaffNishant S3-Feb-05 0:45 
GeneralNice article PinmemberMajid Shahabfar29-Nov-04 21:36 
GeneralRe: Nice article PinstaffNishant S29-Nov-04 23:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141030.1 | Last Updated 29 Nov 2004
Article Copyright 2004 by Nish Sivakumar
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid