As mentioned in the comments, the C++ Standard does not enforce whether or not multiply-defined, identical string literals should be merged:
5.13.5 String literals [lex.string]
…
16 Evaluating
a string-literal results in a string literal object with static
storage duration, initialized from the given characters as specified
above. Whether all string literals are distinct (that is, are stored
in nonoverlapping objects) and whether successive evaluations of a
string-literal yield the same or a different object is unspecified.
Frequently, compilers (or linkers) offer a command-line switch to decide on whether or not to merge identical strings. For example, the MSVC compiler has the "Enable string pooling" option – /GF to merge, or /GF- to keep them separate.
Using the following code units:
#include <iostream>
extern void other();
int main()
{
const char* inmain = "hello";
std::cout << (void*)(inmain) << std::endl;
other();
return 0;
}
and (in a separate source file):
#include <iostream>
void other()
{
const char* inother = "hello";
std::cout << (void*)(inother) << std::endl;
}
Building with the /GF switch yields output like the following (the strings have the same addresses):
00007FF778952230
00007FF778952230
However, using /GF- produces:
00007FF7D5662238
00007FF7D5662230
In fact, even your first code snippet (with minor modifications, shown below), where both literals are in the same translation unit (and even in the same scope) generates two different objects when built with the /GF- option:
#include <iostream>
#include <ios>
int main()
{
const char* p1 = "hello";
const char* p2 = "hello";
std::cout << std::boolalpha << (p1 == p2) << std::endl;
// Output: "false" using /GF- or "true" using /GF
return 0;
}