4 Eylül 2019 Çarşamba

std::string Bellek Alanı

Bellek Alanı
String, C++11'den sonra aynı bir array gibi sıralı yani bitişik bellek alanı sunar. Bu konuda vector ile aynı özellikleri gösterir. C++11'den önce de bir çok gerçekleştirim aynı şekilde çalışıyordu. C++11 ile bu çalışma şekli standartlaştı.
The char-like objects in a basic_string object shall be stored contiguously. That is, for any basic_string object s, the identity &*(s.begin() + n) == &*s.begin() + n shall hold for all values of n such that 0 <= n < s.size().
Bir sınıfın bitişik bellek kullanıp kullanmadığını illaki ispatlamak için iterator'leri kullanan şu yöntem kullanılabilir.

std:.string'i Buffer Olarak Kullanmak
Kötü fikir. Açıklaması şöyle.
It is bad practice to use std::string as a buffer, for several reasons (listed in no particular order):

- std::string was not intended for use as a buffer; you would need to double-check the description of the class to make sure there are no "gotchas" which would prevent certain usage   patterns (or make them trigger undefined behavior).
- Specifically: Before C++17, you can't even write through the pointer you get with data() - it's const Tchar *; so your code would cause undefined behavior.
  (But &(str[0]), &(str.front()), or &(*(str.begin())) would work.)
- Using std::string's for buffers is confusing to readers of the implementation, who assume you would be using std::string for, well, strings.
- Worse yet, it's confusing for whoever might use this function - they too may think what you're returning is a string, i.e. valid human-readable text.
Inline Bellek Alanı - Stack Üzerinde
Small String Optimizaton veya Short String Optimization (SSO) olarak ta bilinir. Bazı std::string sınıfları hız adına belli bir miktar belleği heap'te değil nesnenin kendi içinde yaratıyor. Eğer string'in uzunluğu inline alanı geçerse heap'e taşıyor. Açıklaması şöyle.
When the string data is less than or equal 16 characters, including the null terminator, it is stored in a buffer local to the std::string object itself. Otherwise, it allocates memory on the heap and stores the data over there.
SSO Danling Pointer'a Sebep Olabilir
Açıklaması şöyle.
It is permitted by the standard and widely implemented, and will lead to dangling pointers as the strings actually move their data to their new location.
Örnek
Elimizde şöyle bir kod olsun. String 16 karakterden küçük olduğu için stack üzerindeki bellekte yaratılır.
#include <string>
int main()
{
    std::string a = "ABCDEFGHIJKLMNO";
}
Üretilen assembly kodu şöyledir
main:                                   # @main
    xor     eax, eax
    ret
Ancak şöyle yaparsak string bellek alanı heap'te yaratılır.
#include <string>
int main()
{
    std::string a = "ABCDEFGHIJKLMNOP"; // <-- !!! One Extra P 
}
Örnek
Visual Studio 2015'teki kod kabaca şöyle. Eğer string 16 byte'tan kısa ise stack'taki _Buf alanı içinde saklanır. _Myptr() metodunda stack veya heap'teki belleğin döndürüldüğü görülebilir.
template<class _Val_types>
class _String_val
{
public:
    typedef typename _Val_types::value_type value_type;

    enum
    {   // length of internal buffer, [1, 16]
        _BUF_SIZE = 16 / sizeof (value_type) < 1 ? 1
            : 16 / sizeof (value_type)
    };

    value_type *_Myptr()
    {   // determine current pointer to buffer for mutable string
        return (this->_BUF_SIZE <= _Myres
            ? _Bx._Ptr
            : _Bx._Buf);
    }

    union _Bxty
    {   // storage for small buffer or pointer to larger one
        value_type _Buf[_BUF_SIZE];
        pointer _Ptr;
    } _Bx;
};
Sınıfın nasıl çalıştığı şöyle bulunabilir. &s ile nesnenin adresini p değişkenine alırız. data() ile ilk karakterin adresini q değişkenine alırız. p'yi sizeof() kadar ilerleterek herhangi bir adresin q ile aynı olup olmadığına bakarız. Aynı ise string bu şekilde çalışmaktadır.
bool data_is_inline(const std::string & s)
{
  const char * p = reinterpret_cast<const char *>(&s);  const char * q = s.data();
  for (std::size_t i = 0; i != sizeof(s); ++i)
    if (p + i == q) 
      return true;
  return false;
}
Copy on Write Bellek Alanı - C++11'den önce
Yine hız adına bazı string sınıfları iki string değişkenini birbirine atayınca hemen kopyalama yapmıyor. Değişkenin birisi değiştirilince gerçekten kopya alınıyor.

C++11'den sonra bu kurala artık müsade edilmiyor. İki tane farklı bellek ayrılıyor.

Hiç yorum yok:

Yorum Gönder