LINUX.ORG.RU

Странное поведение sscanf()

 ,


0

2

В коде на Си спользую sscanf(), что бы распарсить строку вида «<0x7fb2a00010a0|1779>» (два числа, шестнадцатеричное и десятичное) шаблоном «<%p|%zu>», и оно то работает, то не работает. Может в форме входной строки или шаблоне что-то не так, но что интересно, что sscanf всегда работает по разному, сегодня он данные принимает и распознаёт, а завтра уже говорит «ничего не вижу», а после завтра видит только одно из двух чисел, и от чего его настроение зависит мне не понятно. Вызов выглядит вот так:

int atk_memfile_from_specresid( const char *residual,
                                char const **bufpp,
                                size_t *szp )
{
    int result = sscanf(residual, "<%p|%zu>", bufpp, szp);
    if( result == EOF )
    {
        return 0;
    }
    if( *bufpp < (const char*)0x100 )
    {
        return 0;
    }
    if( result )
    {
        return 1;
    }
    return 0;
}
Передаётся аргументом residual строка, допустим (из реального примера) «<0x7fb2a00010a0|1779>». Сегодня на моих глазах на одной машине находит, а на другой машине этот текст не принимается, функция возвращает 0. У меня два вопроса:

  • Что не так с входными данными и шаблоном?
  • И почему поведение каждый раз меняется?
★★★

Последнее исправление: normann (всего исправлений: 1)

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

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)

ЕМНИП, у sscanf разное поведение в зависимости от текущей локали. Может быть еще какие-то внешние настройки влияют.

Если удается воспроизвести разное поведение на двух машинах, проверьте отличия в окружении: переменные окружения и проч.

trex6 ★★★★★
()

Через *void пробовали? https://ru.stackoverflow.com/questions/636493/scanf-p-и-касты-указателей

Вобще я ваш код не понял. Ну, может быть bufpp зануляется до вызова функции, тогда не важно, что в result, можно проверять его значение. Но почему не меняется поведение, если в result будет 1, а не 2, как задумывалось?

mky ★★★★★
()

У вас strict aliasing возможно сломался, компилятор выкинул присвоение. Впрочем чтобы быть уверенным наверняка, можно глянуть в дизассемблер

mittorn ★★★★★
()

Дядь, вы передаете в функцию const char **, который внутри sscanf разыменовывается в char const *, а затем модифицируется.

  1. Это должен быть void **
  2. Дядь, этот аргумент не просто так void **. Передайте туда long int * например:
#include <stdio.h>
#include <stdlib.h>

int atk_memfile_from_specresid( const char *residual,
                                void **bufpp,
                                size_t *szp )
{
    int result = sscanf(residual, "<%p|%zu>", bufpp, szp);
    if( result == EOF )
    {
        return 0;
    }
    if( result )
    {
        return 1;
    }
    return 0;
}

int main()
{
    const char * res = "<0x7fb2a00010a0|1779>";
    long int pp = 0;
    size_t size;
    
    int fres = 0;
    fres = atk_memfile_from_specresid(res, (void **)&pp, &size);
    
    
    
    printf("%s %p %d\n", res, (void *)pp, fres);
    
    return 0;
}
PPP328 ★★★★★
()
Последнее исправление: PPP328 (всего исправлений: 1)
Ответ на: комментарий от mky

Ну, может быть bufpp зануляется до вызова функции

Не должно, туда аргументом при вызове передаётся взятие адреса.

Но почему не меняется поведение, если в result будет 1, а не 2, как задумывалось?

Отладка показала, что если функция успешно прочитала входную строку и заполнила обе переменные, может всё равно при этом вернуть 1. Хз почему так выходит. А результат функции расценивается как логическое выражение.

normann ★★★
() автор топика
Последнее исправление: normann (всего исправлений: 1)
Ответ на: комментарий от normann

и заполнила обе переменные

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

mky ★★★★★
()
Ответ на: комментарий от bugfixer

Вы про что вообще? ТС взял область памяти, вписал туда 8 байт инта, а потом пытается интерпретировать это как строку.

Если бы он использовал void * в сигнатуре, то ему бы сразу стало понятно, что %p (внезапно!) это целочисленное представление указателя. Который никакого отношения к строке не имеет.

PPP328 ★★★★★
()

Так, я вроде понял, что делает автор, но мне все еще не ясно, зачем.

Варианты:

  1. Через файл передает информацию об адресах между потоками
  2. Через строки передает адрес указателя.

Автор, что происходит, вообще?

PPP328 ★★★★★
()

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

Рекомендую пройтись профайлером памяти вроде valgrind.

unDEFER ★★★★★
()
Последнее исправление: unDEFER (всего исправлений: 1)
Ответ на: комментарий от PPP328

Тем, что вы в строку пишете инт, алло.

Да где же там строка, там указатель на строку, точнее указатель на указатель. Я всего-то передаю в виде строки адрес в памяти, где лежит другая строка, текст с адресом превращается в число и кладётся в указатель на тип данных, которые по тому адресу и располагаются.

normann ★★★
() автор топика
Ответ на: комментарий от PPP328

Автор, что происходит, вообще?

Мне приходится через чужую библиотеку, которая использует только строку, передать адрес массива памяти.

normann ★★★
() автор топика
Ответ на: комментарий от DrBrown

Сравнение указателей в

А что может произойти?

Просто у меня где-то был баг, из-за которого вместо указателя на указатель туда попадало какое-то очень маленькое число. Баг был устранён, но я пока оставил проверку на всякий случай.

Ну а если я предварительно адрес скастую в целое число и сравню уже числа?

normann ★★★
() автор топика
Ответ на: комментарий от normann

Ну а если я предварительно адрес скастую в целое число и сравню уже числа?

Это единственно правильный способ в данном случае. Только используйте соотв. типы: intptr_t или uintptr_t.

Rootlexx ★★★★★
()