Arrays passed by reference only как исправить

Введение

При использовании новой версии компилятора языка MQL4 некоторые старые программы могут выдавать ошибки.

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

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

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

  1. Ошибки компиляции
    • 1.1. Идентификатор совпадает с зарезервированным словом
    • 1.2. Специальные символы в наименованиях переменных и функций
    • 1.3. Ошибки использования оператора switch
    • 1.4. Возвращаемые значения у функций
    • 1.5. Массивы в аргументах функций
  2. Ошибки времени выполнения
    • 2.1. Выход за пределы массива (Array out of range)
    • 2.2. Деление на ноль (Zero divide)
    • 2.3. Использование 0 вместо NULL для текущего символа
    • 2.4. Строки в формате Unicodе и их использование в DLL
    • 2.5. Совместное использование файлов
    • 2.6. Особенность преобразования datetime
  3. Предупреждения компилятора
    • 3.1. Пересечения имен глобальных и локальных переменных
    • 3.2. Несоответствие типов
    • 3.3. Неиспользуемые переменные

1. Ошибки компиляции

При наличии ошибок в коде программа не может быть скомпилирована.

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

#property strict

Этот режим значительно упрощает поиск ошибок.

1.1. Идентификатор совпадает с зарезервированным словом

Если наименование переменной или функции совпадает с одним из зарезервированных слов:

int char[];  
int char1[]; 
int char()   
{
 return(0);
}

то компилятор выводит сообщения об ошибках:

Рис.1. Ошибки "unexpected token" и "name expected"

Рис.1. Ошибки “unexpected token” и “name expected”

Для исправления данной ошибки нужно исправить имя переменной или функции.

1.2. Специальные символы в наименованиях переменных и функций

Если наименования переменных или функций содержат специальные символы ($, @, точка):

int $var1; 
int @var2; 
int var.3; 
void f@()  
{
 return;
}

то компилятор выводит сообщения об ошибках:

Рис.2. Ошибки "unknown symbol" и "semicolon expected"

Рис.2. Ошибки “unknown symbol” и “semicolon expected”

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

1.3. Ошибки использования оператора switch

Старая версия компилятора позволяла использовать любые значения в выражениях и константах оператора switch:

void start()
  {
   double n=3.14;
   switch(n)
     {
      case 3.14: Print("Pi");break;
      case 2.7: Print("E");break;
     }
  }

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

Рис.3. Ошибки "illegal switch expression type" и "constant expression is not integral"

Рис.3. Ошибки “illegal switch expression type” и “constant expression is not integral”

В таких случаях можно использовать явные сравнения численных значений, например:

void start()
  {
   double n=3.14;
   if(n==3.14) Print("Pi");
   else
      if(n==2.7) Print("E");
  }

1.4. Возвращаемые значений функций

Все функции, кроме void, должны возвращать значение объявленного типа. Например:

int function()
{
}

При строгом режиме компиляции (strict) возникает ошибка:

Рис.4. Ошибка "not all control paths return a value"

Рис.4. Ошибка “not all control paths return a value”

В режиме компиляции по умолчанию компилятор выводит предупреждение:

Рис.5. Предупреждение "not all control paths return a value"

Рис.5. Предупреждение “not all control paths return a value”

Если возвращаемое значение функции не соответствует объявлению:

int init()                         
  {
   return;                          
  }

то при строгом режиме компиляции возникает ошибка:

Рис.6. Ошибка "function must return a value"

Рис.6. Ошибка “function must return a value”

В режиме компиляции по умолчанию компилятор выводит предупреждение:

Рис.7. Предупреждение 'return - function must return a value"

Рис.7. Предупреждение ‘return – function must return a value”

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

1.5. Массивы в аргументах функций

Массивы в аргументах функций теперь передаются только по ссылке.

double ArrayAverage(double a[])
{
 return(0);
}

Данный код при строгом режиме компиляции (strict) приведет к ошибке:

Рис.8. Ошибка компилятора "arrays passed by reference only"

Рис.8. Ошибка компилятора “arrays passed by reference only”

