LINUX.ORG.RU

массивы C/С++

 


0

1

Есть данный код. Почему "(1 + 3)[a]" выводит число 6? Почему это вообще работает?

    int a[] = {2, 3, 4, 5, 6, 7};
    std::cout << (1 + 3)[a];


Последнее исправление: nexus86 (всего исправлений: 1)

В случае обычных указателей и массивов operator [] — это просто синтаксический сахар: a[id] \equiv *(a+id), подскобочное выражение инвариантно относительно перестановок.

thunar ★★★★★
()
Последнее исправление: thunar (всего исправлений: 1)
Ответ на: комментарий от thunar

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

slackwarrior ★★★★★
()
Ответ на: комментарий от slackwarrior

Коммутативность сложения тут не причём, тут отсутствие проверки типов — с кастомным контейнером где [] переопределён такой глупости не будет:

int a[]{1,2,3}, j{0};

struct foo {
	int *d;
	int& operator [] (int i) const {
		return d[i];
	}
}x{a};
	
std::cout<<a[j]<<"\n"; //ok
std::cout<<j[a]<<"\n"; //ok
std::cout<<x[j]<<"\n"; //ok
std::cout<<j[x]<<"\n";
^^ error: no match for ‘operator[]’ (operand types are ‘int’ and ‘main(int, char**)::foo’)
};

thunar ★★★★★
()
Последнее исправление: thunar (всего исправлений: 3)
Ответ на: комментарий от thunar

Как раз она при чем, т.к. пофиг на порядок операндов в доступе по индексу (то же самое можно в самом индексе делать или в арифметике указателей) — отсюда платиновый вопрос «на засыпку» про вот такой доступ к элементу массива, про который знают не только лишь все, а не при чем тут как раз кастомные контейнеры :)

slackwarrior ★★★★★
()
Последнее исправление: slackwarrior (всего исправлений: 2)

a[i] == i[a], а 1+3=4, 4[a] все равно что a[4], а что у нас по индексу 4? правильно, 6.

olelookoe ★★★
()

нумерация с нуля,
1+3 = 4
0 1 2 3 4, пятый элемент, собственно равен 6 в примере
2 3 4 5 6

etwrq ★★★★★
()
Ответ на: комментарий от nexus86

в данном случае имя массива - указатель на область памяти, а обращаясь к нему по индексу ты считываешь из памяти данные относительно указателя.
примерно:
если адрес 0х000000f, то шестой элемент типа int будет начинаться с адреса 0x000000f + размер(инт)*5.
условно на пальцах как-то так, в книжке и мануале будет по точнее.
ps. адепты поправьте если я не прав :)

etwrq ★★★★★
()
Последнее исправление: etwrq (всего исправлений: 1)

Не хватает тега «я познаю мир».

no-such-file ★★★★★
()
Ответ на: комментарий от lovesan

С бъёт палкой по спине С и у того вылетают зубки или орешки изо рта.

LINUX-ORG-RU ★★★★★
()
Последнее исправление: LINUX-ORG-RU (всего исправлений: 1)
Ответ на: комментарий от apt_install_lrzsz
a[3 + 1] == a + (3 + 1) == (3 + 1) + a == (3 + 1)[a]

от перестановок слагаемых местами, сумма не меняется :)

ЗЫ на std::vector/std::array это не сработает, только на указателях.

hatred ★★★
()
Ответ на: комментарий от thunar

Нет, тут просто дело в том, что массивов в языке нет. Встроенный operator[] (на самом деле не оператор, в контексте Си, но допустим такую вольность) чисто синтаксический сахар, что бы не писать:

*(a + (3+1)) = value;

Можно с тем же успехом написать и так:

(a + 1)[3]

так что тут именно коммутативность сложения. operator[] уже сущность другого языка - C++ и именно тут не нужно было заботиться об обратной совместимости.

hatred ★★★
()
Последнее исправление: hatred (всего исправлений: 1)
Ответ на: комментарий от thunar

подскобочное выражение инвариантно относительно перестановок

Это только если функция + определена на обеих парах:
pointer, int (что логично)
int, pointer (что неочевидно)

Shushundr ★★★
()
Ответ на: комментарий от pr849

Трындец. Теперь всегда буду так писать. Чтобы у коллег жопы разорвало.

Ещё пиши if (5 == i), чтобы уж наверняка.

debugger ★★★★★
()

#include <stdio.h>
int main(int argc, char *argv[])
{
    int a[] = {2,3,4,5,6,7};

    //узнаём размер типа, адрессная арифметика предполагает что 
    //минимальная единица расчёта это размер типа, а не 1. Это значит
    //что когда ты в качестве интекса пишешь 1 то это значит взять адрес 
    //и ОДИН раз прибавить к нему размер типа.
    
    //произведём всю адресную арифметику вручную
    //узнаём размер типа, для инта допустим тут будет 4
    size_t size = sizeof(int);
    //превратим наши данные в массив байт где размер типа равен единице
    //это будет означать что и адрес будет увеличиваться на 1 раз по 1му байту
    char * b = (char*)a;
    //но у нас известен размер нашего инта так что мы 4 РАЗА берём по размеру int
    size*=(1+3);
    //на полученную сумму байт мы проходим по указателю
    b+=size;
    //просто так для наглядности заведём ещё переменную и передадим ей наш указатель
    //приведя тип явно к тому что был
    int * c = (int*)b;
    //на выходе получим тоже самое что и у тебя
    printf("%d\n",*c);

    //(1 + 3)[a]
    //взять адрес указателя `a` узнать его размер, умножить размер на `(1+3)`
    //прибавить полученное число к адресу. Фсё.
    //Ты используешь просто черезжопную запись, так можно, но просто непривычно
    //и ты намеренно написал не 4 а 1+3 что-бы запутать.

    int v = *(int*)(((char*)a)+sizeof(int)*4);

    printf("%d\n",v);

    return 0;
}

LINUX-ORG-RU ★★★★★
()
Ответ на: комментарий от debugger

Ещё пиши if (5 == i), чтобы уж наверняка.

Так писать даже рекомендуют. Чтобы застраховаться от зевка лишнего «=», т.е., чтобы компилятор ругнулся на присваивание вместо сравнения.

praseodim ★★★★★
()
Последнее исправление: praseodim (всего исправлений: 1)
Ответ на: комментарий от praseodim

Так писать даже рекомендуют.

Так писать не рекомендуют никогда.

И современные компиляторы ругаются на = в if.

gcc если есть флаг -Wall, clang даже если нет такого флага.

https://gcc.godbolt.org/z/zh9Exzc17

godbolt временно отключил Microsoft компилятор, поэтому я добавлю ссылку на доку.

в MSVC тоже есть такое предупреждение если включены обычные флаги предупреждений /W4: https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-4-c4706?view=msvc-170

fsb4000 ★★★★★
()
Ответ на: комментарий от fsb4000

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

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

P.S. Правда я все-равно так не пишу, предпочитаю более естественный стиль =)

praseodim ★★★★★
()
Последнее исправление: praseodim (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.