Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

251
Views
¿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 answers
Answer question

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 Report

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 Report

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 Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!