В режиме компиляции по умолчанию компилятор выводит предупреждение:

Рис.9. Предупреждение компилятора "arrays passed by reference only"

Рис.9. Предупреждение компилятора “arrays passed by reference only”

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

double ArrayAverage(double &a[])
{
 return(0);
}

Следует отметить, что теперь константные массивы (Time[], Open[], High[], Low[], Close[], Volume[]) не могут быть переданы по ссылке. Например, вызов:

ArrayAverage(Open);

вне зависимости от режима компиляции приводит к ошибке:

Рис.10. Ошибка 'Open' - constant variable cannot be passed as reference

Рис.10. Ошибка ‘Open’ – constant variable cannot be passed as reference

Для устранения подобных ошибок нужно скопировать необходимые данные из константного массива:

   
   double OpenPrices[];
   
   ArrayCopy(OpenPrices,Open,0,0,WHOLE_ARRAY);
   
   ArrayAverage(OpenPrices);

2. Ошибки времени выполнения

Ошибки, возникающие в процессе исполнения кода программы принято называть ошибками времени выполнения (runtime errors). Такие ошибки обычно зависят от состояния программы и связаны с некорректными значениями переменных.

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

2.1. Выход за пределы массива (Array out of range)

Эта ошибка часто возникает в индикаторах при обращении к индикаторным буферам. Функция IndicatorCounted() возвращает количество баров, неизменившихся после последнего вызова индикатора. Значения индикаторов на уже рассчитанных ранее
барах не нуждаются в пересчете, поэтому для ускорения расчетов
достаточно обрабатывать только несколько последних баров.

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



int start()
  {
   
   if (Bars<100) 
     return(-1); 

   
   int counted_bars=IndicatorCounted();
   
   if(counted_bars<0) return(-1);
      
   
   int limit=Bars-counted_bars;

   
   if(counted_bars==0) 
     {
      limit--;  
      
      limit-=10;
     }
   else 
     {     
      
      limit++;
     } 
   
   for (int i=limit; i>0; i--)
   {
     Buff1[i]=0.5*(Open[i+5]+Close[i+10]) 
   }
}

Часто встречается некорректная обработка случая counted_bars==0 (начальную позицию limit нужно уменьшить на значение, равное 1 + максимальный индекс относительно переменной цикла).

Также следует помнить о том, что в момент исполнения функции start() мы можем обращаться к элементам массивов индикаторных буферов от 0 до Bars()-1. Если есть необходимость работы с массивами, которые не являются индикаторными буферами, то их размер следует увеличить при помощи функции ArrayResize() в соответствии с текущим размером индикаторных буферов. Максимальный индекс элемента для адресации также можно получить вызовом ArraySize() с одним из индикаторных буферов в качестве аргумента.

2.2. Деление на ноль (Zero divide)

Ошибка “Zero divide” возникает в случае, если при выполнении операции деления делитель оказывается равен нулю:

void OnStart()
  {

   int a=0, b=0,c;
   c=a/b;
   Print("c=",c);
  }

При выполнении данного скрипта во вкладке “Эксперты” возникает сообщение об ошибке и завершении работы программы:

Рис.11. Сообщение об ошибке "zero divide"

Рис.11. Сообщение об ошибке “zero divide”

Обычно такая ошибка возникает в случаях, когда значение делителя определяется значениями каких-либо внешних данных. Например, если анализируются параметры торговли, то величина задействованной маржи оказывается равна 0 если нет открытых ордеров. Другой пример: если анализируемые данные считываются из файла, то в случае его отсутствия нельзя гарантировать корректную работу. По этой причине желательно стараться учитывать подобные случаи и корректно их обрабатывать.

Самый простой способ – проверять делитель перед операцией деления и выводить сообщение об некорректном значении параметра:

void OnStart()
  {

   int a=0, b=0,c;
   if(b!=0) {c=a/b; Print(c);}
   else {Print("Error: b=0"); return; };
  }

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

Рис. 12. Сообщение о некорректном значении делителя

Рис. 12. Сообщение о некорректном значении делителя

