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

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

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

Столкнулся с такой проблемой, надо передать строку через Queue. Скорее вопрос по Си, но все таки для FreeRTOS.

Есть массив из строк:

char string_received[2][64];

Я хочу передать указатель или на string_received[0] или на string_received[1]. Соотвественно, я делаю так:
 

char * data;

data = string_received[0];

xQueueSend(xQueueTCPRxedString, &data, 0);

И это работает, все хорошо. А как бы мне не заводя новую переменную передать указатель?

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

- так не пойдет, а как правильно?

 

PS: я бы может и не заморачивался сделать одной строкой, но как только я создаю переменную char * data, то я навсегда теряю память в стеке, то есть она не удалится после того, как она перестанет быть нужна, такой дурацкий компилятор у меня (mcc18 если что 🙂).

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


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

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

Я хочу передать указатель или на string_received[0] или на string_received[1]. Соотвественно, я делаю так:

Рекомендую читать вдумчиво книгу Стивен Прата "Язык программирования C. Лекции и упражнения" всё разжевано, дальше некуда.

 

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


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

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

Соотвественно, я делаю так:
 

char * data;

data = string_received[0];

xQueueSend(xQueueTCPRxedString, &data, 0);

А как объявлена xQueueSend? Она точно ожидает char ** в качестве второго параметра? Компилятор предупреждений не выдает?

 

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

Я хочу передать указатель или на string_received[0] или на string_received[1]

Телепатически предположу, что функция ожидает второй параметр в виде char (const?) * и писать нужно 

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

 

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

- так не пойдет

Почему? Вы же взяли адрес от указателя data чуть выше, здесь вы делаете то же самое (там тоже было неправильно, но "оно же работает").

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


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

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

PS: я бы может и не заморачивался сделать одной строкой, но как только я создаю переменную char * data, то я навсегда теряю память в стеке, то есть она не удалится после того, как она перестанет быть нужна, такой дурацкий компилятор у меня (mcc18 если что 🙂).

:shok: Так значит виноват компилятор в вашем незнании языка си???

Написав xQueueSend(xQueueTCPRxedString, &data, 0); вы передаёте не "указатель на стороку", а "указатель на указатель на строку". И если data - локальная переменная (на стеке), а вы сразу выходите из функции после этого безобразия, то программа вполне вправе повиснуть или вылетететь в HardFault. И виноват тут однозначно не "дурацкий" компилятор.

Указатель на строку передаётся так: xQueueSend(xQueueTCPRxedString, &string_received[0][0], 0);

Хотя некоторым здесь больше по нраву: xQueueSend(xQueueTCPRxedString, string_received[0], 0);  (но это уже - дело вкуса).

 

PS:

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

Скорее вопрос по Си

Вот уж точно! Учебник в руки и изучать-изучать.....

 

16 минут назад, Сергей Борщ сказал:

А как объявлена xQueueSend? Она точно ожидает char ** в качестве второго параметра? Компилятор предупреждений не выдает?

FreeRTOS-ом не пользуюсь, но чисто из логики - там скорей всего аргумент типа void *.

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


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

Принимающая сторона ждет не char **, а char * 🙂 По сути - адрес строки в ОЗУ.

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

И да, при приеме из очереди мы сами назначаем тип:

unsigned char * str_rcv;
if (xQueueReceive(xQueueTCPRxedString, &str_rcv, 5000) == pdTRUE)
{

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

Мое мнение такое, что должна возникнуть переменная, в которой будет содержаться адрес строки. И потом эту переменную скормить в QueueSend, после чего переменная может и исчезнуть (и хорошо, если бы исчезла), это уже без разницы, так как её контент уже в queue. И как я уже сообщил ранее, если вручную её создать, то оно и работает, вопросов с этим нет. Но в силу того, что компилятор довольно кривой, он не чистит стек, если переменная не используется далее, то есть все временные переменные остаються в стеке. В итоге стек засоряется так быстро, что не успеваешь моргнуть, поэтому стараюсь писать чище. Кстати, стек под одну функцию в pic18 что то около 100 байт 🙂, в mcc18 к сожалению это не обходится, хотя я не понимаю, почему так они сделали. Компилятор даже такую ошибку выдает - "Стек заполнен"- такие дела...

В общем, как мне видится, запись должна быть что-то в виде такого:

xQueueSend(xQueueTCPRxedString, &((char *)string_received[0]), 0);

Но компилятор не пропускает это, "Syntax error".

Логично, что если делать как я ранее делал

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

то в Queue попадает первые два байта строки, что в общем не то, то нужно. Кстати, если делать как jcxz предложил:

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

то в Quue попадают те же первые два байта строки, не?😎 Передавать в Queue  &string_received[0][0] или string_received[0] или &string_received[0] - это всё одно и то же, но я проверил на всякий случай))

 

В общем, есть ощущение, что без промежуточной переменной не обойтись, как ни крути. Первый раз такое в Си мне встретилось, ранее считал, что все, что можно при желании можно уложить в одну строку кода.

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

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


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

Второй аргумент функции xQueueSend - это указатель на место в памяти,
где хранится то, что нужно положить в очередь.

 portBASE_TYPE xQueueSend(
                              xQueueHandle xQueue,
                              const void * pvItemToQueue,
                              portTickType xTicksToWait
                         );

Стало быть указатель на Вашу строку должен храниться где то в памяти, и в функцию xQueueSend надо передавать указатель на это место в памяти.
Используется он только на время вызова этой функции, как второй аргумент функции memcpy:

memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize );

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

char string_received[2][64];
char *p_strings[] = {string_received[0], string_received[1]};

Возможно более опытные товарищи предложат лучший вариант... :scratch_one-s_head:

queue.h
queue.c
 

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


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

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

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

Указатель = переменная, в которой хранится адрес чего-либо. Зачем вам еще одна переменная, если вы можете занести нужный адрес сразу в то место, где находится параметр функции? К тому же когда ваш компилятор настолько убог, что не может соптимизировать такую временную переменную.

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

В общем, как мне видится, запись должна быть что-то в виде такого:

xQueueSend(xQueueTCPRxedString, &((char *)string_received[0]), 0);

Но компилятор не пропускает это, "Syntax error".

Честно говоря, я не вижу тут синтаксической ошибки.

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

Логично, что если делать как я ранее делал

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

то в Queue попадает первые два байта строки, что в общем не то, то нужно.

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

Дальше была чушь, надо больше спать.

Впрочем, попробую еще раз попрактиковаться в телепатии: вы конец строки определяете каким-то своим образом, скажем, по символу с кодом 0. Тогда следите за руками:

char a;         // переменная типа char
char b[64];     // массив из 64 переменных типа char
char c[64][3];  // массив из 3 массивов, каждый из которых состоит из 64 переменных типа char

char aa = a;        // чтение переменной a
char bb = b[10];    // чтение одиннадцатой ячейки массива b
char *pcc = c[1];   // взятие адреса второго массива c.
char cc = c[2][20]; // чтение двадцать первой ячейки третьего массива c

теперь сравните третью строку со своим объявлением массива:

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

Есть массив из строк:

char string_received[2][64];

Я сильно сомневаюсь, что вы хотели объявить 64 строки по 2 символа. Исходя из этого, а также из того, что многомерные массивы располагаются в памяти в виде непрерывной области, где строки массива идут подряд, подозреваю, что ваша проблема "в Queue попадает первые два байта строки" не в передаче адреса массива в фунуцию xQueueSend(), а в том, что при заполнении исходного массива данными они попадают совсем не туда, куда вы ожидаете. То есть когда вы пишете первую строку, вы начиная с третьего байта выходите за пределы выделенной для этой строки памяти и залезаете в ячейки второй строки, а при записи второй строки вы затираете эти вышедшие за границу байты первой.

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


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

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

Честно говоря, я не вижу тут синтаксической ошибки.

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

Там и нет синтаксической ошибки! Ошибка в использовании функции.

Сергей, прочитайте мое предыдущее сообщение! 

В функцию должно передаваться не значение, помещаемое в очередь, а указатель на это значение! 

Я же прикрепил исходники этой функции из  FreeRtos

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


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

2 часа назад, Shamil сказал:

В функцию должно передаваться не значение, помещаемое в очередь, а указатель на это значение! 

Это было понятно и из контекста и из исходного вопроса. Но дело в том, что любые массивы в языке C всегда в функцию передаются по указателю. Имя массива всегда неявно приводится к типу "указатель на его элемент" и этот указатель содержит адрес первого (нулевого) элемента массива. Поэтому все эти шаманства с явным приведением к типу char * излишни - это приведение выполняется неявно по правилам языка и не требует никаких дополнительных действий от программиста. Точно так же, как и указатель на любой тип неявно приводится к типу void *, поэтому в вашей записи "( void * ) pxQueue->pcWriteTo" (void *) лишнее.

Проблема автора вопроса в том, что двумерный массив объявляется как

type name[cols][rows]

а обращение осуществляется как 

name[row][column]

 

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

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


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

Да это не мои записи, это из исходников FreeRTOS, которые я приложил. 

Топикстартер хочет класть в очередь не сами строки, а указатели на строки. 

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

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


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

Вопрос, наверное, стоит закрыть. И данная псевдопроблема не имеет красивого решения, так как если бы в памяти выделилось место под указатель, то в принципе я бы эту память и потерял после этого. Придется иметь некий массив из временных указателей, спасибо разработчикам mcc18, если кому то интересно про эту фичу пиков почитать - вот здесь обсуждение, например https://forum.microchip.com/s/topic/a5C3l000000M2v4EAC/t241282

А, кстати, есть еще один компилятор от Микрочипа - XC8, может быть кто знает - есть ли в природе порт FreeRTOS под него? Может он попрямее будет, если уж его взяли на смену mcc18.

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

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


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

7 hours ago, kan35 said:

PS: я бы может и не заморачивался сделать одной строкой, но как только я создаю переменную char * data, то я навсегда теряю память в стеке,

Эвучит как бред.

6 minutes ago, kan35 said:

если кому то интересно про эту фичу пиков почитать - вот здесь обсуждение,

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

 

Можно поподробнее про 'потерю стека навсегда'? Если под этим имеется в виду, что компилятор не переиспользует место в стеке для переменных, назодящихся в разных непересекающихся скопах, то он имеет право так делать. Возможно поднятие уровня оптимизации более -O0 поможет. XC8 тоже возможно поможет.

 

11 minutes ago, kan35 said:

Ну в принципе вопрос ясен - не имеет красивого решения, так как если бы в памяти выделилось место под указатель,

Подозреваю, что вы неправильно используете очередь. Очень странно засовывать в неё одни и теже значения (у вас адресов указателей всего 2 штуки)

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


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

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

Действительно на низкой оптимизации такое поведение меня не смутило бы. У меня оптимизация - максимальная.

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


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

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

я навсегда теряю память в стеке

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

то в принципе я бы эту память и потерял после этого.

"Потеря памяти" - это что-то из мексиканских сериалов. Там это обычное явление. Но чтобы память терялась в си... :shok: о чём вы всё время толкуете - тут никто не понимает, думаю.

В си случаются "утечки памяти". Но это только относительно динамической памяти. Коей у вас вроде как не используется.

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


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

2 часа назад, Shamil сказал:

Топикстартер хочет класть в очередь не сами строки, а указатели на строки. 

Читайте по губам: строку (которая есть массив символов) невозможно напрямую передать в функцию, вместо массива всегда автоматически передается указатель на этот массив. Просто потому, что так написано в Стандарте языка C. Яндкес->"передача параметра по значению", "передача параметра по указателю". Единственный способ передать строку в функцию по значению (читай -  в виде копии всего массива символов) - это обернуть этот массив в структуру либо union и передавать в функцию эту структуру/union, но в приведенном коде этого нет. Приведенный здесь код, что 

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

что

6 часов назад, kan35 сказал:
xQueueSend(xQueueTCPRxedString, &string_received[0][0], 0);

передает в функцию указатель на первый элемент массива == на первый символ строки. Без всяких лишних переменных-указателей. То есть делает именно то, что хочет автор вопроса. Другое дело, что этот код работает не так, как ожидает автор вопроса. Я уже дважды разжевал, почему это происходит,

раз:

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

теперь сравните третью строку со своим объявлением массива:

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

Есть массив из строк:

char string_received[2][64];

Я сильно сомневаюсь, что вы хотели объявить 64 строки по 2 символа.

два:

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

Проблема автора вопроса в том, что двумерный массив объявляется как

type name[cols][rows]

а обращение осуществляется как 

name[row][column]

 

Повторю в третий раз: ошибка автора в том, что он неправильно объявил размерности массива, нужно написать

char string_received[64][2];

а не

9 часов назад, kan35 сказал:
char string_received[2][64];

но, похоже, читать (или пытаться понять) это никто не хочет.

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

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


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

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

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

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

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

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

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

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

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

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