Неожиданный результат sscanf

#c #c 11 #gdb

#c #c 11 #gdb

Вопрос:

У меня есть sscanf оператор, который ведет себя неожиданно. Это где-то около цикла # 8000, когда он внезапно делает это. Это код, в котором str строка анализируется из файла:

 char a1[6], a2[6], op[6], a3[6];
int success = sscanf(str.c_str(),"%*s %s %*s %s %s %s",a1, a2, op, a3);
  

И это вывод gdb в проблемной строке ( str is "assign po012 = po011;" ):

 (gdb) print str
$9 = {<std::__1::__basic_string_common<true>> = {<No data fields>}, static __short_mask = 1, static __long_mask = 1,
  __r_ = {<std::__1::__compressed_pair_elem<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >::__rep, 0, false>> = {__value_ = {{__l = {__cap_ = 97, __size_ = 23, __data_ = 0x1003001d0 "  assign po012 = po011;"}, __s = {{
              __size_ = 97 'a', __lx = 97 'a'},
            __data_ = "000000000000002700000000000000320016000010000"}, __r = {__words = {
              97, 23,
              4298113488}}}}}, <std::__1::__compressed_pair_elem<std::__1::allocator<char>, 1, true>> = {<std::__1::allocator<char>> = {<No data fields>}, <No data fields>}, <No data fields>}, static npos = 18446744073709551615}
(gdb) n
81                  string A1(a1);
(gdb) print a1
$10 = "00o012"
(gdb) print a2
$13 = "po011;"
  

a2 имеет ожидаемое значение, но что происходит a1 только в этом одном случае?

Комментарии:

1. почему он имеет a '' вместо 'p ‘? понятия не имею, но попробуйте установить контрольную точку на a1[0]

2. sscanf это C не API C .

3. Все работает так, как должно быть, и так, как это задокументировано: godbolt.org/z/offP9K

4. Показанное str значение не соответствует sscanf искомому шаблону.

Ответ №1:

Ваши массивы имеют длину шесть:

 char a1[6], a2[6], op[6], a3[6];
  

Тем не менее, sscanf вызывает "po011;" запись a2 , и для этого требуется семь символов, поскольку sscanf добавит нулевой терминатор. Следовательно, sscanf вызывает неопределенное поведение.

Прагматично, в вашей реализации нулевой терминатор был добавлен в начало a1 , которое изменилось с предполагаемого "po012" на "00o012" (начальное p было перезаписано). Похоже, ваша реализация решила сохранить a1 сразу после a2 , поэтому переполнение a2 перезаписано a1 . Это одна из вещей, которые могут произойти, когда запускается неопределенное поведение.

Комментарии:

1. Я увеличил длину массива символов до 8, без какого-либо эффекта. Первоначальный запуск был на моем mac, но когда я клонировал на машине RHEL, он работал. Вы знаете, почему это изменило его?

2. увеличение длины массива до 10 исправило его на mac, я думаю, что описанный вами механизм верен. Спасибо!

3. @CaitlinSlicker Я не могу четко понять, что происходит в вашем коде. Может быть, в вашем файле есть более длинное поле? Изменение ОС / компилятора также может привести к тому, что неопределенное поведение может случайно «работать», как ожидалось, но на это не следует полагаться, поскольку оно может перестать работать в любой момент. Убедитесь, что в файле поля достаточно длинные. Вы также можете использовать спецификатор формата с размером строки, например %9s , для предотвращения переполнения буфера. Кроме того, в C вы могли бы избежать scanf и вместо этого работать с std::string .