2.3. Использование 0 вместо NULL для текущего символа

В старой версии компилятора допускалось использование 0 (нуля) в качестве аргумента в функциях, требующих указания финансового инструмента.

Например, значение технического индикатора Moving Average для текущего символа можно было запрашивать следующим образом:

AlligatorJawsBuffer[i]=iMA(0,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); 

В новом компиляторе для указания текущего символа нужно явно указывать NULL:

AlligatorJawsBuffer[i]=iMA(NULL,0,13,8,MODE_SMMA,PRICE_MEDIAN,i); 

Кроме того, текущий символ и период графика можно указать при помощи функций Symbol() и Period().

AlligatorJawsBuffer[i]=iMA(Symbol(),Period(),13,8,MODE_SMMA,PRICE_MEDIAN,i); 

2.4. Строки в формате Unicodе и их использование в DLL

Строки теперь представляют собой последовательность символов Unicode.

Следует учитывать этот факт и использовать соответствующие функции Windows. Например, при использовании функций библиотеки wininet.dll вместо InternetOpenA() и InternetOpenUrlA() следует вызывать InternetOpenW() и InternetOpenUrlW().

В MQL4 изменилась внутренняя структура строк (теперь она занимает 12 байт), поэтому при передаче строк в DLL следует использовать структуру MqlString:

#pragma pack(push,1)
struct MqlString
  {
   int      size;       
   LPWSTR   buffer;     
   int      reserved;   
  };
#pragma pack(pop,1)

2.5. Совместное использование файлов

В новом MQL4 при открытии файлов необходимо явно указывать флаги FILE_SHARE_WRITE и FILE_SHARE_READ для совместного использования.

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

Например, при работе с оффлайновыми графиками требуется явно указывать флаги совместного доступа:

   
   ExtHandle=FileOpenHistory(c_symbol+i_period+".hst",FILE_BIN|FILE_WRITE|FILE_SHARE_WRITE|FILE_SHARE_READ);

Подробности можно найти в статье в статье “Оффлайновые графики и новый MQL4“.

2.6. Особенность преобразования datetime

Следует иметь ввиду, что преобразование типа datetime в строку теперь зависит от режима компиляции:

  datetime date=D'2014.03.05 15:46:58';
  string str="mydate="+date;

Например, попытка работы с файлами, имя которых содержит двоеточие, приведет к ошибке.

3. Предупреждения компилятора

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

Чистый код не должен содержать предупреждений.

3.1. Пересечения имен глобальных и локальных переменных

Если на глобальном и локальном уровнях присутствуют переменные с одинаковыми именами:

int i; 
void OnStart()
  {

   int i=0,j=0; 
   for (i=0; i<5; i++) {j+=i;}
   PrintFormat("i=%d, j=%d",i,j);
  }

то компилятор выводит предупреждение и укажет номер строки, на которой объявлена глобальная переменная:

Рис.13. Предупреждение "declaration of '%' hides global declaration at line %"

Рис.13. Предупреждение “declaration of ‘%’ hides global declaration at line %”

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

3.2. Несоответствие типов

В новой версии компилятора введена операция приведения типов.

#property strict
void OnStart()
  {
   double a=7;
   float b=a;
   int c=b;
   string str=c;
   Print(c);
  }

В строгом режиме компиляции при несоответствии типов компилятор выводит предупреждения:

Рис.14. Предупреждения "possible loss of data due to type conversion" и "implicit conversion from 'number' to 'string'

Рис.14. Предупреждения “possible loss of data due to type conversion” и “implicit conversion from ‘number’ to ‘string’

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

Для исправления нужно использовать явное приведение типов:

#property strict
void OnStart()
  {
   double a=7;
   float b=(float)a;
   int c=(int)b;
   string str=(string)c;
   Print(c);
  }

3.3. Неиспользуемые переменные

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

void OnStart()
  {
   int i,j=10,k,l,m,n2=1;
   for(i=0; i<5; i++) {j+=i;}
  }

Сообщения о таких переменных выводятся вне зависимости от режима компиляции:

