Использование NULL в качестве терминатора в массивах? - PullRequest
0 голосов
/ 02 декабря 2010

Мне нравится «изобретать велосипед» в учебных целях, поэтому я работаю над контейнерным классом для строк.Будет ли использование символа NULL в качестве ограничителя массива (т. Е. Последнее значение в массиве будет NULL) вызывать помехи для строк с нулевым символом в конце?

Я думаю, что это будет проблемой, только еслидобавлена ​​пустая строка, но я могу что-то упустить.

РЕДАКТИРОВАТЬ: Это на C ++.

Ответы [ 7 ]

2 голосов
/ 02 декабря 2010

Это зависит от того, какую строку вы храните.

Если вы храните строки в стиле C, которые в основном являются указателями на символьные массивы (char*), есть разница междуNULL значение указателя и пустая строка.Первый означает, что указатель «пустой», последний означает, что указатель указывает на массив, который содержит один элемент со значением символа 0 ('\0').Таким образом, указатель все еще имеет значение, и его тестирование (if (foo[3])) будет работать, как и ожидалось.

Если вы храните строки стандартной библиотеки C ++ типа string, то * 1010 нет* значение.Это связано с тем, что указатель отсутствует, а тип string рассматривается как одно значение.(Принимая во внимание, что указатель технически не так, но может рассматриваться как ссылка.)

2 голосов
/ 02 декабря 2010

Я думаю, что вы в замешательстве. В то время как C-строки имеют нулевое окончание, символа «NULL» нет. NULL - это имя для нулевого указателя . Терминатором для C-строки является нуль символ , то есть байт со значением ноль. В ASCII этот байт (несколько запутанно) называется NUL.

Предположим, ваш класс содержит массив char, который используется для хранения строковых данных. Вам не нужно «отмечать конец массива»; массив имеет определенный размер, который устанавливается во время компиляции. Вы должны знать, сколько из этого пространства фактически используется; нулевой терминатор в строковых данных выполняет это за вас, но вы можете получить лучшую производительность, фактически запомнив длину. Кроме того, «строковый» класс со статическим размером буфера символов вообще не очень полезен, потому что этот размер буфера является верхним пределом длины строк, которые вы можете иметь.

Таким образом, лучший строковый класс будет содержать указатель типа char*, который указывает на динамически размещаемый (через new[]) массив из char с. Опять же, не имеет смысла «отмечать конец массива», но вы захотите запомнить как длину строки (т. Е. Объем используемого пространства), так и размер выделения (т. Е. Объем пространства, которое может быть использовано перед перераспределением).

2 голосов
/ 02 декабря 2010

Нет, не будет, потому что вы не будете хранить в массиве char, вы будете хранить в массиве char *.

char const* strings[] = {
  "WTF"
, "Am"
, "I"
, "Using"
, "Char"
, "Arrays?!"
, 0
};
2 голосов
/ 02 декабря 2010

"" - это пустая строка в C и C ++, а не NULL.Обратите внимание, что "" имеет ровно один элемент (вместо нуля), что означает, что он эквивалентен {'\0'} как массив char.

char const *notastring = NULL;
char const *emptystring = "";

emptystring[0] == '\0';  // true
notastring[0] == '\0';   // crashes
1 голос
/ 02 декабря 2010

Когда вы копируете из std::string, используйте итераторы begin(), end(), и вам не нужно беспокоиться о NULL - в действительности, NULL присутствует, только если вы вызываете c_str() (в в этом случае блок памяти, на который он указывает, будет иметь значение NULL для завершения строки.) Если вы хотите memcpy, используйте метод data().

0 голосов
/ 02 декабря 2010
#include "Maxmp_crafts_fine_wheels.h"
MaxpmContaner maxpm;
maxpm.add("Hello");
maxpm.add(""); // uh oh, adding an empty string; should I worry?
maxpm.add(0);

На данный момент, как пользователь MaxpmContainer, который не читал вашу документацию, я ожидал бы следующее:

strcmp(maxpm[0],"Hello") == 0;
*maxpm[1] == 0;
maxpm[2] == 0;

Помехи между нулевым терминатором в позиции два и пустой строкой в ​​позиции один исключаются с помощью оператора «интерпретировать это как адрес памяти» *. Первая позиция не будет нулевой; это будет целое число, которое, если вы интерпретируете его как адрес памяти, окажется равным нулю. Вторая позиция будет нулевой, что, если вы интерпретируете ее как адрес памяти, окажется внезапным беспорядочным выходом из вашей программы.

0 голосов
/ 02 декабря 2010

Почему бы вам не следовать шаблону, используемому vector - сохранить количество элементов в вашем классе контейнера, тогда вы всегда будете знать, сколько в нем значений:

vector<string> myVector;

size_t elements(myVector.size());

Создание экземплярастрока с x, где const char* x = 0; может быть проблематичным.Посмотрите этот код в Visual C ++ STL, который вызывается, когда вы делаете это:

_Myt& assign(const _Elem *_Ptr)
    {   // assign [_Ptr, <null>)
    _DEBUG_POINTER(_Ptr);
    return (assign(_Ptr, _Traits::length(_Ptr)));
    }

static size_t __CLRCALL_OR_CDECL length(const _Elem *_First)
    {   // find length of null-terminated string
    return (_CSTD strlen(_First));
    }
...