Страниц: 1 [2] 3
  Печать  
Автор Тема: класс string из C++  (Прочитано 29913 раз)
dmi
QOR.Admin
*****
Offline Offline

Сообщений: 470



Просмотр профиля
« Ответ #15 : Мая 05, 2002, 03:48:00 am »


Olej пишет:
О, чёрт! to dmi: как славно редактор форума реагирует на одиночную литеральную кавычку ! Sorry - читайте предыдущую реплику ТАК .


А можно поподробнее( и лучше почтой) ? Этот форум вообще очень странно реагирует на некоторые символы, специфичные для РНР
Записан
klalafuda
QOR.Team
****
Offline Offline

Сообщений: 1


Просмотр профиля
« Ответ #16 : Мая 16, 2002, 02:39:00 am »


Olej пишет:
Вопрос законный. char* & char[] - это безусловно "тяжёлое наследие" C, да и в C это пришло из B & BCPL. Ошибки операций с char* - это добрая половина всех ощибок в C++: выход за границы... Да и вообще, кто полностью внятно может объяснить, и описать все побочные эффекты, и различия вот таких фрагментов:

char* Func( void ) {
 char *S = ".......";
 ......
 return S;
};
или
char* Func( void ) {
 static char *S = ".......";
 ......
 return S;
};
или
char* Func( void ) {
 char *S = ".......";
 ......
 return strdup( S );
};

И все отличия их толкований в зависимости от компиляторов?
Это не я попридумал - смотрите Страуструпа (хоть меня тут и пинали Страуструпом) - он единственный автор этого языка!


1) выделение internal="..." в CONST, помещение указателя internal в локальный S,в результате -  возращение указателя на internal="..."
2) выделение internal="..." в CONST, выделение S в BSS, помещение inetrnal в S, возвращение указателя на internal="..."
3) см. п.1 + возвращение strdup(internal)

зы: а ноги зачастую при работе "со стрингами" торчат из Pascal

// wbr
Записан
olej
QOR.Team
****
Offline Offline

Сообщений: 42



Просмотр профиля
« Ответ #17 : Мая 16, 2002, 02:23:00 pm »


klalafuda пишет:
1) выделение internal="..." в CONST, помещение указателя internal в локальный S,в результате -  возращение указателя на internal="..."
2) выделение internal="..." в CONST, выделение S в BSS, помещение inetrnal в S, возвращение указателя на internal="..."
3) см. п.1 + возвращение strdup(internal)

Я вскользь (может всуе ) нарисовал эти примеры не в качестве того, что при этом происходит, а интересно - какие последствия это повлечёт для вызывающего фрагмента, где-то там, где:
int n = strlen( Fun() );

Здесь результаты будут различными для различных компиляторов, которые могут постараться "помочь", и из-за искуственности представления строк (размещение  - отдельно, а что считать строкой - определяется содержимым). Чаще всего помощь "умного" компилятора будет медвежьей услугой.

Но из стандарта языка C++ результаты этих трёх близких реализаций будут существенно различные:

1. - аварийный слёт задачи (в QNX - "глухо-немой", так что ещё поковыряться придётся, чтоб локализовать): к моменту использования результата Fun() локально размещённый в стеке объект (из-за выхода за область видимости) - уже разрушен.

2. - здесь всё будет нормально.

3. - здесь тоже нормально, скорее всего (кстати, только с помощью компилятора, который обнаруживает, что на временный объект существует ссылка), но самое интересное начинается после: что дальше делать с временным объектом, который уже использован, когда его разрушать? Это, пожалуй, единственный случай, когда в C++ нужна сборка мусора (об чём как-то обсуждалось в форуме), а поскольку никакие механизмы сборки мусора не упоминаются ни в каких стандартах C++, то тут начинаются "чудеса от реализатора" - каждый делает ... "в меру своей испорченности" .
Записан
klalafuda
QOR.Team
****
Offline Offline

Сообщений: 1


Просмотр профиля
« Ответ #18 : Мая 16, 2002, 07:22:00 pm »


Olej пишет:
Я вскользь (может всуе ) нарисовал эти примеры не в качестве того, что при этом происходит, а интересно - какие последствия это повлечёт для вызывающего фрагмента, где-то там, где:
int n = strlen( Fun() );


последствия: n будет равно вызову strlen(char* Fnc()) не более. если вернется кривая строка то оно конечно да. но она в примерах не вернется.