Рис.15. Предупреждения "variable '%' not used'

Рис.15. Предупреждения “variable ‘%’ not used’

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

Выводы

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

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

Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.

Привет Всем, – доброго здравия,- а также успехов в освоении программирования тем кто решил освоить язык – mql4,

Во первых, – я хочу поблагодарить от всей души,- всех,- кто так или иначе принял участие в проекте – Stels.
Особая благодарность пользователю под ником – SERG…
с ним я знаком лично и на протяжении почти 1.5 месяца ночами напролёт и обьяснявшего и показывавшего на примерах как происходит собственно программирование,- и благодаря его помощи в итоге проект был завершён…
на этом собственно знания по программированию в рамках проекта считаю для себя законченным…

Следующий этап обучения для меня,- научится и желательно виртуозно с помощью кода определять нужное время и нужное место на графике
—————————————————————————————————–
ранее я высказывал – непонимание, в неприятии нашими программистами относительно 5 знака…
и даже высказывал предположение что это не может быть каким то длинным кодом,- и можете сами судить,- вот сам код с помощью которого решается вся эта
– я бы даже назвал это – ленью…
//=============
int init()
{
if(Digits == 3 || Digits ==5) // тоже самое (Digits == 3<5)
{
TP *=10;
SL *=10;
sleep *=10;
}
return(0);
}
//=============

это касательно тейков и стопов …
а вот с помощью этого

//=============
double point;
if(Digits == 5) point = 0.0001/Point;
else
{
if(Digits == 3) point = 0.01/Point;
}
//=============
решается проблема отображения индикаторами ценовых уровней – типа без лишних нулей,- то есть если – от искомого уровня – до цены – 10 п, а индикатор показывает все 100…
надо отметить что это один из нескольких вариантов и причём не оригинальный,- но почему то он мне больше приглянулся…
——————————————————————————————————————
С Вашего позволения я закончу официальную и хвалебную части,- и собственно перейду к проблеме которая выявилась, и в итоге из-за которой весь проект и гроша ломанного не стоит…
——————————————————————————————————————-
речь идёт о тактике – “Прайс Экшн”,- вернее не о самой тактике,- а о возможности в программном исполнении передать максимально точную,- а ещё лучше – идеальную картину происходящего на графике – то есть – об отдельной функции способной определить нужное место в нужное время…

В связи с этим я перерыл и даже нарыл дюжину индикаторов(хотя их и не так уж много,- наверное в связи с тем что это считается – ересью,- в отличии от математически признанных), работающих по этому принципу…
изучил их коды(правда много чего не понял,- пока)- но есть у них одна общая черта – они ничего общего с ситуацией на рынке не имеют, и по этому работают сами по себе,- но исполнение самого когда или даже я бы сказал эволюция кода – показывает что сами программисты не имеющие ничего общего с трейдингом,- начали понимать одну особенность – программа не должна,- не может – пороть отсебятину – это плохо кончится для самого трейдера,- вернее его депозита…

И так сама суть проблемы…
свеча – главный и единственный показатель…
имеет четыре общепризнанных параметра

Open[1]
Close[1]
High[1]
Low[1]
манипулируя этими параметрами предполагается распознавание или определение комбинации
на пример:
if
(
Open[2] > Close[2] || // медведи
Open[1] < Close[1] || // бычки
Open[1] => Close[2] ||
Close[1] > Close[2]||
High[1] => High[2]||
Low[1] =< Low[2]||
Low[1] => Close[2]
)
примерно так,- код – должен определить бычье поглощение на графике,- которое на самом деле ничего общего не имеет с определяемыми обьектами – свечами…- иными словами ложных показаний примерно 10 на один истинный…- эволюционированные коды показывают лучший результат,- но не на стоко – что бы их можно было применить хотя бы на худой конец в советнике…

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

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

void SortLettersInString(char A[]);

This is not a reference. References only exist in C++, and there can be no references to arrays of unknown size; the code would look like this if A was a reference:

void SortLettersInString(char (&A)[100])

