C: Behaviour of the `const` keyword
Asked Answered
A

7

7

I've been told that if I'm coding in ANSI C to declare in the order that the variables will be used, assert that pointers are not null and that indices are within bounds, and to initialize just before usage of the variable.

If I declare a const can I initialize it after a block of assertions and code?

In Java final initializations must occur at declaration, yet is it consistent through ANSI C implementations that I can initialize a const once, but not necessarily at the time of declaration?

Anywhere answered 13/9, 2009 at 10:21 Comment(0)
S
9

The Java compiler has a small amount of flow logic to allow you to initalise final variables after their declaration. This is legal Java:

final int something;

if ( today == Friday )
    something = 7;
else
    something = 42;

Java will detect if any branches leave the final value undefined. It won't analyse the conditions, so this is not legal Java, even though it's logically similar:

final int something;

if ( today == Friday )
    something = 7;

if ( today != Friday )
    something = 42;

In ANSI C89, const variables ( other than extern ) must be initialised in the statement they are declared in.

const int something = ( today == Friday ) ? 7 : 42;

The extern modifier on a declaration tells the compiler that the variable is initialised in a different complation unit ( or elsewhere in this compilation unit ).

In ANSI C99, you can mix declarations and code, so you can declare and initialise a const variable after a block of assertions and code. Portability of 1999 ANSI C remains an issue.

A work around for C89 is to note that the rules for declarations preceding code work at block scope rather than function scope, so you can do this:

#include<stdio.h>

int main ( void )
{
    printf ( "wibble\n" );

    {
        const int x = 10;

        printf ( "x = %d\n", x );
    }

    return 0;
}
Savor answered 13/9, 2009 at 10:53 Comment(4)
Actually, you can declare non-extern const global variables without initializing them. const int a; is called a "tentative definition", or something like that. If the variable is later unambiguously defined, then it's treated as just a declaration. If not, it's a definition. So you can have const int a; in a header, and then const int a = 12; in the .c file. Not that you would often want to, since you risk forgetting the definition and ending up with a being 12 in some compilation units and 0 in others...Arad
@onebyone: In fact, "tentative definitions" do not lead to different values in different compilation units. Try compiling "int x;" in one file and "int x=1;" in another. Variable x contains 1 in both files after linking (with all compilers I tried). This is the widely implemented behavior, although I do not read C99 as specifying exactly this.Turn
@onebyone A specialist once told me that linkers implemented it this way because they need the feature for Fortran anyway.Turn
My example was wrong anyway, since global-scope variables have external linkage by default. I should have had "static" on both of them.Arad
A
3

Be aware that even in C89, you can often move the definition closer to the point of first use by introducing a bare block just for the extra scope. Before:

int a, b, c;

a = 12;
// Do some stuff with a

b = 17;
// Do some stuff with a and b

c = 23;
// Do some stuff with a, b, and c

After:

int a = 12;
// Do some stuff with a
{
    int b = 17
    // Do some stuff with a and b
    {
        int c = 23;
        // Do some stuff with a, b and c
    }
}

With C99 of course, you can define variables other than at the beginning of a block:

int a = 12;
// Do some stuff with a

int b = 17
// Do some stuff with a and b

int c = 23;
// Do some stuff with a, b and c
Arad answered 13/9, 2009 at 10:21 Comment(0)
S
3

const variables are read-only and must be initialised where they're defined.

This code produces error: assignment of read-only variable 'foo' (GCC 4):

const int foo;
foo = 4;

The same goes for const pointers (note here: const int * is not a const pointer, but a pointer to const):

int * const foo;
foo = 4;
Shiverick answered 13/9, 2009 at 10:28 Comment(2)
Does the last example need *foo = 4; to generate the error? Because assigning 4 would be simply using an unlikely pointer value...Dominican
Depends which error you mean. foo = 4 is a compile error because foo is const. *foo = 4 is a runtime error (and hopefully a compile-time warning) because foo is uninitialised (or initialised to NULL if global).Arad
B
2

You can't initialize the const after declaration within the function body, but you can just open one block after your assertions:

void func()
{
    int y;

    // Do assertions
    assert(something);
    {
        int const x = 5;
        // Function body
     }
}
Berkeleian answered 13/9, 2009 at 10:42 Comment(0)
M
2

Short of the block scope and C99 declaration methods other have shown, the answer is no; you cannot defer initialization of a const variable. Anyway, const is not very useful for local variables. The main times I use the const keyword in C are:

  • Pointers in function arguments (or local variable pointers based on arguments) where the function is honoring a contract not to modify the pointed-to data. The const keyword helps ensure that the function implementation respects the requirement not to modify (it requires special effort casting to get rid of const) and allows this requirement to propagate through multiple function calls.
  • For declaring compile-time constant tables (lookup tables, predefined permanent objects, etc.) which I want stored in a read-only section of the binary so they don't use extra physical resources at runtime.

I sometimes declare local variables const if I think it will assist the reader in understanding a function, but that's pretty rare.

Marinate answered 8/7, 2010 at 7:29 Comment(0)
T
1

If you are talking of splitting a definition

const int x = 2;

into two parts:

const int x;

x = 2;

I'm afraid that's not possible in C.

If I were you, I would try to make sure I understand the intent of the coding rules that you describe. I doubt sane coding rules would prevent initializing variables (even non-const variables).

In response to various comments:

const int * p;

is not a declaration of a const variable. It is a declaration of a non-const pointer variable to a const int.

You can declare

extern const int x;

but you can still not initialize x after having executed code, assertion checks, etc.

Turn answered 13/9, 2009 at 10:32 Comment(2)
+1. Probably the intent of "initialize just before use" is (1) so that the initial value is physically close to the code which relies on it, and/or (2) if the initialization does work that can sometimes be avoided, putting it close to first use is the best rule-of-thumb to maximise the likelihood of avoiding it. So you have to weigh up those two things against the value of marking it const.Arad
Yes, it took me Pavel Shved's answer to realize that the question was probably about local const variables. I was thinking of global variables all this time.Turn
P
0

If you'd like to cast away const on the LHS, use this:

const int n = 0;

*((int*)&n) = 23;
Promotive answered 9/6, 2011 at 21:36 Comment(1)
How about this? Very bad idea. It bypasses the restrictions the language has decided to put in place. May even cause bugs, depending on your compiler implementation.Shanley

© 2022 - 2024 — McMap. All rights reserved.