1

I am creating a C program with some hardcoded parameters. (This is by design, lets not worry about that.)

//global constants
const char * const USERNAME = "username";
const int USERNAME_LEN = strlen(USERNAME);

Of course, this errors out because strlen isn't a constant apparently. Is there a way to do this? or should I just not care and pass strncmp the result direct from strlen?

user606723
  • 4,918
  • 2
  • 27
  • 34
  • 1
    Just to be clear, this problem doesn't arise because "strlen isn't a constant". The function signature for `strlen` (on my system, and I suspect most others as well) is `size_t strlen(const char *__s)`. The problem you're having is that you can't initialize a `const` at run-time. You can only initialize it with a constant expression that is known at compile-time. – Mike Holt Jun 11 '15 at 19:14
  • What is the reason that strlen can't be run at compile time for a global constant? (I think I get the reason, but I don't know the verbiage) – user606723 Jun 11 '15 at 19:16
  • The whole point of the `const` keyword is to declare an object whose value cannot be changed at run-time. So the compiler needs to be able to determine its value at compile-time. – Mike Holt Jun 11 '15 at 19:26
  • and why exactly can't strlen be run at compile time? Is it possible to declare a function that can be run at compile time or is that not a feature of the language? @MikeHolt – user606723 Jun 11 '15 at 19:30
  • 1
    Some compilers will optimise away the call to `strlen` at compile time in these situations. But the behaviour is not guaranteed. If you're interested, in C++ this kind of problem can now be solved with `constexpr`: http://stackoverflow.com/a/25891133/1553090 – paddy Jun 11 '15 at 19:40
  • @paddy, thanks I think this clear things up. :) – user606723 Jun 11 '15 at 19:43
  • @paddy: Do you have a specific compiler name? I do not think it's covered by the standard. `$ gcc -std=c11 test.c` `test.c:8:19: warning: initializer element is not a constant expression [enabled by default]` `const size_t cl = strlen(ca);` gcc does not. – too honest for this site Jun 11 '15 at 19:53
  • You might read about the differences about compiled and interpreted languages. You could use a macro as I supposed, if you have multiple such definitions. – too honest for this site Jun 11 '15 at 20:01
  • @Olaf, I think all paddy is saying is that if you run -O3, gcc will optimize the strlen call out of the resulting code... not that the gcc will compile this code without issue. – user606723 Jun 11 '15 at 20:08
  • "`gcc -std=c11 -O3 test.c -lncurses` `test.c:8:19: warning: initializer element is not a constant expression [enabled by default]` `const size_t cl = strlen(ca);`" - Which version of gcc do you actually run to get not complaint? And, yes, although this is "just" a warning, I do not expect this to run properly. From the standard, it is UB at best. (Still wonder why this is not an error ... hmm). – too honest for this site Jun 11 '15 at 20:11
  • @Olaf you're confusing the conversation. This is always going to get a complaint. All that is being said is that if you use strlen on a constant (within a method or something), the call will probably get optimized out in the resulting asm. – user606723 Jun 11 '15 at 20:13
  • There is nothing to "optimize out" here, as there is no code generated for this! All the compiler does is to calculate the expression at compile-time and place the resulting value into the file at the position the variable is located (at run-time). That is actually the reason why that has to be a _constant expression_. Nothing to confuse about. Just have a look at the data in your debugger or objdump. – too honest for this site Jun 11 '15 at 20:15
  • @Olaf, calculating the expression at compile time instead of actually calling the function at runtime is the optimization. I think we're just being bad at communicating. – user606723 Jun 11 '15 at 20:16
  • You forget that the compiler does **not** even **know** the function definition, unless it is specified `static`. And even then, it had to execute it ("C interpreter"). For strlen(), the compiler might speculate for a hosted environment (still very dangerous), but what for a freestanding environment? `strlen` could very well do something completely different. There is no bytecode interpreter as for Java and Python. And the run-time does not provide any services for language execution as C++ does. – too honest for this site Jun 11 '15 at 20:22
  • @Olaf, Specific to functions in the C standard library, there can be compiler specific optimizations that will optimize out these calls. Do an strlen on a constant with -O3 and look at the consulting asm. – user606723 Jun 11 '15 at 20:31
  • http://stackoverflow.com/questions/5770709/can-i-count-on-my-compiler-to-optimize-strlen-on-const-char Take a look at this. @olaf – user606723 Jun 11 '15 at 20:32
  • Read my comment. I _did_ run gcc -O3 on that case. And do get the same result as without. No surprise, as optimization must not deviate from the standard. Your example is a **function execution**, i.e. generated code. That is something **completely different** from a const initializer! In fact, the only one to optimize this finally away might be the linker. You might, however, refer to LTO. But that is still linker-based (as the name implies). I would instantly trash a compiler making unsafe assumptions about function semantics just by name. – too honest for this site Jun 11 '15 at 20:42
  • Why discuss, just read the [**standard**](http://port70.net/~nsz/c/c11/n1570.html#6.6p3). This appies to your code, but not the example given. – too honest for this site Jun 11 '15 at 20:47
  • You ran gcc -O3, but where is your .s output? The optimization doesn't happen until after that error comes out. @Olaf It also looks like gcc might not even do this, you might have to use microsoft's compiler or something else. – user606723 Jun 11 '15 at 20:50
  • No! If you do not stick to the standard, you are in the realm of **undefined behaviour**. There anything can happen. There is no need to inspect the output once you're there. – too honest for this site Jun 11 '15 at 20:53
  • Note that VC is not standard compatible. I will definitively _not_ use a broken non-standard "compiler", nor will I discuss aside the standard for this, as that will lead nowhere. Even _if_ that would produce code for the architectures I use actually. – too honest for this site Jun 11 '15 at 21:08
  • Wow... I make one innocent comment and look what happens. =) – paddy Jun 11 '15 at 23:11

