Author Topic: can't figure out difference between declaration and definition  (Read 4192 times)

0 Members and 1 Guest are viewing this topic.

Offline Nominal Animal

  • Super Contributor
  • ***
  • Posts: 6264
  • Country: fi
    • My home page and email address
Re: can't figure out difference between declaration and definition
« Reply #50 on: January 13, 2023, 08:27:58 pm »
Because you broke my believability, I now demand you to come up with a good example of the difference between declarations and definitions!  8)
"Declarations and definitions" of what specifically?
The target here is to provide OP with an intuitive understanding of the difference between 'declaration' and 'definition'.
In my post, I provided the short snippet from the C11 standard that best describes my own intuitive approach.

I was hoping you could think of something both technically correct, and easily understood.  (As you know, we tend to have a bit different approach to these things.  The different viewpoints might yield different ideas for what is a suitable example here.)

Here's the Collatz conjecture example:

We want to experiment with the Collatz conjecture, printing '^' for each up+down step pair (x ← 3x+1 followed by x ← x/2) and 'v' for each down step (x ← x/2), plus the number of steps needed to reach 1.
Code: [Select]
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

// We declare the two functions so that in their definition, they can recursively call each other.
// It is common to include the types of parameters, but omit the parameter names.
static uintmax_t  collatz_up(FILE *, uintmax_t);
static uintmax_t  collatz_down(FILE *, uintmax_t);

// The actual function.
uintmax_t  collatz(FILE *out, uintmax_t x)
{
    uintmax_t  n;

    if (x < 2) {
        // x == 0 is invalid, and x == 1 is zero-length sequence.
        n = 0;
    } else
    if (x & 1) {
        // x is odd, so we go up.
        n = collatz_up(out, x);
    } else {
        // x is even, so we go down.
        n = collatz_down(out, x);
    }

    if (out) {
        fputc('\n', out);
    }
    return n;
}

static uintmax_t  collatz_up(FILE *out, uintmax_t x)
{
    // Since x is odd, (3*x) is odd, and (3*x+1) is even.
    // Thus, each ^ step is always followed by a v step,
    // and we actually have (3*x+1)/2 = x + (x+1)/2.

    if (out) {
        fputc('^', out);
    }

    uintmax_t  new_x = x + (x + 1)/2;
    if ((uintmax_t)((2*new_x - 1) / 3) != x) {
        // Overflow occurred
        if (out) {
            fputc('!', out);
        }
        return 0;
    }

    if (new_x < 2) {
        return 2;
    } else
    if (new_x & 1) {
        return 2 + collatz_up(out, new_x);
    } else {
        return 2 + collatz_down(out, new_x);
    }
}

static uintmax_t  collatz_down(FILE *out, uintmax_t x)
{
    if (out) {
        fputc('v', out);
    }

    uintmax_t  new_x = x / 2;

    if (new_x < 2) {
        return 1;
    } else
    if (new_x & 1) {
        return 1 + collatz_up(out, new_x);
    } else {
        return 1 + collatz_down(out, new_x);
    }
}

int main(int argc, char *argv[])
{
    const char *arg0 = (argc > 0 && argv && argv[0] && argv[0][0]) ? argv[0] : "(this)";
    if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s [ -h | --help ]\n", arg0);
        fprintf(stderr, "       %s N [ N ... ]\n", arg0);
        fprintf(stderr, "\n");
        fprintf(stderr, "This outputs the number of Collatz conjecture steps from N to 1\n");
        fprintf(stderr, "to standard output, after printing a ^ for each up+down step pair\n");
        fprintf(stderr, "and v for each down step, to standard error.\n");
        fprintf(stderr, "\n");
        return (argc == 2) ? EXIT_SUCCESS : EXIT_FAILURE;
    }

    for (int arg = 1; arg < argc; arg++) {
        uintmax_t  val, n;
        char      *end = argv[arg];

        errno = 0;
        val = strtoumax(end, &end, 0);
        if (errno || end == argv[arg] || *end != '\0' || val < 1) {
            fprintf(stderr, "%s: Invalid N.\n", argv[arg]);
            return EXIT_FAILURE;
        }

        printf("%ju: ", val);
        fflush(stdout);
        n = collatz(stderr, val);
        fflush(stderr);
        printf("%ju steps\n", n);
        fflush(stdout);
    }

    return EXIT_SUCCESS;
}
If we compile that into ./ex, using for example gcc -Wall -O2 above.c -o ex, then running
    ./ex 1161 703
