This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Problem with structs as return values

I am using functions, which returns structs. This shouldn't
be a problem in C. And it works mostly.
But are there contexts where this is not
allowed? Maybe there a conflicts because
those structs are returned by using registers?

for example:

typedef struct {

unsigned char Byte_1;
unsigned char Byte_2;
} two_byz;

two_byz f_a(void)
{
two_byz rueck;
rueck.Byte_1 = 0xAA;
rueck.Byte_2 = 0xBB;
return ( rueck);
}


two_byz test;

test = f_a();

Again: this example does work - mostly!
But as
i used this module in another program
there was just no return value.
It is really weird, the function then
behaves like void f_a(void).

Parents
  • Mark, you need to look at the generated code before making such a sweeping statement. Not all compilers are created to your liking.

       1          #include <stdio.h>
       2          
       3          typedef struct {
       4          
       5          unsigned char Byte_1;
       6          unsigned char Byte_2;
       7          } two_byz;
       8          
       9          two_byz f_a(void)
      10          {
      11   1        two_byz rueck;
      12   1        rueck.Byte_1 = 0xAA;
      13   1        rueck.Byte_2 = 0xBB;
      14   1        return ( rueck);
      15   1      }
      16          
      17          
      18          
      19          
      20          void main(void){
      21   1        two_byz test;
      22   1        two_byz test2;
      23   1      
      24   1        test = test2;
      25   1        test = f_a();
      26   1      
      27   1      }
      28          
    C51 COMPILER V6.10  MAIN                                                                   03/30/2001 08:39:14 PAGE 2   
    
    ASSEMBLY LISTING OF GENERATED OBJECT CODE
    
    
                 ; FUNCTION f_a (BEGIN)
                                               ; SOURCE LINE # 9
                                               ; SOURCE LINE # 10
                                               ; SOURCE LINE # 12
    0000 7500AA      R     MOV     rueck,#0AAH
                                               ; SOURCE LINE # 13
    0003 7500BB      R     MOV     rueck+01H,#0BBH
                                               ; SOURCE LINE # 14
    0006 7B00              MOV     R3,#00H
    0008 7A00        R     MOV     R2,#HIGH rueck
    000A 7900        R     MOV     R1,#LOW rueck
                                               ; SOURCE LINE # 15
    000C         ?C0001:
    000C 22                RET     
                 ; FUNCTION f_a (END)
    
                 ; FUNCTION main (BEGIN)
                                               ; SOURCE LINE # 20
                                               ; SOURCE LINE # 24
    0000 7800        R     MOV     R0,#LOW test
    0002 7C00        R     MOV     R4,#HIGH test
    0004 7D00              MOV     R5,#00H
    0006 7B00              MOV     R3,#00H
    0008 7A00        R     MOV     R2,#HIGH test2
    000A 7900        R     MOV     R1,#LOW test2
    000C 7E00              MOV     R6,#00H
    000E 7F02              MOV     R7,#02H
    0010 120000      E     LCALL   ?C?COPY
                                               ; SOURCE LINE # 25
    0013 120000      R     LCALL   f_a
    0016 7800        R     MOV     R0,#LOW test
    0018 7C00        R     MOV     R4,#HIGH test
    001A 7D00              MOV     R5,#00H
    001C 7E00              MOV     R6,#00H
    001E 7F02              MOV     R7,#02H
    0020 020000      E     LJMP    ?C?COPY
                 ; FUNCTION main (END)
    

Reply
  • Mark, you need to look at the generated code before making such a sweeping statement. Not all compilers are created to your liking.

       1          #include <stdio.h>
       2          
       3          typedef struct {
       4          
       5          unsigned char Byte_1;
       6          unsigned char Byte_2;
       7          } two_byz;
       8          
       9          two_byz f_a(void)
      10          {
      11   1        two_byz rueck;
      12   1        rueck.Byte_1 = 0xAA;
      13   1        rueck.Byte_2 = 0xBB;
      14   1        return ( rueck);
      15   1      }
      16          
      17          
      18          
      19          
      20          void main(void){
      21   1        two_byz test;
      22   1        two_byz test2;
      23   1      
      24   1        test = test2;
      25   1        test = f_a();
      26   1      
      27   1      }
      28          
    C51 COMPILER V6.10  MAIN                                                                   03/30/2001 08:39:14 PAGE 2   
    
    ASSEMBLY LISTING OF GENERATED OBJECT CODE
    
    
                 ; FUNCTION f_a (BEGIN)
                                               ; SOURCE LINE # 9
                                               ; SOURCE LINE # 10
                                               ; SOURCE LINE # 12
    0000 7500AA      R     MOV     rueck,#0AAH
                                               ; SOURCE LINE # 13
    0003 7500BB      R     MOV     rueck+01H,#0BBH
                                               ; SOURCE LINE # 14
    0006 7B00              MOV     R3,#00H
    0008 7A00        R     MOV     R2,#HIGH rueck
    000A 7900        R     MOV     R1,#LOW rueck
                                               ; SOURCE LINE # 15
    000C         ?C0001:
    000C 22                RET     
                 ; FUNCTION f_a (END)
    
                 ; FUNCTION main (BEGIN)
                                               ; SOURCE LINE # 20
                                               ; SOURCE LINE # 24
    0000 7800        R     MOV     R0,#LOW test
    0002 7C00        R     MOV     R4,#HIGH test
    0004 7D00              MOV     R5,#00H
    0006 7B00              MOV     R3,#00H
    0008 7A00        R     MOV     R2,#HIGH test2
    000A 7900        R     MOV     R1,#LOW test2
    000C 7E00              MOV     R6,#00H
    000E 7F02              MOV     R7,#02H
    0010 120000      E     LCALL   ?C?COPY
                                               ; SOURCE LINE # 25
    0013 120000      R     LCALL   f_a
    0016 7800        R     MOV     R0,#LOW test
    0018 7C00        R     MOV     R4,#HIGH test
    001A 7D00              MOV     R5,#00H
    001C 7E00              MOV     R6,#00H
    001E 7F02              MOV     R7,#02H
    0020 020000      E     LJMP    ?C?COPY
                 ; FUNCTION main (END)
    

