Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Начинающим";
Текущий архив: 2017.04.16;
Скачать: [xml.tar.bz2];

Вниз

Строка, лучший способ сравнения   Найти похожие ветки 

 
vadim83   (2015-08-07 12:33) [0]

Друзья, вопрос из разряда понять, как оно "внутри" сравнивается/проверяется. Покажу на примере:
function (s: string): string;
begin
  if s = "" then
    Result := "empty"
  else
    Result := DoSomething(s);
end;


Простая функция, коих найдется в каждом проекте. Вот мне интересно, как лучше/правильнее/быстрее проверить строку на "пустоту". Ведь есть вариант сравнить так:
function (s: string): string;
begin
  if Length(s) = 0 then
    Result := "empty"
  else
    Result := DoSomething(s);
end;


Тип string имеет свой счетчик ссылок и длину строки, по идее второй вариант должен быть быстрее, но я не знаю как оно "под капотом" это проверяет, поскольку не силен в асме.

Еще догадываюсь, что т.н. пустота в выражении (s <> "") это тоже самое что сравнить с nil, но тут есть сомнения. Поясните пожалуйста эти моменты, у кого уже сложилась вся картинка.
Спасибо.


 
кгшзх ©   (2015-08-07 12:45) [1]

var s : string;
begin
s := "";
if PAnsiChar(s) <> nil then
 ShowMessage("А ты точно только только в асме не силен?")
end;


 
vadim83   (2015-08-07 12:55) [2]


> кгшзх ©

Т.е. по вашему это самый быстрый и лучший вариант?
ps: в изящных остротах тоже не силен, согласен)


 
vadim83   (2015-08-07 12:58) [3]


>  if PAnsiChar(s) <> nil then

мне казалось, что если s содержит первым символом #0, а потом другие символы, то ваше преобразование отработает неверно.


 
кгшзх ©   (2015-08-07 13:08) [4]

Т.е. по вашему это самый быстрый и лучший вариант?

по нашему пустая строка это не нил. больше ничего не имелось ввиду.

мне казалось, что если

ну ты же вроде умный.
знаешь что в строке есть и счетчик и длина.
но если строка указывает на нил, то кто и как сможет проверить (по какому етиамать адресу)
что длина нулевая и счетчик нулевой?


 
кгшзх ©   (2015-08-07 13:17) [5]

мне казалось, что если s содержит первым символом #0, а потом другие символы, то ваше преобразование отработает неверно.

а то что под буфер с нулевыми байтами внутри использовать тип string
и непарясь при этом ни о чем
размышлять о длинах строк - это тебе не казалось чудаковатым?


 
Dimka Maslov ©   (2015-08-07 13:17) [6]

Если @S[1] <> nil, то пустота строки проверяется проверкой длины строки, которая записана в двойном слове, расположенном до первого символа. Если же @S[1] = nil, следовательно строка пустая. Так в любом случае происходят обе проверки


 
кгшзх ©   (2015-08-07 13:50) [7]

var pb, pb1 : PByte;
begin
GetMem(pb, 10);     //берем 10 байт
ZeroMemory(pb,10);  //обнуляем все 10
pb1 := pb;
inc(pb1);           //нацеливаемся на второй слева
pb1^ := ord("A");   //пишем в него "другие символы"
if PChar(pb) <> nil then ShowMessage("Ку-ку!");


 
icWasya ©   (2015-08-07 15:46) [8]

В процедуре сравнения строк сначала проверяется равенство указателей - если оба Nil - то строки равны. Если Nil - только один, то проверяется длина второго. Если там ноль, то строки равны, елли нет-то этот другой больше. Если оба не Nil-То делается ещё куча проверок.
Так что сравнивать  с Nil будет быстрее.


 
кгшзх ©   (2015-08-07 16:23) [9]

В процедуре сравнения строк сначала проверяется равенство указателей - если оба Nil

сделай мне переменную стринг чтобы она была в нил
а потом пиши ерунду


 
Palladin ©   (2015-08-07 16:39) [10]


> кгшзх ©   (07.08.15 16:23) [9]

Можешь обкакаться от удивления

procedure TForm11.Button1Click(Sender: TObject);
var
 S: String;
begin
 if Pointer(S) = nil then
   ShowMessage("E");
end;


 
кгшзх ©   (2015-08-07 16:43) [11]

При чем здесь пойнтер, дорогой?

var
S: String;

сразу после этого съедено пять байт минимум.
при выходе из процы они будут освобождены.
и если нет указателя или он нил, то как менеджер памяти будет чистить распределенное?


 
Palladin ©   (2015-08-07 16:45) [12]

да, btw, если присвоишь "" любой переменной string и сравнишь через каст, можешь еще раз исполнить дефекацию


 
Palladin ©   (2015-08-07 16:46) [13]


> кгшзх ©   (07.08.15 16:43) [11]

причем тут "сделай мне переменную стринг чтобы она была в нил", дорогой?
ты не мажся, на уровне кода никто не сможет string с nil сравнить


 
кгшзх ©   (2015-08-07 16:47) [14]

кроме того, твой "Е" - побочный эффект оптимизатора.
ты же никуда не передаешь свою переменную, вот и в exe для нее никакого кода нет и пойнтер на ничего и показывает ничего.

а у нас тут на минуточку function (s: string): string;

в которой кое-когда, со строкой будет кое-что делаться.

так вот внутри этой функции твой оптический обман  не сработает.


 
кгшзх ©   (2015-08-07 16:47) [15]

причем тут "сделай мне переменную стринг чтобы она была в нил", дорогой?

при том, дорогой.


 
Palladin ©   (2015-08-07 16:48) [16]

Удалено модератором


 
Palladin ©   (2015-08-07 16:49) [17]


> так вот внутри этой функции твой оптический обман  не сработает.

татыче
procedure TForm11.Button1Click(Sender: TObject);
var
 S: String;
begin
  S := "sdfasd";
  ppp(S);
end;

procedure TForm11.ppp(var RR: String);
begin
 RR := "";

 if Pointer(RR) = nil then
   ShowMessage("E");

end;

пока


 
кгшзх ©   (2015-08-07 16:51) [18]

да пофик, дорогой, и этот пример.
попробуй по настоящему обмануть оптимизатор


 
кгшзх ©   (2015-08-07 16:52) [19]

Удалено модератором


 
Sha ©   (2015-08-07 18:55) [20]

>vadim83   (07.08.15 12:33)

Из описания формата представления строковых данных можно сделать вывод, что что пустую строку можно хранить двумя способами:
1. С нулевым указателем (и без данных).
2. С ненулевым указателем (и с данными нулевой длины).

Оба варианты ничему не противоречат. Однако в Delphi все внутренние процедуры работы со строками написаны таким образом, что как только они обнаруживают, что результатом операции будет пустая строка, то для представления результата используют только первый вариант хранения (с нулевым указателем).  

Именно поэтому компилятор Delphi для проверки
if s="" then
имеет право использовать оптимизированный код вроде этого
if pointer(s)=nil then

Если ты в своих проектах будешь использовать функции работы со строками из пакетов сторонних производителей, то необходимо быть абсолютно уверенным, что они также следуют этому соглашению. Иначе для проверки на пустоту придется использовать сравнение вида
if Length(s)=0
которое хуже оптимизируется компилятором.


 
vadim83   (2015-08-07 22:09) [21]

Друзья, спасибо всем, разобрался. Sha, вам отдельное спасибо. Даже еще нашел инфу на стековерфлоу, народ там не поленился в еще и в асме все разложить)


 
han_malign ©   (2015-08-10 14:30) [22]


>> причем тут "сделай мне переменную стринг чтобы она была в нил", дорогой?
> при том, дорогой.

- что магия на самом деле здесь
if PAnsiChar(s) <> nil then
удивись(D7 модуль System):
function _LStrToPChar(const s: AnsiString): PChar;
{$IFDEF PUREPASCAL}
const
 EmptyString = "";
begin
 if Pointer(s) = nil then
   Result := EmptyString
 else
   Result := Pointer(s);
end;
{$ELSE}
asm
       { ->    EAX pointer to str              }
       { <-    EAX pointer to PChar    }

       TEST    EAX,EAX
       JE      @@handle0
       RET
{$IFDEF PIC}
@@handle0:
       JMP     PICEmptyString
{$ELSE}
@@zeroByte:
       DB      0
@@handle0:
       MOV     EAX,offset @@zeroByte
{$ENDIF}
end;
{$ENDIF}


> попробуй по настоящему обмануть оптимизатор

{$O-}


 
icWasya ©   (2015-08-10 16:58) [23]

>[9]сделай мне переменную стринг чтобы она была в нил
Если переменная стринг является полем класса, то сразу после NewInstance она содержит как раз nil

> [11]
>var
>S: String;

