0

Why does the return value of the function change when the number of bytes of malloc is large?

func.c:

#include "func.h"

int *func()
{
    int *ptr = (int *)malloc(100000000);
    printf("ptr in func is %p \n", ptr);
    return ptr;
}

func.h:

#ifndef _FUNC_H
#define _FUNC_H
#include <stdio.h>
#include <stdlib.h>
//int *func();
#endif

main.c

#include <stdio.h>
#include "func.h"

int main(int argc, char** argv)
{
    int *ptr = func();
    printf("ptr is %p \n", ptr);
}

compilation and run:

gcc -g main.c func.c -o main

the compilation result is:

main.c: In function ‘main’:
main.c:7:13: warning: implicit declaration of function ‘func’; did you mean ‘putc’? [-Wimplicit-function-declaration]
  int* ptr = func();
             ^~~~
             putc
main.c:7:13: warning: initialization makes pointer from integer without a cast [-Wint-conversion]

run result:

ptr in func is 0x7f557ea88010
ptr is 0x7ea88010

Why does the function return the same value when the number of bytes of malloc is small?

func.c

#include "func.h"

int *func()
{
    int *ptr = (int *)malloc(100);
    ptrintf("ptr in func is %p \n", ptr);
    return ptr;
}

run result:

ptr in func is 0x17af2a0
ptr is 0x17af2a0

Can you tell me why? I want to know what exactly caused it?

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Da Wang
  • 100
  • 7
  • 1
    Please don't link to images of your code but cpy the code properly firmatted into your question – Ingo Leonhardt Jul 18 '23 at 12:38
  • ok, I don't know much about it, i will show my code – Da Wang Jul 18 '23 at 12:40
  • @DaWang _" Why does the function return the same value when the number of bytes of malloc is small"_? You cannot predict what value `malloc` returns and you should't care about it. It depends on the implementation. If you really want to know, look at the source code of malloc of your platform (actually don't, it's way beyond your scope for the moment). – Jabberwocky Jul 18 '23 at 12:55
  • 1
    Any particular reason why the declaration in func.h is commented out? – dbush Jul 18 '23 at 13:10
  • 1
    No particular reason, just want to repeat the work encountered this problem. – Da Wang Jul 18 '23 at 13:13
  • You commented out the declaration of `func` here `//int* func();`. Then you're using the undeclared `func` here: `int* ptr = func();` hence the warning (which BTW should be considered as an error). Consider all warnings containing the word "implicit" as an error. – Jabberwocky Jul 18 '23 at 13:15
  • I not only want to know how to solve, but also want to know what caused the return of the address inconsistency problem. – Da Wang Jul 18 '23 at 13:18
  • Always the same. https://godbolt.org/z/T1dKo9zYf – 0___________ Jul 18 '23 at 13:18
  • Note that you should not, in general, create function, variable, tag or macro names that start with an underscore. Part of [C11 §7.1.3 Reserved identifiers](https://port70.net/~nsz/c/c11/n1570.html#7.1.3) says: — _All identifiers that begin with an underscore and either an uppercase letter or another underscore are always reserved for any use._ — _All identifiers that begin with an underscore are always reserved for use as identifiers with file scope in both the ordinary and tag name spaces._ See also [What does double underscore (`__const`) mean in C?](https://stackoverflow.com/q/1449181) – Jonathan Leffler Jul 18 '23 at 14:32

2 Answers2

5

You're obviously working on 64 bit system with sizeof(int *) == 8 and sizeof(int) == 4. Now when the function prototype is missing ("implicit declaration") the compiler assumes that func() returns int and so when you call int *ptr = func() only 4 bytes (sizeof(int)) of the return value are used, interpreted as int and cast back to int *. That results in the wrong ptr in the first example.

Int the second example, by chance the pointer value is smaller so it fits completely in 4 bytes and after being cast back, the result ist the same pointer again. Nevertheless it's Undefined Behaviour and you should really always treat the warning "implicit declaration" as an error and provide all necessary prototypes.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
Ingo Leonhardt
  • 9,435
  • 2
  • 24
  • 33
  • I get it. Thank you. – Da Wang Jul 18 '23 at 13:45
  • But I didn't give you a prototype, so why is it possible to find the func function that needs to be run when it's running? – Da Wang Jul 18 '23 at 13:45
  • 2
    @DaWang without the prototype the compiler wrongly assumes that the function as been declared `int func();` and it emits a warning that is actually an error (yes, it's weird, it for historical reasons). As already mentioned, consider warnings containing the word "implicit" as errors. – Jabberwocky Jul 18 '23 at 13:48
  • 2
    Well the function exists and so the linker can produce an executable. And the compiler for historical reasons assumes `int func();` (function with unknown parameters returning int) if he sees a call to a function that's not declared. – Ingo Leonhardt Jul 18 '23 at 13:48
  • @JabberwockyI see. Thank you for your patience – Da Wang Jul 18 '23 at 13:55
  • 2
    Note that "no prototype before calling" is only valid in C90 (not in C99, not in any later version of the standard). The OP should be compiling to at least C99 (almost 21st Century C), preferably C11 or C18. (It's a bit too soon to be using C23 for production work.). The OP needs to use more stringent compiler warnings too — preferably something like `-Werror -Wall -Wextra` as a starting point if the compiler is GCC or Clang (I'd add a few more, but that's a reasonable start). – Jonathan Leffler Jul 18 '23 at 14:35
3

You must declare functions before you use them.

Corrected func.c

#include <stdio.h>   // put includes you use in the .c file
#include <stdlib.h>  // put includes you use in the .c file
#include "func.h"

int* func(void)  // put void, so the compiler knows there are no
                 // arguments
{
   int* ptr = malloc(100000000);   // remove the (int*) cast, it's
                                   // not wrong, but useless
   printf("ptr in func is %p \n", ptr);
   return ptr;
}

Corrected func.h:

#ifndef _FUNC_H
#define _FUNC_H
// remove this (not wrong but bad practice) #include <stdio.h>
// remove this (not wrong but bad practice) #include <stdlib.h>

int* func(void);   // uncomment this and put void, so the compiler 
                   // knows there are no arguments

#endif

Corrected main.c

#include <stdio.h>   // put includes you use in the .c file
#include "func.h"

int main(int argc, char** argv)
{
    int* ptr = func();
    printf("ptr is %p \n", ptr);
}

Following happens when you don't declare func prior to using it:

int* ptr = func();

As func() has not been declared, the compiler assumes (wrongly) that func returns an int (it actually returns a pointer to int). The function is called, the returned pointer in converted into an int possibly truncating the value from 64 bits to 32 bis (it depends on the platform).

If the returned address fits into 32 bits (which may be the case on certain platforms for small allocations), no truncation takes place.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115