Здесь результаты будут различными для различных компиляторов, которые могут постараться "помочь", и из-за искуственности представления строк (размещение  - отдельно, а что считать строкой - определяется содержимым). Чаще всего помощь "умного" компилятора будет медвежьей услугой.


пример листинга плиз. желательно, отличного от гнуси.


Но из стандарта языка C++ результаты этих трёх близких реализаций будут существенно различные:


ясен пень бо нутро у функций различно. хотя 1 и 2 практически идентичны.


1. - аварийный слёт задачи (в QNX - "глухо-немой", так что ещё поковыряться придётся, чтоб локализовать): к моменту использования результата Fun() локально размещённый в стеке объект (из-за выхода за область видимости) - уже разрушен.


неправда.


2. - здесь всё будет нормально.


еще бы.


3. - здесь тоже нормально, скорее всего (кстати, только с помощью компилятора, который обнаруживает, что на временный объект существует ссылка), но самое интересное начинается после: что дальше делать с временным объектом, который уже использован, когда его разрушать? Это, пожалуй, единственный случай, когда в C++ нужна сборка мусора (об чём как-то обсуждалось в форуме), а поскольку никакие механизмы сборки мусора не упоминаются ни в каких стандартах C++, то тут начинаются "чудеса от реализатора" - каждый делает ... "в меру своей испорченности" .


дык ить что за дурная практика - выделять память не контролируя ее дальнейшее исользование ? ясен пень, что вернется копия s на куче. но такие вещи указываются в докуметнтации на вызов данной fnc3().

в качестве примера, даем main.c:

#include <stdlib.h>
#include <string.h>

char *fnc1(void) {
   char *s = "test string";
   /* ... */
   return s;
} /* fnc1 */

char *fnc2(void) {
   static char *s = "test string";
   /* ... */
   return s;
} /* fnc2 */

char *fnc3(void) {
   char *s = "test string";
   /* ... */
   return strdup(s);
} /* fnc3 */

watcom 10.6B дает:

cc -c -Oneatx -5r -w9 main.c

.386p
               NAME    main.c
               EXTRN   strdup_ :BYTE
DGROUP          GROUP   CONST,CONST2,_DATA,_BSS
_TEXT           SEGMENT PARA PUBLIC USE32 'CODE'
               ASSUME  CS:_TEXT ,DS:DGROUP,SS:DGROUP
               PUBLIC  fnc1_
               PUBLIC  fnc2_
               PUBLIC  fnc3_
               PUBLIC  test_

fnc1_:          mov     eax,offset DGROUP:L1
               ret
               mov     eax,eax
fnc2_:          mov     eax,dword ptr L2
               ret
               mov     eax,eax
fnc3_:          mov     eax,offset DGROUP:L1
               jmp     near ptr strdup_
               mov     eax,eax
_TEXT           ENDS

CONST           SEGMENT DWORD PUBLIC USE32 'DATA'
L1              DB      74H,65H,73H,74H,20H,73H,74H,72H
               DB      69H,6eH,67H,00H
CONST           ENDS

CONST2          SEGMENT DWORD PUBLIC USE32 'DATA'
CONST2          ENDS

_DATA           SEGMENT DWORD PUBLIC USE32 'DATA'
L2              DD      DGROUP:L1
_DATA           ENDS

_BSS            SEGMENT DWORD PUBLIC USE32 'BSS'
_BSS            ENDS

               END

msvc6 дает:

cl /c -/GB main.c

.386p
               NAME    main
               EXTRN   _strlen :BYTE
               EXTRN   _strdup :BYTE
.drectve        SEGMENT BYTE PUBLIC USE32 ''
               DB      2dH,64H,65H,66H,61H,75H,6cH,74H
               DB      6cH,69H,62H,3aH,4cH,49H,42H,43H
               DB      20H,2dH,64H,65H,66H,61H,75H,6cH
               DB      74H,6cH,69H,62H,3aH,4fH,4cH,44H
               DB      4eH,41H,4dH,45H,53H,20H
.drectve        ENDS

.text           SEGMENT PARA PUBLIC USE32 ''
               ASSUME  CS:.text ,DS:DGROUP,SS:DGROUP
               PUBLIC  _fnc1
               PUBLIC  _fnc2
               PUBLIC  _fnc3
               PUBLIC  _test