>сразу после этого съедено пять байт минимум.
>при выходе из процы они будут освобождены.
>и если нет указателя или он нил, то как менеджер памяти будет чистить распределенное?
Во первых, даже если в S будет указатель на пустую строку, которая, внезапно не пять(длина строки-4 + завершающий байт-1) а девять(4+1+счётчик ссылок-4)(а в последних версиях еще +кодовая страница+размер элемента), то эта строка не выделяется из кучи, а находится в секции констант, и при выходе из функции не удаляется.
> как менеджер памяти будет чистить распределенное?
Если указатель равен nil, то FreeMem ничего не будет делать от слова вообще


 
кгшзх ©   (2015-08-10 18:05) [24]

Если указатель равен nil, то FreeMem ничего не будет делать от слова вообще

зачем так долго тормозить?
стринговая переменная или есть, или ее выбросил оптимизатор и ее нет вообще.
если она не выброшена оптимизатором, память под нее распределена.
длина самой строки + 5 байт.
где тут нил?

при чем здесь Pointer(s) , если PChar не нил (самый первый пример)?

var i : integer;
i := 0;
Pointer(i) скажет что оно нил. Вы мне после этого скажете что значит и i в ниле?


 
han_malign ©   (2015-08-11 10:35) [25]


> стринговая переменная или есть, или ее выбросил оптимизатор и ее нет вообще.

- боюсь Вы путаете понятия - переменная, константа и литерал...
А так же не можете отличить скалярный тип от сложносоставного, и вряд ли сможете понять, что в Dephi словосочетание "магия компилятора" - это вполне официальный термин...

З.Ы. И как говорит Задорнов - "Я не знаю что сейчас с вами будет..." - есть еще ShortString - который - "страаашно сказааать..." - реализован совершенно по другому...


 
кгшзх ©   (2015-08-11 17:37) [26]

не надо бояться.
в [1] приведен пример


 
han_malign ©   (2015-08-12 13:21) [27]


> в [1] приведен пример

- а в [22] приведён ответ - для тех кто "не боится".
для особо "смелых" приведу код эквивалентный [1]
var s : string;
begin
s := "";
if _LStrToPChar(s) <> nil then
 ShowMessage("А ты точно только только в асме не силен?")
end;

- самые бессстрашные могут проверить, нажав в отладчике [Ctrl+Alt+C]...


 
KSergey ©   (2015-08-27 22:21) [28]

Короче, рассказываю, я этот момент изучал когда-то.
Тогда мне думалось, что если я сделаю глобальную переменную на приложение
  gEmptyStr := "";

и все присвоения, если мне понадобится пустая строка, или сравнения буду делать с ней - то всё будет клёво, у меня "один экземпляр пустой строки".

Так вот, самое быстрое - это именно простое

if s = "" then

В этом случае компилятор прямо по месту генерирует простейшие код почти одной командой, где просто фактически проверяет значение указателя строки на 0, т.к. компилятор знает, что пустые строки - это указатель равный 0. В общем про "" компилятор явно знает и отлично оптимизирует.

И "обнуление строки" (в смысле присваивание ей пустой строки) - это тоже прямо так и писать
str := "";

тогда компилятор просто освобождает память из-под строки и указателю на неё присваивает 0.

Если если делать как я (через гло. переменную)  - то это вырождается в довольно длинный код, где берутся указатели на 2 сравнваемые строки, проветяется а не равен ли какой-то из них 0 н уи прочие вокруг этого свистопляски.

Если написать
 if Length(s) = 0

то всё равно вызовется подпрограмма реализации _Length (что уже не быстро), в ней первым делом проверится "а не равно ли переданное значение указателя на строку 0, если равно - то результат функции 0 и выход", потом по возвращении мы результат сравниваем по написанному нами условию... короче снова свистопляска намного длиннее чем то, что генерирует компилятор для if s = "".



Страницы: 1 вся ветка

Форум: "Начинающим";
Текущий архив: 2017.04.16;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.53 MB
Время: 0.002 c
2-1439629079
RZD
2015-08-15 11:57
2017.04.16
Мерцает (подмаргивает) крупный текст на панели.


2-1440148888
lewka
2015-08-21 12:21
2017.04.16
BorderStyle для MDIForm


2-1438940029
vadim83
2015-08-07 12:33
2017.04.16
Строка, лучший способ сравнения


2-1439881198
shunya
2015-08-18 09:59
2017.04.16
Ошибка в Twebbrowser


2-1440748056
АИК
2015-08-28 10:47
2017.04.16
Отправка документа на отдельный принтер





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский