Я нашел функцию, которая вычисляет квадрат числа:
int p(int n) {
int a[n]; //works on C99 and above
return (&a)[n] - a;
}
Возвращает значение n 2 . Вопрос в том, как это сделать? После небольшого тестирования я обнаружил, что между (&a)[k]
и (&a)[k+1]
есть sizeof(a)
/ sizeof(int)
. Это почему?
int p(n)
? Это даже компилируется?int q(int n) { return sizeof (char [n][n]); }
sizeof
его для сохранения символов. Все остальные: это намеренно неясный код, это неопределенное поведение, ответ @ ouah правильный.Ответы:
Очевидно, что взломать ... но способ возведения в квадрат числа без использования
*
оператора (это было требование конкурса кодирования).эквивалентно указателю
int
на местоположениеи, таким образом, все выражение
источник
(&a)
с указателем на объект,n*sizeof(int)
когдаn
он неизвестен во время компиляции. Раньше был простой язык ...Чтобы понять этот хак, сначала нужно понять разницу в указателях, т. Е. Что происходит, когда два указателя указывают на элементы одного массива вычитаются ?
Когда один указатель вычитается из другого, результатом является расстояние (измеренное в элементах массива) между указателями. Итак, если
p
указывает наa[i]
иq
указывает наa[j]
, тоp - q
равноi - j
.C11: 6.5.6 Аддитивные операторы (p9):
Теперь я ожидаю, что вы знаете о преобразовании имени массива в указатель,
a
преобразует указатель на первый элемент массиваa
.&a
это адрес всего блока памяти, т.е. это адрес массиваa
. Рисунок ниже поможет вам понять ( прочитайте этот ответ для подробного объяснения ):Это поможет вам понять , что , почему
a
и&a
имеет тот же адрес , и как(&a)[i]
это адрес I - го массива (такого же размера, как уa
).Итак, утверждение
эквивалентно
и эта разница будет давать количество элементов между указателями
(&a)[n]
и(&a)[0]
, которые являютсяn
массивами каждого изn
int
элементов. Следовательно, все элементы массива равныn*n
=n
2 .НОТА:
C11: 6.5.6 Аддитивные операторы (p9):
Поскольку
(&a)[n]
ни указатель на элементы одного и того же объекта массива, ни на один элемент после последнего элемента объекта массива(&a)[n] - a
не вызовет неопределенное поведение .Также отметим , что, лучше изменить тип возвращаемого значения функции
p
вptrdiff_t
.источник
&a[k]
это адресk
элемента массиваa
. Это(&a)[k]
то, что всегда будет считаться адресом массиваk
элементов. Итак, первый элемент находится в позицииa
(или&a
), второй - в позицииa
+ (количество элементов массива,a
которое естьn
) * (размер элемента массива) и так далее. И обратите внимание, что память для массивов переменной длины выделяется в стеке, а не в куче.a
является (переменным) массивомn
int
.&a
указатель на (переменную) массивn
int
.(&a)[1]
это указатель наint
одинint
после последнего элемента массива. Этот указатель являетсяn
int
элементами после&a[0]
.(&a)[2]
является указателемint
одногоint
последнего элемента массива из двух массивов. Этот указатель является2 * n
int
элементами после&a[0]
.(&a)[n]
является указателем наint
одинint
после последнего элемента массиваn
массивов. Этот указатель являетсяn * n
int
элементами после&a[0]
. Просто вычтите&a[0]
или уa
вас естьn
.Конечно, это технически неопределенное поведение, даже если оно работает на вашем компьютере, так как
(&a)[n]
не указывает внутри массива или после последнего элемента массива (как того требуют правила C арифметики указателей).источник
[n]
синтаксис объявляет массив, а массивы разлагаются на указатели. Три отдельно полезные вещи с этим следствием.(&a)[n]
это типint[n]
и который выражается какint*
массив массивов, выражающий в качестве адреса их первого элемента, в случае, если это не было ясно в описании.Если у вас есть два указателя, которые указывают на два элемента одного и того же массива, то его разность приведет к количеству элементов между этими указателями. Например, этот фрагмент кода выведет 2.
Теперь давайте рассмотрим выражение
В этом выражении
a
имеет типint *
и указывает на его первый элемент.Выражение
&a
имеет типint ( * )[n]
и указывает на первый ряд отображаемого двумерного массива. Его значение соответствует значениюa
разных типов.является n-м элементом этого отображаемого двумерного массива и имеет тип,
int[n]
то есть это n-й ряд отображаемого массива. В выражении(&a)[n] - a
он преобразуется в адрес своего первого элемента и имеет тип `int *.Таким образом, между
(&a)[n]
иa
есть n строк из n элементов. Так что разница будет равнаn * n
.источник
Таким образом,
(&a)[n]
являетсяint[n]
указательa
İŞint
указательТеперь выражение
(&a)[n]-a
выполняет вычитание указателя:источник