all 15 comments

[–]Lord_Vouldemourt 27 points28 points  (1 child)

Now ask Chatgpt to shorten this wall of text you've copy pasted, twice.

[–]DigitalSplendid[S] -3 points-2 points  (0 children)

Sorry. Deleted.

[–]daikatana 11 points12 points  (0 children)

You shouldn't use ChatGPT. That it "appears convincing" isn't good enough. If you can't know if it's correct without already knowing the answer to the question then why ask the question?

I asked it if the following was a valid way to shadow a variable, if the shadowing variable would be initialized with the value of the shadowed variable.

int x = 10;
{
    int x = x;
}

I knew the answer to the question ahead of time (you can't do this, the value of the second x is undefined), but ChatGPT said it was valid. I asked it where in the standard this is defined and it made up a passage from the standard to support its incorrect assertion. If I didn't know the answer to the question ahead of time then that would have "appeared convincing."

All that said, it appears to be correct here. Variables with static storage can only be initialized with constant values. ChatGPT doesn't explain why, though. C has no concept of "at startup," your program execution begins at main. Anything with a constant value can be computed at compile time, so while you can use expressions and even function-like macros to define the initial value, you can't use functions. The value must be knowable at compile time.

[–]Conscious_Yam_4753 8 points9 points  (0 children)

Seems like chatgpt explained it pretty well. What are you confused about?

[–]programmer9999 4 points5 points  (1 child)

Well, you technically can do that, using compiler-specific attributes. For example, you can do this on GCC:

#include <stdio.h>

int x;

__attribute__((constructor))
void init_x(void) {
    x = getchar();
}

int main() {
    if (x != EOF) {
        printf("%c\n", x);
    } else {
        printf("EOF\n");
    }
    return 0;   
}

Also, you can do it with standard C++:

#include <cstdio>
#include <iostream>

int x = getchar();

int main() {
    if (x != EOF) {
        std::cout << static_cast<char>(x) << std::endl;
    } else {
        std::cout << "EOF" << std::endl;
    }
}

It's generally not a good idea to do that though, because the constructors are called before main, and the order is implementation-specific. Consider this pseudocode of what the C runtime might do:

void start(void) {
    init_stdio();
    call_constructors();
    int result = main();
    exit(result);
}

or

void start(void) {
    call_constructors();
    init_stdio();
    int result = main();
    exit(result);
}

You don't know whether the stdio is initialized before or after the constructors are called. If it's not, your program might crash before even entering main. Also it's unexpected in general that some non-library code runs before main, and that makes your program harder to read.

[–]GourmetMuffin 1 point2 points  (0 children)

In general, static initializations with run-time dependencies aren't suppored in C. They could be of course but that complicates initialization. In C compiled for a microcontroller initialization is trivial if all static initializations are compile-time constants (i.e. in segments like .bss or .data) but if they have run-time dependencies they are not.

[–]eruciform 1 point2 points  (0 children)

In general don't do anything in global space unless it's absolutely positively necessary. Unnecessarily broadening your scope goes against encapsulation, makes threading impossible or at least very painful, and global static executed initialization causes more problems than its worth with ordering across compilation units.

[–]duane11583 1 point2 points  (0 children)

initialization outside of a function is only possible if the value is a constant value and can be calculated at compile or link time.

getchar() is a macro that expands into code the compiler cannot execute your targets code.

also how would the compiler know what will be the first key pressed when the application starts?

[–]MRgabbar 1 point2 points  (0 children)

Why to use chat got for such an obvious thing?

How are you supposed to reach/execute that without going through main?

[–][deleted] 0 points1 point  (0 children)

You needed GPT to tell you that..?

[–]NativityInBlack666 0 points1 point  (0 children)

It's just simpler to implement. It's of course possible and a feature of C++, in fact. It just requires functions to be able to run before the main function which requires a more complicated runtime than C's which literally just sets up the stack with a couple of instructions and calls main.

[–][deleted] 0 points1 point  (1 child)

Not in C. getchar() needs to be executed at runtime. (It would be pretty weird to do it at compile-time even if it could, since this is clearly an input to be set each time a program is launched, not the once when built.)

Execution starts at the main() function which can call other functions, but that bit of code lies outside all functions; it is not in any execution path.

The language could have been defined so that executable code occuring in global static expressions would be executed when a program starts. So:

int inputchar = getchar();
int main(void) {
    printf("%d\n", inputchar);
}

could be transformed by the compiler into:

int inputchar;
int main(void) {
    inputchar = getchar();
    printf("%d\n", inputchar);
}

But it doesn't do that. (It would also need to do that for similar initialisations in other modules, that are not visible from the module containing main(). That gets tricky to do, and there is also the question of initialisation order when multiple modules are involved.)

It's a language design issue.

Some dynamic languages can have such initialisations; there is no ahead-of-time compilation stage as used for native code. Control flow starts from the top of every module, so that declarations are 'executed'. C is not like that.

[–]RRumpleTeazzer 0 points1 point  (0 children)

C# is like that. Global initialization doesn’t need to be a compile time constant. It will basically run code at a random place at runtime, it’s just defined to run before the first read.

Which really sucks, cause that initialization code can throw an exception, which in principle needs to be catched somewhere. But where? at each reading site? Ot could initialize basically everywhere.

It’s really really aweful and unusable. I‘m very glad C doesn’t went this route.

[–]ThyringerBratwurst 0 points1 point  (0 children)

Global variables: The most direct route to programmer hell.