Версия для печати темы
Форум разработчиков электроники 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?
Цитата(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()-ом, но при этом даже не задумываясь лепит кучу константных данных в ОЗУ (строки, массивы указателей на строки и т.п. - как видно из разных его сообщений на форуме). В местах где это ну совершенно не нужно.
Автор: Jenya7 Jul 24 2018, 11:23
Цитата(jcxz @ Jul 24 2018, 16:09)
Поразительно - человек беспокоится о расходе стека printf()-ом, но при этом даже не задумываясь лепит кучу константных данных в ОЗУ (строки, массивы указателей на строки и т.п. - как видно из разных его сообщений на форуме). В местах где это ну совершенно не нужно.
а что плохого если строки лежат во 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++, ни в чём себе не отказывайте
Автор: jcxz Jul 25 2018, 10:18
Цитата(scifi @ Jul 25 2018, 11:25)
Компилируйте в режиме C++, ни в чём себе не отказывайте
И...? У меня и так C++.
Автор: VladislavS Jul 25 2018, 11:13
Цитата(jcxz @ Jul 24 2018, 16:49)
Значит Ваш компилятор в этом случае не проверяет соответствие типов передаваемых данных и аргументов функции. И плохо. Это его недостаток.
Чего это он мой? Забери его себе
Цитата(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
В 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++, ни в чём себе не отказывайте
Цитата(jcxz @ Jul 25 2018, 15:16)
Как Вы добились сего??? Версия IAR? какие ключи компиляции?
Цитата(VladislavS @ Jul 25 2018, 15:24)
Проект С++ с настройками по умолчанию.
Цитата(jcxz @ Jul 25 2018, 13:18)
И...? У меня и так C++.
Очевидно, у вас таки не включен режим С++
Автор: VladislavS Jul 25 2018, 12:30
Цитата(scifi @ Jul 25 2018, 15:27)
Очевидно, у вас таки не включен режим С++
Вам показать список типов из проекта? :D
Автор: jcxz Jul 25 2018, 15:42
Цитата(scifi @ Jul 25 2018, 15:27)
Очевидно, у вас таки не включен режим С++
ку?
Расширение си-файлов тоже: .cpp. Без скриншота поверите?
Цитата(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)