What you have here instead is a function taking a pointer to a modifiable (i.e. non-const) char object. The [] syntax is merely a hint that the object may be the beginning of an entire array of modifiable char objects; it’s not otherwise relevant or different from a char* A.

In any case, the function does use the non-const liberty you’ve granted it to modify the object(s), with code like: A[i]=A[i]^A[j];.

So far, so good.

char str[]="sbcdhiksjaghgfhsghjawyeqsadfsazxx";

This is like writing int x[] = { 1, 2, 3 };. You can modify the three elements of the array, but you can of course not modify the numbers one, two and three themselves. The array elements are just modifiable copies of unmodifiable literals.

//THIS WORKS:
SortLettersInString(str);

A string literal like "sbcdhiksjaghgfhsghjawyeqsadfsazxx" is just like an array of integer literals 1, 2 and 3. The literal itself cannot be modified, but the literal can be used to create modifiable copies. Your str[] is an array of modifiable char objects copied from the literal’s unmodifiable char objects.

//THIS CRASHES THE PROGRAM:
//SortLettersInString("AAACBSVSFSA");

Depending on what language, compiler and compiler settings you use, this shouldn’t even compile. If it does, it’s undefined behaviour. You pass the unmodifiable literal directly to the function. The function tries to modify something which shouldn’t be modified.

Crashing is one possible result of undefined behaviour.

What you are doing here is quite comparable to this:

void changeToTwo(int* x)
{
    *x = 2;
}

int main()
{
  changeToTwo(&1); // will not compile
  int one = 1;
  changeToTwo(&one); // will compile and work fine
}

A good practical reason for why it’s a bad idea to try and modify string literals is that compilers are allowed to perform an optimisation called string-literal pooling, which allows identical string literals to share the same memory area.

<BLOCKQUOTE class=”ip-ubbcode-quote”><font size=”-1″>quote:</font><HR>Originally posted by strandgeck:<BR>Can you do this? When I try to compile in VC++ 6.0 I get the error: ‘arrays of references are illegal'<HR></BLOCKQUOTE><BR>Yes, you can do it, if you’re actually asking for what you want. You <I>can</I> pass an array by reference (as Peter correctly notes). You cannot have an array of references (for the same reason that you can’t have a pointer to a reference).<BR><BLOCKQUOTE class=”ip-ubbcode-quote”><font size=”-1″>quote:</font><HR>for the function prototype i have<BR><pre class=”ip-ubbcode-code-pre”>void ZeroArray(int&, int);</pre><HR></BLOCKQUOTE><BR><I>ZeroArray</I> takes two arguments. The first has type “reference to int”, the second has type “int”. If you want the first argument to have type “reference to array of int”, you must declare it thusly:<BR><pre class=”ip-ubbcode-code-pre”>void ZeroArray(int (&)[], int);</pre><BR>Except you cannot have a reference to an array of unknown bounds, so you can declare it like this:<BR><pre class=”ip-ubbcode-code-pre”>int const ArraySize = 100;

void ZeroArray(int (&)[ArraySize], int);</pre><BR>You know the size of the array in ZeroArray, if you declare it like this, so you no longer need to pass the size unless you want to allow the possibility of “zeroing” a prefix of the array.<BR><BLOCKQUOTE class=”ip-ubbcode-quote”><font size=”-1″>quote:</font><HR>the call to the function is,<BR><pre class=”ip-ubbcode-code-pre”>ZeroArray(ReadArray[ArraySize], ArraySize);</pre><HR></BLOCKQUOTE><BR>You are not passing the array, you are passing the <I>ArraySize</I>+1th (i.e. 101st) element of the array. If you want to pass the <I>array</I>, you can write:<BR><pre class=”ip-ubbcode-code-pre”>ZeroArray(ReadArray, ArraySize);</pre><BR><BLOCKQUOTE class=”ip-ubbcode-quote”><font size=”-1″>quote:</font><HR>and finally the heading itself is<BR><pre class=”ip-ubbcode-code-pre”>
void ZeroArray(int& Array[], int ArraySize)
</pre><HR></BLOCKQUOTE><BR>That is something else, again. The first argument in the declaration above has type “array of references to int”. That is the type that the compiler is complaining about. Again, this is how to say what you are asking for:<BR><pre class=”ip-ubbcode-code-pre”>void ZeroArray(int (&Array)[], int ArraySize)</pre><BR><BLOCKQUOTE class=”ip-ubbcode-quote”><font size=”-1″>quote:</font><HR>Originally posted by btmlltt:<BR>Arrays are passed by reference by _default_, thus explicitly stating this is an error.<HR></BLOCKQUOTE><BR>And, as Peter notes, this is wrong.<BR><BR>Arrays are not passed by reference, unless they are <I>explicitly</I> passed by reference. Otherwise, they are passed by value. The appropriate question to ask is: “what is the value of an array?”. And the answer is that it is* a pointer to the first element of the array. This pointer is passed by value.<BR><BLOCKQUOTE class=”ip-ubbcode-quote”><font size=”-1″>quote:</font><HR>Originally posted by PeterB:<BR>Because the value of an array is the address of the first element, the necessary indirection is already performed, so you don’t have to.<BR><BR><pre class=”ip-ubbcode-code-pre”>
void ZeroArray(int* array, const int elementCount)
{
for(int* end(array + elementCount); array != end; ++array) { *array = 0; }
}
</pre><HR></BLOCKQUOTE><BR>Peter’s code above is what you probably want.<BR><BLOCKQUOTE class=”ip-ubbcode-quote”><font size=”-1″>quote:</font><HR>Originally posted by btmlltt:<BR>As Sherrif states if I have a function:<BR><pre class=”ip-ubbcode-code-pre”>
void f(int a[], int size)
{
for (int i = 0; i < size; i++)
a = 0;
}
</pre><BR>That will, in fact, modify the external copy of the array that was handed in.<HR></BLOCKQUOTE><BR>That’s not the right question to ask. First, the type signature is a lie, because it is simply impossible to pass a value of type “array of <I>T</I>” to a function in C++. That you are allowed the fiction of pretending to do so is a flaw, IMO. The actual signature is:<BR><pre class=”ip-ubbcode-code-pre”>void f(int *a, int size)</pre><BR>And you can clearly see this if you try to define overloaded functions with the signatures:<BR><pre class=”ip-ubbcode-code-pre”>void f(int *a, int size) {}
void f(int a[], int size) {}</pre><BR><BLOCKQUOTE class=”ip-ubbcode-quote”><font size=”-1″>quote:</font><HR>it’s essentially passing it by reference by default (meaning that the pointer is being hidden from the user)<HR></BLOCKQUOTE><BR>Nope. This is passing by reference:<BR><pre class=”ip-ubbcode-code-pre”>void swapInt(int& x, int& y)
{
int temp = x;
x = y;
y = temp;
}</pre><BR>Which you can see by calling swapint:<BR><pre class=”ip-ubbcode-code-pre”>int x = 3;
int y = 4;
std::cout << “before: ” << x << “, ” << y << std::endl;
swapInt(x, y);
std::cout << “after: ” << x << “, ” << y << std::endl;</pre><BR>Whereas, if this were passing an array by reference:<BR><pre class=”ip-ubbcode-code-pre”>void swapArray(int x[], int y[])
{
int* temp = x;
x = y;
y = temp;
}</pre><BR>then you would not see what you see when you do:<BR><pre class=”ip-ubbcode-code-pre”>int xa[] = {3};
int ya[] = {4};
std::cout << “before: ” << xa[0] << “, ” << ya[0] << std::endl;
swapArray(x, y);
std::cout << “after: ” << xa[0] << “, ” << ya[0] << std::endl;</pre><BR>Clearly, arrays are not passed by reference by default. They are passed by value, and the value of an array is a pointer to the first element (which is passed by <I>value</I>).<BR><BLOCKQUOTE class=”ip-ubbcode-quote”><font size=”-1″>quote:</font><HR>Whether that’s technically correct isn’t really the point.<HR></BLOCKQUOTE><BR>Is it correct in some non-technical sense?<BR><BR>* In almost all contexts, except sizeof, & (address-of), pre- or post- increment or decrement, left hand side of an assignment, or the . operator.