outputs
Code: [Select]
1161: ^v^^^v^^^^^^v^^^^^^v^vvv^v^^v^^vvv^vv^vv^^v^v^vv^^^^^vvv^^v^^^v^^^^v^vv^^^v^^v^^^^^^vv^^^^vvv^v^v^vvv^vv^^^vvvv^vvv
181 steps
703: ^^^^^^v^v^^^^^^v^^v^v^^^vv^vvv^^^^v^^^^^v^^^v^^^vv^v^vv^^^v^^^^vvvv^^^^^vvv^v^vvvvv^vv^^v^^vvv^^vv^^v^vv^vvv
170 steps

Alternatively, ./ex 1161 703 >/dev/null shows only the standard output part:
    1161: 181 steps
    703: 170 steps

This is not the most sensible way to implement such a Collatz conjecture exploration program, but it shows how declaring things that are defined later, works.

The declaration itself does not generate any code or reserve any memory, it only tells the compiler enough to know how to refer to/access/call that thing.
The definition is what generates code or reserves memory.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14476
  • Country: fr
Re: can't figure out difference between declaration and definition
« Reply #51 on: January 13, 2023, 09:00:48 pm »
For sure, a function prototype just declares the prototype. It doesn't require any kind of 'extern' qualifier, and adding one usually shows a poor understanding of the C language.

Not necessarily - one can use the keyword to communicate the intent to a human reader. Then it's more a question about taste than understanding of the language.

Ah, coding style. It's like colors. Very subjective.

To me it doesn't convey any additional meaning, as a prototype with or without 'extern' is exactly the same thing (as opposed to variables!). But I can understand why it could for some other people. Just down to style and habits.

The problem I was pinpointing, as far as style goes, is that it can be either a sign of poor understanding of C (so a kind of cargo cult programmig), or something intentional (as with what you said here, although the conveyed intention, while clear to you, may not be to others.) So any coding style that can ambiguously show either cargo cult or explicit intention is not really my favorite. But that again has a pretty subjective side to it.

 

Offline TheCalligrapher

  • Regular Contributor
  • *
  • Posts: 151
  • Country: us
Re: can't figure out difference between declaration and definition
« Reply #52 on: January 14, 2023, 03:55:59 am »
For sure, a function prototype just declares the prototype. It doesn't require any kind of 'extern' qualifier, and adding one usually shows a poor understanding of the C language.

Not necessarily - one can use the keyword to communicate the intent to a human reader. Then it's more a question about taste than understanding of the language.

Can you describe that intent in more specific terms? What spectifically does it communucate?
 

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8172
  • Country: fi
Re: can't figure out difference between declaration and definition
« Reply #53 on: January 14, 2023, 06:46:55 am »
Can you describe that intent in more specific terms? What spectifically does it communucate?

I already did in reply #37. It might be bad taste, but there you are, anyway.

Or, one could always use extern in all function declarations. Then it would communicate: "I'm a logical thinker and prefer explicit over implicit, even when language rules are clear about the implicit. Because variables need to be explicitly declared using extern, I follow the exact same logic for functions, using the exact same keyword." I don't do that myself, but I would totally understand if someone did that.
 
The following users thanked this post: newbrain, DiTBho

Offline TheCalligrapher

  • Regular Contributor
  • *
  • Posts: 151
  • Country: us
Re: can't figure out difference between declaration and definition
« Reply #54 on: January 14, 2023, 07:37:47 pm »
Or, one could always use extern in all function declarations. Then it would communicate: "I'm a logical thinker and prefer explicit over implicit, even when language rules are clear about the implicit. Because variables need to be explicitly declared using extern, I follow the exact same logic for functions, using the exact same keyword." I don't do that myself, but I would totally understand if someone did that.

I don't understand this "implicit vs. explicit" logic when it comes to "import" semantic of `extern` keyword.

When it is used with variable declarations, it is not about "implicit vs. explicit" at all. It is used to turn a variable definition into a non-defining declaration. It is a fundamental change, not a case of "implicit vs. explicit". C does not offer "prototypes" for variables. That's why we are forced to use `extern` with variables when we want to make a non-defining declaration.

When it is used with function prototypes, it changes nothing, but I still don't get the "implicit vs. explicit" part. There's nothing "implicit" about function prototype. The only purpose function prototype serves is to provide a non-defining declaration for a function. It is already as explicit as it gets. Adding `extern` does not make it more explicit.

The above also illustrates that variables significantly different from functions w.r.t. pre-declaration methods offered by the language. Why would one want to "follow the exact same logic" for such drastically different things? Using `extern` in both contexts does not bring about any kind of uniformity: function declarations and variable declarations are still fundamentally different.

