Lifetime of a String Literal Returned by a Function

Lifetime of a string literal returned by a function

The C++ Standard does not say where string literals should be stored. It does however guarantee that their lifetime is the lifetime of the program. Your code is therefore valid.

Life-time of a string literal in C

Yes, the lifetime of a local variable is within the scope({,}) in which it is created.

Local variables have automatic or local storage. Automatic because they are automatically destroyed once the scope within which they are created ends.

However, What you have here is a string literal, which is allocated in an implementation-defined read-only memory. String literals are different from local variables and they remain alive throughout the program lifetime. They have static duration [Ref 1] lifetime.

A word of caution!

However, note that any attempt to modify the contents of a string literal is an undefined behavior (UB). User programs are not allowed to modify the contents of a string literal.

Hence, it is always encouraged to use a const while declaring a string literal.

const char*p = "string"; 

instead of,

char*p = "string";    

In fact, in C++ it is deprecated to declare a string literal without the const though not in C. However, declaring a string literal with a const gives you the advantage that compilers would usually give you a warning in case you attempt to modify the string literal in the second case.

Sample program:

#include<string.h> 
int main()
{
char *str1 = "string Literal";
const char *str2 = "string Literal";
char source[]="Sample string";

strcpy(str1,source); // No warning or error just Undefined Behavior
strcpy(str2,source); // Compiler issues a warning

return 0;
}

Output:

cc1: warnings being treated as errors

prog.c: In function ‘main’:

prog.c:9: error: passing argument 1 of ‘strcpy’ discards qualifiers from pointer target type

Notice the compiler warns for the second case, but not for the first.


To answer the question being asked by a couple of users here:

What is the deal with integral literals?

In other words, is the following code valid?

int *foo()
{
return &(2);
}

The answer is, no this code is not valid. It is ill-formed and will give a compiler error.

Something like:

prog.c:3: error: lvalue required as unary ‘&’ operand

String literals are l-values, i.e: You can take the address of a string literal, but cannot change its contents.

However, any other literals (int, float, char, etc.) are r-values (the C standard uses the term the value of an expression for these) and their address cannot be taken at all.


[Ref 1]C99 standard 6.4.5/5 "String Literals - Semantics":

In translation phase 7, a byte or code of value zero is appended to each multibyte character sequence that results from a string literal or literals. The multibyte character sequence is then used to initialize an array of static storage duration and length just sufficient to contain the sequence. For character string literals, the array elements have type char, and are initialized with the individual bytes of the multibyte character sequence; for wide string literals, the array elements have type wchar_t, and are initialized with the sequence of wide characters...

It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.

How does the lifetime work on constant strings / string literals?

Lifetime elision infers that the full type of

fn get_str(s: &str) -> &str

is

fn get_str<'a>(s: &'a str) -> &'a str

which basically means that the return value of get_str has to be valid as long as s is valid. The actual type of the string literal "Hello world" is &'static str, which means that it is valid for the entire run of the program. Since this satisfies the lifetime constraints in the function signature (because 'static always includes 'a for any 'a), this works.

However, a more sensible way to get your original code to work would be to add an explicit lifetime to the function type:

fn get_str() -> &'static str {
"Hello World"
}

How does "Hello World" borrow from the parameter s, even it has nothing to do with s?

There are only two options that would make sense for the return value's lifetime in a function with a single reference argument:

  1. It can be 'static, as it should be in your example, or
  2. The return value's lifetime has to be tied to the lifetime of the argument, which is what lifetime elision defaults to.

There is some rationale for choosing the latter in the link at the top of this post, but it basically comes down to the fact that the latter is the far more common case. Note that lifetime elision does not look at the function body at all, it just goes by the function signature. That's why it won't take the fact that you're just returning a string constant into account.

How long does a string constant live in c++?

A string literal has static storage duration and lasts the life of the program. From the draft C++ standard section 2.14.5 String literals paragraph 8 which says (emphasis mine going forward):

Ordinary string literals and UTF-8 string literals are also referred
to as narrow string literals. A narrow string literal has type “array
of n const char”, where n is the size of the string as defined below,
and has static storage duration (3.7).

and from section 3.7.1 Static storage duration paragraph 1:

All variables which do not have dynamic storage duration, do not have
thread storage duration, and are not local have static storage
duration. The storage for these entities shall last for the duration
of the program (3.6.2, 3.6.3).

The second case in func3 on the other other hand is not valid. The lifetime of a temporary bound to a reference persists for the life of the reference, which in this case ends when the function returns. This is covered in section 12.2 which says:

The second context is when a reference is bound to a temporary.115 The
temporary to which the reference is bound or the temporary that is the
complete object of a subobject to which the reference is bound
persists for the lifetime of the reference except
:

Is it safe to return a static string_view created from a string literal?

string literals are guaranteed to have the lifetime as long as that of the program itself

That's correct.

Therefore, is this usage of string_view safe and appropriate?

Yes, your code is fine.

Is returning a pointer to a local variable always undefined behavior

Returning a pointer to a non-static function local variables will cause the pointer you get at the call site to be a dangling pointer and using it will have undefined behavior.

Here, this is not the case. A string literal has static storage duration, meaning it will live until the end of the program. This means it is safe to return a pointer to a string literal that was declared in a function.

So both foo and func are safe, but if you had

const char * bar()
{
std::string text = "some text";
// stuff
return text.c_str();
}

Then you would be returning a pointer to an object that no longer exits and would have UB trying to read from that returned pointer.

Proper way to return a new string in Rust

You cannot return a &str if you've allocated the String in the function. There's further discussion about why, as well as the fact that it's not limited to strings. That makes your choice much easier: return the String.

Strings are heap-allocated and built to be mutable.

Strings are heap-allocated because they have an unknown length. Since that allocation is solely owned by the String, that's what grants the ability to mutate the string.

My function just returns a filepath for reference purposes, and I'd rather leave it up to the caller to decide if they need a heap-stored mutable string.

This isn't possible. Your function has performed an allocation. If you don't return the allocation to the caller, then the value must be deallocated to prevent memory leaks. If it was returned after deallocation, that would be an invalid reference, leading to memory safety violations.

But I can also seemingly do this:

fn hello_string(x: &str) -> &str {
return "hello world";
}

to get a &str out of my function. Can someone explain to me why this
is bad and why I should never do it? Or maybe it's not bad and okay in
certain situations?

It's not bad, it just doesn't really allow you to do what you want in your original case. That "hello world" is a &'static str, a string slice that's been stored inside the code of the program itself. It has a fixed length and is known to live longer than main.

The signature fn hello_string(x: &str) -> &str can be expanded to fn hello_string<'a>(x: &'a str) -> &'a str. This indicates that the resulting string slice must have the same lifetime as the input string. A static string will outlive any lifetime, so that's valid to substitute.

This would be useful for a function where the result is based on the input string only:

fn long_string(x: &str) -> &str {
if x.len() > 10 {
"too long"
} else {
x
}
}

However, in your case, the function owns the String. If you attempted to return a reference to a String, completely unrelated to the input string:

fn hello_string(x: &str) -> &str {
&String::from("hello world")
}

You'll run into the common error message "borrowed value does not live long enough". That's because the borrowed value only lives until the end of method, not as long as the input string slice. You can't "trick" the compiler (or if you can, that's a major bug).

How returning a string_view of a local literal works

but both g++ and clang say nothing, so this code seems legit and works.

You cannot deduce latter from the former. A lot of non-legit code produces no warnings.

That said, string literals have static storage duration, so there is no problem with their lifetime. to_stringview is legit indeed.

P.S. String literals are arrays of char.

I was wondering by which mechanism the lifetime of "hello" is different from the lifetime of "hey".

There is no difference between the lifetime of those two string literals. But "hey"s is not a string literal. It is a "user defined" literal that creates a temporary instance of the class std::string. That temporary object has no static storage duration.

Error in function returning string literal after borrowing String

You don't precise the lifetime of the output of your function function. So it's assumed by the borrow checker to be the same as the argument (see lifetime elision rules).

You have to tell the borrow checker that those lifetimes aren't the same (meaning in this case that the output slice isn't dependent of the input one). More precisely in your case it's static.

Change your function declaration to

fn function(a :&String) -> &'static str{


Related Topics



Leave a reply



Submit