Модератор: Модераторы

Hint: C arrays are passed by reference

Есть модуль с заголовками функций к сишной библиотеке. Для примера, одна из функций выглядит так:

Код: Выделить всё
function VyrviGlaz(ar1: array of integer; count: integer): integer; cdecl;

Вопрос про передачу в неё “безразмерного” массива. Если он передаётся так, как приведено выше в примере кода, он передаётся по ссылке или по значению? Компилятор выдаёт предупреждение, как в заголовке. Только я не могу понять – это предупреждение, что массив надо передавать в функцию в виде адреса или компилятор всё-таки где-то там глубоко у себя внутри уже переделал параметр массива на ссылку?

Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Hint: C arrays are passed by reference

Сообщение wadman » 02.04.2019 10:36:04

Hint: C arrays are passed by reference
Any array passed to a C function is passed by a pointer (i.e. by reference).

То есть “где-то глубоко у себя внутри”.

wadman
постоялец
 
Сообщения: 122
Зарегистрирован: 18.10.2016 15:54:28
  • Профиль
  • Сайт

Re: Hint: C arrays are passed by reference

Сообщение Vadim » 02.04.2019 11:38:41

wadman
И если я сделаю так:

Код: Выделить всё
function VyrviGlaz(@ar1: array of integer; count: integer): integer; cdecl;

Это уже будет совсем по сишному? Библиотечная функция меня поймёт?
Я проверить пока не могу, работа ишшо в полном разгаре… :-)

Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск


Re: Hint: C arrays are passed by reference

Сообщение wadman » 02.04.2019 12:55:09

Vadim писал(а):Это уже будет совсем по сишному?

Оно уже по сишному, о чем компилятор просто предупреждает.

wadman
постоялец
 
Сообщения: 122
Зарегистрирован: 18.10.2016 15:54:28
  • Профиль
  • Сайт

Re: Hint: C arrays are passed by reference

Сообщение Дож » 02.04.2019 12:58:03

Код: Выделить всё
function VyrviGlaz(ar1: PInteger; count: integer): integer; cdecl;
Аватара пользователя
Дож
энтузиаст
 
Сообщения: 892
Зарегистрирован: 12.10.2008 16:14:47

Re: Hint: C arrays are passed by reference

Сообщение Снег Север » 02.04.2019 13:50:38

На форуме по ссылке выше писали:
You cannot pass dynamic arrays to non-pascal dlls, because they are managed types and @a is not the same as @a[0,0].

Аватара пользователя
Снег Север
долгожитель
 
Сообщения: 2872
Зарегистрирован: 27.11.2007 16:14:47
  • Профиль
  • Сайт

Re: Hint: C arrays are passed by reference

Сообщение Дож » 02.04.2019 14:11:28

Снег Север, а тут и не динамические массивы, тут открытые массивы

Аватара пользователя
Дож
энтузиаст
 
Сообщения: 892
Зарегистрирован: 12.10.2008 16:14:47

Re: Hint: C arrays are passed by reference

Сообщение daesher » 02.04.2019 15:45:39

А есть ли вообще смысл передавать открытые массивы без указания const? Технически это возможно (но не в cdecl), но крайне вредно ими забивать стек. Разве что число элементов будет совсем небольшим.

daesher
постоялец
 
Сообщения: 221
Зарегистрирован: 09.03.2010 22:17:14

Re: Hint: C arrays are passed by reference

Сообщение Vadim » 03.04.2019 19:52:51

В общем, для проверки всех высказанных тут идей была состряпана библиотечка с таким кодом:

Код: Выделить всё
#include <stdio.h>

void printarr(int *arr, int count)
{
  int i;

    for(i=0;i<count;i++)
  {
    printf("arr[%d] = %dn", i, arr[i]);
  }
}

Компиляция:

gcc -Wall -fPIC -c lib.c
gcc -shared -o libcore.so lib.o

И две тестовые программки:

Код: Выделить всё
{$mode objfpc}{$h+}
{Вариант №1.
В процедуру сишной библиотеки передаётся
указатель на первый элемент массива.}
program test1;

procedure printarr(arr: Pinteger; count: integer); cdecl; external 'libcore.so' name 'printarr';

Var
  a: array[0..5] of integer;
  i: integer;

Begin
  for i:=0 to 5 do
    a[i]:=i;

    i:=6;
  printarr(@a[0], i);

End.

Код: Выделить всё
{$mode objfpc}{$h+}
{Вариант №2.
В процедуру сишной библиотеки передаётся
сам массив.}
program test2;

procedure printarr(arr: array of integer; count: integer); cdecl; external 'libcore.so' name 'printarr';

Var
  a: array[0..5] of integer;
  i: integer;

Begin
  for i:=0 to 5 do
    a[i]:=i;

    i:=6;
  printarr(a, i);

End.

Обе программы выдают один и тот же результат. Так что оба варианта рабочие.

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

Добавлено спустя 4 минуты 32 секунды:

daesher писал(а):А есть ли вообще смысл передавать открытые массивы без указания const?

Риторический вопрос… :-D Я только делаю паскалевский заголовок для сишной библиотеки, а уж почему они там “const” не всунули – сии тайны мадридского двора мне неизвестны… ;-) От себя могу сказать, что без “const” подобные массивы в сишных библиотеках встречаются сплошь и рядом.

У вас нет необходимых прав для просмотра вложений в этом сообщении.

Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Hint: C arrays are passed by reference

Сообщение Снег Север » 03.04.2019 21:49:09

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

Там речь шла про динамические массивы. У вас статический массив, который вы скармливаете процедуре, в виде ссылки или открытого массива. Т.е. вы доказали, что открытый массив в заголовке сишной процедуры – это ссылка. :D Что ж, примем к сведению.

Аватара пользователя
Снег Север
долгожитель
 
Сообщения: 2872
Зарегистрирован: 27.11.2007 16:14:47
  • Профиль
  • Сайт

Re: Hint: C arrays are passed by reference

Сообщение Vadim » 04.04.2019 05:02:35

Снег Север
Динамический массив я тоже пробовал:

Код: Выделить всё
{$mode objfpc}{$h+}
{Вариант №2-1.
В процедуру сишной библиотеки передаётся
динамический массив.}
program test21;

procedure printarr(arr: array of integer; count: integer); cdecl; external 'libcore.so' name 'printarr';

Var
  a: array of integer;
  i: integer;

Begin
  SetLength(a, 6);
  for i:=0 to 5 do
    a[i]:=i;

    i:=6;
  printarr(a, i);

End.

но, сами понимаете, его в процедуру никак кроме открытого массива не передашь. Работает на ура… ;-)

Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Hint: C arrays are passed by reference

Сообщение Дож » 04.04.2019 05:14:20

его в процедуру никак кроме открытого массива не передашь

Код: Выделить всё
type
  TDynArr = array of Integer;

procedure PassedDynArr(A: TDynArr);
begin
  ...
end;

Аватара пользователя
Дож
энтузиаст
 
Сообщения: 892
Зарегистрирован: 12.10.2008 16:14:47

Re: Hint: C arrays are passed by reference

Сообщение Vadim » 04.04.2019 08:57:24

Дож
Ну, это мы знаем. Примерно то же самое… :-)

Vadim
долгожитель
 
Сообщения: 4112
Зарегистрирован: 05.10.2006 08:52:59
Откуда: Красноярск

Re: Hint: C arrays are passed by reference

Сообщение Дож » 04.04.2019 15:35:49

Vadim, вы в предыдущем же сообщении написали, что динамический массив никак не передать в процедуру, — но его можно передать, и я показал как. И нет: передать динамический массив не то же самое, что передать открытый массив.

Аватара пользователя
Дож
энтузиаст
 
Сообщения: 892
Зарегистрирован: 12.10.2008 16:14:47


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 5

Добавить комментарий