---

The only context where `extern` can be used to make something explicit is its "export" semantics, i.e. when you apply `extern` to a definition (function or variable):

Code: [Select]
/* File scope */

extern int a = 42;
/* This is a variable definition. We explicitly give `a` external linkage, even though it'd have it anyway */

extern void foo(void) {}
/* This is a function definition. We explicitly give `foo` external linkage, even though it'd have it anyway */

The above would be an example of "implicit vs. explicit". But that's apparently not what you are talking about.
« Last Edit: January 14, 2023, 07:48:42 pm by TheCalligrapher »
 
The following users thanked this post: Siwastaja, SiliconWizard

Online Siwastaja

  • Super Contributor
  • ***
  • Posts: 8172
  • Country: fi
Re: can't figure out difference between declaration and definition
« Reply #55 on: January 14, 2023, 07:42:47 pm »
Or, one could always use extern in all function declarations. Then it would communicate: "I'm a logical thinker and prefer explicit over implicit, even when language rules are clear about the implicit. Because variables need to be explicitly declared using extern, I follow the exact same logic for functions, using the exact same keyword." I don't do that myself, but I would totally understand if someone did that.

But C does not offer "prototypes" for variables. That's why we are forced to use `extern` with variables when we want to make a non-defining declaration. Meanwhile, functions in C do have prototypes. So, this already makes variables significantly different from functions w.r.t. pre-declaration methods offered by the language. Why would one want to "follow the exact same logic" for such drastically different things? Using `extern` in both contexts does not bring about any kind of uniformity: function declarations and variable declarations are still fundamentally different.

I don't find them that different. Declaring a variable
extern volatile float var;
tells the reader (and compiler) that somewhere exists a variable with this name, this type and these qualifiers.

Declaring a function
(extern) int func(const char arg);
tells the reader (and compiler) that somewhere exists this function with this name, this return value type and these argument types with these qualifiers.

Same logic could be followed even if functions and variables of course are not the same thing. The use of word "prototype" for functions but "extern" for variables is just an arbitrary name choice.
« Last Edit: January 14, 2023, 07:44:56 pm by Siwastaja »
 

Offline IDEngineer

  • Super Contributor
  • ***
  • Posts: 1926
  • Country: us
Re: can't figure out difference between declaration and definition
« Reply #56 on: January 14, 2023, 08:10:30 pm »
I continue to agree with Siwastaja's approach. But this is approaching pedantry. As many friends will attest, I'm often a big fan of pendantry {grin} but in this case I favor clarity, even if it violates some notion of purity.

Using extern with variables and functions communicates what's important to both humans and compilers: There's an entity out there with this name and these characteristics, and you'll learn its actual location at link time, but for now you can generate code based on the attributes described in the extern. Yes, according to K&R one is considered a prototype and the other is not. But when you see "extern" in a header file, you (and the compiler) know exactly what is being communicated. That's worth a lot.

YMMV, just my $0.02, etc. Neither approach is broken.

EDIT: Here's another detail likely to generate controversy. When prototyping a function, do you leave in the parameter names? Do you write:

extern void myFunction(int iValue1,float fValue2);

...or:

extern void myFunction(int,float);

...?
« Last Edit: January 14, 2023, 08:13:02 pm by IDEngineer »
 

Offline sokoloff

  • Super Contributor
  • ***
  • Posts: 1799
  • Country: us
Re: can't figure out difference between declaration and definition
« Reply #57 on: January 14, 2023, 09:04:57 pm »
I do. I know they’re ignored, but there’s documentation value in including them.
 
The following users thanked this post: newbrain

Offline madires

  • Super Contributor
  • ***
  • Posts: 7765
  • Country: de
  • A qualified hobbyist ;)
Re: can't figure out difference between declaration and definition
« Reply #58 on: January 14, 2023, 09:48:42 pm »
In my case I add 'extern' to a function declaration to indicate that the function is in some other file. When the function declaration is meant to be a forward declaration I keep it as it is. This helps me tell them apart more easily.
 

Online SiliconWizard

  • Super Contributor
  • ***
  • Posts: 14476
  • Country: fr
Re: can't figure out difference between declaration and definition
« Reply #59 on: January 14, 2023, 10:11:04 pm »
I do. I know they’re ignored, but there’s documentation value in including them.

Yes, indeed. Plus, unless you're a masochist, you usually copy&paste prototypes from your function definitions, so in order to have no parameter name, you'll need to manually remove them, extra pain for making things less readable. To each their own, as they say, I suppose.
 
The following users thanked this post: newbrain


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf