Как разработчики языков программирования выбирают имя для функции, которая считает длину массива / it-юмор :: geek (Прикольные гаджеты. Научный, инженерный и айтишный юмор)

it-юмор geek 

Как разработчики языков программирования выбирают имя для функции, которая считает длину массива

it-юмор,geek,Прикольные гаджеты. Научный, инженерный и  айтишный юмор
Подробнее

it-юмор,geek,Прикольные гаджеты. Научный, инженерный и айтишный юмор
Еще на тему
Развернуть
Poschitat
Программист 1С ?
drenug drenug 26.11.201821:47 ответить ссылка 15.1
А еще есть #array+1
Тащемта sizeof не функция, а оператор, и считает он не длину массива, а количество байтов в любой поеботе, включая массив...
Mints97 Mints97 26.11.201821:25 ответить ссылка 3.7
.length тоже не функция/метод, а совершение доступа к полю массива.
Ну, в шарпе это реализовано как вызов геттера/сеттера. А обращаться к полям - нельзя. Инкапсуляция-с.
Muwka Muwka 26.11.201821:32 ответить ссылка 1.9
Гы, а в джаве это как раз поле. Final-ное, правда, присваивать ему ничего нельзя, так что с инкапсуляцией проблем в общем тут нет...
таки да,а есть еще и расширение для linq с лямбда выражением в параметрах
> а количество байтов в любой поеботе, включая массив...
Только для статических массивов, которые в текущей области видимости известны именно как массив. В противном случае - размер указателя.

// OK
int a[4];
sizeof(a);

// OK
void foo(int a[4]){sizeof(a);}

// FAIL - будет размер указателя
void foo(int* a){sizeof(a);}
Технически, в C указатель и не является массивом. Если ты передаешь массив интов в функцию, которая принимает указатель на инт, массив decay-нется до указателя на свой первый элемент. Вообще, если я правильно помню, массив decay-ится до этого указателя практически при любом использовании, за исключением sizeof (ну и ситуации с указателем-на-массив, типа такого: int func(int (*arr)[10]) и func(&arr), но это уже наркомания).

Кстати, твой второй пример неправильный - 4-ка там будет проигнорирована, и это будет эквивалентно void foo(int a[]){sizeof(a);}, и, соответственно, эквивалентно void foo(int *a){sizeof(a);}, то бишь тебе тоже выдаст размер указателя. Можешь проверить.
Правда ваша про второй пример. Где-то давно читал про какой-то трюк, как это обойти, видимо, попутал. Сам таких извращений не использую.
Что ты имеешь в виду под "обойти"? Если ты про передачу чего-то, на что можно вызвать sizeof, то тут применим трюк с указателем на массив, о котором я упомянул (int func(int (*arr)[10]) , пример: https://ideone.com/n3GUke). Это имеет свои применения - можно таким образом прописать, что твоя функция принимает массив только конкретного размера. Например, таким образом удобно прописывать матрицу как массив массивов (указатель на массив[ы]).

Если ты про то, чтобы передавать массив by-value, то можно запхать его в struct или union и уже их передавать by-value. Это иногда очень полезно, например, может быть удобно иметь union с uint32_t и массивом из 4 uint8_t, и его таким манером передавать в функции. Получится, что ты технически передаешь массив by-value!
что-то вы заплели простой вопрос. Массив в C трактуется как указатель только в одном контексте - в качестве формального параметра функции. На самом деле это и есть указатель на первый элемент фактического массива, а возможность описать его в функции как массив - чисто для удобства и выразительности. В любом другом контексте (static, auto, extern, компонент struct) массив - это именно массив, и sizeof учитывает его размерность, как бы она ни задавась - явно или через длину инициатора (если размерность никак не задана, например в extern, то это incomplete type, и применение sizeof некорректно). Более того, начиная с C99 допускаются variable length arrays, и sizeof в общем случае вычисляется уже на этапе выполнения.
Вот пример (компилируется gss -std=c99 ...):

#include
#include

static float farr[23];
typedef long la[80];
extern double exarr[];
extern double exarr_def[4];


static void f1(int n, short lfarr[77]) {
struct {
double d[n];
int n;
} s;
printf("In f1(): sz(%u)=%u\n", n, sizeof(s)); // n * sizeof(double) + sizeof(int)
// >= n * 8 + 4
printf("In f1(): sz(lfarr)=%u\n", sizeof(lfarr)); // sizeof(short *) == 4 || 8
}

int main(int ac, char **av) {
short lfarr[77];
static long long ll[] = {1, 2, 3};
if (ac != 2) {
fprintf(stderr, "Usage: %s num\n", av[0]);
exit(1);
}
f1(atoi(av[1]), lfarr);
printf("sz(farr)=%u\n", sizeof(farr)); // 23 * sizeof(float) == 92
printf("sz(lfarr)=%u\n", sizeof(lfarr)); // 77 * sizeof(short) == 154
printf("sz(char [20]))=%u\n", sizeof(char [20])); // 20 * sizeof(char) == 20
printf("sz(long)=%u\n", sizeof(long)); // sizeof(long) == 8
printf("sz(la)=%u\n", sizeof(la)); // 80 * sizeof(long) == 640
printf("sz(long long)=%u\n", sizeof(long long)); // sizeof(long long) == 8
printf("sz(ll)=%u\n", sizeof(ll)); // 3 * sizeof(long long) == 24
// printf("sz(exarr)=%u\n", sizeof(exarr)); // unknown
printf("sz(exarr_def)=%u\n", sizeof(exarr_def)); // 4 * sizeof(double) == 32
}
виноват, в предыдущем посте, конечно gcc, а не gss а include подключают stdio.h и stdlib.h. Кстати - как избежать здесь интерпретации спец. HTML символов, неужели писать \< \>
Ну нет, не только в контексте формального параметра функции. Например, можно еще так:

int a[10];
int *a2 = a;


Или вообще так:

int a[10];
a[0] = 1;
printf("%d\n", *a); // выдаст 1


Даже больше тебе скажу:

int a[10];
printf("%p %p\n", a, a + 1); // высрет 2 числа с 4 байтами разницы


Однако:

int a[10];
printf("%p %p\n", a, &a + 1); // высрет 2 числа с 40 байтами разницы

При decay-инге массив не "трактуется как указатель" - это фактически implicit cast в указатель на 0-й элемент.

То, что ты описал, это просто разные способы этот массив создать. Суть того, что я сказал, от этого не меняется - где бы ты массив не задавал, на каждый чих в его направлении, за исключением sizeof и & (вроде только их, если мне память не изменяет, а в доки/стандарт лезть лень), массив будет схлопываться в указатель на свой 0-й элемент.

И знаю я, что такое VLA-шники! И при чем тут вообще вопрос о том, когда считается этот самый sizeof? Да даже при вызове на VLA-шник он стопудов на адекватных компиляторах не считается-пересчитывается во время исполнения, а просто подгружает уже посчитанное для инициализации массива значение длины! И это не имеет ровно никакого значения в данном контексте...
Давай различать указатель-тип и указатель-переменную, и, чтобы не запутаться,
называть тип "ссылка на ..." вместо "указатель на ...".


int a[10];
int *a2 = a;

void func(int *arg_p, int arg_a[10]) {
int *la;
la = a;
la = a2;
la = arg_p;
la = arg_a;
}


Здесь все переменные имеют тип "ссылка на int", то бишь 'int *'.
А вот результат трансляции в ассемблерный код:


5:ptr.c **** int *la;
6:ptr.c **** la = a;
27 .loc 1 6 0
28 000c 48C745F8 movq $a, -8(%rbp)
28 00000000
7:ptr.c **** la = a2;
29 .loc 1 7 0
30 0014 488B0500 movq a2(%rip), %rax
30 000000
31 001b 488945F8 movq %rax, -8(%rbp)
8:ptr.c **** la = arg_p;
32 .loc 1 8 0
33 001f 488B45E8 movq -24(%rbp), %rax
34 0023 488945F8 movq %rax, -8(%rbp)
9:ptr.c **** la = arg_a;
35 .loc 1 9 0
36 0027 488B45E0 movq -32(%rbp), %rax
37 002b 488945F8 movq %rax, -8(%rbp)


Отчетливо видно, что присвоение переменой 'la' имени внешнего массива 'a' отличается
от присвоения внешнего указателя 'a2' - в первом случае это непосредственный операнд
'$a' - именно адрес первого элемента массива a, во втором это содержимое
32-разрядного (или 64-разрядного) слова - указателя, размещенного по адресу '$a2',
т.е. в первом случае происходит одно разыменование, во втором - два.
Так же отчетливо видно, что присвоения 'la' указателя 'arg_p' и "массива" 'arg_a' ничем
не отличаеются - во обоих случаях происходит двойное разыменование. Кажется, Ритчи
напрасно допустил это послабление в языке, ведь ясно же было сказано
изначально, что массивы в C не передаются by value - чтобы не провоцировать
на написание неэффективных программ, но все же - (вот оно - послабление)
формальный параметр можно описать как массив - для удобства, а размерность
можно и не указывать - она все равно будет проигнорирована, ведь реально это указатель.
Кому-то это реально заплело мозги, я встречал программистов, которые на 5-ом году
активного использования C линковали 2 объектника, в исходниках одного
из которых писали 'int a[10]',
а в исходниках другого 'extern int *a', и потом жаловались на 'Memory fault' или
'Segmentation violation' - мол, в компиляторе ошибка.

Так вот, еще раз: никто ничего не "схлопывает",
имя массива в любом контексте имеет тип "ссылка на" свой
элемент, но приведение массива к указателю как к объекту данных (не к типу,
а к объекту данных на уровне объектного кода!) происходит только в одном контексте -
когда формальный параметр функции описан как массив, а все потому, что фактический
параметр - это реально засунутый в стек в точке вызова 32-битный (или 64-битный)
указатель, с которым в объектном коде функции надо и обращаться как с указателем - т.е.
двойным разыменованием. Именно поэтому sizeof от
массива-формального параметра всегда будет возвращать sizeof указателя (т.е. 4 или 8
в зав-сти от арх-ры), тогда как sizeof от любого другого массива будет возвращать
именно размер массива или error в случае incomplete type.

Что касается VLA, то просто откомпилируй и выполни тот код, там длина 'n' массива
'double d[n]' задается аргументом командной строки, какое там может быть
"уже посчитанное для инициализации массива значение длины" ? Там sizeof реально
вычисляется на этапе выполнения, и при запусках с разным значением аргумента
выводятся разные значения sizeof(s), и размер фрейма стека, выделяемого
активации функции f1, вычисляется непосредственно уже в объектном коде самой функции.
Перечитал, что написал, и понял, что опять немного погорячиллся ("имя массива в любом контексте имеет тип "ссылка на" свой элемент"): в контексте операнда sizeof и унарной '&' имя массива действительно сохраняет свой изначально декларированный тип 'type [n]' - иначе все остальное про sizeof теряет смысл.
...О чем я, собственно, и говорил.

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

И я не понимаю, что ты хочешь сказать всем этим текстом про dereference-ы. На уровне ассемблера нет вообще никакой разницы между массивом и адресом его первого элемента. Это один и тот же адрес. И нету на уровне ассемблера такого понятия, как тип, там есть только размерность. У тебя там нет массива - например, в твоем примере со статическим массивом, у тебя есть $a, константа-адрес куска памяти, начиная с которого у тебя лежат данные в количестве 10 интов. А по адресу $a2 у тебя будет лежать в памяти один указатель с ровно тем же значением. И от того, лежит ли этот адрес ($a) где-то в памяти или уже где-то подгружен (или, как в твоем примере, уже известен как константа на этапе сборки), в сути этого ничего не меняется. Тут уже тогда вопрос не в том, что тут массив, а что - указатель, вопрос в том, как компилятору это сподручнее вышло организовать на уровне ассемблера. В твоей программе компилятор с тем же успехом мог вместо свистоплясок с дереференсом $a2 спокойно использовать вместо a2 тот же $a, так как a2 ты больше нигде вообще не изменяешь, и с точки зрения стандарта C такой компилятор был бы совершенно прав.

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

// OK
int a[4];
sizeof(a);

// OK
void foo(int a[4]){sizeof(a);}

// FAIL - будет размер указателя
void foo(int* a){sizeof(a);}

где автор ошибочно считал, что в случаях 1 и 2 результат одинаков, и ты справедливо указал на ошибку. Хотя операнд операции sizeof чисто внешне декларирован одинаково: 'int [4]', т.е. непосредственный контекст (sizeof) здесь ни при чем, здесь существенно то, что во 2 случае 'int a[4]' - это не декларация массива, а декларация указателя в "особо извращенной форме", и это не махинации конкретного компилятора, это определение языка, ну не
может формальный параметр функции реально (т.е. по реализации в объектном коде)
иметь тип "int [4]", не передаются массивы by value. И все дальнейшее про 'decaying' можно было не оглашать. А уж если оглашать, то опять же с ремаркой о том, что, во всех случаях, кроме вышеозначенного, приведение 'type [n]' к 'type *' - чисто умозрительное, и только для массива - параметра функции в точке вызова создается в стеке ячейка-указатель, в нее кладется адрес фактического массива (хотя это может результат любого выражения с типом "ссылка на"), а в объектном коде самой функции
этот "массив" во всех контекстах (в том числе в sizeof) юзается точно так же, как указатель-параметр или указатель-auto-переменная, т.е. как и любой другой указатель это переменная периода исполнения, тогда как имя настоящего массива на этапе исполнения - это константный виртуальный адрес (static) или константное смещение относительно SP (auto). Ну а дополнение по поводу VLA вынужден был сделать, потому что при их наличии смещение относительно SP может быть и не константным.

Короче, еще раз - разница в результатах 1 и 2 в том, что во втором случае 'a' - это указатель, не массив, "приведенный к указателю", а просто указатель, физически. Точка.
DlinaMassiva()
Рискну побыть занудой, но .length (и схожие конструкции), вероятнее всего, не функция, а поле (переменная внутри объекта) массива, содержащее в себе длину массива. Так проще, когда длина массива задаётся разово, при его создании, и не может быть изменена в дальнейшем.
Trallolo Trallolo 26.11.201821:31 ответить ссылка 0.0
в JS это геттер, вычисляемое значение, в примере ниже длина массива будет вычисляться n+1 раз, что очень неэффекивно на больших массивах:
for (let i=0; i<arr.length; i++) { ... }
> var arr = _.range(0, 1000000);
console.time('with1); let suml = 0;
for(let i = 0; i < arr.length; ++ i) suml += arr[i]; console.log({ suml >); console.timeEnd('with');
console.time('without’);
let sum2 = 0;
const n = arr.length;
for(let i = 0; i < n; ++ i) sum2 += arr[i]; console.log({
faiwer faiwer 27.11.201808:07 ответить ссылка 0.0
В общем ничего там не считается. И это не совсем getter. Это скорее поле, которое автоматически выставляется после каждой операции над массивом. А по факту просто муть в спецификации. Не совсем стандартный JS-объект со своими заморочками. Разница конечно может быть и больше 2%, но обуславливается она здесь тем, что взять поле из объекта дольше, чем воспользоваться значением из регистра. Т.е. arr.length по-любому медленнее просто n.
faiwer faiwer 27.11.201808:10 ответить ссылка 0.0
миллисекунда к миллисекунде, а при многомиллионных тиражах такого кода сотни тонн лишнего угля придётся сжечь для процессоров
Речь не о миллисекундах. Речь об асимптотике. Она тут O(1). В этом суть.
faiwer faiwer 27.11.201810:30 ответить ссылка 0.0
А это в каком языке такая чудная функция ${#}
OneUser OneUser 26.11.201821:31 ответить ссылка 0.9
возможно, это, но на 100% не уверен:
How do I find out bash shell array length?
${#ArrayName[@]}
Похоже, намёк на Perl, но там не совсем так. Определить длину массива @array в Perl можно либо оценив массив в скалярном контексте оператором scalar(@array), либо получив индекс последнего элемента массива выражением $#array и прибавив 1 (так как нумерация с нуля).
dadv dadv 26.11.201821:52 ответить ссылка 0.0
А возможность изменять ключ массива не предусмотрен, или пропуска значения??? ( кмх PHP кхм )
LorDee LorDee 27.11.201800:15 ответить ссылка 0.0
В Perl жесткое разделение между массивами и хэшами. В PHP это смешанный тип, поэтому так можно. А еще меня в перле дико вымораживают указатели, первое время скриптил только в обнимку с Data::Dumper. Хотя в целом от перла очень положительные эмоции по итогу.
Для конкретно *массивов* - нет, было в качестве экспериментальной фичи, но в итоге выпилили. А хеши можешь индексировать любыми ключами.
dadv dadv 27.11.201802:22 ответить ссылка 0.0
только не бейте!
.Количество()
Ловите 1С-ника!
hatlol hatlol 26.11.201821:34 ответить ссылка 13.7
You do it wrong

Пока 1С-ник >= 0
Цикл
Прописать(Пиздюли[1С-ник]);
КонецЦикла;
1СПрограммист
count (и производные) - имеет сложность О(n) (то есть нужно тупо посчитать каждый элемент)
size (и производные) - имеет сложность O(1), то есть размер уже известен, его просто нужно вернуть
length (и производные) - очень непонятная херня, в разных языках веде себя и как count, и как size
empiro empiro 27.11.201800:47 ответить ссылка 0.3
Уже чуть улучшили метод выбора!
Только зарегистрированные и активированные пользователи могут добавлять комментарии.
Похожие темы

Похожие посты
Выбираем первый язык программирования
Да
т
У вас есть друзья?

i
Да
Т
Хотите много зарабатывать?
jL
Да
ш
	Вы тупой?	
		
т.		
Т
Вы насмотрелись уроков ХАУДИ ХО?
/Г
Да
7
Python
	Вам		
г~	нравится		1
1	Windows?		
Нет

Fortran
А они вам нужны?
Они тоже	РНР	
тупые?		
Да
т
подробнее»

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

Выбираем первый язык программирования Да т У вас есть друзья? i Да Т Хотите много зарабатывать? jL Да ш Вы тупой? т. Т Вы насмотрелись уроков ХАУДИ ХО? /Г Да 7 Python Вам г~ нравится 1 1 Windows? Нет Fortran А они вам нужны? Они тоже РНР тупые? Да т
Почему?
Почему?!
^>о->Ьаг() — Почему?



— А, вот почему... Newbie: So which programming language should I learn first?
Programmers: e* \
; -s Tomasz is building cloudash.dev 1d
^ npm install esllnt-conflg-airbnb
'••'.K r
Q 31 tn 683	5 023 ¿j