Перейти к содержанию
    

Как передать указатель на строку

Расползание темы в разные дебри не связанные с вопросом я всегда одобряю, так как это однозначно лучше молчания! Спасибо всем гуру.

Не знаю, как так уже вышло, но я всегда объявлял массивы строк как

char string_received[2][64];

В данном случае при обращении к строкам они лежат друг за другом в памяти, сперва string_received[0], потом string_received[1], каждая по 64.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

54 минуты назад, kan35 сказал:

В данном случае при обращении к строкам они лежат друг за другом в памяти, сперва string_received[0], потом string_received[1], каждая по 64.

Вот это меня переклинило, мама дорогая! Да, вы правы, ваш массив объявлен правильно, а мне надо больше спать. Но про передачу массива в функцию по указателю я все написал правильно, так что рассказывайте, как вы определяете, что в функцию передается только два байта строки. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, kan35 said:

Не надо меня подозревать, пожалуйста 🙂 Мне так нужно и я так использую.

Тогда объясните, почему именно так. Строки у вас так передаваться не будут - только их адреса, которые всегда одинаковые.

Если у вас из очереди данные читаются и обрабатываются всегда быстро (так, что исходный массив не успевает затереться), то работать будет. Но тогда и очередь особенно не нужна 🙂

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 часов назад, Сергей Борщ сказал:

Читайте по губам: строку (которая есть массив символов) невозможно напрямую передать в функцию, вместо массива всегда автоматически передается указатель на этот массив. 

Спасибо за ликбез конечно. :smile:
Но я то писал не про передачу строки/указателя в функцию, а про помещение в очередь строки или указателя.
Сергей, я безмерно уважаю Вас и Ваш нелегкий труд по подержанию высокого профессионального уровня нашего форума,
но в данном случае, Вы никак не улавливаете суть того, о чем я пишу (наверное я плохо объясняю).

Попробую еще раз:
- Очередь в FreeRTOS огранизована как простой кольцевой буфер, хранящий элементы фиксированного размера. Размер элемента и их максимальное количество задается при создании очереди.
- Элементы помещаются в очередь побайтным копированием содержимого элемента в буфер очереди функцией memcpy.
Следовательно чтобы поместить в очередь что либо (например указатель на строку, как желает топикстартер),
это что то должно располагаться в памяти (чтобы можно было передать указатель на него).
Расмотрим на примере того, что желает топикстартер:
 

xQueueTCPRxedString = xQueueCreate( 2, sizeof( char *) );

kan35 создает очередь с максимальным количеством элементов 2, с размером элементов sizeof(char*).
Не знаю какой размер указателя на строку в PIC-ах, но будем для определенности считать что 2 байта.
 

char string_received[2][64];

char * data;
data = string_received[0];

xQueueSend(xQueueTCPRxedString, &data, 0);

Здесь в функцию xQueueSend передается указатель на локальную переменную data, которая содержит значение указателя на строку.
Функция xQueueSend копирует 2 байта (т.к. при создании очереди был указан именно такой размер элемента) содержимого переменной data в буфер очереди,
при этом в очереди оказывается значение указателя на строку, как и задумывалось. Все работает корректно.
Никаких проблем с тем что в функцию передается указатель на локальную переменную не возникает, т.к. этот указатель нигде не запоминается,
а используется только значение переменной, на которую он указывает.

Теперь другой вариант:

xQueueSend(xQueueTCPRxedString, string_received[0], 0);

Здесь в функцию xQueueSend передается указатель на саму строку, и происходит копирование 2-х байт самой строки в буфер очереди, на что и жалуется топикстартер.
Т.е. явно не то поведение, что требовалось.

Вот и собственно и все, о чем я хотел сказать своими сообщениями.
 

Я бы на месте топикстартера, сделал простую функцию-обертку для функции xQueueSend, которая помещала бы в очередь значения передаваемых ей указателей на строку.
Что то вроде такого:
 

portBASE_TYPE xQueueSend_My(xQueueHandle xQueue, char * pvItemToQueue, portTickType xTicksToWait)
{
	char *tmpVar = pvItemToQueue;

	return xQueueSend(xQueue, &tmpVar, xTicksToWait);
}

 

Тогда можно будет указывать сразу указатель на строку:
 

xQueueSend_My(xQueueTCPRxedString, string_received[0], 0);

Без всяких вспомогательных переменных.


 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 hours ago, xvr said:

Тогда объясните, почему именно так. Строки у вас так передаваться не будут - только их адреса, которые всегда одинаковые.

Если у вас из очереди данные читаются и обрабатываются всегда быстро (так, что исходный массив не успевает затереться), то работать будет. Но тогда и очередь особенно не нужна 🙂

 

Именно, передаются указатели, я же так и написал в самом первом сообщении. Queue - это инструмент, который мне удобен, я передаю сигнал о готовности строки и другая задача просыпается и сразу получает указатель на эту строку.

24 minutes ago, Shamil said:
portBASE_TYPE xQueueSend_My(xQueueHandle xQueue, char * pvItemToQueue, portTickType xTicksToWait)
{
	char *tmpVar = pvItemToQueue;

	return xQueueSend(xQueue, &tmpVar, xTicksToWait);
}

 

Тогда можно будет указывать сразу указатель на строку:
 

xQueueSend_My(xQueueTCPRxedString, string_received[0], 0);

Без всяких вспомогательных переменных.


 

Обертка в данном случае это наиболее изящное решение, спасибо!

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

33 минуты назад, Shamil сказал:

Здесь в функцию xQueueSend передается указатель на локальную переменную data, которая содержит значение указателя на строку.
Функция xQueueSend копирует 2 байта (т.к. при создании очереди был указан именно такой размер элемента) содержимого переменной data в буфер очереди,
при этом в очереди оказывается значение указаталя на строку, как и задумывалось. Все работает корректно.
Никаких проблем с тем что в функцию передается указатель на локальную переменную не возникает, т.к. этот указатель нигде не запоминается,
а используется только значение переменной, на которую он указывает.

А ничего, что сама string_received у ТС возможно - локальная и расположена на стеке и после вызова xQueueSend() возможно сразу же происходит выход из функции?

О том, что string_received - глобальная или что после xQueueSend() происходит ожидание окончания использования переданной через очередь строки - нигде ТС не упоминает.

PS: Если string_received всё-таки глобальная, то зачем вообще нужен такой громоздкий механизм, как передача указателя на её члены через очередь? Можно сделать гораздо проще.

8 минут назад, kan35 сказал:

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

Зачем передавать указатели на статический объект (если он всё-таки статический)??? Достаточно передать индекс. А лучше - вообще использовать более простые механизмы синхронизации, а не очередь.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

string_received это глобальный массив к сожалению и действительно, строки можно было бы передавать индексом в таком случае, но я решил сделать указателем. Это как раз на случай если массив в какой то момент сделаю динамическим.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

55 минут назад, Shamil сказал:

Спасибо за ликбез конечно. :smile:

Так, ладно, все проржались? А я целых три часа поспал!

55 минут назад, Shamil сказал:

Следовательно чтобы поместить в очередь что либо (например указатель на строку, как желает топикстартер),

Думаю, все непонятки возникли от того, что kan35 в самом первом сообщении написал

19 часов назад, kan35 сказал:

надо передать строку через Queue.

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Доброго дня всем!

Еще один не понятный момент. Указано в справке к порту для PIC18

https://www.freertos.org/a00097.html

что код компилятора C18 не реентрантный, в частности по 32 битной математике,  и это отдельно решается, ок.

В то же время я обнаружил, что стандартные функции типа strtsr, strcpy, memcpy и т д - тоже глючат если их в разных потоках вызывать, что значит, что они тоже не реентратные. Попробовал написать свои функции для работы со строками, и оказалось, что они получаются тоже не реентрантные, в итоге все их заключаю в критические секции и все работает нормально. Конечно использовать критические секции по этому поводу не хотелось бы, так как это потребует снижать скорости по последовательным портам, и накладывать прочие ограничения. Может быть кто сталкивался с такой вот проблемой нереентранности на PIC18 и C18 и есть какие-нибудь изящные решения...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 часов назад, kan35 сказал:

тоже глючат если их в разных потоках вызывать, что значит, что они тоже не реентратные...

Реентерабельность != потокобезопасности, хоть и тесно соприкасаются.

Ваши функции в первую очередь не потокобезопасны.
 

Цитата

Конечно использовать критические секции по этому поводу не хотелось бы, так как это потребует снижать скорости по последовательным портам, и накладывать прочие ограничения.

Включать режим джедая + оптимизировать длину критических секций. Зачастую бывает так, что потокобезопасным нужно сделать небольшой фрагмент кода, на что обычно забивают и покрывают секцией всю функцию / модуль.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, режим джедая это конечно хорошо, но все таки хотелось бы победить его, а не как сейчас - он победил меня. Вот, например мои утилиты работы со строками в карте памяти:

       .udata_string_my.o      udata   0x000dae       data   0x000000
       .idata_string_my.o      idata   0x000dae       data   0x000000
то есть не занимают память вне стека.

И вот так выглядит функция, которая "ошибается" при сравнении строк (если без критических секций):

unsigned char strings_strcmpram2pgm (const char * str_pgm, rom const char * str_ram)
{
    unsigned int ptr = 0;
    STRINGS_ENTER_CRITICAL();
    while (str_pgm[ptr] == str_ram[ptr] && str_ram[ptr] != 0)
    {
     ptr++;   
    }
    if (ptr && str_pgm[ptr] == str_ram[ptr] && str_ram[ptr] == 0)
    {
        STRINGS_EXIT_CRITICAL();
        return 0;
    }
    STRINGS_EXIT_CRITICAL();
    return 1;
}

(другие функции тоже ошибаются если что).

В общем - что тут криминального, чего ему не хватает то - просто даже любопытно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, kan35 сказал:

Да, режим джедая это конечно хорошо, но все таки хотелось бы победить его, а не как сейчас - он победил меня.

https://ru.wikipedia.org/wiki/Реентерабельность

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Похоже я нашел таки проблему!

И она в недостаточно подробной справке к порту или точнее недоисследованности ситуации с математикой.

Quote

Memory areas used by the compiler

The MPLAB compiler does not generate reentrant code. In particular it uses memory areas as scratch memory when performing mathematical operations. These memory areas are saved by the RTOS kernel as part of the context of each task to ensure re-entrancy. The amount of RAM saved within the context of a task is set by the macro portCOMPILER_MANAGED_MEMORY_SIZE within Source/portable/MPLAB/PIC18F/port.c.

After building an application, check the size of the .tmpdata and MATH_DATA sections within the map file. If these total more than 19 bytes the constant portCOMPILER_MANAGED_MEMORY_SIZE and the macros portSAVE_CONTEXT and portRESTORE_CONTEXT will have to be modified accordingly. It is not clear whether this data block will always be of fixed size, or be application specific.

Да, я посмотрел в карте памяти

                MATH_DATA      udata   0x000000       data   0x000010
                 .tmpdata      udata   0x000010       data   0x00000c

В моем случае сумма не 19 байт, а 28. И я как честный человек поменял portCOMPILER_MANAGED_MEMORY_SIZE с 19 на 28.

И решил я поискать где этот макрос используется, обнаружил- что нигде кроме резервирования памяти и вот полез я в port.c.

		/* Store the .tempdata and MATH_DATA areas as described above. */		\
		CLRF	FSR0L, 0														\
		CLRF	FSR0H, 0														\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	POSTINC0, PREINC1												\
		MOVFF	INDF0, PREINC1													\
		MOVFF	FSR0L, PREINC1													\
		MOVFF	FSR0H, PREINC1													\

Не разумею я этот птичий язык, но понимаю, что MOVFF    POSTINC0, PREINC1 делается 19 раз, соответсвенно делалось и на Restore.

Поэтому, добавил еще 9 строк

MOVFF    POSTINC0, PREINC1

и 9 строк соответственно в

MOVFF    POSTDEC1, POSTDEC0

И таким образом я наконец то, я надеюсь, что сохраняю и восстанавливаю математический контекст как надо!

Надеюсь, ком нибудь будет полезно.

Изменено пользователем kan35

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...