4

The following code compiles fine with, for example, the default settings in Xcode 11.3.1:

#include <stdio.h>

int main(int argc, const char * argv[]) {
    char* thing = "123";
    thing[2] = '4';
    printf("%s\n", thing);
    return 0;
}

However, at runtime the code traps with EXC_BAD_ACCESS on thing[2] = '4'. I assume this is because the memory for the bytes representing "123" is compiled into my program's binary somewhere that on a modern processor/OS gets marked as for code rather than data. (This answer confirms that — not to mention there's a leaq 0x4d(%rip), %rsi ; "123" line in the disassembly, passing the pointer to an address relative to the instruction pointer!)

Is it just a historical artifact that C allows this, from the era of self-modifying code? I notice that I can also assign void* x = main; without any complaint that I'm discarding modifiers.

This answer says:

According to the C99 rationale, there were people in the committee who wanted string literals to be modifiable, so the standard does not explicitly forbid it.

Is there any further discussion I could read on that? More practically, is there a way to tell clang and/or gcc to flag such assignments (even though they are not actually forbidden) with a warning, without compiling as C++?

natevw
  • 16,807
  • 8
  • 66
  • 90

3 Answers3

3

The answer you have quoted is an opinion without citation, and frankly nonsense. It is about nothing more than not breaking the vast quantity of existing legacy C code that it is desirable to remain compilable in a modern compiler.

However many compilers will issue a warning if you set the necessary warning level or options. In GCC for example:

-Wwrite-strings

When compiling C, give string constants the type const char[length] so that copying the address of one into a non-const char* pointer produces a warning. These warnings help you find at compile time code that can try to write into a string constant, but only if you have been very careful about using const in declarations and prototypes. Otherwise, it is just a nuisance. This is why we did not make -Wall request these warnings.

When compiling C++, warn about the deprecated conversion from string literals to char *. This warning is enabled by default for C++ programs.

CLANG also has -Wwrite-strings, where is a synonym for -Wwriteable-strings

-Wwritable-strings

This diagnostic is enabled by default.

Also controls -Wdeprecated-writable-strings.

Diagnostic text:

warning: ISO C++11 does not allow conversion from string literal to A

The diagnostic text is different for C compilation - I'm just quoting the manual.

In GCC with -Wwrite-strings:

int main()
{
    char* x = "hello" ;
    return 0;
}

produces:

main.c:3:15: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]    

CLANG produces:

source_file.c:3:15: warning: initializing 'char *' with an expression of type 'const char [6]' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
Clifford
  • 88,407
  • 13
  • 85
  • 165
  • Confirmed. Adding `-Wwrite-strings` to "Other Warning Flags" in Xcode makes clang complain of "Initializing 'char *' with an expression of type 'const char [4]' discards qualifiers" even though afaict it is still compiling as C (`-std=gnu11`). – natevw Feb 07 '20 at 19:34
  • @natevw : Yes, I since tested at https://rextester.com/l/c_online_compiler_clang and updated the answer. – Clifford Feb 07 '20 at 19:44
1

Opposite to C++, in C, string literals have types of non-constant character arrays.

However, according to the C Standard, any attempt to modify a string literal results in undefined behavior.

Historically, the C language did not have the qualifier const. The qualifier const at first appeared in C++. So for the backward compatibility string literals in C have types of non-constant character arrays.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Is there a specific warning I can turn on to flag this assignment? Seems funny that they define *using* as non-`const` to be UB, yet don't at all deprecate/discourage the assignment itself. – natevw Feb 07 '20 at 19:02
  • @natevw I do not know. You should look through the documentation of clang for example at https://clang.llvm.org/docs/DiagnosticsReference.html#w-warnings – Vlad from Moscow Feb 07 '20 at 19:16
1

You have the -Wwrite-strings:

When compiling C, give string constants the type const char[length] so that copying the address of one into a non-const char * pointer produces a warning. These warnings help you find at compile time code that can try to write into a string constant, but only if you have been very careful about using const in declarations and prototypes. Otherwise, it is just a nuisance. This is why we did not make -Wall request these warnings.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
anastaciu
  • 23,467
  • 7
  • 28
  • 53