Есть ли безопасная версия strlen? - PullRequest
24 голосов
/ 09 мая 2011

std :: strlen не обрабатывает строки c, которые не заканчиваются на \ 0.Существует ли его безопасная версия?

PS Я знаю, что в c ++ вместо строк c следует использовать std :: string, но в этом случае моя строка хранится в общей памяти.

РЕДАКТИРОВАТЬ

Хорошо, мне нужно добавить некоторые пояснения.

Мое приложение получает строку из общей памяти (которая имеет некоторую длину), поэтому она может быть представлена ​​в виде массива символов.Если в библиотеке есть ошибка, пишущая эту строку, строка не будет заканчиваться нулем, и strlen может завершиться ошибкой.

Ответы [ 10 ]

15 голосов
/ 10 мая 2011

Вы добавили, что строка находится в общей памяти. Это гарантированно читаемо и имеет фиксированный размер. Поэтому вы можете использовать size_t MaxPossibleSize = startOfSharedMemory + sizeOfSharedMemory - input; strnlen(input, MaxPossibleSize) (обратите внимание на дополнительные n в strnlen).

Возвращается MaxPossibleSize, если в общей памяти нет \0 после input или длины строки, если она есть. (Максимально возможная длина строки, конечно, MaxPossibleSize-1, в случае, если последний байт разделяемой памяти является первым \0)

12 голосов
/ 09 мая 2011

C-строки, которые не заканчиваются нулем, не являются C-строками, это просто массивы символов, и нет способа найти их длину.

8 голосов
/ 09 мая 2011

Если вы определяете к-строку как

char* cowSays = "moo";

тогда вы автоматически получите '\ 0' в конце и strlen вернет 3. Если вы определите его как:

char iDoThis[1024] = {0};

вы получаете пустой буфер (и массив символов, каждый из которых является нулевым символом). Затем вы можете заполнить его тем, что вам нравится, до тех пор, пока вы не превысите длину буфера. В начале strlen вернет 0, и как только вы что-то напишите, вы также получите правильный номер из strlen.
Вы также можете сделать это:

char uhoh[100];
int len = strlen(uhoh);

но это было бы плохо, потому что вы понятия не имеете, что находится в этом массиве. Это может ударить нулевой символ, который вы не можете. Дело в том, что нулевой символ - это определенный стандартный способ , чтобы объявить, что строка закончена.
Отсутствие нулевого символа означает по определению , что строка не завершена. Изменение этого нарушит парадигму работы строки. То, что вы хотите сделать, это создать свои собственные правила. C ++ позволит вам сделать это, но вам придется самостоятельно писать много кода.

EDIT Из вашей недавно добавленной информации, вы хотите сделать цикл по массиву и вручную проверить наличие нулевого символа. Вам также следует выполнить некоторую проверку, если вы ожидаете только символы ASCII (особенно если вы ожидаете буквенно-цифровые символы). Это предполагает, что вы знаете максимальный размер. Если вам не нужно проверять содержимое строки, вы можете использовать одну из функций семейства strnlen: http://msdn.microsoft.com/en-us/library/z50ty2zh%28v=vs.80%29.aspx
http://linux.about.com/library/cmd/blcmdl3_strnlen.htm

6 голосов
/ 23 декабря 2013
size_t safe_strlen(const char *str, size_t max_len)
{
    const char * end = (const char *)memchr(str, '\0', max_len);
    if (end == NULL)
        return max_len;
    else
        return end - str;
}
3 голосов
/ 10 мая 2011

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

То есть, если предположить, что вы знаете длину буфера, в котором находится строка, как насчет

buffer[-1+sizeof(buffer)]=0 ;
 x = strlen(buffer) ; 
  • сделать буфер больше, чем нужно, и вы можете протестировать библиотеку.

    assert(x<-1+sizeof(buffer));
    
0 голосов
/ 20 июня 2017

Как Нейл Баттерворт уже сказал в своем ответе выше: C-строки, которые не заканчиваются символом \ 0, не являются C-строками!

Единственный шанс, который у вас есть, - это написать неизменяемый адаптер или что-то, что создает действительную копию строки C с завершающим символом \ 0.Конечно, если ввод неправильный и есть C-строка, определенная как:

char cstring[3] = {'1','2','3'};

действительно приведет к неожиданному поведению, потому что сейчас в памяти может быть что-то вроде 123@4x\0.Таким образом, результат функции strlen (), например, теперь равен 6, а не 3. Как и ожидалось.

Следующий подход показывает, как создать безопасную строку C в любом случае:

char *createSafeCString(char cStringToCheck[]) {
    //Cast size_t to integer
    int size = static_cast<int>(strlen(cStringToCheck)) ;
    //Initialize new array out of the stack of the method
    char *pszCString = new char[size + 1];
    //Copy data from one char array to the new
    strncpy(pszCString, cStringToCheck, size);
    //set last character to the \0 termination character
    pszCString[size] = '\0';
    return pszCString;
}

Это гарантирует, что если вы манипулируете C-String, чтобы не записывать в память что-то еще.

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

0 голосов
/ 15 июня 2015

Как насчет этого портативного самородка:

int safeStrlen(char *buf, int max)
{
   int i;
   for(i=0;buf[i] && i<max; i++){};
   return i;
}
0 голосов
/ 20 января 2014

простое решение:

buff[BUFF_SIZE -1] = '\0'

ofc. Это не скажет вам, была ли строка изначально длиной точно BUFF_SIZE-1 или она просто не была завершена ... так что для этого вам нужна логика xtra

0 голосов
/ 21 февраля 2012

Если вам нужно получить размер разделяемой памяти, попробуйте использовать

// get memory size
struct shmid_ds shm_info;
size_t shm_size;
int shm_rc;
if((shm_rc = shmctl(shmid, IPC_STAT, &shm_info)) < 0)
    exit(101);
shm_size = shm_info.shm_segsz;

Вместо использования strlen вы можете использовать shm_size - 1, если вы уверены, что оно завершено нулем.В противном случае вы можете завершить его нулевым значением data [shm_size - 1] = '\ 0';затем используйте strlen (data);

0 голосов
/ 09 мая 2011

Вам нужно будет закодировать вашу строку. Например:

struct string
{
    size_t len;
    char *data;
} __attribute__(packed);

Затем вы можете принять любой массив символов, если вы знаете, что первым размером байта (size_t) места в разделяемой памяти является размер массива char. Это сложно, когда вы хотите связать массивы таким образом.

Лучше довериться другому концу, чтобы завершить его строки или бросить свой собственный strlen, который не выходит за границы сегмента разделяемой памяти (при условии, что вы знаете хотя бы размер этого сегмента).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...