Is this type punning well defined?












13















I have structure like below.



struct result{
int a;
int b;
int c;
int d;
}


and union like below.



union convert{
int arr[4];
struct result res;
}


and I type pun as below.



  int arr1[4] = {1,2,3,5};
union convert *pointer = (union convert *) arr1; // Here is my question, is it well defined?

printf("%d %dn", pointer->res.a, pointer->res.b);









share|improve this question

























  • @StoryTeller I'm sorry, it typing mistake.

    – KBlr
    Jan 17 at 14:29






  • 2





    Id you can assure no paddign in the structure - it is safe at list with gcc

    – P__J__
    Jan 17 at 14:30











  • @AndyG; You're correct on the first point, but on the second, doesn't {1,2,3,5} initialise the array?

    – Bathsheba
    Jan 17 at 14:36






  • 2





    @Bathsheba: Darn me and me illiteracy

    – AndyG
    Jan 17 at 14:37






  • 1





    Hmmm, If static_assert(sizeof(convert.arr) == sizeof(convert.res), "Hmmm"); was added, I think the concern about padding is gone - Code would simply not compile when padding occurred.

    – chux
    Jan 17 at 16:53
















13















I have structure like below.



struct result{
int a;
int b;
int c;
int d;
}


and union like below.



union convert{
int arr[4];
struct result res;
}


and I type pun as below.



  int arr1[4] = {1,2,3,5};
union convert *pointer = (union convert *) arr1; // Here is my question, is it well defined?

printf("%d %dn", pointer->res.a, pointer->res.b);









share|improve this question

























  • @StoryTeller I'm sorry, it typing mistake.

    – KBlr
    Jan 17 at 14:29






  • 2





    Id you can assure no paddign in the structure - it is safe at list with gcc

    – P__J__
    Jan 17 at 14:30











  • @AndyG; You're correct on the first point, but on the second, doesn't {1,2,3,5} initialise the array?

    – Bathsheba
    Jan 17 at 14:36






  • 2





    @Bathsheba: Darn me and me illiteracy

    – AndyG
    Jan 17 at 14:37






  • 1





    Hmmm, If static_assert(sizeof(convert.arr) == sizeof(convert.res), "Hmmm"); was added, I think the concern about padding is gone - Code would simply not compile when padding occurred.

    – chux
    Jan 17 at 16:53














13












13








13








I have structure like below.



struct result{
int a;
int b;
int c;
int d;
}


and union like below.



union convert{
int arr[4];
struct result res;
}


and I type pun as below.



  int arr1[4] = {1,2,3,5};
union convert *pointer = (union convert *) arr1; // Here is my question, is it well defined?

printf("%d %dn", pointer->res.a, pointer->res.b);









share|improve this question
















I have structure like below.



struct result{
int a;
int b;
int c;
int d;
}


and union like below.



union convert{
int arr[4];
struct result res;
}


and I type pun as below.



  int arr1[4] = {1,2,3,5};
union convert *pointer = (union convert *) arr1; // Here is my question, is it well defined?

printf("%d %dn", pointer->res.a, pointer->res.b);






c language-lawyer






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 17 at 14:30









AndyG

26.7k77097




26.7k77097










asked Jan 17 at 14:23









KBlrKBlr

18210




18210













  • @StoryTeller I'm sorry, it typing mistake.

    – KBlr
    Jan 17 at 14:29






  • 2





    Id you can assure no paddign in the structure - it is safe at list with gcc

    – P__J__
    Jan 17 at 14:30











  • @AndyG; You're correct on the first point, but on the second, doesn't {1,2,3,5} initialise the array?

    – Bathsheba
    Jan 17 at 14:36






  • 2





    @Bathsheba: Darn me and me illiteracy

    – AndyG
    Jan 17 at 14:37






  • 1





    Hmmm, If static_assert(sizeof(convert.arr) == sizeof(convert.res), "Hmmm"); was added, I think the concern about padding is gone - Code would simply not compile when padding occurred.

    – chux
    Jan 17 at 16:53



















  • @StoryTeller I'm sorry, it typing mistake.

    – KBlr
    Jan 17 at 14:29






  • 2





    Id you can assure no paddign in the structure - it is safe at list with gcc

    – P__J__
    Jan 17 at 14:30











  • @AndyG; You're correct on the first point, but on the second, doesn't {1,2,3,5} initialise the array?

    – Bathsheba
    Jan 17 at 14:36






  • 2





    @Bathsheba: Darn me and me illiteracy

    – AndyG
    Jan 17 at 14:37






  • 1





    Hmmm, If static_assert(sizeof(convert.arr) == sizeof(convert.res), "Hmmm"); was added, I think the concern about padding is gone - Code would simply not compile when padding occurred.

    – chux
    Jan 17 at 16:53

















