Стек становится восходящим или нисходящим?

Хорошо. Так что я предвзят, потому что я автор Flask, но здесь кое-что, чтобы помочь вам сделать выбор:

  • itty - очень минимальный фреймворк, бутылка, вероятно, является более стабильной альтернативой, если вы хотите установка одного файла.
  • Фляга - новая и активно развивающаяся, форма похожа на Синатру, но также отличается в нескольких пунктах. Большое количество расширений для SQLAlchemy, CouchDB и др.
  • Юнона - не обновляется в течение года. Обычно не самый лучший знак.

Кроме тех, что вы упомянули, есть также бутылка, которая похожа на Flask, но более минималистична. В отличие от Flask, он также заново реализует все с нуля, а не опирается на независимый фонд, такой как Werkzeug.

Другими альтернативами является web.py, одна из первых в мире микрофраморок. То же правило, что и в бутылке: заново все реализует с нуля.

82
задан Spikatrix 29 April 2016 в 05:19
поделиться

10 ответов

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

На протяжении всей своей жизни программа обязана взаимодействовать с другими программами, такими как ОС. ABI определяет, как программа может взаимодействовать с другой программой.

Стек для разных архитектур может расти в любом случае, но для архитектуры он будет согласованным. Пожалуйста, проверьте эту вики-ссылку. Но рост стека определяется ABI этой архитектуры.

Например, если вы берете MIPS ABI, стек вызовов определяется, как показано ниже.

Давайте рассмотрим, что функция 'fn1' вызывает 'fn2'. Теперь кадр стека, который видит 'fn2', выглядит следующим образом:

direction of     |                                 |
  growth of      +---------------------------------+ 
   stack         | Parameters passed by fn1(caller)|
from higher addr.|                                 |
to lower addr.   | Direction of growth is opposite |
      |          |   to direction of stack growth  |
      |          +---------------------------------+ <-- SP on entry to fn2
      |          | Return address from fn2(callee) | 
      V          +---------------------------------+ 
                 | Callee saved registers being    | 
                 |   used in the callee function   | 
                 +---------------------------------+
                 | Local variables of fn2          |
                 |(Direction of growth of frame is |
                 | same as direction of growth of  |
                 |            stack)               |
                 +---------------------------------+ 
                 | Arguments to functions called   |
                 | by fn2                          |
                 +---------------------------------+ <- Current SP after stack 
                                                        frame is allocated

Теперь вы можете видеть, что стек растет вниз. Так, если переменные размещены в локальном фрейме функции, адреса переменных фактически растут вниз. Компилятор может выбрать порядок переменных для выделения памяти. (В вашем случае это может быть 'q' или 's', которая является первой выделенной стековой памятью. Но обычно компилятор выделяет стековую память в соответствии с порядком объявления переменных.)

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

(В вашем случае это может быть 'q' или 's', которая является первой выделенной стековой памятью. Но, как правило, компилятор выделяет стековую память в соответствии с порядком объявления переменных).

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

(В вашем случае это может быть 'q' или 's', которая является первой выделенной стековой памятью. Но, как правило, компилятор выделяет стековую память в соответствии с порядком объявления переменных).

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

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

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

70
ответ дан 24 November 2019 в 09:16
поделиться

Это зависит от вашей операционной системы и вашего компилятора.

0
ответ дан 24 November 2019 в 09:16
поделиться

The stack grows down (on x86). However, the stack is allocated in one block when the function loads, and you don't have a guarantee what order the items will be on the stack.

In this case, it allocated space for two ints and a three-int array on the stack. It also allocated an additional 12 bytes after the array, so it looks like this:

a [12 bytes]
padding(?) [12 bytes]
s [4 bytes]
q [4 bytes]

For whatever reason, your compiler decided that it needed to allocate 32 bytes for this function, and possibly more. That's opaque to you as a C programmer, you don't get to know why.

If you want to know why, compile the code to assembly language, I believe that it's -S on gcc and /S on MS's C compiler. If you look at the opening instructions to that function, you'll see the old stack pointer being saved and then 32 (or something else!) being subtracted from it. From there, you can see how the code accesses that 32-byte block of memory and figure out what your compiler is doing. At the end of the function, you can see the stack pointer being restored.

0
ответ дан 24 November 2019 в 09:16
поделиться

I don't think it's deterministic like that. The a array seems to "grow" because that memory should be allocated contiguously. However, since q and s are not related to one another at all, the compiler just sticks each of them in an arbitrary free memory location within the stack, probably the ones that fit an integer size the best.

What happened between a[2] and q is that the space around q's location wasn't large enough (ie, wasn't bigger than 12 bytes) to allocate a 3 integer array.

0
ответ дан 24 November 2019 в 09:16
поделиться

My stack appears to extend towards lower numbered addresses.

It may be different on another computer, or even on my own computer if I use a different compiler invocation. ... or the compiler muigt choose not to use a stack at all (inline everything (functions and variables if I didn't take the address of them)).

$ cat stack.c
#include <stdio.h>

int stack(int x) {
  printf("level %d: x is at %p\n", x, (void*)&x);
  if (x == 0) return 0;
  return stack(x - 1);
}

int main(void) {
  stack(4);
  return 0;
}
$ /usr/bin/gcc -Wall -Wextra -std=c89 -pedantic stack.c
$ ./a.out
level 4: x is at 0x7fff7781190c
level 3: x is at 0x7fff778118ec
level 2: x is at 0x7fff778118cc
level 1: x is at 0x7fff778118ac
level 0: x is at 0x7fff7781188c
0
ответ дан 24 November 2019 в 09:16
поделиться

There's nothing in the standard that mandates how things are organized on the stack at all. In fact, you could build a conforming compiler that didn't store array elements at contiguous elements on the stack at all, provided it had the smarts to still do array element arithmetic properly (so that it knew, for example, that a1 was 1K away from a[0] and could adjust for that).

The reason you may be getting different results is because, while the stack may grow down to add "objects" to it, the array is a single "object" and it may have ascending array elements in the opposite order. But it's not safe to rely on that behaviour since direction can change and variables could be swapped around for a variety of reasons including, but not limited to:

  • optimization.
  • alignment.
  • the whims of the person the stack management part of the compiler.

See here for my excellent treatise on stack direction :-)

In answer to your specific questions:

  1. Does stack grow up or down?
    It doesn't matter at all (in terms of the standard) but, since you asked, it can grow up or down in memory, depending on the implementation.
  2. What happen between a[2] and q memory addresses? Why there are a big memory difference there? (20 bytes)?
    It doesn't matter at all (in terms of the standard). See above for possible reasons.
4
ответ дан 24 November 2019 в 09:16
поделиться

On an x86, the memory "allocation" of a stack frame consists simply of subtracting the necessary number of bytes from the stack pointer (I believe other architectures are similar). In this sense, I guess the stack growns "down", in that the addresses get progressively smaller as you call more deeply into the stack (but I always envision the memory as starting with 0 in the top left and getting larger addresses as you move to the right and wrap down, so in my mental image the stack grows up...). The order of the variables being declared may not have any bearing on their addresses -- I believe the standard allows for the compiler to reorder them, as long as it doesn't cause side effects (someone please correct me if I'm wrong). They're just stuck somewhere into that gap in the used addresses created when it subtracts the number of bytes from the stack pointer.

The gap around the array may be some kind of padding, but it's mysterious to me.

2
ответ дан 24 November 2019 в 09:16
поделиться

The direction is which stacks grow is architecture specific. That said, my understanding is that only a very few hardware architectures have stacks that grow up.

The direction that a stack grows is independent of the the layout of an individual object. So while the stack may grown down, arrays will not (i.e &array[n] will always be < &array[n+1]);

13
ответ дан 24 November 2019 в 09:16
поделиться

This is actually two questions. One is about which way the stack grows when one function calls another (when a new frame is allocated), and the other is about how variables are laid out in a particular function's frame.

Neither is specified by the C standard, but the answers are a little different:

  • Which way does the stack grow when a new frame is allocated -- if function f() calls function g(), will f's frame pointer be greater or less than g's frame pointer? This can go either way -- it depends on the particular compiler and architecture (look up "calling convention"), but it is always consistent within a given platform (with a few bizarre exceptions, see the comments). Downwards is more common; it's the case in x86, PowerPC, MIPS, SPARC, EE, and the Cell SPUs.
  • How are a function's local variables laid out inside its stack frame? This is unspecified and completely unpredictable; the compiler is free to arrange its local variables however it likes to get the most efficient result.
44
ответ дан 24 November 2019 в 09:16
поделиться

The compiler is free to allocate local (auto) variables at any place on the local stack frame, you cannot reliably infer the stack growing direction purely from that. You can infer the stack grow direction from comparing the addresses of nested stack frames, ie comparing the address of a local variable inside the stack frame of a function compared to it's callee :

#include <stdio.h>
int f(int *x)
{
  int a;
  return x == NULL ? f(&a) : &a - x;
}

int main(void)
{
  printf("stack grows %s!\n", f(NULL) < 0 ? "down" : "up");
  return 0;
}
1
ответ дан 24 November 2019 в 09:16
поделиться
Другие вопросы по тегам:

Похожие вопросы: