Thread: Function "size"

    #1
  1. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    May 2013
    Posts
    36
    Rep Power
    2

    Function "size"


    While attempting to learn C programming, came across the following code, and wonder if someone can explain why the int 'function' is 16 bytes in size? I would have expected 8 bytes for an 'int' function.
    Code:
    #include <stdio.h>
    
    int func1(int a);
    int func2(int a);
    
    int main(void){
    
    /*
    We do not need to use the 
    & operator to get the memory
    address of the function
    */
    
    printf("func1 address = %p\n", func1);
    
    printf("func2 address = %p\n", func2);
    
    return 0;
    
    }
    
    int func1(int a){
    return a + a;
    }
    int func2(int a){
    return a * a;
    }
  2. #2
  3. Contributed User
    Devshed Specialist (4000 - 4499 posts)

    Join Date
    Jun 2005
    Posts
    4,417
    Rep Power
    1871
    > and wonder if someone can explain why the int 'function' is 16 bytes in size?
    Study the output of say
    gcc -S prog.c
    Hint: look in prog.s

    Padding, alignment, compiler flags, instruction set all play a part in how large a function is. You can't expect it to be anything.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper
  4. #3
  5. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,255
    Rep Power
    2222
    func1 and func2 are the addresses of the two functions. As such, they are pointers. The size of the data type that a function returns has nothing whatsoever to do with the sizeof of the pointers that point to the function.

    You are doing this on a 64-bit machine, I trust. As I understand, pointers on a 64-bit machine tend to be 64 bits long, which is 8 bytes.

    But you say they are 16 bytes long. Could you please copy-and-paste the output of your program here?

    BTW, 0xAB is one byte.
    Last edited by dwise1_aol; September 15th, 2013 at 06:18 PM.
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    May 2013
    Posts
    36
    Rep Power
    2
    Originally Posted by dwise1_aol
    func1 and func2 are the addresses of the two functions. As such, they are pointers. The size of the data type that a function returns has nothing whatsoever to do with the sizeof of the pointers that point to the function.

    You are doing this on a 64-bit machine, I trust. As I understand, pointers on a 64-bit machine tend to be 64 bits long, which is 8 bytes.

    But you say they are 16 bytes long. Could you please copy-and-paste the output of your program here?

    BTW, 0xAB is one byte.
    I'm using a 32 bit PC, and output is:
    func1 address = 0x80484c0
    func2 address = 0x80484d0

    Just realized that I think I'm confusing bytes & bits.
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    May 2013
    Posts
    36
    Rep Power
    2
    [QUOTE=salem]> and wonder if someone can explain why the int 'function' is 16 bytes in size?
    Study the output of say
    gcc -S prog.c
    Hint: look in prog.s
    {/QUOTE]

    Using above cmd line, get 10 byte difference in addresses:
    func1 address = 0x8048454
    func2 address = 0x804845e

    Don't know enough about compilers to know what you mean by "prog.s"
  10. #6
  11. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,255
    Rep Power
    2222
    The -S option in gcc generates an assembly listing which has a .s extension. Whatever compiler you're using should have its own option switch that generates an assembly listing, though the extension may be different (eg, .asm).

    The assembly code is what the compiler normally generates; it's the translation of your higher level language source code into the individual instructions that the computer needs to execute to perform the functionality described by the source code. By reading the assembly listing for the two functions, you will see exactly what instructions were generated. And by converting each instruction into its machine code (the actual numbers that the computer reads and executes), you can add up the bytes of each instruction to get the total number of bytes in each function; some, but not all, assembly listings include the machine code.

    You should also be aware that different compilers will very likely generate different assembly code; even the same compiler may generate different assembly when given different optimization parameters. So the size of the function code (which is what I assume you are trying to get, rather than the size of the address) that you get with your compiler is only valid for that particular compiler with its particular settings. We would expect other compilers to generate different size code.

    But what I want to know is why you expected the function code to only be 8 bytes in size. What did you base that prediction on?
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    May 2013
    Posts
    36
    Rep Power
    2
    Originally Posted by dwise1_aol
    The -S option in gcc generates an assembly listing which has a .s extension. Whatever compiler you're using should have its own option switch that generates an assembly listing, though the extension may be different (eg, .asm).

    The assembly code is what the compiler normally generates; it's the translation of your higher level language source code into the individual instructions that the computer needs to execute to perform the functionality described by the source code. By reading the assembly listing for the two functions, you will see exactly what instructions were generated. And by converting each instruction into its machine code (the actual numbers that the computer reads and executes), you can add up the bytes of each instruction to get the total number of bytes in each function; some, but not all, assembly listings include the machine code.

    You should also be aware that different compilers will very likely generate different assembly code; even the same compiler may generate different assembly when given different optimization parameters. So the size of the function code (which is what I assume you are trying to get, rather than the size of the address) that you get with your compiler is only valid for that particular compiler with its particular settings. We would expect other compilers to generate different size code.

    But what I want to know is why you expected the function code to only be 8 bytes in size. What did you base that prediction on?
    I was assuming that the function address for an int function pointer should be (actually) 4 bytes in size, and the two pointers would be in adjacent addresses.
    I'm using a clang compiler, and am not sure that I'll be able to understand the assembly code, but I'll take a look to see if there's an .s program in my directory.
    Oddly enough, the 16 byte difference arises when I use 'make' cmd, while if I use 'clang' cmd, I get only a 10 byte difference in addresses for the two pointers.
    I guess that this question that I'm asking, as well as the explanations regarding it, may simply be way above my level of understanding, and doing nothing to help in my learning C programming.
    Last edited by atlantis43; September 16th, 2013 at 01:19 PM. Reason: addnl info
  14. #8
  15. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,255
    Rep Power
    2222
    The function addresses are the addresses of the machine code for the functions; ie, the entry points into those functions. The distance between entry points of two adjacent functions would depend on the size of the machine code for the first function.

    Here is another function that returns int :
    Code:
    int SocketStart()
    {
        struct sockaddr_in ServAddr; /* Local address */
        WORD wVersionRequested;          /* Version of Winsock to load */
        WSADATA wsaData;                 /* Winsock implementation details */ 
    
    
        /* Winsock DLL and library initialization  */
        wVersionRequested = MAKEWORD(2, 0);   /* Request Winsock v2.0 */
        if (WSAStartup(wVersionRequested, &wsaData) != 0) /* Load Winsock DLL */
        {
            fprintf(stderr,"WSAStartup() failed");
            logger.Add("WSAStartup() failed");
            return -1;
        }
    
        /* Create socket for incoming connections */
        if ((m_servSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
        {
            ReportError("socket() failed");
            return -1;
        }
    
    /* 2. Bind the socket to a specific port with the bind() function.  */
        /* First we construct local address structure containing that port we will bind to*/
        memset(&ServAddr, 0, sizeof(ServAddr));   /* Zero out structure */
        ServAddr.sin_family = AF_INET;                /* Internet address family */
        ServAddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */
        ServAddr.sin_port = htons(m_port);      /* Local port */
    
        /* Then we perform the actual binding operation */
        if (bind(m_servSocket, (struct sockaddr *) &ServAddr, sizeof(ServAddr)) < 0)
        {
            DieWithError("bind() failed");
            return -1;
        }
    
    /* 3. Listen on that port for a client trying to connect to it.  */
        /* Mark the socket so it will listen for incoming connections */
        if (listen(m_servSocket, MAXPENDING) < 0)
            DieWithError("listen() failed");
            
        // we got this far, so report success
        return 0;        
    }
    Obviously, all the code generated by that amount of source code would not take up only 8 bytes. Equally obviously, the entry points of two adjacent functions are not side-by-side in memory.

    I'm using a clang compiler, and am not sure that I'll be able to understand the assembly code, but I'll take a look to see if there's an .s program in my directory.
    I do not know of any compiler that will generate an assembly listing without you asking for one. And while gcc will generate the assembly source in a .s file (when you ask it to), clang very likely uses a different extension. I have to doubt very much whether a .s file exists in that project's directory.

    Read the documentation for clang in order to see what command options it offers. Usually, they will be options that you would add at the command line, and hence also in the makefile. Some IDEs, such as Turbo C, have/had a checkbox in the options dialog for generating an assembly listing. There should also be an option for generating a link map which will give you a file that lists all kinds of information about your program, including the addresses of variables (sometimes) and of functions, plus their sizes. But exactly what options clang offers and what the option switches are are unique to clang, so you need to read clang's documentation for that information.

    Still, there seems to be some confusion as to what exactly you are seeking. Perhaps if you could state it clearly. In particular, are you asking for the size of a memory address (AKA "pointer") or the size of the machine code of a particular function?
  16. #9
  17. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,255
    Rep Power
    2222
    I modified your program to demonstrate a number of things, including the only situation I could think of that would place the addresses of the two functions (ie, their pointers) next to each other:
    Code:
    #include <stdio.h>
    
    // typedef for a pointer to int function that takes an int
    typedef int (*intFn)(int);  
    
    // prototypes for func1 and func2
    int func1(int a);
    int func2(int a);
    
    int main(void)
    {
        // an array of function pointers loaded with the addresses for
        //      func1 and func2.
        intFn  funcTable[] = {func1, func2};
    
        // Display the addresses of the two functions
        // Using both ways to reference those addresses in order
        //      to demonstrate their equivalence and to show that
        //      the function addresses are indeed stored in funcTable.
        printf("func1 address = %p (%p)\n", funcTable[0], func1);
        printf("func2 address = %p (%p)\n", funcTable[1], func2);
        
        // Display the size of an function pointer
        printf("sizeof function address = %d\n", sizeof(intFn));
        
        // Display where the function pointers are stored
        // Note that they are stored adjacently and are 4 bytes apart, 
        //      which is sizeof(intFn)
        printf("Pointer to func1 stored at = %p\n", &funcTable[0]);
        printf("Pointer to func2 stored at = %p\n", &funcTable[1]);
    
        // demonstrate how to call a function using a function pointer
        // included for your edification
        printf("func1(4) = %d\n", (*funcTable[0])(4));
        printf("func2(4) = %d\n", (*funcTable[1])(4));
        
        return 0;
    }
    
    int func1(int a)
    {
        return a + a;
    }
    
    int func2(int a)
    {
        return a * a;
    }
    Here is what I get then I ran it -- WinXP, 32-bit, compiled with MinGW gcc:
    C:TEST>a
    func1 address = 004013E8 (004013E8)
    func2 address = 004013FC (004013FC)
    sizeof function address = 4
    Pointer to func1 stored at = 0022FF78
    Pointer to func2 stored at = 0022FF7C
    func1(4) = 8
    func2(4) = 16

    C:TEST>
    Note the 0x14 (decimal 20) byte difference between the two function addresses. Note also the direct method to obtain the size in bytes of a pointer type and that it verifies against the offset between two pointers stored adjacently in memory (ie, in the funcTable array).

    Here is the assembly listing of the two functions that MinGW gcc generated:
    Code:
    	.align 4
    	.globl _func1
    	.def	_func1;	.scl	2;	.type	32;	.endef
    _func1:
    	pushl %ebp
    	movl %esp,%ebp
    	movl 8(%ebp),%eax
    	movl 8(%ebp),%ecx
    	leal (%ecx,%eax),%edx
    	movl %edx,%eax
    	jmp L5
    	.align 4
    L5:
    	leave
    	ret
    	.align 4
    .globl _func2
    	.def	_func2;	.scl	2;	.type	32;	.endef
    _func2:
    	pushl %ebp
    	movl %esp,%ebp
    	movl 8(%ebp),%edx
    	imull 8(%ebp),%edx
    	movl %edx,%eax
    	jmp L6
    	.align 4
    L6:
    	leave
    	ret
    This shows you exactly what my compiler generated. Though this begs the question of whether what you seek would be here.
  18. #10
  19. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    May 2013
    Posts
    36
    Rep Power
    2
    Originally Posted by dwise1_aol
    I modified your program to demonstrate a number of things, including the only situation I could think of that would place the addresses of the two functions (ie, their pointers) next to each other:
    Code:
    #include <stdio.h>
    
    // typedef for a pointer to int function that takes an int
    typedef int (*intFn)(int);  
    
    // prototypes for func1 and func2
    int func1(int a);
    int func2(int a);
    
    int main(void)
    {
        // an array of function pointers loaded with the addresses for
        //      func1 and func2.
        intFn  funcTable[] = {func1, func2};
    
        // Display the addresses of the two functions
        // Using both ways to reference those addresses in order
        //      to demonstrate their equivalence and to show that
        //      the function addresses are indeed stored in funcTable.
        printf("func1 address = %p (%p)\n", funcTable[0], func1);
        printf("func2 address = %p (%p)\n", funcTable[1], func2);
        
        // Display the size of an function pointer
        printf("sizeof function address = %d\n", sizeof(intFn));
        
        // Display where the function pointers are stored
        // Note that they are stored adjacently and are 4 bytes apart, 
        //      which is sizeof(intFn)
        printf("Pointer to func1 stored at = %p\n", &funcTable[0]);
        printf("Pointer to func2 stored at = %p\n", &funcTable[1]);
    
        // demonstrate how to call a function using a function pointer
        // included for your edification
        printf("func1(4) = %d\n", (*funcTable[0])(4));
        printf("func2(4) = %d\n", (*funcTable[1])(4));
        
        return 0;
    }
    
    int func1(int a)
    {
        return a + a;
    }
    
    int func2(int a)
    {
        return a * a;
    }
    Here is what I get then I ran it -- WinXP, 32-bit, compiled with MinGW gcc:

    Note the 0x14 (decimal 20) byte difference between the two function addresses. Note also the direct method to obtain the size in bytes of a pointer type and that it verifies against the offset between two pointers stored adjacently in memory (ie, in the funcTable array).

    Here is the assembly listing of the two functions that MinGW gcc generated:
    Code:
    	.align 4
    	.globl _func1
    	.def	_func1;	.scl	2;	.type	32;	.endef
    _func1:
    	pushl %ebp
    	movl %esp,%ebp
    	movl 8(%ebp),%eax
    	movl 8(%ebp),%ecx
    	leal (%ecx,%eax),%edx
    	movl %edx,%eax
    	jmp L5
    	.align 4
    L5:
    	leave
    	ret
    	.align 4
    .globl _func2
    	.def	_func2;	.scl	2;	.type	32;	.endef
    _func2:
    	pushl %ebp
    	movl %esp,%ebp
    	movl 8(%ebp),%edx
    	imull 8(%ebp),%edx
    	movl %edx,%eax
    	jmp L6
    	.align 4
    L6:
    	leave
    	ret
    This shows you exactly what my compiler generated. Though this begs the question of whether what you seek would be here.
    I'm embarrassed to say that I just realized that my original question was not meaningful at all. The code I posted was in a section of my text on function pointers, and I was treating func1 & func2 as if they were pointers, and never came across pointers of size 16 bytes.
    Sorry that you all spent so much time trying to deal with my ignorance.
    (I did find the .s file, but the assembly language is way beyond my level of understanding)

IMN logo majestic logo threadwatch logo seochat tools logo