2016-01-26 5 views
2

Я получаю ошибку о неверном прочтении, когда строка ЦСИ заканчивается \n, ошибка исчезает, когда я удалить \n:strdup недействительным чтения размера 4, когда строка символов заканчивается с новой строки п

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

int main (void) 
{ 
    char *txt = strdup ("this is a not socket terminated message\n"); 
    printf ("%d: %s\n", strlen (txt), txt); 
    free (txt); 
    return 0; 
} 

выход VALGRIND :

==18929== HEAP SUMMARY: 
==18929==  in use at exit: 0 bytes in 0 blocks 
==18929== total heap usage: 2 allocs, 2 frees, 84 bytes allocated 
==18929== 
==18929== All heap blocks were freed -- no leaks are possible 
==18929== 
==18929== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) 
==18929== 
==18929== 1 errors in context 1 of 1: 
==18929== Invalid read of size 4 
==18929== at 0x804847E: main (in /tmp/test) 
==18929== Address 0x4204050 is 40 bytes inside a block of size 41 alloc'd 
==18929== at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
==18929== by 0x8048415: main (in /tmp/test) 
==18929== 
==18929== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) 

Как исправить это, не жертвуя новым символом линии?

+3

Убедитесь, что 'strdup()' прототипирован. Используйте ''% zu'' для 'strlen()'. '' \ n'' вряд ли проблема. – chux

+0

Нет проблем с MSVC с соответствующими файлами заголовков. Где они? –

+0

на MSVC все возможно, это не является правильной ссылкой, когда дело доходит до C – 4pie0

ответ

5

Это не символ новой строки, а также спецификатор формата printf. Вы обнаружили, что, возможно, ошибка в strlen(), и я могу сказать, что вы должны использовать gcc.

Ваш код программы в порядке. Спецификатор формата printf может быть немного лучше, но это не приведет к ошибке valgrind, которую вы видите. Давайте посмотрим на эту ошибку valgrind:

==18929== Invalid read of size 4 
==18929== at 0x804847E: main (in /tmp/test) 
==18929== Address 0x4204050 is 40 bytes inside a block of size 41 alloc'd 
==18929== at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) 
==18929== by 0x8048415: main (in /tmp/test) 

«Неверное чтение размера 4» - это первое сообщение, которое мы должны понимать. Это означает, что процессор выполнил команду, которая загрузила бы 4 последовательных байта из памяти. Следующая строка указывает, что адрес, который был прочитан, был «Адрес 0x4204050 равен 40 байтам внутри блока размера 41 alloc'd».

С помощью этой информации мы можем понять это. Во-первых, если вы замените это '\n' на '$' или любой другой символ, будет произведена та же ошибка. Попробуй.

Во-вторых, мы можем видеть, что ваша строка содержит 40 символов. Добавление символа завершения \0 приводит к тому, что суммарные байты используются для представления строки до 41.

Поскольку у нас есть сообщение «Адрес 0x4204050 составляет 40 байт внутри блока размером 41 alloc'd», мы теперь знаем все о том, что идет не так.

  1. strdup() присвоен правильный объем памяти 41 байт.
  2. strlen() попытался прочитать 4 байта, начиная с 40-го числа, которое будет распространяться на несуществующий 43-й байт.
  3. Valgrind поймал проблему

Это бойкий() ошибка. Когда-то начинался проект под названием Tiny C Compiler (TCC). По совпадению, glib был полностью изменен так, что нормальные строковые функции, такие как strlen(), больше не существовали. Они были заменены оптимизированными версиями, которые считывают память с использованием различных методов, таких как чтение по четыре байта за раз. gcc был изменен одновременно, чтобы генерировать вызовы для соответствующих реализаций, в зависимости от выравнивания указателя ввода, скомпилированного оборудования и т. д.Проект TCC был оставлен, когда это изменение в среде GNU затруднило создание нового компилятора C, исключив возможность использования glib для стандартной библиотеки.

Если вы сообщите об ошибке, то поддерживающие glib, вероятно, не исправит ее. Причина в том, что при практическом использовании это, вероятно, никогда не приведет к фактическому сбою. Функция strlen считывает байты 4 за раз, так как видит, что адреса выровнены по 4 байт. Всегда можно читать 4 байта из 4-байтового выровненного адреса без segfault, учитывая, что чтение 1 байта с этого адреса будет успешным. Поэтому предупреждение от valgrind не выявляет потенциального сбоя, просто несоответствие в предположениях о том, как программировать. Я считаю, что valgrind технически корректен, но я думаю, что нет никаких шансов, что разработчики glib сделают все, чтобы отменить предупреждение.

+0

Я могу подтвердить, что замена '\ n' на любые другие символы вызывает ту же ошибку, я также использую gcc. Спасибо за длинное объяснение, все это имеет смысл сейчас. – razzak

+0

Хороший ответ, но результат, который он дал, не имеет ничего общего с кодом. В выводе он показал нам это «общее использование кучи: 2 allocs, 2 frees, 84 bytes alloc». Где «alloc» происходит два раза? – Michi

+0

@ Мичи - возможно, внутри 'printf'. –

3

Сообщение об ошибке, похоже, указывает на то, что это strlen, который считывает буфер malloc, выделенный strdup. На 32-битной платформе оптимальная реализация strlen может читать по 4 байта за раз в 32-битный регистр и делать некоторые бит-twiddling, чтобы увидеть, есть ли там нулевой байт. Если в конце строки осталось меньше 4 байтов, но 4 байта по-прежнему читаются для выполнения проверки нулевого байта, тогда я мог видеть, что эта ошибка печатается. В этом случае, предположительно, разработчик strlen знал бы, безопасно ли это сделать на конкретной платформе, и в этом случае ошибка valgrind является ложным положительным.