_fnc1:          push    ebp
               mov     ebp,esp
               push    ecx
               mov     dword ptr -4H[ebp],offset `.data`
               mov     eax,dword ptr -4H[ebp]
               mov     esp,ebp
               pop     ebp
               ret    

_fnc2:          push    ebp
               mov     ebp,esp
               mov     eax,dword ptr `_?s@?1??fnc2@@9@9`
               pop     ebp
               ret    

_fnc3:          push    ebp
               mov     ebp,esp
               push    ecx
               mov     dword ptr -4H[ebp],offset `$SG620`
               mov     eax,dword ptr -4H[ebp]
               push    eax
               call    near ptr _strdup
               add     esp,00000004H
               mov     esp,ebp
               pop     ebp
               ret    
.text           ENDS

.data           SEGMENT DWORD PUBLIC USE32 ''
               DB      74H,65H,73H,74H,20H,73H,74H,72H
               DB      69H,6eH,67H,00H
`_?s@?1??fnc2@@9@9` LABEL BYTE
               DD      `$SG615`
`$SG615`        DB      74H,65H,73H,74H,20H,73H,74H,72H
               DB      69H,6eH,67H,00H
`$SG620`        DB      74H,65H,73H,74H,20H,73H,74H,72H
               DB      69H,6eH,67H,00H
.data           ENDS

               END

что в сущьности и следовало ожидать

// wbr

зы: еще бы кто сказал, как втавлять тэг <pre> на текс с кодом..


[ Это Сообщение было отредактировано: klalafuda в 2002-05-16 16:30 ]
Записан
olej
QOR.Team
****
Offline Offline

Сообщений: 42



Просмотр профиля
« Ответ #19 : Мая 17, 2002, 03:24:00 pm »


klalafuda пишет:
неправда.

Ну почему "неправда"? Правда, правда ... .
Я, правда, виноват в том, что пример написал в спешке, чтоб только идею продемонстрировать, а должно бы быть, например, так (чтоб показать то, для чего это писалось):

char* Fun( void ) {
  char sS[ 50 ] = "Bad string";
  return (char*)&sS;
};

void main( void ) {
  char *sD = Fun();
  cout << sD << endl;
};

Я специально этот примерчик собрал и проверил qcc: если вам повезло - выведет ахинею, если нет - слёт. Время жизни локальной переменной - расклад, батенька.

Почему если собрать пример типа:

  strcpy( xx, Fun() );
  cout << xx << endl;

всё (казалось бы) работает: да потому, что нам повезло - локальная переменная sS в стеке в Fun уже ликвидирована (но содержимое - не попорчено!), а мы используем указатель, который указывает на то место которое было стеком, нарушение защиты не возникает, а содержимое там - старое.


ясен пень бо нутро у функций различно. хотя 1 и 2 практически идентичны.

Так что не идентичны.

Я выходные листинги компилера не стал сознательно смотреть - муторно это, но, самое главное, всё это - вопросы семантики языка и языка (к счастью) хорошо стандартизированного. И разгребать семантическое поведение той или иной синтаксической конструкции корректно только с позиции стандарта языка. Если даже 33 компилятора все как один говорят "да", а стандарт требует "нет", то отсюда следует только то, что все 33 компилятора - глюкавые.

Случай 3 который я написал - это тоже не искуственная выдумка. Всё становится ещё хуже запущенным, когда мы подходим к классам:

class XXX {
  char* Fun( void );
};
char* XXX::Fun( void ){
  char* s = "YYYYY";
  return s;
};

тут всё гораздо хуже - это в рассматриваемой выше ситуации просто не работает! Во многих (аналогичных, но более сложных) случаях одним из решений и может быть:

char* XXX::Fun( void ){
  char* s = "YYYYY";
  return strdup( s );
};

и хорошо, если я использую это просто:

  XXX Z();
  ...
  char* s = Z.Fun();
  ...
  delete s;

а если я хочу писать нечто в стиле функционально программирования (LISP):

Fun1( Fun2( Z.Fun() ), Fun3( Z.Fun() ) );

всё - "утечка памяти". Над этим (время жизни и правила уничтожения объектов, в том числе и char* привнесённым в классы и временные объекты) - комитет ANSI бился 2 года и нашёл к 1993г. ... 7 (!) взаимоисключающих решения:
- сразу после первого использования;
- в конце предложения;
- в следующей точке ветвления;
- в конце блока (начальное, раннее правило C++);
- в конце функции;
- после последнего вхождения (неявно предполагается сборка мусора);
- где-то между первым использованием и концом блока (документ "The Annotated C++ Reference Manual");
И в конце-концов, в стандарт ANSI вошло правило 2 с рекомендацией иметь необязательную сборку мусора.

В частности, это всё отголоски той же песни о char* в C++ как "тяжёлом наследии C" - если один из самых, пожалуй, употребимых и простейших типов данных порождает проблемы, которые нужно "разгребать", то такому типу нужно искать замену.
Записан
klalafuda
QOR.Team
****
Offline Offline

Сообщений: 1


Просмотр профиля
« Ответ #20 : Мая 17, 2002, 08:18:00 pm »


Olej пишет:
Ну почему "неправда"? Правда, правда ... .
Я, правда, виноват в том, что пример написал в спешке, чтоб только идею продемонстрировать, а должно бы быть, например, так (чтоб показать то, для чего это писалось):

char* Fun( void ) {
  char sS[ 50 ] = "Bad string";
  return (char*)&sS;
};

void main( void ) {
  char *sD = Fun();
  cout << sD << endl;
};

Я специально этот примерчик собрал и проверил qcc: если вам повезло - выведет ахинею, если нет - слёт. Время жизни локальной переменной - расклад, батенька.


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

char *fnc1(void) {
   char *s = "test string";
   return (char*)&s;
} /* fnc1 */

.386p
               NAME    main.cpp
DGROUP          GROUP   CONST,CONST2,_DATA,_BSS
_TEXT           SEGMENT PARA PUBLIC USE32 'CODE'
               ASSUME  CS:_TEXT ,DS:DGROUP,SS:DGROUP
               PUBLIC  `W?fnc1$n()pna`
`W?fnc1$n()pna`:
            push    edx
               sub     esp,00000004H
               mov     edx,offset DGROUP:L1
               mov     eax,esp
               mov     dword ptr [esp],edx
               add     esp,00000004H
               pop     edx
               ret    
_TEXT           ENDS

CONST           SEGMENT DWORD PUBLIC USE32 'DATA'
L1              DB      74H,65H,73H,74H,20H,73H,74H,72H
               DB      69H,6eH,67H,00H
CONST           ENDS

CONST2          SEGMENT DWORD PUBLIC USE32 'DATA'
CONST2          ENDS

_DATA           SEGMENT DWORD PUBLIC USE32 'DATA'
_DATA           ENDS

_BSS            SEGMENT DWORD PUBLIC USE32 'BSS'
_BSS            ENDS

               END

main.cpp(7): Warning! W008: (col 22) returning address of function argument or of auto or register variable

SIC!

so если предупреждение компилятора не подействовало - пеняйте на себя..

зы: ну зануда я, зануда.. иногда

// wbr

Записан
olej
QOR.Team
****
Offline Offline

Сообщений: 42



Просмотр профиля
« Ответ #21 : Мая 17, 2002, 09:01:00 pm »


klalafuda пишет:
конструкция &sS даст адрес локальной переменной что суть есть смещение в стеке, которое будет невалидно после выхода из функции.

Я для того, чтоб сказать это, собственно и писал вариант 1. А то, что в текст вкралось:

  char sS[ 50 ] = "Bad string";
  return (char*)&sS;

так это ... побочный продукт ... процесса редактирования. Оно ничем по смыслу не отличается от первоначального, что в лоб, что по лбу:

char* Fun( void ) {
  char* sS = "Bad string";
  return sS;
};

Об этом и весь разговор (в связи с тем, что говорилось в начале темы): конструкции с char* - далеко небезопасны, а если эти char* внутри класса - то просто караул. А это значит, что, по крайней мере в приложениях, где идёт активная трансформация текстовых строк, для представления строк целесообразно создать новый класс для их представления.

Записан
klalafuda
QOR.Team
****
Offline Offline

Сообщений: 1


Просмотр профиля
« Ответ #22 : Мая 17, 2002, 09:31:00 pm »


Olej пишет:
Я для того, чтоб сказать это, собственно и писал вариант 1. А то, что в текст вкралось:

  char sS[ 50 ] = "Bad string";
  return (char*)&sS;

так это ... побочный продукт ... процесса редактирования.


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


Оно ничем по смыслу не отличается от первоначального, что в лоб, что по лбу:

char* Fun( void ) {
  char* sS = "Bad string";
  return sS;
};



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


Об этом и весь разговор (в связи с тем, что говорилось в начале темы): конструкции с char* - далеко небезопасны, а если эти char* внутри класса - то просто караул. А это значит, что, по крайней мере в приложениях, где идёт активная трансформация текстовых строк, для представления строк целесообразно создать новый класс для их представления.


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


static void fnc(const char *Msg) {
   char MsgBuf[256];
   sprintf(MsgBuf,"User message %s",Msg);
   /* ... */
} /* fnc */


..может написать только человек недалекий. а, кстати, сплошь и рядом. и самое последнее дело в приведенном примере говорить что мол "реализация строк в С есть кривость и вообще масдай". разработчик сам допустил ошибки - сам и виноват. а не компилятор С ну собссно в том-же духе и далее по тексту. и, кстати, переход на С++ ну ничем не поможет, скорее, только усугубит ситуацию. бо система становится существенно сложнее.

// wbr
Записан
ed1k
QOR.Moderator
*****
Offline Offline

Сообщений: 739


Просмотр профиля WWW
« Ответ #23 : Мая 20, 2002, 07:07:00 pm »


Olej пишет:
конструкции с char* - далеко небезопасны

Ей-богу, по своей простоте не понимаю... В Си вообще все опасно! Верьте мне! Чем указатель на char отличается от любого другого указателя? Ошибка не в том же, как строка представляется в Си, а в том, что Вы получили невалидный указатель!!! Проблема оказалась в языке - он позволяет Вам получить невалидный указатель. И мне нравиться возможность иметь и пользовать указатели, а также возможность любых указателей - хоть валидных хоть инвалидных - это всецело моя забота, и я рад этому. Решение есть одно - использовать другой язык программирования, только не надо хлопотать об том, чтобы С++ урезать до безопасного состояния дел (ну там запретить указатели и ссылки, как опасные элементы языка), а нужно выбрать что-нибудь радикальное и безопасное. Вы знаете, о чем я Я верю, что есть языки, где заботятся о культуре программирования программиста и его граммотности (точнее об отсутстсвии таковых) и везьде солома, чтобы мягче было... мусоровозы ездят... собаки ходят... ну, в общем, все фичи сладкой жизни.
Записан
klalafuda
QOR.Team
****
Offline Offline

Сообщений: 1


Просмотр профиля
« Ответ #24 : Мая 20, 2002, 08:39:00 pm »


ed1k пишет:

Olej пишет:
конструкции с char* - далеко небезопасны

Ей-богу, по своей простоте не понимаю... В Си вообще все опасно! Верьте мне! Чем указатель на char отличается от любого другого указателя? Ошибка не в том же, как строка представляется в Си, а в том, что Вы получили невалидный указатель!!! Проблема оказалась в языке - он позволяет Вам получить невалидный указатель. И мне нравиться возможность иметь и пользовать указатели, а также возможность любых указателей - хоть валидных хоть инвалидных - это всецело моя забота, и я рад этому. Решение есть одно - использовать другой язык программирования, только не надо хлопотать об том, чтобы С++ урезать до безопасного состояния дел (ну там запретить указатели и ссылки, как опасные элементы языка), а нужно выбрать что-нибудь радикальное и безопасное. Вы знаете, о чем я Я верю, что есть языки, где заботятся о культуре программирования программиста и его граммотности (точнее об отсутстсвии таковых) и везьде солома, чтобы мягче было... мусоровозы ездят... собаки ходят... ну, в общем, все фичи сладкой жизни.


)) где-то так.

// wbr
Записан
olej
QOR.Team
****
Offline Offline

Сообщений: 42



Просмотр профиля
« Ответ #25 : Мая 20, 2002, 09:35:00 pm »


ed1k пишет:
Ей-богу, по своей простоте не понимаю... В Си вообще все опасно! Верьте мне!

Это хорошо ... . Нужно только расширить подмножество: "В жизни вообще все опасно! Верьте мне!"

Чем указатель на char отличается от любого другого указателя? Ошибка не в том же, как строка представляется в Си, а в том, что Вы получили невалидный указатель!!!

Указатель на char не отличается от любого другого указателя ничем! Но когда мы придаём массиву байт (а с UNICODE всё становится гораздо хуже) особый смысл: "что та часть массива, которая предшествует (одиночные кавычки я уже боюсь в этом форуме ставить, и вам не советую... ) - то это текстовая строка" - вот эта искусственная конструкция уже несколько отличается, не правда ли? А если этого "нигде и никогда" не найдётся в этом массиве? Воспроизводится эта ситуация в отношении других указателей?  

Проблема оказалась в языке - он позволяет Вам получить невалидный указатель.

Т.е. не совсем так - я могу иметь абсолютно валидный указатель char*, и корректно размещённый массив char[...], но только там не случился, и при элементарном strlen() я получаю ошибку защиты памяти.

и везьде солома, чтобы мягче было...

... и коровки, коровки, коровки ...
Записан
ed1k
QOR.Moderator
*****
Offline Offline

Сообщений: 739


Просмотр профиля WWW
« Ответ #26 : Мая 20, 2002, 10:21:00 pm »


Olej пишет:
Указатель на char не отличается от любого другого указателя ничем! Но когда мы придаём массиву байт (а с UNICODE всё становится гораздо хуже) особый смысл: "что та часть массива, которая предшествует   (одиночные кавычки я уже боюсь в этом форуме ставить, и вам не советую... ) - то это текстовая строка" - вот эта искусственная конструкция уже несколько отличается, не правда ли? А если этого   "нигде и никогда" не найдётся в этом массиве? Воспроизводится эта ситуация в отношении других указателей?  

Не вижу где отличается и вполне воспроизводиться. Все зависит от Вас и Вашей фантазии. Как раз в проекте над которым работаю - у меня полно структур-описателей параметров устройства. Так вот, так как один параметр может иметь много полей, то последний элемент моей структуры - указатель на следующий описатель, который соответсвует следующему полю параметра, или 0, если это последний дескриптор в цепочке. Поставте каким-то боком не ноль, а плохой адрес в конце последнего дескриптора - система вылетит. Но я не ставлю - и она работает. Проблема в слове "а если..." - а вот этого я насмотрелся... Тут до нас парень один работал, так он периодически, на всякий случай, переинициализирует переферийные устройства... еще много финтов делает, вдруг процессор проглючит... В общем, все переделать пришлось. Ну да ладно. Прием с завершающим нулем очень старый и обсуждать насколько он хорош или плох здесь не совсем уместно. А по поводу искусственности конструкций - так все вокруг такое... мы же не ботаники, а программисты Если правильно работать с указателями, то компилятор и все строковые функции делают всю работу с заключительным нулем.

Т.е. не совсем так - я могу иметь абсолютно валидный указатель char*, и корректно размещённый массив char[...], но только   там не случился, и при элементарном strlen() я получаю ошибку защиты памяти.

А понял! Функция strlen() плохая Ну напишите свою (правда, если длину будете где-то хранить, скажем в начале массива, то резонный вопрос: "а что если запортится эта длина?"), IMHO ноль завершающий должен быть - мы не в казино, на расклад тут нечего пенять
Записан
olej
QOR.Team
****
Offline Offline

Сообщений: 42



Просмотр профиля
« Ответ #27 : Мая 21, 2002, 03:00:00 pm »


ed1k пишет:
Так вот, так как один параметр может иметь много полей, то последний элемент моей структуры - указатель на следующий описатель, который соответсвует следующему полю параметра, или 0, если это последний дескриптор в цепочке.

Ну так это ж другое дело - односвязный динамический список - любимая структура (и всем кто его ещё не любит советую полюбить ). Но вот как-раз самый малотрудоёмкий, в конечном итоге, способ работы с односвязными списками (по крайней мере, если пользовать C++) - это:
1. сначала сделать для списка класс с требуемым набором методов: Add(), Remove(), Replace()...., потерять на это время, но довести до ума;
2. ...беспроблемно реализовывать целевую задачу.

Этот пример как раз подкрепляет моё утверждение (или предложение) из-за которого и весь сыр-бор: хотите не иметь проблем с разгребанием текстовых строк - постройте класс, близкий к char*, но поддерживающий контроль и соответствие размещения и содержимого (т.е. вот тот будет всегда присутствовать, не будет находиться за пределами выделенной памяти...). Для малого объёмов символьной обработки - там командную строку разгрести или что-то - это сомнительно целесообразно, а вот если много (по-настоящему много: синтаксический анализатор, построение разборщика какого-то непроцедурного языка описания данных) - выигрыш налицо.


Поставте каким-то боком не ноль, а плохой адрес в конце последнего дескриптора - система вылетит. Но я не ставлю - и она работает.

А я, как альтернатива, пишу более высокоуровневые методы класса, которые просто не позволяют это сделать, пользуюсь только ними - и она тоже работает.


А понял! Функция strlen() плохая Ну напишите свою (правда, если длину будете где-то хранить, скажем в начале массива, то резонный вопрос: "а что если запортится эта длина?"),

Ну я вообще только об том и говорил: инкапсулируйте char* в класс, скажем, String; сделайте операцию преобразования (явного или неявного) String к char* - так и функций новых делать не надо - все классические str* будут работать, но только корректно (или в некорректных случаях сообщать об ошибке, а ещё лучше - возбуждать исключение). Из новых функций можно бы добавить только для удобства: "+" - конкатенация, "+=" - конкатенация на месте и т.д.

Всё это - дело вкуса и удобства, и говорим мы уже почти все и всё - об одном и том же. Есть предложение - давайте завершать это обсуждение, которое всем уже ясное. Если кому интересно посмотреть или поиграться - я образец такого класса String могу бросить мэйлом, или сюда в тему. Тот же char*, но - удобно.
Записан
Evgeniy
Jr. Member
**
Offline Offline

Сообщений: 73


Просмотр профиля
« Ответ #28 : Мая 21, 2002, 11:34:00 pm »

Не хотел ввязываться в спор, но вопрос поднялся на такие теоретические высоты (или погрузился к фундаменту? ), что больше нет сил сдерживаться

"Проблема С-строки", как мне кажется, заключается в общем подходе к массивам в С, когда массив тождественнен указателю на первый элемент массива. Ошибочность такого подхода отмечалась очень давно - практически с момента начала анализа первых реализаций Фортрана, но увы...


Указатель на char не отличается от любого другого указателя ничем! Но когда мы придаём массиву байт ... особый смысл: "что та часть массива, которая предшествует - то это текстовая строка" - вот эта искусственная конструкция уже несколько отличается, не правда ли?


Olej, позволю себе сделать одно уточнение : в данном случае речь идет о весьма искуственной попытке построить массив с переменной верхней границей в языке, изначально не имеющем никаких средств описания массивов с вычисляемыми границами. В подобной ситуации принципиально есть два решения - опасное, которое принято в С и безопасное, принятое в виртовской интерпритации Pascal - ввести для строк специальный, отличный от простого массива символов, контролируемый тип строки.
Оба решения - это полумеры, но одно из них более опасно.


Теперь по поводу вашего класса строк (как я его понимаю, исходя из многократных ваших упоминаний). А точнее, по поводу приводимости вашего String к char*.
Мне это напоминает сам C++, а именно: ввести жесткий контроль типов на уровне классов при сохранении полной совместимости с С и думать, что язык стал безопаснее, чем С. Интересно, как будет работать весь ваш контроль после применения стандартных сишных функций, изменяющих содержимое (а значит и, возможно, длину) вашей строки - например, strcpy(...) или scanf("%s"...) ? Неужели вы всерьез считаете, что при этом ваш String более надежен, чем простой char* ?


ed1k пишет:
...И мне нравиться возможность иметь и пользовать указатели, а также возможность любых указателей - хоть валидных хоть инвалидных - это всецело моя забота, и я рад этому.

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

А безопасные (относительно, конечно) языки существуют - например в моем любимом Алголе-68 просто невозможно получить "инвалидный" указатель - вы просто не сможете написать такую программу . Ну, разве это плохо?
Записан
MaxShan
Участник
*
Offline Offline

Сообщений: 2


Просмотр профиля
« Ответ #29 : Мая 26, 2002, 09:11:00 pm »


Olej пишет:
Если кому интересно посмотреть или поиграться - я образец такого класса String могу бросить мэйлом, или сюда в тему. Тот же char*, но - удобно.


Безусловно интересно. Этот класс так много обсуждался в разных темах.
А почему бы не выложить его на ftp.qnx.org.ru в Вашу папку?  
Записан
Страниц: 1 [2] 3
  Печать  
 
Перейти в: