как объяснить такое поведение PROGMEM

alexbmd
Offline
Зарегистрирован: 15.01.2016

Добрый день старожилы и гуру.

Заметил что ни смотря на наличие прогмем атрибута, компилятор не всегда помещает строки во флэш. Не могли бы вы объяснить чем рукаводствуется компилятор/почему так происходит ?

первый пример. если объявляем без инициализации то list[3] не сохраняется во флэш

const PROGMEM char one[] = HELLO;
const PROGMEM char two[] = WORD;
const PROGMEM char three[] = WORD;

//const PROGMEM char *const list[] = {one, two, three};
const PROGMEM char *const list[3];

 

второй пример. если элементы list массивы как выше то сохраняет во флэш. если так, то нет.

const PROGMEM char *const one = HELLO;
const PROGMEM char *const two = WORD;

const PROGMEM char *const list[] = {one, two};

 

b707
Offline
Зарегистрирован: 26.05.2017

Все, что находится в ПРОГМЕМ - по определению константа. Все константы должны быть проинициализированы в момеент декларации, поэтому в первом примере в ПРГМЕМ пишется пустой массив.

Во втором примере вы создаете в ПРОГМЕМ не строки, а пустые указатели в никуда.

Ну и в третьих забавные строки у вас.

 

Ну а еще - тут нет никакого особенного "поведения ПРОГМЕМ". Все перечисленные вопросы не имеют ровно никакого отношения к ПРОГМЕМ вообще  - просто  вы элементарно не знаете Си

 

alexbmd
Offline
Зарегистрирован: 15.01.2016

я не знаю Си.

"поэтому в первом примере в ПРГМЕМ пишется пустой массив.", пустой да. но вот в прогмем то он и не пишется. я про это спрашивал. он отжирает память из SRAM

"Во втором примере вы создаете в ПРОГМЕМ не строки, а пустые указатели в никуда." - почему в никуда? и даже если никуда то опять же не в прогмем они пишуться а в SRAM. я про это спрашивал

а чем строки забавные ?  #define HELLO "Hello" вы же не подумали что HELLO это и есть строка

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

alexbmd пишет:

я не знаю Си.

Так, может, уж выучить?

Всё, что нужно знать про массивы в прогмем - http://arduino.ru/forum/programmirovanie/ocherednoi-raz-progmem#comment-336371

alexbmd
Offline
Зарегистрирован: 15.01.2016

Я на основе этого поста и писал свой вопрос. Что там написано все понятно. Но там нет ответа на мои вопросы.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

alexbmd пишет:
Что там написано все понятно.

Все твои дискуссии одинаковы. тебе говорят "ты не понял", ты начинаешь доказывать, что "понял, только не работает". Ну, раз у людей работает, а у тебя - нет, значит не понял!

Так, давай внятно рассказывай что у теб сохраняется, а что нет (не "как выше", а внятно). И заодно, обязательно скажи, с чего ты взял, что оно сохраняется или нет - как ты это проверял?

alexbmd
Offline
Зарегистрирован: 15.01.2016

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

Рассказываю внятно
Вариант 1.а рабочий - как поместить три строки во флэш и все это в *list [] , мы знаем и занимает это sram памяти с учетом программы 400байт. Повторяю строки во флэш.
Вариант 1.b не рабочий - заменяем строчку с массовом указателей на такую const PROGMEM char *const list[3]; остальное не трогаем, и теперь оперативки на 6 байт меньше (ровно наши три пустых указателя). Почему три пустых указателя не помещаются во флэш?

Вариант 2.а рабочий - опять же наши строки во флэш
Вариант 2.b не рабочий - меняем только первую строчку.
Было const PROGMEM char one[] = "HELLO";
Стало const PROGMEM char *const one = "HELLO";
Оперативки стало на 6 байт меньше, ровно наша строчка. Почему указатель на литерал не сохраняется во флэш точно также как массив символов ?

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

Помниться даже в 90ые на ассемблере в zx spectrum +- пару сотен байт никому не важно было....а тут вам 6 байт не хватило - вы нормальный?

b707
Offline
Зарегистрирован: 26.05.2017

andycat пишет:
Помниться даже в 90ые на ассемблере в zx spectrum +- пару сотен байт никому не важно было....а тут вам 6 байт не хватило - вы нормальный?

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

b707
Offline
Зарегистрирован: 26.05.2017

alexbmd пишет:
Вариант 1.b не рабочий - заменяем строчку с массовом указателей на такую const PROGMEM char *const list[3]; остальное не трогаем, и теперь оперативки на 6 байт меньше (ровно наши три пустых указателя). Почему три пустых указателя не помещаются во флэш?

У вас предупреждения в компиляторе включены?

Дело в том, что помещение пустого массива в ПРОГМЕМ строчкой

const PROGMEM char *const list[3];

- абсолютно бессмысленная операция. Настолько бессмысленная. что это очевидно даже для тупого железного компилятора, так что я не исключаю, что компилятор выдает вам предупреждение о недопустимости создания неинициализированного массива в ПРОГМЕМе и исправляет вашу ошибку, помещая массив в оперативку.

b707
Offline
Зарегистрирован: 26.05.2017

alexbmd пишет:
Вариант 1.а рабочий - как поместить три строки во флэш и все это в *list [] , мы знаем и занимает это sram памяти с учетом программы 400байт. Повторяю строки во флэш.

Вариант 1.b не рабочий - заменяем строчку с массовом указателей на такую const PROGMEM char *const list[3]; остальное не трогаем, и теперь оперативки на 6 байт меньше (ровно наши три пустых указателя). Почему три пустых указателя не помещаются во флэш?

 

Попробовал скомпилировать ваши варианты 1а и 1в -

1а =  флеш 1452 байта. оперативки 184 байта
1в =  флеш 1430 байт, оперативка 184

Никакие шесть байтов от RAM не отъедаются, а то что первый вариант на 22 байта больше во флеше - так это вполне ожидаемо, так как во втором варианте строчки в программе не используются и компилятор их выкинул

Иде 1.8.3, плата Ардуино Уно

nik182
Offline
Зарегистрирован: 04.05.2015

К тому же если не пользоваться pgm_read_??? при попытке вывода массивов из PROGMEM компилятор всегда будет резервировать место в оперативке. Будет казаться по размеру памяти программы что всё плохо.

alexbmd
Offline
Зарегистрирован: 15.01.2016

Ну правильно если их не использовать компилятор вообще их оптимизируют до уничтожения. Поэтому у вас оперативка одинаковая. Могу скинуть позже пример с разной оперативкой.
"потому что пустой поэтому помещаем в оперативк" - ну он сейчас так и делает. Я это еще в первом посте сказал. Но я например ожидаю размещение во флэш тк атрибут прогмем у нас. Или ошибку что надо инициализировать. А не так что компилятор делает по своему и нас не слушает.

А Про второй вопрос где не все так очевидно, есть соображения?

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Даже компилятор понял, что нет смысла Вас слушать и объяснять как не стоит писать, а надо делать как его научили.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

alexbmd пишет:
ТЯ же описал как работает и как нет.

Вариант 1.а ...
Вариант 1.b ...
Вариант 2.а ...
Вариант 2.b ...

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

И ещё. Выложи код, которым ты проверяешь поместила она во флешь или нет. А то доверия к твоему умению что-то делать, извини, невелико.

alexbmd
Offline
Зарегистрирован: 15.01.2016
//пример 1
//всё во флэш. 
//Sketch uses 3786 bytes (13%) of program storage space. Maximum is 28672 bytes.
//Global variables use 151 bytes (5%) of dynamic memory, leaving 2409 bytes for local variables. Maximum is 2560 bytes.
const PROGMEM char one[] = "NUMBER1";
const PROGMEM char two[] = "NUMBER2";

const PROGMEM char *const list[] = {one, two};

bool print_P(const char * addrOfString){
    char textBuffer[30];
    strcpy_P(textBuffer, addrOfString);
    return Serial.println(textBuffer);
}

void setup() {
}

void loop() {
   	int8_t i = 0;
	while (pgm_read_word(&list[i])) {
		if (!print_P(pgm_read_word(&list[i++]))) return false;
		if (i>1) break;
	} 
}
// пример 2
// теперь две строки в оперативки. ни смотря на прогмем. сам поинтер в прогмем. как запихнуть и строки туда но не через массив?
// Sketch uses 3786 bytes (13%) of program storage space. Maximum is 28672 bytes.
// Global variables use 167 bytes (6%) of dynamic memory, leaving 2393 bytes for local variables. Maximum is 2560 bytes.
const PROGMEM char *const one = "NUMBER1";
const PROGMEM char *const two = "NUMBER2";

const PROGMEM char *const list[] = {one, two};

bool print_P(const char * addrOfString){
    char textBuffer[30];
    strcpy_P(textBuffer, addrOfString);
    return Serial.println(textBuffer);
}

void setup() {
    
}

void loop() {
   	int8_t i = 0;
	while (pgm_read_word(&list[i])) {
		if (!print_P(pgm_read_word(&list[i++]))) return false;
		if (i>1) break;
	} 
}
//пример 3
//пустой массив указателей в оперативки. занимает 20 байт. причем запихнуть то его нулевой в прогмем можно. но чего в таком виде он в оперативке
//Sketch uses 3766 bytes (13%) of program storage space. Maximum is 28672 bytes.
//Global variables use 171 bytes (6%) of dynamic memory, leaving 2389 bytes for local variables. Maximum is 2560 bytes.
const PROGMEM char one[] = "NUMBER1";
const PROGMEM char two[] = "NUMBER2";

const PROGMEM char *const list[10];

bool print_P(const char * addrOfString){
    char textBuffer[30];
    strcpy_P(textBuffer, addrOfString);
    return Serial.println(textBuffer);
}

void setup() {
    
}

void loop() {
   	int8_t i = 0;
	while (pgm_read_word(&list[i])) {
		if (!print_P(pgm_read_word(&list[i++]))) return false;
		if (i>1) break;
	} 
}

 

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Сдаётся мне, что тут сохраняется в прогмеме указатель на строку в RAM, а не сама строка.

b707
Offline
Зарегистрирован: 26.05.2017

"Число правильных вариантов - ограничено, неправильных  - бесконечно" (с) мой :)

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Ну, и чего ж тебе понятно по той ссылке, если ты всё сделал не так?

А твоём первом примере в прогмем попадает массива символов. А во втором, масив остётся в рам, а в прогмем попадает значение указателя на него.

alexbmd
Offline
Зарегистрирован: 15.01.2016

ну так а я что спрашивал в первом посте.  хоть там и не массив а указатель (ты опечатался наверно) но это я и спрашиваю. почему если массив прогмем то он во флэш а если указатель прогмем то он не хочет во флэш?

и почему нулевой в раме может быть а в прогмем нет ? 

 

andycat
andycat аватар
Offline
Зарегистрирован: 07.09.2017

alexbmd пишет:

 

и почему нулевой в раме может быть а в прогмем нет ? 

 

потому что смысла нет, в ОЗУ выделяется пустое место для работы.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

alexbmd пишет:

почему если массив прогмем то он во флэш а если указатель прогмем то он не хочет во флэш?

Прежде всего - все данные, в сущности, располагаются в Progmem Space. Изначально. Но после, так как компилятор не был проинструктирован не вставлять в прошивку операции копирования данных в RAM, они туда дублируются. Всё.

Все фантазиии насчёт хочет / не хочет - это недопонимание происходящих процессов.

alexbmd
Offline
Зарегистрирован: 15.01.2016

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

const PROGMEM char *const one = "NUMBER1";

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Проинструктирован оставить без копирования в PROGMEM char* - он и оставлен. Насчёт остального распоряжений не поступало.

alexbmd
Offline
Зарегистрирован: 15.01.2016

а как про остальное ему намекнуть ?

sadman41
Онлайн
Зарегистрирован: 19.10.2016

alexbmd пишет:

а как про остальное ему намекнуть ?

http://arduino.ru/forum/programmirovanie/progmem-tricks#comment-439006

alexbmd
Offline
Зарегистрирован: 15.01.2016

andycat пишет:

потому что смысла нет,

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

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

alexbmd пишет:
почему если массив прогмем то он во флэш а если указатель прогмем то он не хочет во флэш?
Прости, но ты, как всегда, не понял что тебе говорят, а лезешь "дискутировать". Я тебе что написал?

Ворота пишет:

А твоём первом примере в прогмем попадает массива символов. А во втором, массив остаётся в рам, а в прогмем попадает значение указателя на него.

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

Теперь ответ на вопрос "почему так". Ответ очень простой - потому, что ты так написал.

В прогмем можно пихать что угодно. Что программист хочет, то и пихает. Кто-то пихает туда сам массив, кто-то указатель на него, а кто-то и то и другое. Именно об этом был пост, который я тебе "проссылил" и который ты "понял". Так вот ты написал "запихать туда указатель, а то, на что он указывает - оставить в RAM". Компилятор ответил "Есть, сэр!" и сделал именно это.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

alexbmd пишет:

какое дело железке до смысла. 

Слышал такое слово "оптимизация"? Вот железка и удаляет из программы всё, что никак не используется и не может использоваться. Она (железка), похоже, поразумнее некоторых программистов будет.

alexbmd
Offline
Зарегистрирован: 15.01.2016

Ворота пишет:

А твоём первом примере в прогмем попадает массива символов. А во втором, массив остаётся в рам, а в прогмем попадает значение указателя на него.

сори проглядел написанное.  как заставить компилятор саму строку засунуть в PGM (чтоб по итогу получить и поинтер и литерал во флэш)? без объявления массива

судя по всему никак , правильно я понимаю?

 

alexbmd
Offline
Зарегистрирован: 15.01.2016

Ворота пишет:

 Вот железка и удаляет из программы

ну она не удаляет а рзмещает в рам. плюс переменная то используется в том примере.  и компилятору вроде должно быть всеравно откуда использовать перемнную. а мы ему чтоб он долго не думал указываем пускай оно полежит во флэш.  причем если явно объявить её как NULL то он разместит во флеш. а если не инициализированна (при этом она тоже NULL) то в рам.  честно но в данном конкретном примере явно железка тугодумит

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

alexbmd пишет:

ну она не удаляет а рзмещает в рам. 

ни хрена не размещает - именно удаляет.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

alexbmd пишет:

сори проглядел написанное.  как заставить компилятор саму строку засунуть в PGM (чтоб по итогу получить и поинтер и литерал во флэш)? без объявления массива

судя по всему никак , правильно я понимаю?

Почему, никак - размещай на здоровье, если умеешь.

Если хочешь, чтобы я тебе учил этому, для начала объясни мне нахрена тебе это нужно, чем тебе [] не угодили? А то будет как всегда - тратишь часы. а выясняется. что надо было не это, просто человек вопросы задавать не умеет.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

b707 пишет:

"Число правильных вариантов - ограничено, неправильных  - бесконечно" (с) мой :)

Ой ли!

Я это слышал еще в исполнении А. Райкина. Значит, скорее всего, (с) М. Жванецкий.

alexbmd
Offline
Зарегистрирован: 15.01.2016

Ворота пишет:

 именно удаляет.

а что тогда размещается в оперативки ровно на длину строки ?

alexbmd
Offline
Зарегистрирован: 15.01.2016

Ворота пишет:

 объясни мне нахрена тебе это нужно, чем тебе [] не угодили? 

да всем угодил . просто знать как еще можно ?

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

alexbmd пишет:

Ворота пишет:

 именно удаляет.

а что тогда размещается в оперативки ровно на длину строки ?

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

const PROGMEM char *const list[3];

Какой тут нахер строки????

------------

В общем, так, теряется много времени на беспорядочные тексты. Давай наведеём порядок.

Сейчас я хочу знать насколько ты понимаешь самые основы. Потому жду ответа на вопрос деда. Всё остальное я игнорирую (в том числе и вопрос "какой тут ..." выше - не отвечай!).

Хочешь помощи - делай что говорят.

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

alexbmd пишет:
просто знать как еще можно ?
Просто знать без знания языка программирования не получится. Для начала сделай что-нибудь с
alexbmd пишет:

я не знаю Си.

Проштудируй К&R. Вопросы отпадут сами.

alexbmd
Offline
Зарегистрирован: 15.01.2016

да согласен давай по одному вопросу. ато путаемся.

const PROGMEM char *const list[10];

у меня было 10 чтоб меньше мосх ((с)Дед) напрягать. оперативки на 20 байт меньше.  других же изменений в программе не было. значит это не инопланетные наводки. и не произвол компилятора. это ровно наши 10 указателей в оперативке.  еслиб оно просто удалилось как ты говоришь то оперативка бы не изменилась. ?

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Ты КАК ВСЕГДА не приводишь полный код, который я могу скомпилировать и посмотреть. Повторится ещё раз, просто пошлю тебя нафиг - задолбало тебе одно и то же писать.

Вот смотри, твой пример (и кстати, смотри как надо вопросы задавать, чтобы тебя понимали)

Вот такой код (прямо сейчас скомпилировал)

void setup() {}
void loop() {}

у меня занимает 444/9

Вставляем твой массив, компилируем

const PROGMEM char *const list[10];
void setup() {}
void loop() {}

по-прежнему 444/9 - значит, массив выброшен.

Вставляем какое-нибудь использование твоего массива

const PROGMEM char *const list[10];
void setup() {
	for (unsigned i = 0; i < sizeof(list); i++) TCNT1 = (unsigned)list[i];
}
void loop() {}

теперь получилось 460/9. Разница в 16 байтов. Для массив этого мало. Вывод - он по-прежнему выбрасывается. А эти 16 байтов - вывод 10 раз нуля в регистр TCNT1

Надеюсь, ты понял как надо задавать вопросы? Вот ты пишешь про 20 байтов. А у меня получается 16. Чтобы понять что происходит, я хочу видеть твои 20. Так покажи же их мне! Я всю тему тебя прошу - вставляй ПОЛНЫЕ коды.

Не будешь этого делать, разбирайся сам.

sadman41
Онлайн
Зарегистрирован: 19.10.2016

Подолью маслица.

  1.  w/o USE_PROGMEM

  Sketch uses 472 bytes (1%) of program storage space. Maximum is 32,256 bytes.
  Global variables use 9 bytes (0%) of dynamic memory, leaving 2,039 bytes for local variables.
 
  2.  with USE_PROGMEM
 
  Sketch uses 472 bytes (1%) of program storage space. Maximum is 32,256 bytes.
  Global variables use 29 bytes (1%) of dynamic memory, leaving 2,019 bytes for local variables. Maximum is 2,048 bytes.
 
 
#pragma GCC optimize ("O0")

#define USE_PROGMEM

#ifdef USE_PROGMEM
const PROGMEM char *const list[10];
#endif

void setup() {}
void loop() {}

К тому же компилятор выдаёт сообщение: warning: uninitialized variable 'list' put into program memory area [-Wuninitialized]  -> const PROGMEM char *const list[10];

Ежели оптимизацию включаем, то соопчения нет - бесполезный функционал выкидывается до линковки.

 

b707
Offline
Зарегистрирован: 26.05.2017

sadman41 пишет:

К тому же компилятор выдаёт сообщение: warning: uninitialized variable 'list' put into program memory area [-Wuninitialized]  -> const PROGMEM char *const list[10];

Ежели оптимизацию включаем, то соопчения нет - бесполезный функционал выкидывается до линковки.

 

О! про то, что должно быть предупреждение - я писал ТС в сообщении #9. А оно, оказывается, при оптимизации не выдается...

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

У меня и с оптимизацией (Os) выдаётся, но другое "warning: uninitialized const 'list' [-fpermissive]"

alexbmd
Offline
Зарегистрирован: 15.01.2016

alexbmd пишет:

//пример 3
//пустой массив указателей в оперативки. занимает 20 байт. причем запихнуть то его нулевой в прогмем можно. но чего в таком виде он в оперативке
//Sketch uses 3766 bytes (13%) of program storage space. Maximum is 28672 bytes.
//Global variables use 171 bytes (6%) of dynamic memory, leaving 2389 bytes for local variables. Maximum is 2560 bytes.
const PROGMEM char one[] = "NUMBER1";
const PROGMEM char two[] = "NUMBER2";

const PROGMEM char *const list[10];

bool print_P(const char * addrOfString){
    char textBuffer[30];
    strcpy_P(textBuffer, addrOfString);
    return Serial.println(textBuffer);
}

void setup() {
    
}

void loop() {
   	int8_t i = 0;
	while (pgm_read_word(&list[i])) {
		if (!print_P(pgm_read_word(&list[i++]))) return false;
		if (i>1) break;
	} 
}

 

Ворота, пример как ты и просил привёл выше. Вот дублирую еще раз. Запускай на здоровье и смотри как проподает оперативка на 20 байт.

alexbmd
Offline
Зарегистрирован: 15.01.2016

Получается ворота ты брешишь :) размещается то в рам массив указателей и пример был в15 посте. Ох уж эти профессионалы :)

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

Это ты меня на слабо что ли берёшь? Смешной ты. 

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

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

alexbmd
Offline
Зарегистрирован: 15.01.2016

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

Ворота
Ворота аватар
Offline
Зарегистрирован: 10.01.2016

alexbmd пишет:
Если ты считаешь...

Я считаю, что ты либо тролль, либо идиот. Что ты ещё раз подтвердил не поняв (сделав вид, что не понял) написанного в посте #45. А я предпочитаю не общаться ни с троллями, ни с идиотами.

alexbmd пишет:
То ты показал что ты за человек.

Да, это так.

alexbmd
Offline
Зарегистрирован: 15.01.2016

Конечно , брехать не мешки таскать

1. На очевидный пример N3 где переменная в рам утверждает что она не в рам
2. Строку без массива тоже не запихнул во флэш, а говорил у меня нет проблем со строками