@StoryTeller I'm sorry, it typing mistake.

– KBlr
Jan 17 at 14:29





@StoryTeller I'm sorry, it typing mistake.

– KBlr
Jan 17 at 14:29




2




2





Id you can assure no paddign in the structure - it is safe at list with gcc

– P__J__
Jan 17 at 14:30





Id you can assure no paddign in the structure - it is safe at list with gcc

– P__J__
Jan 17 at 14:30













@AndyG; You're correct on the first point, but on the second, doesn't {1,2,3,5} initialise the array?

– Bathsheba
Jan 17 at 14:36





@AndyG; You're correct on the first point, but on the second, doesn't {1,2,3,5} initialise the array?

– Bathsheba
Jan 17 at 14:36




2




2





@Bathsheba: Darn me and me illiteracy

– AndyG
Jan 17 at 14:37





@Bathsheba: Darn me and me illiteracy

– AndyG
Jan 17 at 14:37




1




1





Hmmm, If static_assert(sizeof(convert.arr) == sizeof(convert.res), "Hmmm"); was added, I think the concern about padding is gone - Code would simply not compile when padding occurred.

– chux
Jan 17 at 16:53





Hmmm, If static_assert(sizeof(convert.arr) == sizeof(convert.res), "Hmmm"); was added, I think the concern about padding is gone - Code would simply not compile when padding occurred.

– chux
Jan 17 at 16:53












4 Answers
4






active

oldest

votes


















12














pointer->res.a is fine but the behaviour of pointer->res.b is undefined.



There could be an arbitrary amount of padding between the a and b members.



Some compilers allow you to specify that there is no padding between members but of course then you are giving up portability.






share|improve this answer



















  • 1





    @KBlr: Yep! Just not portable

    – AndyG
    Jan 17 at 14:37






  • 3





    pointer->res.a accesses an object (arr1) through an lvalue (pointer->res) in violation of C 2018 6.5 7.

    – Eric Postpischil
    Jan 17 at 16:13








  • 1





    @EricPostpischil So the aliasing rule applies not only to the "final" expression which is actually used to access (read or modify) an object, but to all glvalue subexpressions of the "final" expression?

    – Language Lawyer
    Jan 17 at 18:22






  • 1





    @EricPostpischil What I don't like in this aliasing rules, they talk about "expression used to access". I'm not sure does it mean only the "final" expression or all the subexpressions are also "used to access". Any authoritative references here? Also, arrays of different structures with identical definitions did not understand this.

    – Language Lawyer
    Jan 17 at 18:59






  • 1





    @LanguageLawyer: struct A { int x; }; and struct B { int x; }; have identical definitions but are different types. The C standard says they are not compatible, and one may not alias the other. If only the int x mattered, the aliasing rule would be useless. It is the fact that one structure cannot alias the other that enables optimization based on the aliasing rules. The aliasing rules must apply to the structures lvalues, not just the int.

    – Eric Postpischil
    Jan 17 at 20:14



















6















Is this type punning well defined?




struct result{
int a,b,c,d;
}

union convert {
int arr[4];
struct result res;
}

int arr1[4] = {1,2,3,5};
union convert *pointer = (union convert *) arr1;


(union convert *) arr1 risks alignment failure.




A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. C11dr §6.3.2.3 8




There is no requirement that union convert and int share the same alignment. union convert requirements may exceed int for example.



Consider this possibility: arr1 lives on int street where all addresses are multiple of 4. union and struct friends lives on "multiple of 8" street. arr1 might have address 0x1004 (not a multiple of 8).



In 2019, alignment failures are more commonly seen with char (needing 1) and other types needing 2 or more. In OP's select case, I doubt a real platform will have alignment issues, yet incorrect alignment remains possible.



This type punning is not well defined.





Additional concerns



Other answers and comments discuss padding issues, which further identifies trouble.



@Eric Postpischil comment about improper access with pointer->res.a adds more reasons to consider this UB.






share|improve this answer

































    2














    C imposes no rule about how much padding is left between 2 consecutive members of a structure.



    This is why the implementations define many #pragma directives -- specially to change this behaviour.



    So, as the answer of Bathsheba says, ...->b is undefined.



    I answered the very same question some time ago, here.






    share|improve this answer

































      1














      Pointer punning is not safe. Use real union punning instead.



      Assumptions: the struct is properly packed (no padding between the members)



      #include <stdio.h>
      #include <string.h>



      struct __attribute__((packed)) result{
      int a;
      int b;
      int c;
      int d;
      };

      union convert{
      int arr[4];
      struct result res;
      };

      volatile int arr1[4];

      void foo(void)
      {

      union convert cnv;

      memcpy(&cnv, (void *)arr1, sizeof(arr1));

      printf("%d %dn", cnv.res.a, cnv.res.b);
      }


      all modern compilers will optimize out the memcpy call



      https://godbolt.org/z/4qtRIF



      .LC0:
      .string "%d %dn"
      foo:
      mov rsi, QWORD PTR arr1[rip]
      xor eax, eax
      mov rdi, QWORD PTR arr1[rip+8]
      mov edi, OFFSET FLAT:.LC0
      mov rdx, rsi
      sar rdx, 32
      jmp printf





      share|improve this answer
























      • Why the volatile global?

        – curiousguy
        Jan 18 at 11:30











      • to prevent optimizing it out. The example is trivial and the compiler will otherwise just reduce it to the single const assignment.

        – P__J__
        Jan 18 at 11:40











      • I don't follow you.

        – curiousguy
        Jan 18 at 19:38











      • @curiousguy so you need to read more about the optimizations. Try to remove the volatile and see what will happen. Try yourself that is the only way

        – P__J__
        Jan 18 at 19:40











      • Actually you are the one who needs to read more.

        – curiousguy
        Jan 18 at 19:42











      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54237976%2fis-this-type-punning-well-defined%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      4 Answers
      4






      active

      oldest

      votes








      4 Answers
      4






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      12














      pointer->res.a is fine but the behaviour of pointer->res.b is undefined.



      There could be an arbitrary amount of padding between the a and b members.



      Some compilers allow you to specify that there is no padding between members but of course then you are giving up portability.






      share|improve this answer



















      • 1





        @KBlr: Yep! Just not portable

        – AndyG
        Jan 17 at 14:37






      • 3





        pointer->res.a accesses an object (arr1) through an lvalue (pointer->res) in violation of C 2018 6.5 7.

        – Eric Postpischil
        Jan 17 at 16:13








      • 1





        @EricPostpischil So the aliasing rule applies not only to the "final" expression which is actually used to access (read or modify) an object, but to all glvalue subexpressions of the "final" expression?

        – Language Lawyer
        Jan 17 at 18:22






      • 1





        @EricPostpischil What I don't like in this aliasing rules, they talk about "expression used to access". I'm not sure does it mean only the "final" expression or all the subexpressions are also "used to access". Any authoritative references here? Also, arrays of different structures with identical definitions did not understand this.

        – Language Lawyer
        Jan 17 at 18:59






      • 1





        @LanguageLawyer: struct A { int x; }; and struct B { int x; }; have identical definitions but are different types. The C standard says they are not compatible, and one may not alias the other. If only the int x mattered, the aliasing rule would be useless. It is the fact that one structure cannot alias the other that enables optimization based on the aliasing rules. The aliasing rules must apply to the structures lvalues, not just the int.

        – Eric Postpischil
        Jan 17 at 20:14
















      12














      pointer->res.a is fine but the behaviour of pointer->res.b is undefined.



      There could be an arbitrary amount of padding between the a and b members.



      Some compilers allow you to specify that there is no padding between members but of course then you are giving up portability.






      share|improve this answer



















      • 1





        @KBlr: Yep! Just not portable

        – AndyG
        Jan 17 at 14:37






      • 3





        pointer->res.a accesses an object (arr1) through an lvalue (pointer->res) in violation of C 2018 6.5 7.

        – Eric Postpischil
        Jan 17 at 16:13








      • 1





        @EricPostpischil So the aliasing rule applies not only to the "final" expression which is actually used to access (read or modify) an object, but to all glvalue subexpressions of the "final" expression?

        – Language Lawyer
        Jan 17 at 18:22






      • 1





        @EricPostpischil What I don't like in this aliasing rules, they talk about "expression used to access". I'm not sure does it mean only the "final" expression or all the subexpressions are also "used to access". Any authoritative references here? Also, arrays of different structures with identical definitions did not understand this.

        – Language Lawyer
        Jan 17 at 18:59






      • 1





        @LanguageLawyer: struct A { int x; }; and struct B { int x; }; have identical definitions but are different types. The C standard says they are not compatible, and one may not alias the other. If only the int x mattered, the aliasing rule would be useless. It is the fact that one structure cannot alias the other that enables optimization based on the aliasing rules. The aliasing rules must apply to the structures lvalues, not just the int.

        – Eric Postpischil
        Jan 17 at 20:14














      12












      12








      12







      pointer->res.a is fine but the behaviour of pointer->res.b is undefined.



      There could be an arbitrary amount of padding between the a and b members.



      Some compilers allow you to specify that there is no padding between members but of course then you are giving up portability.






      share|improve this answer













      pointer->res.a is fine but the behaviour of pointer->res.b is undefined.



      There could be an arbitrary amount of padding between the a and b members.



      Some compilers allow you to specify that there is no padding between members but of course then you are giving up portability.







      share|improve this answer












      share|improve this answer



      share|improve this answer










      answered Jan 17 at 14:33









      BathshebaBathsheba

      179k27254382




      179k27254382








      • 1





        @KBlr: Yep! Just not portable

        – AndyG
        Jan 17 at 14:37






      • 3





        pointer->res.a accesses an object (arr1) through an lvalue (pointer->res) in violation of C 2018 6.5 7.

        – Eric Postpischil
        Jan 17 at 16:13








      • 1





        @EricPostpischil So the aliasing rule applies not only to the "final" expression which is actually used to access (read or modify) an object, but to all glvalue subexpressions of the "final" expression?

        – Language Lawyer
        Jan 17 at 18:22






      • 1





        @EricPostpischil What I don't like in this aliasing rules, they talk about "expression used to access". I'm not sure does it mean only the "final" expression or all the subexpressions are also "used to access". Any authoritative references here? Also, arrays of different structures with identical definitions did not understand this.

        – Language Lawyer
        Jan 17 at 18:59






      • 1





        @LanguageLawyer: struct A { int x; }; and struct B { int x; }; have identical definitions but are different types. The C standard says they are not compatible, and one may not alias the other. If only the int x mattered, the aliasing rule would be useless. It is the fact that one structure cannot alias the other that enables optimization based on the aliasing rules. The aliasing rules must apply to the structures lvalues, not just the int.

        – Eric Postpischil
        Jan 17 at 20:14














      • 1





        @KBlr: Yep! Just not portable

        – AndyG
        Jan 17 at 14:37






      • 3





        pointer->res.a accesses an object (arr1) through an lvalue (pointer->res) in violation of C 2018 6.5 7.

        – Eric Postpischil
        Jan 17 at 16:13








      • 1





        @EricPostpischil So the aliasing rule applies not only to the "final" expression which is actually used to access (read or modify) an object, but to all glvalue subexpressions of the "final" expression?

        – Language Lawyer
        Jan 17 at 18:22






      • 1





        @EricPostpischil What I don't like in this aliasing rules, they talk about "expression used to access". I'm not sure does it mean only the "final" expression or all the subexpressions are also "used to access". Any authoritative references here? Also, arrays of different structures with identical definitions did not understand this.

        – Language Lawyer
        Jan 17 at 18:59






      • 1





        @LanguageLawyer: struct A { int x; }; and struct B { int x; }; have identical definitions but are different types. The C standard says they are not compatible, and one may not alias the other. If only the int x mattered, the aliasing rule would be useless. It is the fact that one structure cannot alias the other that enables optimization based on the aliasing rules. The aliasing rules must apply to the structures lvalues, not just the int.

        – Eric Postpischil
        Jan 17 at 20:14








      1




      1





      @KBlr: Yep! Just not portable

      – AndyG
      Jan 17 at 14:37





      @KBlr: Yep! Just not portable

      – AndyG
      Jan 17 at 14:37




      3




      3





      pointer->res.a accesses an object (arr1) through an lvalue (pointer->res) in violation of C 2018 6.5 7.

      – Eric Postpischil
      Jan 17 at 16:13







      pointer->res.a accesses an object (arr1) through an lvalue (pointer->res) in violation of C 2018 6.5 7.

      – Eric Postpischil
      Jan 17 at 16:13






      1




      1





      @EricPostpischil So the aliasing rule applies not only to the "final" expression which is actually used to access (read or modify) an object, but to all glvalue subexpressions of the "final" expression?

      – Language Lawyer
      Jan 17 at 18:22





      @EricPostpischil So the aliasing rule applies not only to the "final" expression which is actually used to access (read or modify) an object, but to all glvalue subexpressions of the "final" expression?

      – Language Lawyer
      Jan 17 at 18:22




      1




      1





      @EricPostpischil What I don't like in this aliasing rules, they talk about "expression used to access". I'm not sure does it mean only the "final" expression or all the subexpressions are also "used to access". Any authoritative references here? Also, arrays of different structures with identical definitions did not understand this.

      – Language Lawyer
      Jan 17 at 18:59





      @EricPostpischil What I don't like in this aliasing rules, they talk about "expression used to access". I'm not sure does it mean only the "final" expression or all the subexpressions are also "used to access". Any authoritative references here? Also, arrays of different structures with identical definitions did not understand this.

      – Language Lawyer
      Jan 17 at 18:59




      1




      1





      @LanguageLawyer: struct A { int x; }; and struct B { int x; }; have identical definitions but are different types. The C standard says they are not compatible, and one may not alias the other. If only the int x mattered, the aliasing rule would be useless. It is the fact that one structure cannot alias the other that enables optimization based on the aliasing rules. The aliasing rules must apply to the structures lvalues, not just the int.

      – Eric Postpischil
      Jan 17 at 20:14





      @LanguageLawyer: struct A { int x; }; and struct B { int x; }; have identical definitions but are different types. The C standard says they are not compatible, and one may not alias the other. If only the int x mattered, the aliasing rule would be useless. It is the fact that one structure cannot alias the other that enables optimization based on the aliasing rules. The aliasing rules must apply to the structures lvalues, not just the int.

      – Eric Postpischil
      Jan 17 at 20:14













      6















      Is this type punning well defined?




      struct result{
      int a,b,c,d;
      }

      union convert {
      int arr[4];
      struct result res;
      }

      int arr1[4] = {1,2,3,5};
      union convert *pointer = (union convert *) arr1;


      (union convert *) arr1 risks alignment failure.




      A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. C11dr §6.3.2.3 8




      There is no requirement that union convert and int share the same alignment. union convert requirements may exceed int for example.



      Consider this possibility: arr1 lives on int street where all addresses are multiple of 4. union and struct friends lives on "multiple of 8" street. arr1 might have address 0x1004 (not a multiple of 8).



      In 2019, alignment failures are more commonly seen with char (needing 1) and other types needing 2 or more. In OP's select case, I doubt a real platform will have alignment issues, yet incorrect alignment remains possible.



      This type punning is not well defined.





      Additional concerns



      Other answers and comments discuss padding issues, which further identifies trouble.



      @Eric Postpischil comment about improper access with pointer->res.a adds more reasons to consider this UB.






      share|improve this answer






























        6















        Is this type punning well defined?




        struct result{
        int a,b,c,d;
        }

        union convert {
        int arr[4];
        struct result res;
        }

        int arr1[4] = {1,2,3,5};
        union convert *pointer = (union convert *) arr1;


        (union convert *) arr1 risks alignment failure.




        A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. C11dr §6.3.2.3 8




        There is no requirement that union convert and int share the same alignment. union convert requirements may exceed int for example.



        Consider this possibility: arr1 lives on int street where all addresses are multiple of 4. union and struct friends lives on "multiple of 8" street. arr1 might have address 0x1004 (not a multiple of 8).



        In 2019, alignment failures are more commonly seen with char (needing 1) and other types needing 2 or more. In OP's select case, I doubt a real platform will have alignment issues, yet incorrect alignment remains possible.



        This type punning is not well defined.





        Additional concerns



        Other answers and comments discuss padding issues, which further identifies trouble.



        @Eric Postpischil comment about improper access with pointer->res.a adds more reasons to consider this UB.






        share|improve this answer




























          6












          6








          6








          Is this type punning well defined?




          struct result{
          int a,b,c,d;
          }

          union convert {
          int arr[4];
          struct result res;
          }

          int arr1[4] = {1,2,3,5};
          union convert *pointer = (union convert *) arr1;


          (union convert *) arr1 risks alignment failure.




          A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. C11dr §6.3.2.3 8




          There is no requirement that union convert and int share the same alignment. union convert requirements may exceed int for example.



          Consider this possibility: arr1 lives on int street where all addresses are multiple of 4. union and struct friends lives on "multiple of 8" street. arr1 might have address 0x1004 (not a multiple of 8).



          In 2019, alignment failures are more commonly seen with char (needing 1) and other types needing 2 or more. In OP's select case, I doubt a real platform will have alignment issues, yet incorrect alignment remains possible.



          This type punning is not well defined.





          Additional concerns



          Other answers and comments discuss padding issues, which further identifies trouble.



          @Eric Postpischil comment about improper access with pointer->res.a adds more reasons to consider this UB.






          share|improve this answer
















          Is this type punning well defined?




          struct result{
          int a,b,c,d;
          }

          union convert {
          int arr[4];
          struct result res;
          }

          int arr1[4] = {1,2,3,5};
          union convert *pointer = (union convert *) arr1;


          (union convert *) arr1 risks alignment failure.




          A pointer to an object type may be converted to a pointer to a different object type. If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. C11dr §6.3.2.3 8




          There is no requirement that union convert and int share the same alignment. union convert requirements may exceed int for example.



          Consider this possibility: arr1 lives on int street where all addresses are multiple of 4. union and struct friends lives on "multiple of 8" street. arr1 might have address 0x1004 (not a multiple of 8).



          In 2019, alignment failures are more commonly seen with char (needing 1) and other types needing 2 or more. In OP's select case, I doubt a real platform will have alignment issues, yet incorrect alignment remains possible.



          This type punning is not well defined.





          Additional concerns



          Other answers and comments discuss padding issues, which further identifies trouble.



          @Eric Postpischil comment about improper access with pointer->res.a adds more reasons to consider this UB.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Jan 17 at 19:12

























          answered Jan 17 at 17:01









          chuxchux

          83.2k872151




          83.2k872151























              2














              C imposes no rule about how much padding is left between 2 consecutive members of a structure.



              This is why the implementations define many #pragma directives -- specially to change this behaviour.



              So, as the answer of Bathsheba says, ...->b is undefined.



              I answered the very same question some time ago, here.






              share|improve this answer






























                2














                C imposes no rule about how much padding is left between 2 consecutive members of a structure.



                This is why the implementations define many #pragma directives -- specially to change this behaviour.



                So, as the answer of Bathsheba says, ...->b is undefined.



                I answered the very same question some time ago, here.






                share|improve this answer




























                  2












                  2








                  2







                  C imposes no rule about how much padding is left between 2 consecutive members of a structure.



                  This is why the implementations define many #pragma directives -- specially to change this behaviour.



                  So, as the answer of Bathsheba says, ...->b is undefined.



                  I answered the very same question some time ago, here.






                  share|improve this answer















                  C imposes no rule about how much padding is left between 2 consecutive members of a structure.



                  This is why the implementations define many #pragma directives -- specially to change this behaviour.



                  So, as the answer of Bathsheba says, ...->b is undefined.



                  I answered the very same question some time ago, here.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited Jan 17 at 15:58

























                  answered Jan 17 at 14:53









                  alinsoaralinsoar

                  8,44813149




                  8,44813149























                      1














                      Pointer punning is not safe. Use real union punning instead.



                      Assumptions: the struct is properly packed (no padding between the members)



                      #include <stdio.h>
                      #include <string.h>



                      struct __attribute__((packed)) result{
                      int a;
                      int b;
                      int c;
                      int d;
                      };

                      union convert{
                      int arr[4];
                      struct result res;
                      };

                      volatile int arr1[4];

                      void foo(void)
                      {

                      union convert cnv;

                      memcpy(&cnv, (void *)arr1, sizeof(arr1));

                      printf("%d %dn", cnv.res.a, cnv.res.b);
                      }


                      all modern compilers will optimize out the memcpy call



                      https://godbolt.org/z/4qtRIF



                      .LC0:
                      .string "%d %dn"
                      foo:
                      mov rsi, QWORD PTR arr1[rip]
                      xor eax, eax
                      mov rdi, QWORD PTR arr1[rip+8]
                      mov edi, OFFSET FLAT:.LC0
                      mov rdx, rsi
                      sar rdx, 32
                      jmp printf





                      share|improve this answer
























                      • Why the volatile global?

                        – curiousguy
                        Jan 18 at 11:30











                      • to prevent optimizing it out. The example is trivial and the compiler will otherwise just reduce it to the single const assignment.

                        – P__J__
                        Jan 18 at 11:40











                      • I don't follow you.

                        – curiousguy
                        Jan 18 at 19:38











                      • @curiousguy so you need to read more about the optimizations. Try to remove the volatile and see what will happen. Try yourself that is the only way

                        – P__J__
                        Jan 18 at 19:40











                      • Actually you are the one who needs to read more.

                        – curiousguy
                        Jan 18 at 19:42
















                      1














                      Pointer punning is not safe. Use real union punning instead.



                      Assumptions: the struct is properly packed (no padding between the members)



                      #include <stdio.h>
                      #include <string.h>



                      struct __attribute__((packed)) result{
                      int a;
                      int b;
                      int c;
                      int d;
                      };

                      union convert{
                      int arr[4];
                      struct result res;
                      };

                      volatile int arr1[4];

                      void foo(void)
                      {

                      union convert cnv;

                      memcpy(&cnv, (void *)arr1, sizeof(arr1));

                      printf("%d %dn", cnv.res.a, cnv.res.b);
                      }


                      all modern compilers will optimize out the memcpy call



                      https://godbolt.org/z/4qtRIF



                      .LC0:
                      .string "%d %dn"
                      foo:
                      mov rsi, QWORD PTR arr1[rip]
                      xor eax, eax
                      mov rdi, QWORD PTR arr1[rip+8]
                      mov edi, OFFSET FLAT:.LC0
                      mov rdx, rsi
                      sar rdx, 32
                      jmp printf





                      share|improve this answer
























                      • Why the volatile global?

                        – curiousguy
                        Jan 18 at 11:30











                      • to prevent optimizing it out. The example is trivial and the compiler will otherwise just reduce it to the single const assignment.

                        – P__J__
                        Jan 18 at 11:40











                      • I don't follow you.

                        – curiousguy
                        Jan 18 at 19:38











                      • @curiousguy so you need to read more about the optimizations. Try to remove the volatile and see what will happen. Try yourself that is the only way

                        – P__J__
                        Jan 18 at 19:40











                      • Actually you are the one who needs to read more.

                        – curiousguy
                        Jan 18 at 19:42














                      1












                      1








                      1







                      Pointer punning is not safe. Use real union punning instead.



                      Assumptions: the struct is properly packed (no padding between the members)



                      #include <stdio.h>
                      #include <string.h>



                      struct __attribute__((packed)) result{
                      int a;
                      int b;
                      int c;
                      int d;
                      };

                      union convert{
                      int arr[4];
                      struct result res;
                      };

                      volatile int arr1[4];

                      void foo(void)
                      {

                      union convert cnv;

                      memcpy(&cnv, (void *)arr1, sizeof(arr1));

                      printf("%d %dn", cnv.res.a, cnv.res.b);
                      }


                      all modern compilers will optimize out the memcpy call



                      https://godbolt.org/z/4qtRIF



                      .LC0:
                      .string "%d %dn"
                      foo:
                      mov rsi, QWORD PTR arr1[rip]
                      xor eax, eax
                      mov rdi, QWORD PTR arr1[rip+8]
                      mov edi, OFFSET FLAT:.LC0
                      mov rdx, rsi
                      sar rdx, 32
                      jmp printf





                      share|improve this answer













                      Pointer punning is not safe. Use real union punning instead.



                      Assumptions: the struct is properly packed (no padding between the members)



                      #include <stdio.h>
                      #include <string.h>



                      struct __attribute__((packed)) result{
                      int a;
                      int b;
                      int c;
                      int d;
                      };

                      union convert{
                      int arr[4];
                      struct result res;
                      };

                      volatile int arr1[4];

                      void foo(void)
                      {

                      union convert cnv;

                      memcpy(&cnv, (void *)arr1, sizeof(arr1));

                      printf("%d %dn", cnv.res.a, cnv.res.b);
                      }


                      all modern compilers will optimize out the memcpy call



                      https://godbolt.org/z/4qtRIF



                      .LC0:
                      .string "%d %dn"
                      foo:
                      mov rsi, QWORD PTR arr1[rip]
                      xor eax, eax
                      mov rdi, QWORD PTR arr1[rip+8]
                      mov edi, OFFSET FLAT:.LC0
                      mov rdx, rsi
                      sar rdx, 32
                      jmp printf






                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered Jan 17 at 14:56









                      P__J__P__J__

                      10.9k2725




                      10.9k2725













                      • Why the volatile global?

                        – curiousguy
                        Jan 18 at 11:30











                      • to prevent optimizing it out. The example is trivial and the compiler will otherwise just reduce it to the single const assignment.

                        – P__J__
                        Jan 18 at 11:40











                      • I don't follow you.

                        – curiousguy
                        Jan 18 at 19:38











                      • @curiousguy so you need to read more about the optimizations. Try to remove the volatile and see what will happen. Try yourself that is the only way

                        – P__J__
                        Jan 18 at 19:40











                      • Actually you are the one who needs to read more.

                        – curiousguy
                        Jan 18 at 19:42



















                      • Why the volatile global?

                        – curiousguy
                        Jan 18 at 11:30











                      • to prevent optimizing it out. The example is trivial and the compiler will otherwise just reduce it to the single const assignment.

                        – P__J__
                        Jan 18 at 11:40











                      • I don't follow you.

                        – curiousguy
                        Jan 18 at 19:38











                      • @curiousguy so you need to read more about the optimizations. Try to remove the volatile and see what will happen. Try yourself that is the only way

                        – P__J__
                        Jan 18 at 19:40











                      • Actually you are the one who needs to read more.

                        – curiousguy
                        Jan 18 at 19:42

















                      Why the volatile global?

                      – curiousguy
                      Jan 18 at 11:30





                      Why the volatile global?

                      – curiousguy
                      Jan 18 at 11:30













                      to prevent optimizing it out. The example is trivial and the compiler will otherwise just reduce it to the single const assignment.

                      – P__J__
                      Jan 18 at 11:40





                      to prevent optimizing it out. The example is trivial and the compiler will otherwise just reduce it to the single const assignment.

                      – P__J__
                      Jan 18 at 11:40













                      I don't follow you.

                      – curiousguy
                      Jan 18 at 19:38





                      I don't follow you.

                      – curiousguy
                      Jan 18 at 19:38













                      @curiousguy so you need to read more about the optimizations. Try to remove the volatile and see what will happen. Try yourself that is the only way

                      – P__J__
                      Jan 18 at 19:40





                      @curiousguy so you need to read more about the optimizations. Try to remove the volatile and see what will happen. Try yourself that is the only way

                      – P__J__
                      Jan 18 at 19:40













                      Actually you are the one who needs to read more.

                      – curiousguy
                      Jan 18 at 19:42





                      Actually you are the one who needs to read more.

                      – curiousguy
                      Jan 18 at 19:42


















                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54237976%2fis-this-type-punning-well-defined%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      MongoDB - Not Authorized To Execute Command

                      How to fix TextFormField cause rebuild widget in Flutter

                      in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith