В настоящее время я переписываю srtcat()
из стандартной библиотеки C и настроил некоторые проверки, чтобы избежать проблем с перекрытием. Проблема в том, что моя программа все равно входит в обработку ошибок.
Вот код:
char *my_strcat(char *restrict dest, const char *restrict src)
{
size_t dest_len = 0, src_len = 0;
char *p = dest;
src_len = my_strlen(src);
if (!dest || !src)
return NULL;
dest_len = my_strlen(dest);
if (src >= dest && src < dest + dest_len) {
return NULL;
}
if (dest >= src && dest < src + src_len) {
return NULL;
}
while (*p != '\0') p++, dest_len++;
if (dest_len + src_len + 1 > sizeof(dest))
return NULL;
p = dest + dest_len;
while (*src != '\0')
*p++ = *src++;
*p = '\0';
return dest;
}
size_t my_strlen(const char *s)
{
size_t count = 0;
if (s != NULL) {
while (*s != 0) {
count++;
s++;
}
}
return count;
}
Я тестировал так:
int main(int argc, char **argv)
{
const char *src = "Hello";
char dest[100] = " world!";
char *test = my_strcat(dest, src);
printf("Src : %s Dest : %s\n", src, dest);
printf("Return adress : %p, Value : %s\n", test, test);
return 0;
}
Согласно gdb
:
if (src >= dest && src < dest + dest_len)
1: dest = 0x7fffffffda70 " world!"
2: src = 0x555555557004 "Hello"
3: dest_len = 0
4: src_len = 5
Src : Hello Dest : world!
Return adress : (nil), Value : (null)
Ты видишь проблему?
Следуя вашим предложениям, я изменил код следующим образом:
char *my_strcat(char *restrict dest, const char *restrict src, size_t d_size)
{
size_t dest_len = 0, src_len = 0;
char *p = dest;
if (!dest || !src)
return NULL;
src_len = my_strlen(src);
dest_len = my_strlen(dest);
if (src >= dest && src < dest + dest_len) {
return NULL;
}
if (dest >= src && dest < src + src_len) {
return NULL;
}
while (*p != '\0') p++, dest_len++;
if (dest_len + src_len + 1 > d_size)
return NULL;
p = dest + dest_len;
while (*src != '\0')
*p++ = *src++;
*p = '\0';
return dest;
}
И в основном : char *test = my_strcat(dest, src, sizeof(dest));
Но все равно не работает:
Src : Hello Dest : world!
Return adress : 0x7fff74bc5650, Value : world!
🤔 А знаете ли вы, что...
C оставляет множество возможностей для оптимизации кода и достижения высокой производительности приложений.
dest_len + src_len + 1 > sizeof(dest)
sizeof(dest)
— размер указателя sizeof(char*)
. Если вы хотите проверить, не переполнится ли dest
, вы должны передать размер в качестве аргумента. Смотрите strlcpy
или strncpy
.
src >= dest
Обратите внимание, что сравнение указателей, которые не указывают на один и тот же массив, технически недопустимо. Чтобы привнести немного валидности, вы можете сделать (uintptr_t)stc >= (uintptr_t)dest
. Как работает сравнение указателей в C? Можно ли сравнивать указатели, которые не указывают на один и тот же массив? Является ли сравнение двух указателей поведением < undefined, если они оба приведены к целочисленному типу? Почему сравнение указателей с неопределенным поведением все равно дает правильные результаты? и др.
Пытаясь направить к пониманию этой проблемы, лучше всего представить, какой должен быть работающий код (для изучения). Иногда слишком много слов просто запутывают ситуацию:
char *my_strcat(char *restrict dest, const char *restrict src, size_t d_size) {
if ( !dest || !src )
return NULL;
size_t src_len = strlen( src );
size_t dest_len = strlen( dest );
if ( dest_len + src_len + 1 > d_size )
return NULL;
char *p = dest + dest_len;
while( (*p++ = *src++ ) != '\0' )
;
return dest;
}
int main() {
const char *src = "Hello";
char dest[100] = " world!";
printf("Src : %s Dest : %s\n", src, dest);
char *test = my_strcat( dest, src, sizeof dest );
if ( test )
printf("Value : %s\n", test );
return 0;
}
Теперь можно поэкспериментировать, уменьшив размер dest
до чего-то большего, чем «мир!» но меньше, чем "world!Hello"... Возможно, 9 байт???
И теперь, когда конкатенация должна работать (в достаточно большой буфер), добавьте код, чтобы гарантировать отсутствие перекрытия фактических массивов символов. Известен размер dest и измерена длина src.