1

I have one participant in a basic C course who created a mystery. The task is to write a function double square_to(double *) which just squares the number in-place and also returns the result.

Stripped down, the code looks like this:

main.c:

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

int main() {
    double t = 5;
    double *p;
    double m;
    p = &t;
    m = square_to(p);
    printf("%f\n%f\n", t, m);
    return 0;
}

mymath.h:

#ifndef MYMATH_H
#define MYMATH_H

double square_to(double *p);

#endif

mymath.c:

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

double square_to(double *p) {
    *p = (*p) * (*p);
    return *p;
}

Compile flags are:

-Wall -pedantic -o day5 main.c mymath.c

I do not see anything wrong with it. It also works just fine with Clang and GCC on his Ubuntu and my Fedora. The program outputs 25.000 twice in each case.

The part where it gets strange is when his original files are used. There are a couple of other functions from other parts of the course in the mymath.c. Those function compile apparently. In the main.c, his GCC 5.4 warns about the implicit declaration of square_to. When running the program, it outputs 25.000 and some random number which appears to be an integer. Running it again outputs a different number. Therefore this is no casting problem, it is a real undefined behavior.

Compiling the same code with Clang (just exchanging gcc with clang in the command line, -Wall and -Wpedantic active), it works just fine, giving 25.000 in every case. This is a red flag as standard compliant code should compile just fine with both compilers. On my Fedora installation it works with GCC 6.2.1.

The full code is in this gist.

I have no idea why the full code changes the behavior of the square_to function. What is wrong with the full program?


Update

Before I left the tutorial I asked the student to download the code in the gist and compile it. He did so with gcc -Wall -pedantic main.c mymath.c and it compiled without errors. Running it gave 25.000 25.000 as it should be.

So the problem is nowhere in any code that I have. There has to be something very strange in the directory that he works from. I have double checked the path in the editor and the terminal, I also did a less main.c and it was the correct one.

There is no way to answer this question now as I don't have anything to reproduce the problem. The participant also had various other similar problems. See those here:

enter image description here

There it can be seen that a function somehow is called with double * although there are only int in the functions. Then gcc points to a declaration which supposedly conflicts and it is the very same prototype.

I'm for closing this question because nothing more can be learned without having access to the particular environment that the participant had. He just said that he would start in a new directory tomorrow. If it still persists, I will collect the whole directory.

Martin Ueding
  • 8,245
  • 6
  • 46
  • 92
  • 1
    can you share all the flags you are passing to the compiler (or even the entire invocation of the compiler)? – Peter Varo Oct 10 '16 at 13:30
  • Probably didn't add mymath.c when compiling. – Lundin Oct 10 '16 at 13:31
  • @Lundin normally that would rear as a link-time *error*, not an implicit declaration *warning*. The code eventually builds, but does so without the proper decls within mymath.h (apparently). – WhozCraig Oct 10 '16 at 13:34
  • `mymath.c` cannot possibly compile with an _implicit declaration of square_to_ warning with the files you show here. Are you sure that the GCC 5.4 where this happens is configured correctly and are you sure that the `mymath.h` file you show here is _actually_ the `mymath.h` that is included? – Jabberwocky Oct 10 '16 at 13:43
  • 3
    Make sure the header you *think* is pulled in is actually the header pulled in on the platform where you're warned-and-fail. Drop a `#error Included this header` inside the header fencepost of `mymath.h` and recompile. If the preprocessor doesn't puke, than than these aren't the droids you're looking for (that isn't the `mymath.h` header being pulled in). Related, the *exact* and *complete* build command for your project should be part of your question. – WhozCraig Oct 10 '16 at 13:50
  • 1
    Check for typos. Is the code you posted **exactly** the one the program in question was built from? Do a `grep` on both files for the name or `diff`. – too honest for this site Oct 10 '16 at 13:52
  • Not related to your problem, but `m = square_to(p)` can be replaced by `m = square_to(&t)`. – Jabberwocky Oct 10 '16 at 13:52
  • 2
    The only possible explanation I can see that fits all the symptoms (implicit declaration warning and seemingly random return value from the function while the double pointed to was correctly modified) is that you have a mymath.h header somewhere in the system that got pulled in instead of the mymath.h in the gist. A header that only gcc knows to find while clang doesn't know about its path. How exactly do you compile this? – Art Oct 10 '16 at 14:03
  • I added the compilation flags. It is just a simple three files, and a single compiler call in the same directory. The participant copied the files to a thumb drive and I have uploaded them in the gist. – Martin Ueding Oct 10 '16 at 14:11
  • 1
    FWIW, I cannot get your [gist](https://gist.github.com/martin-ueding/74b80d96a5cc4ac593b33d11901677af) to issue any kind of warning (with `-Wall` and `-Wpedantic` set) for any of the versions of GCC on my Debian "testing" machine: "gcc-4.9 (Debian 4.9.3-14) 4.9.3", "gcc-5 (Debian 5.4.1-1) 5.4.1 20160803", and "gcc-6 (Debian 6.1.1-11) 6.1.1 20160802". – Ian Abbott Oct 10 '16 at 14:13
  • 1
    @MartinUeding I can almost bet that the wrong include is pulled in somehow. Run `gcc -E main.c` and check the output. – Art Oct 10 '16 at 14:21
  • As an addendum to my comment above about GCC versions on my Debian "testing" machine, I tried it on an Ubuntu 14.04.5 machine with GCC 4.8 ("gcc (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4") and got a whole bunch of warnings about "ISO C90 forbids mixed declarations and code". But adding `-std=c11` fixed those. – Ian Abbott Oct 10 '16 at 14:23
  • Post the relevant code here that re-creates the problem. – chux - Reinstate Monica Oct 10 '16 at 16:07
  • Use the `gcc -H` option to list the actual header files that are read (and maybe but probably not `gcc -v` to see what is being executed). The chances are that the list of headers will tell you about what is going on. – Jonathan Leffler Oct 10 '16 at 16:39

1 Answers1

1

From: Implicit function declarations sometimes work in C?

If the expression that precedes the parenthesized argument list in a function call consists solely of an identifier, and if no declaration is visible for this identifier, the identifier is implicitly declared exactly as if, in the innermost block containing the function call, the declaration

extern int identifier();

appeared.

Since main.c thinks the function should return an int, that is what you get (later cast to a double in the assignment).

On Intel and AMD processors running Linux, integers are returned in rax and doubles are returned in the floating point registers. So my guess is that in this case, the integer you are seeing is whatever happened to be in the rax register when the function returned.

But this is just a guess, undefined behaviour is undefined.


Since there seems to be some confusion: If you get a warning about implicit declarations, that means that GCC didn't see any valid declarations before the usage. Either that, or your compiler is broken.

Properly declaring int-returning functions in C<99 is optional but highly recommended.

Community
  • 1
  • 1
jforberg
  • 6,537
  • 3
  • 29
  • 47
  • 2
    The OP knows this. The question is *why*, in their as-of-yet-apparently-secret build configuration, the proper prototype for `square_to`, is *not* pulled in when `#include "mymath.h"` is present. – WhozCraig Oct 10 '16 at 13:38
  • 1
    "GCC 5.4 warns about the implicit declaration of square_to". If there **was** an explicit declaration, GCC wouldn't be giving this warning. The reasonable conclusion is that there is **no** valid declaration of square_to in that case. – jforberg Oct 10 '16 at 13:42
  • 1
    @jforberg, yes, we pretty much all agree that the compiler must not be seeing a declaration for the function, and we agree that that likely accounts for the observed misbehavior. But that does not answer the question of why no declaration is seen when the program appears to be including a header that provides just such a declaration. *That's* the question, and you haven't answered it. – John Bollinger Oct 10 '16 at 15:11
  • @JohnBollinger: I think you are missing an important point. When he runs the code **in the question**, it does work. However, when his student compiles **some other code**, he gets a warning about implicit declarations. – jforberg Oct 10 '16 at 15:19
  • @jforberg, I am not missing anything. The OP asserts that the code he does present is representative of the student's original code (he presents a "stripped down" version), and indeed the gist, though not technically part of the question, supports this. Nobody, including the OP, fails to understand the need for an explicit declaration of the function. – John Bollinger Oct 10 '16 at 15:45
  • @JohnBollinger Then I don't understand what you mean? Possibly the question should be closed as "no longer reproducible". My answer attempts to answer the limited question of "why does implicit declarations cause these particular problems". I don't see what else there is to answer currently. With respect and no offense intended, I wrote this answer on the theory that it might be useful. If you disagree, your recourse is to use the downvote button. – jforberg Oct 10 '16 at 16:04
  • @jforberg, indeed, it may not be a question that can or should be answered in its present state. As such, closing it might indeed be the best course. In any event, if yours were an appropriate answer for it, then certainly the best course would be to close it as a dupe, not to answer it. – John Bollinger Oct 10 '16 at 17:14