Why does this compile without errors? And why does it run on a 32-bit machine but not a 64?

StackOverflow https://stackoverflow.com/questions/23574551

  •  19-07-2023
  •  | 
  •  

Question

I am able to compile this with gcc using the -Wextra -g flags and I get no errors. Why does the compiler not catch that I don't return anything from a non-void function? Also, I've run this on a 32-bit and a 64-bit machine. It ran perfectly fine on the 32-bit, and even printed 'Apple stem length 10. Apple color r'. But it seg faults on the 64-bit machine. What is gcc doing here that gives me such unpredictable behavior and why is it not giving me any errors at compile time?

#include <stdlib.h>
#include <stdio.h>

struct apple
{
    int stem_length;
    char color;
};

struct apple* alloc_apple(const int stem_length, const char color)
{
    struct apple* new_apple = (struct apple*)malloc(sizeof(struct apple));
    new_apple->stem_length = stem_length;
    new_apple->color = color;
}

int main(int argc, char* argv[])
{
    struct apple* some_apple = alloc_apple(10, 'r');
    printf("Apple stem length %d. Apple color %c\n", some_apple->stem_length,
        some_apple->color);

    free(some_apple);
    return 0;
}
Was it helpful?

Solution

alloc_apple fails to return new_apple; at the end. This may work by coincidence if new_apple happens to be left lying around in the register the return value is expected in when the function returns (on i386 this is usually EAX), but allowing control to reach the end of a non-void returning function produces undefined behavior (which may include anything from accidentally working as expected to causing demons to fly out of your nose).

I am fairly certain that if you compile with gcc -Wall (enable all warnings) it would point out the issue; return-type is the specific warning.

The function wouldn't even work for compilers that use the ancient convention of returning the value of the last expression evaluated if no return is present, since the last expression evaluates to the value of color.

OTHER TIPS

You are using -Wextra, but this is wrong option.

Gcc (and most unix compilers) uses -Wall option to enable most warnings, including the one you want to see, the -Wreturn-type: http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

-Wall

This enables all the warnings about constructions that some users consider questionable, and that are easy to avoid (or modify to prevent the warning), even in conjunction with macros. -Wall turns on the following warning flags:

... list of -Wreturn-type and 30 more -W... flags

And your -Wextra only enables several additional warnings which are not turned on by -Wall.

-Wextra

This enables some extra warning flags that are not enabled by -Wall. ... list of 12 rare -W... options

Example:

$ gcc t.c -Wextra
# No output
$ gcc t.c -Wall
t.c: In function ‘alloc_apple’:
t.c:15:1: warning: control reaches end of non-void function [-Wreturn-type]
 }
 ^

So, just use "-Wall" to get most warnings, -Wall -Wextra to get more. And sometimes you can also use set -Wall -Wextra -std=c99 -pedantic -pedantic-errors to be sure that your program is very close to C99 standard (but system headers have non-standard code and this is not caught).

Also use clang, it has better warnings and better warnings set by default:

$ clang t.c
t.c:15:1: warning: control reaches end of non-void function [-Wreturn-type]
}
^
1 warning generated.

The issue is that you're failing to return a value from a function.

This is undefined behavior. It just "seems to work" on 32-bit, and it crashes on 64-bit. If you're curious why it's failing, the first step is to get a stack trace.

As to "why didn't GCC tell me?"

It can:

http://gcc.gnu.org/onlinedocs/gcc-3.4.3/gcc/Warning-Options.html

-Wreturn-type

Warn whenever a function is defined with a return-type that defaults to int. Also warn about any return statement with no return-value in a function whose return-type is not void.

For C++, a function without return type always produces a diagnostic message, even when -Wno-return-type is specified. The only exceptions are main and functions defined in system headers.

Personally, I almost always use these switches during development:

gcc -g -pedantic -Wall ...

Errors? What errors?

"Not returning anything from a non-void function" has never been considered a formal "error" (i.e. a constraint violation) in C. (BTW, it is not an "error" in C++ either). It is just how the language is defined. Some compilers might issue a warning, but in general case they are not allowed to treat this as a constraint violation.

As a side note, the difference between C and C++ in that regard is that in C++ forgetting to return something is always undefined behavior. In C the behavior is undefined only if you attempt to use the function's result in the calling code. Otherwise, if the returned value is ignored by the caller, C sees it as perfectly normal legal code.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top