LINUX.ORG.RU

Максимально допустимый размер массива на стеке?

 


3

7

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

Какой то вот такой кривой пример кода:

int Nmax = 1024; // максимально возможный размер буфера на стеке
void func(){
  int N = ...;
  T p_buf[(N<=Nmax)*N];
  T* p = p_buf; if(N>Nmax) p = new T[N];
  ...
  if(N>Nmax) delete [] p;
}
★★★★★
Ответ на: комментарий от t184256

Ну круто, а почему теперь её нельзя называть функцией?

Потому что у неё нет тела, от которого можно было бы взять адрес, чтобы потом по этому адресу её можно было вызвать.

Инлайн-функции тоже теперь нельзя называть функцией?

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

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

Потому что у неё нет тела, от которого можно было бы взять адрес, чтобы потом по этому адресу её можно было вызвать.

А вот мне неочевидно, почему ты считаешь это необходимым условием. Функция - понятие более широкое, чем first-class function.

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

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

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

Что значит «инстанцированы»? Будет ошибка undefined reference to `имяфункции' - можешь проверить

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

Эм…

Беру

#include <stdio.h>

static inline void func(void) {
    printf("hello\n");
}

int main(void) {
    func();
    //printf("%p",&func);
    return 0;
}

Собираю с -O2 — в выхлопе нет отдельной func:. Раскомментирую printf — в выхлопе появляется func:.

i-rinat ★★★★★
()
Ответ на: комментарий от t184256

А вот мне неочевидно, почему ты считаешь это необходимым условием. Функция - понятие более широкое, чем first-class function.

Хм. Ну давай тогда определение функции, будем его гнобить.

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

А я уже собрался тебя потом спрашивать, подходит под твоё определение функции __builtin_expect() или нет.

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

А как прокомментируете вот это (ошибки undefined reference to `func' присутствуют):

#include <stdio.h>

inline void func(void)
{
    printf("hello\n");
}

typedef void (*myfunc)(void);

myfunc test()
{
    return func;
}

int main(void) {
    func();
    printf("%p\n",&func);
    myfunc f_ptr = test();
    f_ptr();
    return 0;
}

SZT ★★★★★
()
Ответ на: комментарий от i-rinat

Хм. Ну давай тогда определение функции, будем его гнобить.

Ммм, какое пространство для слива. Ну что, рискну забавы ради.

Строгого определения у меня, естественно, нет, есть претензия на собирательно-эмпирическое. Функция - фрагмент кода, для которого определено понятие «вызова». Вызов - исполнение этого кода в ином (необязательно непересекающемся/несовпадающем) лексическом пространстве, содержащем, как минимум, пары «входной параметр - значение аргумента».

Иными словами, свой locals и возможность при желании подкинуть в него значения на стороне вызывающего обязательны. Return value и его подстановка на место вызова, out parameters, first-class citizenship, call-by-reference, pass-by-reference, реентерабельность, детали calling convention, любые особенности реализации и ворох других свойств - необязательны, потому что функциями принято называть самую разную хрень.

Что упускаю?

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

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

Ну допустим. Ассемблерная вставка как в GCC (с clobber list - https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html) это функция?

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

Хм, типа определение и вызов неразрывны? Уел.

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

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

Хороший вопрос. Не знаю точного ответа. Но есть предположения.

Для начала нужно заметить, что пример можно укоротить до

inline void func(void) {}
int main(void) {
  func();
  return 0;
}

GCC версии 9.2.1 его собирать не хочет, жалуется: «function main: error: undefined reference to ‘func’».

Смотрю сейчас в драфт c11 (n1570). Там есть раздел 6.7.4 Function specifiers, в котором написано:

7 Any function with internal linkage can be an inline function. For a function with external linkage, the following restrictions apply: If a function is declared with an inline function specifier, then it shall also be defined in the same translation unit. If all of the file scope declarations for a function in a translation unit include the inline function specifier without extern, then the definition in that translation unit is an inline definition. An inline definition does not provide an external definition for the function, and does not forbid an external definition in another translation unit. An inline definition provides an alternative to an external definition, which a translator may use to implement any call to the function in the same translation unit. It is unspecified whether a call to the function uses the inline definition or the external definition.

У декларации (которая является частью definition) функции func() нет extern, поэтому это inline definition. У компилятора есть право использовать внешнюю реализацию функции, которая вроде как обязана присутствовать. Но её нет.

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

Для начала нужно заметить, что пример можно укоротить до
...

Ну тогда можно как-то так:

#include <stdio.h>

inline void func(void) __attribute__ ((always_inline));

inline void func(void)
{
  printf("hello\n");
}

int main(void) {
  func();
  return 0;
}

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

Но попытка взять указатель на такую функцию приведет к ошибке. Т.е. если дописать:

typedef void (*myfunc)(void);

myfunc test(void)
{
    return func;
}
получим ошибку на этапе линковки: undefined reference to `func'

Тогда получается что этот inline void func(void) __attribute__ ((always_inline)); - не функция?

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

какое пространство для слива

Кстати, раз уж это слово всплыло. Что такое «слив»? Если один из собеседников, получив новую информацию во время беседы, поменял своё мнение, это «слив»?

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

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

__builtin_alloca() туда ещё как-то можно притянуть за уши, но у __builtin_alloca() нет предопределённого кода, в котором бы был входной параметр. Что-нибудь вроде __builtin_expect() — вообще не фрагмент кода.

Что упускаю?

Я бы ещё добавил требование непротиворечивости стандарту Си.

Я там не нашёл определение функции (иначе бы уже давно написал), но зато про функции там говорится косвенно. Например, указывается, что у унарного & операндом должен быть function designator, результат [], результат унарного *, lvalue, которое является объектом, но не битовым полем или с классом распределения register.

Если я правильно понимаю, нечто из класса типов function type должно поддерживать унарный &. Это раздел 6.5.3.2, если интересно.

i-rinat ★★★★★
()
Ответ на: комментарий от SZT

Тогда получается что этот inline void func(void) __attribute__ ((always_inline)); - не функция?

По тому определению, что я косвенно дал, получается, так. И без __attribute__ ((always_inline)) тоже выходит, что не функция.

Мда. Не знаю, что сказать на такое.

i-rinat ★★★★★
()
Ответ на: комментарий от AntonI

А мне без разницы.

Да, не скрою, я иной раз «балуюсь» всякой литературщиной (гуглится при случае). Но мне важнее другое. С технической точки зрения я сказал и увидел всё что хотел. А кто там увидит-не увидит, это уже дело десятое. Я никого не насилую и не заставляю.

Я (замечу для особо пытливых) даже сказал по-честному где именно я быо не прав. Да, я поленился проверить что именно включено в код, виноват. Глянул что что-то там в коде из glibc включено, а что именно не посмотрел. Но я уже даже со счёта сбился как там и на каких платформах реализовано. Что есть часть gcc, что есть часть glibc. Ну не прав и не прав, я не обломаюсь от признания своей неправоты. На любого хакера ламерства выше крыши, впредь буду умнее.

Почему именно чел захотел пофлеймить, если приглядеться к моим коментам, я тоже объяснил. Ну тут уж «наказал» за дело, если честно.

Сосбственно, как-то вот так. И, если честно, то последних четыре часа я провёл в ветклинике, откачивая кота от приступа МКБ (мочекаменной болезни). Ему (моему коту) сейчас вставили уретральный катетер, венозный катетер (в венозный катетер вогнали химию, благодаря которой он перестал пускать в ход когти и уснул, вообще-то он как бы так, мейкун и «мальчика» угомонить крайне сложно из-за габаритов), сейчес он на капельнице, с него скачали порядка 200 мл. мочи и прогноз положительный. Так что, что там подумает или нет Ринат… Да как бы Вам сказать-то… Это в моей таблице приоритетов где-то между «нафига рыбе зонтик» и «нафига козе боян».

Ну вот, как-то вот так. Фотку кота на операционном столе приложить или и так поверите?

P.S. Что касаемо «функции» как таковой, то я бы назвал функцией произвольное число statements (выражений), вызывающихся и исполняющихся как единый блок. В отношении данного блока должны действовать единые соглашения о вызовах (как это принято в С или, для виндов, как это принято в Паскаль). А вот дальше не важно как именно они (этот блок выражений) реализованы. Написаны они на ассемблере, заинлайнены они, не заинлайнены, реализованы они как встроенные ф-ии компилятора или как ф-ии в glibc (библиотеки времени исполнения) или вообще сторонней библиотеки… Пофиг. Это уже «детали реализации», которые к делу никак не относятся.

Moisha_Liberman ★★
()
Последнее исправление: Moisha_Liberman (всего исправлений: 1)
Ответ на: Спасибо. от Moisha_Liberman

Прям разговор благородных мужей.

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

Что такое «слив»? Если один из собеседников, получив новую информацию во время беседы, поменял своё мнение, это «слив»?

Ну, мнение я поменял, а вот строгого определения так и не родил.

Я бы ещё добавил требование непротиворечивости стандарту Си.

Тогда я тебя не понял, потому что я попытался как раз определить функцию так, чтобы под неё попало максимум ереси, называемой «функцией» во всех других известных мне языках, но при этом не попало лишнего. Лишнее попало.

Если ограничиваться C, то я бы добавил «вызов производится из любой другой функции либо по имени, либо по указателю». Мне не жмёт возможность вызова чего-то, не укладывающегося в систему типов C. А вот ты требуешь возможности взять указатель у функции, иначе несчитово. Так и непонятно, что функция. Вот не могли написать, а?

t184256 ★★★★★
()
Ответ на: А мне без разницы. от Moisha_Liberman

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

#define ololo() \
{\
  trololo();\
  vashepizdets();\
}

По такому определению и это функция. Но это демагогия.

DELIRIUM ☆☆☆☆☆
()
Ответ на: комментарий от DELIRIUM

Нет. Это не функция.

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

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

И, что самое главное, такая псевдофункция будет развёрнута в код на этапе работы препроцессора сишного компиля. Т.е., считайте на «нулевом этапе» работы компиля. Все остальные преобразования кода это потом.

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

P.S. И да, инлайнинговые ф-ии это тема отдельная. Просто потому, что инлайнинг не всегда выполняется. Это момент косвенно обсуждали где-то в другом треде.

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

Чисто для полноты картины.

Мы ещё забыли о nested functions. Я про них напомню чтоб тут некотрые не орали попусту что об этих ф-ях забыли по незнанию. Я напомню. Мне не в напряг. =)

Да, вложенные функции это тоже функции, но, если честно, то «почти». Да, это тоже блоки кода, но нет, для них соглашения о вызовах исполняются не полностью. Там компиль должен сгенерить редуцированные prolog и epilog функции. Следовательно, в отношении этих… я бы их назвал «недо-лямбд», в полном объёме соглашения о вызовах исполнены быть не могут. Т.е., тут вопрос, конечно. Для простоты их считают функциями, а уж в полном ли объёме и как они там реализуются «унутре», это мало кого волнует.

Хотя, с другой стороны, это как посмотреть, потому как nested functions, это gcc’изм, т.е., расширение gcc и сам по себе стандарт ANSI C про них ничего не говорит и ни как их «не освящает». Так что, можно считать даже что их и нет и не использовать. Ни кто не осудит.

P.S. И да. Вот так изменились реализации языка С со времён не то чтобы K&R, а банального с89. Как изменилась реализация С++ с тех времён, когда С++ был прекомпилятором к языку С (т.н. реализация CFront), это и подумать страшно. =)

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

ты специально игноришь меня, потому что любишь тупить?

https://paiza.io/projects/kiPs_DqcOqgkdf4Bh4PquA?language=c

If a function is declared inline in some translation units, it does not need to be declared inline everywhere: at most one translation unit may also provide a regular, non-inline non-static function, or a function declared extern inline. This one translation unit is said to provide the external definition. One external definition must exist in the program if the name of the function with external linkage is used in an expression, see one definition rule.

https://en.cppreference.com/w/c/language/inline

Если читать для тебя тяжело, то послушай деда, он шарит, и ты хотя бы немного узнаешь про С(в этой беседе будет всё про inline и про взятие адреса, и про разницу С и С++ и другое): https://youtu.be/ieERUEhs910

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

Мне не жмёт возможность вызова чего-то, не укладывающегося в систему типов C. А вот ты требуешь возможности взять указатель у функции, иначе несчитово. Так и непонятно, что функция.

Я уже не понимаю, что обсуждаем. Мне кажется, что нет смысла выходить за рамки Си. Хотя бы потому что в Паскале, например, функциями называется только то, что значение возвращает. Если не возвращает, это уже процедура. В Си разницы нет, и процедуры тоже называются функциями. В каких-то других языках будут свои определения, и так далее.

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

ты специально игноришь меня, потому что любишь тупить?

А в чем я туплю? Что и где я не так написал?

Никаких external definition нет, есть только объявление inline void func(void) __attribute__ ((always_inline)); и само тело функции в конкретном файле, которое только там и вызывается

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

Никаких external definition нет

Ну да. Про это и речь.

есть только объявление

Это internal definition, которое является альтернативой external definition. Чтобы это internal definition было альтернативой external definition, последнее должно существовать. А ты его не предоставил. Если начать докапываться, то твоя программа не является корректной программой на Си. Но мы уже тут слишком много докапывались.

always_inline

Эта особенность компилятора по сути делает макрос. Если функцию сделать рекурсивной, GCC скажет, что не может заинлайнить функцию, которая обязана быть заинлайнена. Для него это ошибка. Формально, выглядит как определение функции, но вот атрибут по факту делает из него макрос.

i-rinat ★★★★★
()
Ответ на: комментарий от AntonI

выделение памяти в куче - это дорогая операция

Почему не использовать глобальный(статический) массив размером максимально тебе нужным?

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

Чем это отличается от кучи (кроме ограничения на глобальную память)?

Тем, что не происходит обращения к ядру системы для получения памяти?

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

Будет оверхед, местами это плохо

Хреначить выделение по ифу - это тоже плохо. Какой оверхед в выделение в статической(глобальной) памяти пула данных?

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

Один раз обратился в начале и норм. Глобальная память сильно ограничена по размеру.

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

Такой что не используемая часть пула не используется?

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

Глобальная память сильно ограничена по размеру.

Но тебе решение уже написали - использование alloca. Но это игры со стеком. Я не очень понимаю проблемы. Проблемы и методы решения на стеке тебе описали, методы решения вне стека тоже очевидны. О чем тогда вообще разговор? Выбирай любой метод.

Dudraug ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.