Children
  • My old K&R 2/ed (1988) says, "a function may return an arithmetic type, a structure, a union, a pointer, or void"
    The section on Function Return Values (p115) in the User's Guide 11.2000 makes no menion of how C51 returns structures or unions.

  • The bottom line is that, no matter how you would like it to work, the Keil compiler return structures as pointers. So if you do not immediately "consume" the returned structure, you have to worry about where the structure was originally allocated. This can cause some bugs to be as baffling as stack overflows.

    Here is an example. It should (and does on Microsoft VC++) print "3 = 3". On the Keil compiler, it prints "3 = 4". (Use optimization level 0. At optimization level 9 the last assignment gets left out.)



    C51 COMPILER V6.10  MAIN                                                                   04/02/2001 17:35:06 PAGE 1   
    
    
    C51 COMPILER V6.10, COMPILATION OF MODULE MAIN
    OBJECT MODULE PLACED IN .\Main.OBJ
    COMPILER INVOKED BY: C:\PROGRAM FILES\KEIL\C51\BIN\C51.EXE .\Main.C OPTIMIZE(0,SIZE) DEBUG OBJECTEXTEND CODE
    
    stmt level    source
    
       1          #include <stdio.h>
       2          #include <reg52.h>
       3          
       4          typedef struct {
       5            char Chars[2];
       6          } Struct;
       7          
       8          Struct CharToStruct(char v)
       9          {
      10   1        Struct S;
      11   1      
      12   1        S.Chars[0] = v;
      13   1        S.Chars[1] = 0;
      14   1        return( S );
      15   1      }
      16          
      17          char AddIndrect( const char *S0,  const char *S1)
      18          {
      19   1        return( *S0 + *S1 );
      20   1      }
      21          
      22          void main(void){
      23   1        char ShouldBe3Not4;
      24   1      
      25   1        ShouldBe3Not4 = AddIndrect( CharToStruct(1).Chars, CharToStruct(2).Chars );
      26   1      
      27   1        TI = 1;
      28   1        printf( "3 = %d", (int)ShouldBe3Not4 );
      29   1        
      30   1        while(1) ;
      31   1      }
      32          
    C51 COMPILER V6.10  MAIN                                                                   04/02/2001 17:35:06 PAGE 2   
    
    ASSEMBLY LISTING OF GENERATED OBJECT CODE
    
    
                 ; FUNCTION _CharToStruct (BEGIN)
    0000 8F00        R     MOV     v,R7
                                               ; SOURCE LINE # 8
                                               ; SOURCE LINE # 9
                                               ; SOURCE LINE # 12
    0002 850000      R     MOV     S,v
                                               ; SOURCE LINE # 13
    0005 750000      R     MOV     S+01H,#00H
                                               ; SOURCE LINE # 14
    0008 7B00              MOV     R3,#00H
    000A 7A00        R     MOV     R2,#HIGH S
    000C 7900        R     MOV     R1,#LOW S
                                               ; SOURCE LINE # 15
    000E         ?C0001:
    000E 22                RET     
                 ; FUNCTION _CharToStruct (END)
    
                 ; FUNCTION _AddIndrect (BEGIN)
    0000 8B00        R     MOV     S0,R3
    0002 8A00        R     MOV     S0+01H,R2
    0004 8900        R     MOV     S0+02H,R1
                                               ; SOURCE LINE # 17
                                               ; SOURCE LINE # 18
                                               ; SOURCE LINE # 19
    0006 AB00        R     MOV     R3,S1
    0008 AA00        R     MOV     R2,S1+01H
    000A A900        R     MOV     R1,S1+02H
    000C 120000      E     LCALL   ?C?CLDPTR
    000F FF                MOV     R7,A
    0010 AB00        R     MOV     R3,S0
    0012 AA00        R     MOV     R2,S0+01H
    0014 A900        R     MOV     R1,S0+02H
    0016 120000      E     LCALL   ?C?CLDPTR
    0019 FE                MOV     R6,A
    001A EE                MOV     A,R6
    001B 2F                ADD     A,R7
    001C FF                MOV     R7,A
                                               ; SOURCE LINE # 20
    001D         ?C0002:
    001D 22                RET     
                 ; FUNCTION _AddIndrect (END)
    
                 ; FUNCTION main (BEGIN)
                                               ; SOURCE LINE # 22
                                               ; SOURCE LINE # 25
    0000 7F01              MOV     R7,#01H
    0002 120000      R     LCALL   _CharToStruct
    0005 C003              PUSH    AR3
    0007 C002              PUSH    AR2
    0009 C001              PUSH    AR1
    000B 7F02              MOV     R7,#02H
    000D 120000      R     LCALL   _CharToStruct
    0010 8B00        R     MOV     ?_AddIndrect?BYTE+03H,R3
    0012 8A00        R     MOV     ?_AddIndrect?BYTE+04H,R2
    0014 8900        R     MOV     ?_AddIndrect?BYTE+05H,R1
    0016 D001              POP     AR1
    0018 D002              POP     AR2
    001A D003              POP     AR3
    001C 120000      R     LCALL   _AddIndrect
    C51 COMPILER V6.10  MAIN                                                                   04/02/2001 17:35:06 PAGE 3   
    
    001F 8F00        R     MOV     ShouldBe3Not4,R7
                                               ; SOURCE LINE # 27
    0021 D299              SETB    TI
                                               ; SOURCE LINE # 28
    0023 7BFF              MOV     R3,#0FFH
    0025 7A00        R     MOV     R2,#HIGH ?SC_0
    0027 7900        R     MOV     R1,#LOW ?SC_0
    0029 AF00        R     MOV     R7,ShouldBe3Not4
    002B EF                MOV     A,R7
    002C 33                RLC     A
    002D 95E0              SUBB    A,ACC
    002F FE                MOV     R6,A
    0030 8E00        E     MOV     ?_printf?BYTE+03H,R6
    0032 8F00        E     MOV     ?_printf?BYTE+04H,R7
    0034 120000      E     LCALL   _printf
    0037         ?C0003:
                                               ; SOURCE LINE # 30
    0037 80FE              SJMP    ?C0003
    0039         ?C0004:
                                               ; SOURCE LINE # 31
    0039         ?C0005:
    0039 22                RET     
                 ; FUNCTION main (END)
    
    
    
    MODULE INFORMATION:   STATIC OVERLAYABLE
       CODE SIZE        =    103    ----
       CONSTANT SIZE    =      7    ----
       XDATA SIZE       =   ----    ----
       PDATA SIZE       =   ----    ----
       DATA SIZE        =     10    ----
       IDATA SIZE       =   ----    ----
       BIT SIZE         =   ----    ----
    END OF MODULE INFORMATION.
    
    
    C51 COMPILATION COMPLETE.  0 WARNING(S),  0 ERROR(S)
    

  • "the Keil compiler return structures as pointers."
    As you have discovered.

    My point was that we shouldn't have to "discover" such things; Keil should document it in the manuals - as they have done with the other return types.

  • Since we're currently working on the C51 compiler manual, I'll see about putting this in there.

    Jon

  • Great!

    While you're at it, how about a major overhaul of the discussion of the bit-addressing extensions?
    Obviously, this is totally implementation-specific and therefore needs to be given a detailed treatment in the body of the manual, rather than merely part of an appendix right at the end of the document!

  • There is a discussion of bit-addressable stuff in the C51 UG on P65-66. Is there more that should be said there?

    I've used the tools for so long that this is second-nature to me now.

    Jon

  • Since you can not use the volatile keyword with the sf bits & registers, I would feel better if it was stated that these are implicitly volatile.

  • I may no sweeping statement, just reiterated ANSI/ISO C requirements. How the implementation actually returns a struct should not matter. The fact is the code works as if the structure was returned. That is, you don't need to return a pointer to the struct and you can still access the data of the returned struct (it doesn't dissappear when the function returns).

    I stand by ANSI C, the struct returned is returned as an object from the point of view of the code. How the compiler implements this facet of ANSI C is of no concern to me unless I'm worried about efficiency.

    Regards.

    - Mark