Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

259
Visualizações
¿Creando "clases" en C, en la pila frente al montón?

Cada vez que veo una "clase" de C (cualquier estructura que esté destinada a ser utilizada para acceder a funciones que toman un puntero como primer argumento) las veo implementadas de esta manera:

 typedef struct { int member_a; float member_b; } CClass; CClass* CClass_create(); void CClass_destroy(CClass *self); void CClass_someFunction(CClass *self, ...); ...

Y en este caso CClass_create siempre malloc s su memoria y devuelve un puntero a eso.

Cada vez que veo que aparece algo new en C++ innecesariamente, por lo general parece volver locos a los programadores de C++, pero esta práctica parece aceptable en C. ¿Qué sucede? ¿Hay alguna razón detrás de por qué las "clases" de estructura asignadas al montón son tan comunes?

over 4 years ago · Santiago Trujillo
3 Respostas
Responde à pergunta

0

Hay varias razones para esto.

  1. Uso de punteros "opacos"
  2. Falta de destructores
  3. Sistemas integrados (problema de desbordamiento de pila)
  4. Contenedores
  5. Inercia
  6. "Pereza"

Discutámoslos brevemente.

Para punteros opacos , le permite hacer algo como:

 struct CClass_; typedef struct CClass_ CClass; // the rest as in your example

Por lo tanto, el usuario no ve la definición de struct CClass_ , lo que lo aísla de los cambios y permite otras cosas interesantes, como implementar la clase de manera diferente para diferentes plataformas.

Por supuesto, esto prohíbe el uso de variables de pila de CClass . Pero, OTOH, uno puede ver que esto no prohíbe la asignación de objetos CClass estáticamente (de algún grupo), devueltos por CClass_create o tal vez otra función como CClass_create_static .

Falta de destructores : dado que el compilador C no destruirá automáticamente los objetos de la pila CClass , debe hacerlo usted mismo (llamando manualmente a la función destructora). Entonces, el único beneficio que queda es el hecho de que la asignación de pila es, en general, más rápida que la asignación de montón. OTOH, no tiene que usar el montón: puede asignar desde un grupo, una arena o algo así, y eso puede ser casi tan rápido como la asignación de pilas, sin los problemas potenciales de asignación de pilas discutidos a continuación.

Sistemas integrados : Stack no es un recurso "infinito", ya sabes. Claro, para la mayoría de las aplicaciones en los sistemas operativos "regulares" actuales (POSIX, Windows...), casi lo es. Pero, en los sistemas integrados, la pila puede ser tan baja como unos pocos KB. Eso es extremo, pero incluso los sistemas integrados "grandes" tienen una pila que está en MB. Por lo tanto, se agotará si se usa en exceso. Cuando lo hace, en su mayoría no hay garantía de lo que sucederá: AFAIK, tanto en C como en C ++ eso es "Comportamiento indefinido". OTOH, CClass_create() puede devolver un puntero NULL cuando no tiene memoria, y puede manejar eso.

Contenedores : a los usuarios de C++ les gusta la asignación de pilas, pero si crea un std::vector en la pila, su contenido se asignará en montón. Puede modificar eso, por supuesto, pero ese es el comportamiento predeterminado, y hace que la vida sea mucho más fácil decir "todos los miembros de un contenedor están asignados al montón" en lugar de tratar de averiguar cómo manejar si no lo están.

Inercia : bueno, el OO vino de SmallTalk. Todo es dinámico allí, por lo que la traducción "natural" a C es la forma de "poner todo en el montón". Entonces, los primeros ejemplos fueron así e inspiraron a otros durante muchos años.

" Pereza ": si sabe que solo quiere apilar objetos, necesita algo como:

 CClass CClass_make(); void CClass_deinit(CClass *me);

Pero, si desea permitir tanto la pila como el montón, debe agregar:

 CClass *CClass_create(); void CClass_destroy(CClass *me);

Esto es más trabajo para el implementador, pero también es confuso para el usuario. Se pueden crear interfaces ligeramente diferentes, pero eso no cambia el hecho de que necesita dos conjuntos de funciones.

Por supuesto, la razón de los "contenedores" también es parcialmente una razón de "pereza".

over 4 years ago · Santiago Trujillo Relatório

0

Suponiendo, como en su pregunta, CClass_create y CClass_destroy usan malloc/free , entonces para mí hacer lo siguiente es una mala práctica:

 void Myfunc() { CClass* myinstance = CClass_create(); ... CClass_destroy(myinstance); }

porque podríamos evitar un malloc y un free fácilmente:

 void Myfunc() { CClass myinstance; // no malloc needed here, myinstance is on the stack CClass_Initialize(&myinstance); ... CClass_Uninitialize(&myinstance); // no free needed here because myinstance is on the stack }

con

 CClass* CClass_create() { CClass *self= malloc(sizeof(CClass)); CClass_Initialize(self); return self; } void CClass_destroy(CClass *self); { CClass_Uninitialize(self); free(self); } void CClass_Initialize(CClass *self) { // initialize stuff ... } void CClass_Uninitialize(CClass *self); { // uninitialize stuff ... }

En C++ también preferimos hacer esto:

 void Myfunc() { CClass myinstance; ... }

que esto:

 void Myfunc() { CClass* myinstance = new CCLass; ... delete myinstance; }

Para evitar un new / delete innecesario.

over 4 years ago · Santiago Trujillo Relatório

0

En C, cuando algún componente proporciona una función de "creación", el implementador del componente también controla cómo se inicializa el componente. Por lo tanto, no solo emula el operator new de C++, sino también el constructor de clases.

Renunciar a este control sobre la inicialización significa muchas más comprobaciones de errores en las entradas, por lo que mantener el control facilita proporcionar un comportamiento consistente y predecible.

También me opongo a que malloc siempre se use para asignar memoria. Esto puede ser a menudo el caso, pero no siempre. Por ejemplo, en algunos sistemas integrados, encontrará que malloc / free no se usa en absoluto. Las funciones X_create pueden asignarse de otras formas, por ejemplo, desde una matriz cuyo tamaño se fija en tiempo de compilación.

over 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda