{"id":93081,"date":"2016-02-24T07:00:00","date_gmt":"2016-02-24T22:00:00","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/oldnewthing\/?p=93081"},"modified":"2019-03-13T10:30:44","modified_gmt":"2019-03-13T17:30:44","slug":"20160224-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20160224-00\/?p=93081","title":{"rendered":"A static_cast is not always just a pointer adjustment"},"content":{"rendered":"<p>Even without considering virtual base classes, a <code>static_cast<\/code> to move between a base class and a derived class can be more than just a pointer adjustment. <\/p>\n<p>Consider the following classes and functions. <\/p>\n<pre>\nclass A\n{\npublic:\n int a;\n void DoSomethingA();\n};\n\nclass B\n{\npublic:\n int b;\n void DoSomethingB();\n};\n\nclass C : public A, public B\n{\npublic:\n int c;\n void DoSomethingC();\n};\n\nB* GetB(C* c)\n{\n return static_cast&lt;B*&gt;(c);\n}\n\nvoid AcceptB(B* b);\n\nvoid AcceptC(C* c)\n{\n AcceptB(c);\n}\n<\/pre>\n<p>Suppose the compiler decided to lay out the memory for <code>C<\/code> like this: <\/p>\n<table BORDER=\"0\" STYLE=\"border-collapse: collapse\">\n<tr>\n<td><code>int a;<\/code><\/td>\n<td>} <code>A<\/code><\/td>\n<td ROWSPAN=\"3\" VALIGN=\"center\" STYLE=\"font-size: 300%\">}<\/td>\n<td ROWSPAN=\"3\" VALIGN=\"center\"><code>C<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>int b;<\/code><\/td>\n<td>} <code>B<\/code><\/td>\n<\/tr>\n<tr>\n<td><code>int c;<\/code><\/td>\n<td><\/td>\n<\/tr>\n<\/table>\n<p>Now, you would think that converting a pointer to a <code>C<\/code> into a pointer to a <code>B<\/code> would be a simple matter of adding <code>sizeof(int)<\/code>, since that&#8217;s what you need to do to get from the <code>a<\/code> to the <code>b<\/code>. <\/p>\n<p>Unless you happen to have started with a null pointer. <\/p>\n<p>The rule for null pointers is that casting a null pointer to anything results in another null pointer. <\/p>\n<p>This means that if the parameter to <code>GetB<\/code> is a null pointer, the function cannot return <code>nullptr + sizeof(int)<\/code>; it has to return <code>nullptr<\/code>. <\/p>\n<pre>\nGetB:\n    xor rax, rax\n    test rcx, rcx\n    jz @F\n    lea rax, [rcx+sizeof(int)]\n@@: ret\n<\/pre>\n<p>Similarly, if the parameter to <code>AcceptC<\/code> is <code>nullptr<\/code>, then it must call <code>AcceptB<\/code> with <code>nullptr<\/code>. <\/p>\n<pre>\nAcceptC:\n    test rcx, rcx\n    jz   @F\n    add  rcx, sizeof(int)\n@@: jmp  AcceptB\n<\/pre>\n<p>A na&iuml;ve compiler would insert all these conditional jumps every time you cast between a base class and a derived class that involves an adjustment. But this is also a case where a compiler that <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2012\/08\/31\/10345196.aspx\">takes advantage of undefined behavior<\/a> can <a HREF=\"http:\/\/blogs.msdn.com\/b\/oldnewthing\/archive\/2014\/06\/27\/10537746.aspx\">optimize the test away<\/a>: If it sees that every code path through the <code>static_cast<\/code> dereferences either the upcast or downcast pointer, then that means that if the pointer being converted were <code>nullptr<\/code>, it would result in undefined behavior. Therefore, the compiler can assume that the pointer is never <code>nullptr<\/code> and remove the test. <\/p>\n<pre>\nvoid AcceptC2(C* c)\n{\n c-&gt;DoSomethingB();\n}\n<\/pre>\n<p>Here, the test can be elided because the result of the conversion is immediate dereferenced in order to call the <code>B::Do&shy;SomethingB<\/code> method. The C++ language says that if you try to call an instance method on a null pointer, the behavior is undefined. Doesn&#8217;t matter whether the method actually accesses any member variables; just the fact that you invoked an instance method is enough to guarantee that the pointer is not null. Therefore, the <code>AcceptC2<\/code> function compiles to <\/p>\n<pre>\nAcceptC2:\n    add rcx, sizeof(int)\n    jmp B::DoSomethingB\n<\/pre>\n<p>The same logic applies on the receiving end of the method call: A method call can assume that <code>this<\/code> is never null. <\/p>\n<pre>\nvoid C::DoSomethingC()\n{\n AcceptB(this);\n}\n\nC::DoSomethingC:\n    add rcx, sizeof(int)\n    jmp AcceptB\n<\/pre>\n<p>Since <code>this<\/code> is never null, the conversion from <code>C*<\/code> to <code>B*<\/code> can elide the test and perform the adjustment unconditionally. <\/p>\n<p>This means that you could add a dummy method to ever class: <\/p>\n<pre>\nclass C : public A, public B\n{\npublic:\n <font COLOR=\"blue\">void IsNotNull() { }<\/font>\n int c;\n void DoSomethingC();\n};\n<\/pre>\n<p>and call <code>c-&gt;IsNotNull()<\/code> to tell the compiler, &#8220;I guarantee on penalty of undefined behavior that <code>c<\/code> is not null.&#8221; <\/p>\n<pre>\nvoid AcceptC3(C* c)\n{\n c-&gt;IsNotNull();\n AcceptB(c);\n}\n\nAcceptC3:\n    add rcx, sizeof(int)\n    jmp AcceptB\n<\/pre>\n<p>I don&#8217;t know whether any compilers actually take advantage of this hint, but at least this is a way of providing it in a standard-conforming way. <\/p>\n<p>Now, it looks like the purpose of this article is to delve into optimization tweaking in order to remove unwanted tests, but that wasn&#8217;t actually the point. The point of the article was to explain what these tests are for. You&#8217;ll be stepping through some code, and you&#8217;ll see these strange tests against zero, so here&#8217;s an explanation of why those tests are there. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>That null pointer thingie.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-93081","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>That null pointer thingie.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/93081","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=93081"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/93081\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=93081"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=93081"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=93081"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}