2 Answers2

5

If you really need your USERNAME as a const char * const object (do you?), then one way to it is

#define USERNAME_LITERAL "username"

const char * const USERNAME = USERNAME_LITERAL;
const int USERNAME_LEN = sizeof USERNAME_LITERAL - 1;

It is as elegant as one might wish, but does keep the code in "self-maintaining" shape. I.e. you only have to edit the literal, the rest will update itself automatically.

You can also do

const char USERNAME[] = "username";
const int USERNAME_LEN = sizeof USERNAME - 1;

or even

#define USERNAME "username"
const int USERNAME_LEN = sizeof USERNAME - 1;

but in this case USERNAME is not const char * const anymore, which is why I'm asking whether you really need it as such.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
0

If you have to initialize a pointer for the literal, you can use the following:

const char un_str[] = "...", *un_ptr = un_str;
const size_t un_len = sizeof(un_str);

Note: sizeof() returns a size_t, which is unsigned.

You can pack that into a macro:

#define STR_LEN_PAIR(name, value) const char name##_str[] = value; \
        const size_t name##_len = sizeof(name##_str)

This uses argument concatenation. Note the missing ; after the last line which allows to use standard syntax on invocation. Be careful to use it only as intended:

// at file level
STR_LEN_PAIR(un,"...");

The fields can be accessed like un_len, un_str, ...

Note that the pointer itself is not const, so there is not much use in it (you quite likely do not want to have a modifyable pointer to each such string). You can simply omit its definitions as I already did in the *macro.

Compatibility note:

C differs in the semantics of const. Actually, C does not have "constants" as C++ has. objects defined as const are actually "unmodifyable variables": they can only be initialized at compiler-time, but they consume space as any other variable (however, they might be located in unchangeable memory) and the compiler will generate read accesses as for any other variable. In C++, un_len for instance will be replaced by its value at compile-time and might consume no memory in the final program - as long as its address is not taken.

What comes next to real constants in C are either enum constants, or `#define'ed names. The latter are processed at a very different level (textual replacement by the pre-processor), however.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52