Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум разработчиков электроники ELECTRONIX.ru _ Kinetis _ Функция sprintf

Автор: aBoomest Jul 4 2018, 09:41

Добрый день.
Работаю на NXP MK66FN2M0xxx18.
При этом при использовании функции sprintf для вывода чисел с плавающей запятой, программа на этой функции вылетает. Убираешь %f (т.е. выводишь все кроме float'ов) - все работает.
Кто сталкивался с подобным?

На сайте IAR нашел следующее http://electronix.ru/redirect.php?https://www.iar.com/support/tech-notes/general/problems-with-printf-floating-point-f-on-arm/
Правда думается, что не о том.

Автор: jcxz Jul 4 2018, 09:55

Цитата(aBoomest @ Jul 4 2018, 12:41) *
При этом при использовании функции sprintf для вывода чисел с плавающей запятой, программа на этой функции вылетает. Убираешь %f (т.е. выводишь все кроме float'ов) - все работает.
Кто сталкивался с подобным?

Вангую использование FPU в разных задачах РТОС (или ISR) и несохранение контекста FPU при их переключении.
Либо тупо: используется stdlib без поддержки float.

Автор: megajohn Jul 4 2018, 10:34

Цитата(jcxz @ Jul 4 2018, 13:55) *
Вангую использование FPU в разных задачах РТОС (или ISR) и несохранение контекста FPU при их переключении.
Либо тупо: используется stdlib без поддержки float.


либо стек не выровнен на 8

Автор: scifi Jul 4 2018, 10:42

Цитата(megajohn @ Jul 4 2018, 13:34) *
либо стек не выровнен на 8

У меня такое приводило просто к неправильной печати. Но то был GNU Arm Embedded...

Автор: aBoomest Jul 4 2018, 10:49

1. Операционок нет, для прерываний контекст сохраняется и восстанавливается автоматически.

2. "Стэк не выровнен на 8." - поясните плз что это значит? Стэк должен начинаться с адреса кратного восьми? или все элементы в стэке должны размещаться в нем с "шагом" кратном 8? Или еще как-то?

Автор: scifi Jul 4 2018, 11:13

Цитата(aBoomest @ Jul 4 2018, 13:49) *
Стэк должен начинаться с адреса кратного восьми?

Именно. Вернее, указатель стека при вызове внешних функций должен быть кратен 8, но если стек выравнен в начале, то в дальнейшем компилятор соблюдает эту кратность. Если нарушить, бывают чудеса в частности вокруг sprintf. Должно быть, как-то замешаны вычисления с double.
Кстати, насчёт double. Если при инициализации программы не включен FPU, то при попытке его использования будет именно "улетать".

Автор: jcxz Jul 4 2018, 11:28

Цитата(scifi @ Jul 4 2018, 14:13) *
Кстати, насчёт double. Если при инициализации программы не включен FPU, то при попытке его использования будет именно "улетать".

Вообще - ТС даже не раскрыл значение понятия "вылетает". Может если он определится - что это такое, то всё сразу станет ясно?

Автор: demiurg_spb Jul 4 2018, 12:36

Может банальное переполнение буфера?
HINT: Попробуйте на snprintf заменить и в будущем только её и использовать...

Автор: scifi Jul 4 2018, 12:56

Цитата(demiurg_spb @ Jul 4 2018, 15:36) *
Может банальное переполнение буфера?
HINT: Попробуйте на snprintf заменить и в будущем только её и использовать...

Кстати, да. Банальнейший код может привести к неожиданным результатам:
Код
char buf[16];
sprintf(buf, "%f", 1.0e99); // boom

Автор: aBoomest Jul 5 2018, 19:29

Цитата(scifi @ Jul 4 2018, 14:13) *
Именно. Вернее, указатель стека при вызове внешних функций должен быть кратен 8, но если стек выравнен в начале, то в дальнейшем компилятор соблюдает эту кратность. Если нарушить, бывают чудеса в частности вокруг sprintf. Должно быть, как-то замешаны вычисления с double.
Кстати, насчёт double. Если при инициализации программы не включен FPU, то при попытке его использования будет именно "улетать".
Стек начинается с адреса кратного 8. При заходе в подпрограмму функции sprintf в регистре адреса число (почему-то) не кратное 8. Почему - фиг его знает. Переменная, в которую я запихиваю строчку при помощи функции sprintf - локальная, т.е. объявлена в той же функции, в которой я вызываю sprintf. Пока пришел к следующему: если переменную объявить глобально, то все работает. Однако объяснения этому всему пока нет.

Уважаемые форумчане, есть мысли на этот счет?

Автор: jcxz Jul 6 2018, 08:50

Цитата(aBoomest @ Jul 5 2018, 22:29) *
Уважаемые форумчане, есть мысли на этот счет?

Уже вроде писали: телепаты в отпуске. А пока не объясните значение понятия "вылетает" помочь Вам смогут только они.

Автор: Jenya7 Jul 7 2018, 06:05

уверен там хардварный FPU. включите его

Код
#define CORTEX_M4_BLOCK  0xe000e000
#define CPACR *(unsigned long *)(CORTEX_M4_BLOCK + 0xd88)  // Co-processor Access Control

//__enable_FPU();  
CPACR |= (0xf << 20);   // enable access to FPU
а вообще sprintf монструозная функция для эмбедед. никогда не использовал ее.

Автор: aBoomest Jul 21 2018, 09:57

Цитата(Jenya7 @ Jul 7 2018, 09:05) *
а вообще sprintf монструозная функция для эмбедед. никогда не использовал ее.
Как же тогда делать?

Давно просили - про "Программа вылетает":
не знаю как это может помочь
Hard fault
HFSR 0E80

Автор: Arlleex Jul 21 2018, 11:10

Цитата(aBoomest @ Jul 21 2018, 12:57) *
Как же тогда делать?

Давно просили - про "Программа вылетает":
не знаю как это может помочь
Hard fault
HFSR 0E80

Вы уверены что правильно считали HFSR? wacko.gif

Цитата(Jenya7 @ Jul 7 2018, 09:05) *
а вообще sprintf монструозная функция для эмбедед. никогда не использовал ее.

Ну и зря. Не такая она и монструозная.

Автор: thermit Jul 21 2018, 11:57

Цитата(aBoomest @ Jul 21 2018, 12:57) *
Как же тогда делать?

Давно просили - про "Программа вылетает":
не знаю как это может помочь
Hard fault
HFSR 0E80


Посмотрите в дизассемблере содержимое функций isnanf и isinff (isinfd isnand). Скорее всего они пустые (с мусором).

Автор: Jenya7 Jul 24 2018, 09:14

Цитата(aBoomest @ Jul 21 2018, 14:57) *
Как же тогда делать?
Код
void Reverse(char *str, int len)
{
    int i=0, j=len-1, temp;
    while (i<j)
    {
        temp = str[i];
        str[i] = str[j];
        str[j] = temp;
        i++; j--;
    }
}
int IntToStr(int x, char str[], int d)
{
    int i = 0;
    while (x)
    {
        str[i++] = (x%10) + '0';
        x = x/10;
    }

    // If number of digits required is more, then
    // add 0s at the beginning
    while (i < d)
        str[i++] = '0';

    Reverse(str, i);
    str[i] = '\0';
    return i;
}
// Converts a floating point number to string.
void FtoA(float n, char *res, int afterpoint)
{
    // Extract integer part
    int ipart = (int)n;

    // Extract floating part
    float fpart = n - (float)ipart;

    // convert integer part to string
    int i = IntToStr(ipart, res, 0);

    // check for display option after point
    if (afterpoint != 0)
    {
        if (ipart==0)
            res[i++] = '0';
            
        res[i] = '.';  // add dot

        // Get the value of fraction part upto given no.
        // of points after dot. The third parameter is needed
        // to handle cases like 233.007
        fpart = fpart * pow(10, afterpoint);

        IntToStr((int)fpart, res + i + 1, afterpoint);
    }
}


Автор: jcxz Jul 24 2018, 11:09

Цитата(Jenya7 @ Jul 7 2018, 09:05) *
а вообще sprintf монструозная функция для эмбедед. никогда не использовал ее.

Поразительно - человек беспокоится о расходе стека printf()-ом, но при этом даже не задумываясь лепит кучу константных данных в ОЗУ (строки, массивы указателей на строки и т.п. - как видно из разных его сообщений на форуме). В местах где это ну совершенно не нужно. wacko.gif

Автор: Jenya7 Jul 24 2018, 11:23

Цитата(jcxz @ Jul 24 2018, 16:09) *
Поразительно - человек беспокоится о расходе стека printf()-ом, но при этом даже не задумываясь лепит кучу константных данных в ОЗУ (строки, массивы указателей на строки и т.п. - как видно из разных его сообщений на форуме). В местах где это ну совершенно не нужно. wacko.gif


а что плохого если строки лежат во txt секции?

Автор: Arlleex Jul 24 2018, 11:40

Цитата(Jenya7 @ Jul 24 2018, 15:23) *
а что плохого если строки лежат во txt секции?

Да если бы они лежали только в .text...

Автор: jcxz Jul 24 2018, 11:46

Цитата(Jenya7 @ Jul 24 2018, 14:23) *
а что плохого если строки лежат во txt секции?

В какой txt-секции? Так как Вы пишете, все Ваши строки и указатели на них лежат в ОЗУ + инициализирующие значения лежат во флешь.
А если передаёте в некую функцию строку типа: Func("си_не_знаю_пишу_как_попало") и эта Func объявлена как Func(char *), то такой вызов говорит компилятору до вызова функции создать переменную char t[] = "си_не_знаю_пишу_как_попало"; где-то в ОЗУ - или на стеке или в static памяти, проинициализировав её в runtime из константы char const ti[] = "си_не_знаю_пишу_как_попало"; во флешь.

Автор: scifi Jul 24 2018, 11:52

Цитата(jcxz @ Jul 24 2018, 14:46) *
А если передаёте в некую функцию строку типа: Func("си_не_знаю_пишу_как_попало") и эта Func объявлена как Func(char *), то такой вызов говорит компилятору до вызова функции создать переменную char t[] = "си_не_знаю_пишу_как_попало"; где-то в ОЗУ - или на стеке или в static памяти, проинициализировав её в runtime из константы char const ti[] = "си_не_знаю_пишу_как_попало"; во флешь.

Ну это уже совсем трэш, угар и содомия. Вот не при помню ни одного повода, по которому неявно создавалась бы строка в ОЗУ. И в данном случае точно нет.
Поправка: если возвращать из функции по значению структуру, содержащую строку, эта структура будет временно создана на стеке. Но это совсем другой случай, конечно.

Автор: jcxz Jul 24 2018, 12:03

Цитата(scifi @ Jul 24 2018, 14:52) *
Ну это уже совсем трэш, угар и содомия. Вот не при помню ни одного повода, по которому неявно создавалась бы строка в ОЗУ. И в данном случае точно нет.

попробуйте:
char t[] = "строка";
где будет создана t?

Автор: scifi Jul 24 2018, 12:06

Цитата(jcxz @ Jul 24 2018, 15:03) *
попробуйте:
char t[] = "строка";
где будет создана t?

Это явно созданная в ОЗУ строка, о чём "char t[]" недвусмысленно говорит. У вас в дивном примере чуть выше написано совсем иначе.

Автор: Jenya7 Jul 24 2018, 12:14

я создам отдельную тему в http://electronix.ru/redirect.php?https://electronix.ru/forum/index.php?showforum=134 > http://electronix.ru/redirect.php?https://electronix.ru/forum/index.php?showforum=192 - где хранить строки.

Автор: jcxz Jul 24 2018, 12:21

Цитата(scifi @ Jul 24 2018, 15:06) *
Это явно созданная в ОЗУ строка, о чём "char t[]" недвусмысленно говорит. У вас в дивном примере чуть выше написано совсем иначе.

Ну так если аргумент функции описан как char *, то эта функция внутри имеет право выполнять запись по такому указателю. А значит компилятор не должен позволять передавать туда указатели на константные объекты. Разве не так?

Автор: scifi Jul 24 2018, 12:26

Цитата(jcxz @ Jul 24 2018, 15:21) *
Ну так если аргумент функции описан как char *, то эта функция внутри имеет право выполнять запись по такому указателю. А значит компилятор не должен позволять передавать туда указатели на константные объекты. Разве не так?

Нет, не так. Явным приведением типа можно отсечь от указателя const, и компилятор не пикнет. Копию в ОЗУ точно не будет создавать.
В порядке ликбеза: объявление переменной и список аргументов функции - это совсем разные штуки. Какое-то совпадение в семантике есть, но есть и кардинальные различия.

Автор: Сергей Борщ Jul 24 2018, 12:54

QUOTE (scifi @ Jul 24 2018, 15:26) *
Нет, не так. Явным приведением типа можно отсечь от указателя const, и компилятор не пикнет.
Это жульничество, от такого компилятор не защищает. Вы напрасно спорите с jcxz, он прав. Более того, gcc уже давно ругается на char * text = "слава мне, победителю драконов";
QUOTE
warning: ISO C++ forbids converting a string constant to ‘char*’

Автор: scifi Jul 24 2018, 13:04

Цитата(Сергей Борщ @ Jul 24 2018, 15:54) *
Вы напрасно спорите с jcxz, он прав.

Нет, не напрасно. Я спорю вот с этим:
Цитата(jcxz @ Jul 24 2018, 14:46) *
А если передаёте в некую функцию строку типа: Func("си_не_знаю_пишу_как_попало") и эта Func объявлена как Func(char *), то такой вызов говорит компилятору до вызова функции создать переменную char t[] = "си_не_знаю_пишу_как_попало"; где-то в ОЗУ - или на стеке или в static памяти

Это очевидное заблуждение.

Автор: VladislavS Jul 24 2018, 13:11

Цитата(scifi @ Jul 24 2018, 16:04) *
Это очевидное заблуждение.

Вы правы. У меня в проекте вся менюшка выполнена в стиле lcd.Printx(0, " си_не_знаю_пишу_как_попало ");, где void Printx(uint8_t x,char *s);. Заглядываем в map по модулю меню.
Код
    Module                       ro code  ro data  rw data
    ------                       -------  -------  -------
    Menu.o                         2 436        7       20


Автор: jcxz Jul 24 2018, 13:49

Цитата(scifi @ Jul 24 2018, 15:26) *
Нет, не так. Явным приведением типа можно отсечь от указателя const, и компилятор не пикнет. Копию в ОЗУ точно не будет создавать.

Ну я вообще-то говорю не про явное приведение типа.
А когда вообще без приведения типа функции, принимающей указатель на переменную, передаётся константа.
Явным приведением типа программист просто говорит, что в данном конкретном случае здесь передаётся "не то что можно ожидать по типу объявленного символического имени, а нечто другое".
Если функция использует принятый указатель только для операций чтения, то при объявлении такой функции надо этому указателю (аргументу) назначать модификатор const. Частный случай - член-функция класса, использующая члены класса только на чтение (и вызывающая только const член-функции класса) - тоже должна объявляться с модификатором const.
Если функция использует принятый аргумент-указатель и для операций записи, то тогда нужно описывать без слова const.
И компилятор, при передаче аргумента такой фукции, будет проверять чтобы const-указатели не передавались в не-const аргументы функций.

Цитата(VladislavS @ Jul 24 2018, 16:11) *
Вы правы. У меня в проекте вся менюшка выполнена в стиле lcd.Printx(0, " си_не_знаю_пишу_как_попало ");, где void Printx(uint8_t x,char *s);. Заглядываем в map по модулю меню.

Значит Ваш компилятор в этом случае не проверяет соответствие типов передаваемых данных и аргументов функции. И плохо. Это его недостаток.
Но это не значит что все компиляторы так делают.

Цитата(scifi @ Jul 24 2018, 16:04) *
Это очевидное заблуждение.

Почему?
В чём разница между:
char t[] = "XXXX";
и
void Func(char *)
?
И первом случае Вы говорите компилятору, что хотите иметь переменную (с начальным значением), а значит - хотите иметь возможность записи в неё. И во-втором случае говорите ему, что функция Func() хочет иметь возможность записи в принимаемый ею аргумент (т.е. - она принимает указатель на переменную). В чём принципиальная разница?

Автор: scifi Jul 24 2018, 14:23

Цитата(jcxz @ Jul 24 2018, 16:49) *
Значит Ваш компилятор в этом случае не проверяет соответствие типов передаваемых данных и аргументов функции. И плохо. Это его недостаток.

На самом деле типы соответствуют. Строковая константа имеет тип char[] без всяких const (сюрприз!), поэтому всё нормально. Это в C так, этот курьёз оставили, предположительно, из-за того, что к моменту изобретения const уже настрогали кучу кода, ломать его не хотелось. В то же время менять эту строку нельзя, поэтому компилятор мог бы предупреждать, но не по причине несоответствия типов, а по смыслу. В C++ всё проще: строка таки имеет тип const char[].

Цитата(jcxz @ Jul 24 2018, 16:49) *
Почему?

Вообще в данном случае ничто не запрещает компилятору создать копию строки в ОЗУ перед вызовом функции. Просто он этого не делает (и хорошо, зачем лишнюю работу делать?). Предлагаю собрать небольшой пример и заглянуть в листинг дизассемблера.

Автор: jcxz Jul 25 2018, 06:03

Цитата(scifi @ Jul 24 2018, 17:23) *
Вообще в данном случае ничто не запрещает компилятору создать копию строки в ОЗУ перед вызовом функции. Просто он этого не делает (и хорошо, зачем лишнюю работу делать?).

Хотя-бы потому, что это - идеологически было бы правильно.
Имхо: если функция только читает данные из переданного ей указателя - объявляй такой указатель с const, если и пишет - без const. А дальше - компилятор должен проверить соответствие типов и предупредить об ошибке. Что является только плюсом в отладке и уменьшении багов. Ну или автоматом устранить её, создав временный не-const объект на стеке или в static памяти. Ещё б желательно чтобы первое или второе поведение - определялось ключом компилятора.
А то, что почти все быдлокодеры будут писать без const и передавать туда константы - так это их проблемы.

Цитата(scifi @ Jul 24 2018, 17:23) *
Предлагаю собрать небольшой пример и заглянуть в листинг дизассемблера.

PS: Я уже проверял. Для IAR.

Автор: scifi Jul 25 2018, 07:05

Цитата(jcxz @ Jul 25 2018, 09:03) *
Хотя-бы потому, что это - идеологически было бы правильно.

Идеология - штука субъективная. У нас гласность, свобода совести и плюрализм мнений.
Я довольно хорошо изучил язык Си, и мне он кажется вполне логичным и непротиворечивым. Есть там спорные моменты, но для них есть понятные обоснования, с которыми несложно согласиться. Обсуждаемый момент к спорным не могу отнести.

Автор: jcxz Jul 25 2018, 07:38

Цитата(scifi @ Jul 25 2018, 10:05) *
Я довольно хорошо изучил язык Си, и мне он кажется вполне логичным и непротиворечивым.

Что-ж тут логичного, если char const t[] = "..."; Func(char *); Func(t); - вызывает ошибку, а Func("..."); - не вызывает?
Но при этом 2-й вариант хоть и компилируется, но может приводить к неработоспособному коду.
Хотя и в том и в другом случае данные одного и того же типа char const []. И даже более того - компилятор (линкёр?) могут объединить эти две константы в один объект в памяти.

Автор: scifi Jul 25 2018, 08:25

Цитата(jcxz @ Jul 25 2018, 10:38) *
Но при этом 2-й вариант хоть и компилируется, но может приводить к неработоспособному коду.

Компилируйте в режиме C++, ни в чём себе не отказывайте laughing.gif

Автор: jcxz Jul 25 2018, 10:18

Цитата(scifi @ Jul 25 2018, 11:25) *
Компилируйте в режиме C++, ни в чём себе не отказывайте laughing.gif

И...? У меня и так C++.

Автор: VladislavS Jul 25 2018, 11:13

Цитата(jcxz @ Jul 24 2018, 16:49) *
Значит Ваш компилятор в этом случае не проверяет соответствие типов передаваемых данных и аргументов функции. И плохо. Это его недостаток.

Чего это он мой? Забери его себе sm.gif

Цитата(jcxz @ Jul 24 2018, 16:49) *
Но это не значит что все компиляторы так делают.

GCC и IAR компилят одинаково. Какой ещё эталон нужен?

Они передают в функцию const char* и если внутри работать как с char *, то матерятся.
Код
void Printx(const uint8_t x,const char *s)
{
  const char *c;
  Goto(x);
  if (!(c=s)) return;
  while (*c)
  {
    WriteData(codepage[(uint8_t)*c]);
    c++;
  }
}


Вот в IAR AVR, с его кривой памятью, можно управлять процессом, кстати.

Автор: Arlleex Jul 25 2018, 11:41

Цитата(VladislavS @ Jul 25 2018, 15:13) *
Вот в IAR AVR, с его кривой памятью, можно управлять процессом, кстати.

В AVR память RAM и ROM находятся не в одном адресном пространстве, поэтому и есть явное разделение.

Автор: jcxz Jul 25 2018, 11:46

Цитата(VladislavS @ Jul 25 2018, 14:13) *
Они передают в функцию const char* и если внутри работать как с char *, то матерятся.

Вы не поняли. Разговор о случае если аргумент функции - char *, а передаваемое значение: Func("xxx") (char const *). То компилятор не ругается. А по уму должен бы.
См. посты выше и http://electronix.ru/redirect.php?https://electronix.ru/forum/index.php?showtopic=148073&view=findpost&p=1574877

Автор: VladislavS Jul 25 2018, 11:51

Ещё как ругается. Если в Print(char *s); передать Print(" ");, то много нового о себе от компилятора узнаете.

Ругается, правда, только варнингом. Как и GCC sm.gif



В IAR это звучит как
Код
Warning[Pe2464]: conversion from a string literal to "char *" is deprecated Menu.cpp 54

Автор: jcxz Jul 25 2018, 12:16

Цитата(VladislavS @ Jul 25 2018, 14:51) *
Ещё как ругается. Если в Print(char *s); передать Print(" ");, то много нового о себе от компилятора узнаете.

Как Вы добились сего??? Версия IAR? какие ключи компиляции?
У меня тоже IAR - проглатывает такое молча. Среди подавляемых варнингов Pe2464 - нету.

Автор: VladislavS Jul 25 2018, 12:24

Цитата(jcxz @ Jul 25 2018, 15:16) *
Как Вы добились сего??? Версия IAR? какие ключи компиляции?

Перепроверил ещё раз. 8.22.2 выдаёт варнинг, и 8.30.1 соответственно тоже. Младше у меня нет. Проект С++ с настройками по умолчанию.

Автор: scifi Jul 25 2018, 12:27

Цитата(scifi @ Jul 25 2018, 11:25) *
Компилируйте в режиме C++, ни в чём себе не отказывайте laughing.gif

Цитата(jcxz @ Jul 25 2018, 15:16) *
Как Вы добились сего??? Версия IAR? какие ключи компиляции?

Цитата(VladislavS @ Jul 25 2018, 15:24) *
Проект С++ с настройками по умолчанию.

Цитата(jcxz @ Jul 25 2018, 13:18) *
И...? У меня и так C++.

Очевидно, у вас таки не включен режим С++ laughing.gif

Автор: VladislavS Jul 25 2018, 12:30

Цитата(scifi @ Jul 25 2018, 15:27) *
Очевидно, у вас таки не включен режим С++ laughing.gif

Вам показать список типов из проекта? :D

Автор: jcxz Jul 25 2018, 15:42

Цитата(scifi @ Jul 25 2018, 15:27) *
Очевидно, у вас таки не включен режим С++ laughing.gif


ку?
Расширение си-файлов тоже: .cpp. Без скриншота поверите? cool.gif

Цитата(VladislavS @ Jul 25 2018, 15:24) *
Перепроверил ещё раз. 8.22.2 выдаёт варнинг, и 8.30.1 соответственно тоже. Младше у меня нет. Проект С++ с настройками по умолчанию.

Ясно. У меня 7.80.4. Есть ещё 8.20 - завтра может проверю.
А всё что выше 8.22 - ставить не рискую. Почему - тема была тут: http://electronix.ru/redirect.php?https://electronix.ru/forum/index.php?showtopic=147939&hl=

Русская версия Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)