Tak, lubię, jak okazuje się, że można zrobić coś, czego uczymy się że nie można tego zrobić.
Na przykład tego, że „strings are immutable”, albo jak kto woli, że łańcuchy znakowe są niezmienne. To znaczy jak mamy zmianę, to tak naprawdę tworzy się dla nas pod spodem nowy string. Czy to prawda? No jeżeli zamkniemy się do bezpiecznego kodu zarządzalnego to jest prawda, jednakże ogólnie da się zmodyfikować stringa, pod warunkiem że odtańczymy taniec szamana (użyjemy wskaźników), przeskoczymy nad ogniem (czyli nasz kod będzie w sekcji unsafe) i zagrozimy śmieciarzowi by trzymał się od naszej zmiennej z daleka (więc zablokujemy „wskaźnik” na stercie).
Więc zróbmy tak, by w naszym stringu poszło oczko.
using System;
namespace Oczko
{
class Program
{
static void Main(string[] args)
{
int przesunięcie = 4;
string naszString = "=================================================";
Console.WriteLine($"Tak wygląda nowy string:\n {naszString}");
unsafe
{
fixed (char* wskaźnik = naszString)
{
char* p = wskaźnik + przesunięcie;
foreach (char znak in "oczko")
{
*++p = znak;
}
}
}
Console.WriteLine($"Tak wygląda zużyty string:\n {naszString}");
Console.ReadKey();
}
}
}
Nie zapomnijcie ustawić w preferences-> build -> [debug/release] -> Allow unsafe code lub skompilować plik z przełącznikiem /unsafe!
Co tu się stało?
Oczywiście w kodzie niezarządzalnym jak dobrze wiemy, string jest tablicą znaków. No +vat, ale my nie o tym:p No więc sobie wykorzystajmy ten fakt. Wykorzystajmy również fakt, że wskaźnik to tak naprawdę liczba, więc można iterować po znakach dodając sobie cyferki i wtedy przesuwamy się na kolejne pola. Słówko fixed nam zakotwiczyło wskaźnik na stercie (żeby GC go nie przeniósł), potem zadeklarowaliśmy wskaźnik o tym samym adresie, przesunęliśmy kawałek i puściliśmy w nim oczko. No i wyświetlilismy tego stringa jeszcze raz, zatrzymując konsolę. Blok unsafe służy do tego by kompilator się nie wystraszył wskaźnika (pozwala nam na bezpośredni dostęp do pamięci – ale z wszystkimi konsekwencjami – np nie posprząta po nas). I tak oto zmodyfikowaliśmy rzekomo niemodyfikowalny string puszczając w nim oczko.
Z tym że uwaga! Istnieje coś takiego jak string interning i polega mniej więcej na tym, że jak środowisko uruchomieniowe zobaczy kilka stringów (które przecież są niemutowalne:p) o takiej samej wartości, to pomimo że nie mają one ze sobą nic wspólnego, będą zapisane w jednym miejscu w pamięci (czyli będzie kilka referencji do de facto tego samego ciągu znaków) – więc wtedy pytanie jak platforma zareaguje. Czy stworzy kopię dla pozostałych wystąpień i zostawi zmodyfikowane tylko dla tego jednego, czy oleje bo to przecież unsafe i programista wszystko robi na własną odpowiedzialność?
O samym „internowaniu” napisów dowiedziałam się o tym w dziwnych okolicznościach, a mianowicie czytałam sobie w książce na temat synchronizacji wątków że nie powinno się zakładać locka na stringach aby uniknąć właśnie blokowania za dużo. Swoją drogą locka na this też nie zalecają.
Hmm, starczy na dzisiaj. Do zobaczenia kiedyś tam.
One Comment
Modyfikacja stringa w c#
Dziękujemy za dodanie artykułu – Trackback z dotnetomaniak.pl
Comments are closed.