On-line: гостей 1. Всего: 1 [подробнее..]
Программисты всех стран, объединяйтесь!

АвторСообщение



ссылка на сообщение  Отправлено: 13.03.25 21:28. Заголовок: Баг компилятора MS VS-22 C++ относительно оператора индексирования (subscript operator)


Начиная со стандарта C++17, была изменена семантика таких операторов, как, например, операторы сдвига (shift operators) и оператор индексирования (subscript operator). Вычисления их операндов стало детерминированным: сначала вычисляется первый операнд, и все его побочные эффекты применяются, а затем второй операнд.

В стандартах C++17 и C++20 оператор индексирования определяется следующим образом:
 
postfix-expression [ expr-or-braced-init-list ]

В стандарте C++23 он определяется как
 
postfix-expression [ expression-listopt ]

Условно выражение с оператором индексирования можно представить в виде E1[E2]. Это выражение вычисляется как *((E1)+(E2)).

И, начиная со стандарта C++17, говорится, что

 цитата:
The expression E1 is sequenced before the expression E2.



Например, если имеется массив
 
T a[N];

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

Так как оператор суммирования + в выражении *((E1)+(E2)) является коммутативным, то выше приведенное выражение обращения к i-ому элементу массива можно также записать как
 
i[a]

или как
 
( i )[a]

В этом случае операнд E1 представляет из себя i или ( i ), и операнд E2: a.

Итак, сначала вычисляется операнд E1, и применяются все его побочные эффекты, и только затем операнд E2.

Однако компилятор MS VS-22 C++ ошибочно вычисляет сначала выражение в квадратных скобках, а лишь затем выражение перед квадратными скобками, если массив или указатель на элемент массива поместить в квадратные скобки вместо индексного выражения.

Ниже приведена демонстрационная программа, которая иллюстрирует описанный баг компилятора.
 
#include <iostream>

int main()
{
int a[] = { 1, 2, 3, 4, 5 };

for (const int *p = a; p != a + sizeof( a ) / sizeof( *a ); ++p)
{
std::cout << ( *p++ - 1 )[( --p, a )] << ' ';
}
std::cout << '\n';
}

или в соответствии с новыми стандартами C++
 
import std;

int main()
{
int a[] = { 1, 2, 3, 4, 5 };

for (const int *p = a; p != a + std::size( a ); ++p)
{
std::cout << ( *p++ - 1 )[( --p, a )] << ' ';
}
std::cout << '\n';
}

Это корректная программа, которая должна вывести на консоль все элементы массива, не смотря на то, что обращение к элементам массива переусложнено (ради демонстрации бага компилятора).

Однако при использовании компилятора MS VS-22 C++ данная программа завершается при выполнении аварийно, так как при первой итерации цикла сначала вычисляется выражение в квадратных скобках [( --p, a )], в результате чего указатель p после декримента указывает за пределы массива, и происходит обращение к памяти, не принадлежащей массиву, что влечет за собой неопределенное поведение программы.

В этом легко убедиться, если записать предложение цикла следующим образом:
 
for (const int *p = a + 1; p != a + std::size( a ); ++p)

Однако в этом случае последний элемент массива не будет выведен на консоль.

О данном баге я также сообщил на сайте www.stackoverflow.com.Run-time error using a compound subscripting expression compiled by the MS VS22 C++ compiler

Спасибо: 0 
ПрофильЦитата Ответить
Новых ответов нет


Ответ:
1 2 3 4 5 6 7 8 9
большой шрифт малый шрифт надстрочный подстрочный заголовок большой заголовок видео с youtube.com картинка из интернета картинка с компьютера ссылка файл с компьютера русская клавиатура транслитератор  цитата  кавычки моноширинный шрифт моноширинный шрифт горизонтальная линия отступ точка LI бегущая строка оффтопик свернутый текст

показывать это сообщение только модераторам
не делать ссылки активными
Имя, пароль:      зарегистрироваться    
Тему читают:
- участник сейчас на форуме
- участник вне форума
Все даты в формате GMT  3 час. Хитов сегодня: 22
Права: смайлы да, картинки да, шрифты да, голосования нет
аватары да, автозамена ссылок вкл, премодерация откл, правка нет