0

Is this code well-defined according to the C++ standard? It seems slightly hacky.

#include <string>
char* GetPointer(std::string& s)
{
    return &s[0];
}

Is that code ensured by the standard to return a pointer to the first element in the array storing the characters?

Alecto Irene Perez
  • 10,321
  • 23
  • 46
  • Why wouldn't it, unless s is null? You should just use char arrays instead of std::string though. – Ryan Aug 05 '16 at 14:53
  • 1
    It returns a pointer to array of chars (char *). The advantage of this solution above c_str() is that you have NON const pointer. – nosbor Aug 05 '16 at 14:53
  • 1
    Yes it is, but it's useless. See [data](http://en.cppreference.com/w/cpp/string/basic_string/data) – rocambille Aug 05 '16 at 14:55
  • 1
    @nosbor No, it returns a pointer-to-one-`char`, which just happens to be the start of a null-terminated C string. – underscore_d Aug 05 '16 at 14:55
  • Did you check the [reference documentation](http://en.cppreference.com/w/cpp/string/basic_string/operator_at)? – πάντα ῥεῖ Aug 05 '16 at 14:56
  • @wasthishelpful It's not useless at all if one wants a non-`const` `char *`, which isn't an available return type until C++17. – underscore_d Aug 05 '16 at 14:56
  • It is not usless. It depends which version of c++ compiler you use. ::data is NOT available in all versions. – nosbor Aug 05 '16 at 14:57
  • @nosbor ...and even in those versions where `.data()` is available, it cannot (officially) return a non-`const` pointer unless using C++17 and a sufficiently up-to-date stdlib. – underscore_d Aug 05 '16 at 14:59
  • @underscore_d first, that string is not guaranteed to be null-terminated, and the rest what I was going to say here is said in Nicola Benes's answer below – mvidelgauz Aug 05 '16 at 15:01
  • 1
    Since C++11, it is guaranteed to be null-terminated, see http://en.cppreference.com/w/cpp/string/basic_string/data. "The returned array is null-terminated, that is, data() and c_str() perform the same function." – Nikola Benes Aug 05 '16 at 15:13

4 Answers4

8

Yes.

The elements of a basic_string are stored contiguously, that is, for a basic_string s, &*(s.begin() + n) == &*s.begin() + n for any n in [0, s.size()), or, equivalently, a pointer to s[0] can be passed to functions that expect a pointer to the first element of a CharT[] array.

source: http://en.cppreference.com/w/cpp/string/basic_string

Nikola Benes
  • 2,372
  • 1
  • 20
  • 33
  • 1
    If the strings `.size()` is `0` you are in trouble though. – Jesper Juhl Aug 05 '16 at 14:58
  • 2
    No, you're not. See http://en.cppreference.com/w/cpp/string/basic_string/operator_at. "If pos == size(), a reference to the character with value CharT() (the null character) is returned." – Nikola Benes Aug 05 '16 at 15:00
  • Hmm, seems you are right. Interresting difference from vector. Shows how assumptions about one container can bite you when talking about a different one. Today I learned. Thanks. – Jesper Juhl Aug 05 '16 at 15:10
1

That's the canonical way of getting a char pointer from std::string, so there's nothing wrong with it. The caller needs to be aware that the pointer becomes invalid if the string changes.

It's convenient to put stuff that "looks hacky" into a function so you don't have to worry about it.

Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 1
    @MarkRansom While this method works, I doubt it should be called the *canonical* one. What is canonical and what isn't is specified by the standard. – lisyarus Aug 05 '16 at 15:02
  • _"That's the canonical way of getting a char pointer from std::string"_ more canonical than `std::string::data()`? – mvidelgauz Aug 05 '16 at 15:03
  • @MarkRansom I pasted the wrong comment on the wrong answer and got confused. This _is_ the canonical way until C++17 adds non-`const` `data()`. Carry on :-) – underscore_d Aug 05 '16 at 15:06
  • 1
    @lisyarus it's canonical because it's the *only* way to get a non-const pointer, at least until C++17 as underscore_d points out. And having it a function makes it easy to change once that's possible. – Mark Ransom Aug 05 '16 at 15:51
1

Is this code well-defined according to the C++ standard?

Is that code ensured by the standard to return a pointer to the first element in the array storing the characters?

Yes and yes.

However, you must realize that the pointed array of chars is not guaranteed to be null terminated. So, you may not pass the pointer to functions that require a null terminated string - unless you set the terminator yourself.

It seems slightly hacky.

It might seem so, but currently this is how to get non-const pointer to the internal array. C++17 will add char* std::string::data() to make this cleaner.

Community
  • 1
  • 1
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • That's not a proposal anymore ;-) – underscore_d Aug 05 '16 at 15:02
  • Related to [my question](http://stackoverflow.com/questions/38506473/in-c11-and-beyond-does-stdstringoperator-do-bounds-checking) in the comments from the answer by LRIO we find 21.4.7.1 where data is defined as *A pointer p such that p + i == &operator[](i) for each i in [0,size()].* This leads me to believe that it must be null terminated. – NathanOliver Aug 05 '16 at 15:02
  • @NathanOliver I only see how that guarantees that the string returned by `data()` must be null terminated. I don't see the guarantee about the null being there unless `data()` (or `c_str()` or `operator[](size())`) was called. – eerorika Aug 05 '16 at 15:08
  • Well it states that `&foo[n]` is valid for all `n` in `[0, size()]` and then we also have that `[size()]` returns `charT()`. There might be a way to do this without storing the `charT()` at `[size()]` but I don't know of a way and keeping constant complexity. – NathanOliver Aug 05 '16 at 15:14
  • @NathanOliver always allocate memory for the terminator, but only initialize it to null if one of the mentioned functions are called. Probably not most efficient, but seems to comply with standard as far as I can tell. – eerorika Aug 05 '16 at 15:18
  • Maybe. Hopefully it is cleared up in C++17 – NathanOliver Aug 05 '16 at 15:19
1

Yes, it's perfectly fine to return a pointer into some memory whose location was passed into the function.
It would have been a different story if the std::string would have been passed by value.

Just, remember that the return-value's validity is tied to the passed object's internal buffer.

Deduplicator
  • 44,692
  • 7
  • 66
  • 118