¿Puede el código que es válido tanto en C como en C ++ producir un comportamiento diferente cuando se compila en cada idioma?

C y C ++ tienen muchas diferencias y no todo el código C válido es código C ++ válido.
(Por "válido" me refiero a código estándar con comportamiento definido, es decir, no específico de la implementación / indefinido / etc.)

¿Existe algún escenario en el que un fragmento de código válido tanto en C como en C ++ produciría una experiencia diferente comportamiento cuando se compila con un compilador estándar en cada idioma?

Para que sea una comparación razonable / útil (estoy tratando de aprender algo prácticamente útil, no para tratar de encontrar lagunas obvias en la pregunta), supongamos:

  • Nada relacionado con el preprocesador (lo que significa que no hay hacks con #ifdef __cplusplus, pragmas, etc.)
  • Todo lo definido por la implementación es el mismo en ambos idiomas (por ejemplo, límites numéricos, etc.)
  • Estamos comparando versiones razonablemente recientes de cada estándar (por ejemplo, C ++ 98 y C90 o posterior)
    Si las versiones son importantes, mencione qué versiones de cada una producen un comportamiento diferente.

preguntado Oct 14 '12, 21:10

Por cierto, puede resultar útil programar en un dialecto que sea C y C ++ al mismo tiempo. He hecho esto en el pasado y un proyecto actual: el lenguaje TXR. Curiosamente, los desarrolladores del lenguaje Lua hicieron lo mismo, y llamaron a este dialecto "Clean C". Obtiene el beneficio de una mejor verificación del tiempo de compilación y posiblemente diagnósticos útiles adicionales de los compiladores de C ++, pero conserva la portabilidad de C. -

Fusioné la pregunta anterior en esta pregunta, ya que tiene más puntos de vista y respuestas a favor. Este sigue siendo un ejemplo de una pregunta no constructiva, pero está bastante en el límite, ya que sí, les enseña algo a los usuarios de SO. Lo cierro como no constructivo solo para reflejar el estado de la pregunta antes de la fusión. No dude en no estar de acuerdo y reabrir. -

Votar para reabrir ya que creo que se puede responder objetivamente con un "sí" seguido de un ejemplo (como se demuestra a continuación). Creo que es constructivo en el sentido de que las personas pueden aprender un comportamiento relevante de él. -

@AndersAbel El número puro de respuestas, todas las cuales son correctas, demuestra sin ambigüedades que sigue siendo una pregunta para hacer una lista. No había forma de que pudieras haber hecho esta pregunta sin obtener una lista. -

@dmckee Por lo que vale, estoy de acuerdo contigo. Sin embargo, la gente de la etiqueta C ++ es ... digamos ... luchadora. -

19 Respuestas

Lo siguiente, válido en C y C ++, va a (muy probablemente) dar como resultado diferentes valores en i en C y C ++:

int i = sizeof('a');

Ver Tamaño del carácter ('a') en C / C ++ para una explicación de la diferencia.

Otro de este artículo:

#include <stdio.h>

int  sz = 80;

int main(void)
{
    struct sz { char c; };

    int val = sizeof(sz);      // sizeof(int) in C,
                               // sizeof(struct sz) in C++
    printf("%d\n", val);
    return 0;
}

Respondido el 03 de diciembre de 17 a las 15:12

¡Definitivamente no esperaba este! Esperaba algo un poco más dramático, pero sigue siendo útil, gracias. :) +1 - user541686

+1 el segundo ejemplo es bueno por el hecho de que C ++ no requiere struct antes de los nombres de estructuras. - Seth Carnegie

@Andrey Pensé lo mismo hace un tiempo y lo probé y funcionó en GCC 4.7.1 sin el estándar, contrariamente a mis expectativas. ¿Eso es un error en GCC? - Seth Carnegie

@SethCarnegie: Un programa no conforme no tiene por qué dejar de funcionar, pero tampoco se garantiza que funcione. - Andrei Vihrov

struct sz { int i[2];}; significaría que C y C ++ tienen para producir diferentes valores. (Mientras que un DSP con sizeof (int) == 1, podría producir el mismo valor). - Martin Bonner apoya a Monica

Aquí hay un ejemplo que aprovecha la diferencia entre las llamadas a funciones y las declaraciones de objetos en C y C ++, así como el hecho de que C90 permite la llamada de funciones no declaradas:

#include <stdio.h>

struct f { int x; };

int main() {
    f();
}

int f() {
    return printf("hello");
}

En C ++ esto no imprimirá nada porque un temporal f se crea y destruye, pero en C90 imprimirá hello porque las funciones se pueden llamar sin haber sido declaradas.

En caso de que te estés preguntando por el nombre f Al ser usado dos veces, los estándares C y C ++ explícitamente permiten esto, y para hacer un objeto tienes que decir struct f para desambiguar si quieres la estructura, o dejar struct si quieres la función.

contestado el 09 de mayo de 16 a las 05:05

Estrictamente hablando bajo C, esto no se compilará, porque la declaración de "int f ()" es posterior a la definición de "int main ()" :) - Sogartar

@Sogartar, ¿de verdad? codepad.org/STSQlUhh Los compiladores de C99 le darán una advertencia, pero aún le permitirán compilarlo. - jrajav

Las funciones de @Sogartar en C pueden declararse implícitamente. - Alex B

@AlexB No en C99 y C11. - usuario529758

@jrajav Esos no son compiladores C99, entonces. Un compilador C99 detecta identificadores no declarados como un error de sintaxis. Un compilador que no hace eso es un compilador C89, un preestándar u otro tipo de compilador no conforme. - usuario529758

Para C ++ vs. C90, hay al menos una forma de obtener un comportamiento diferente que no está definido por la implementación. C90 no tiene comentarios de una sola línea. Con un poco de cuidado, podemos usar eso para crear una expresión con resultados completamente diferentes en C90 y en C ++.

int a = 10 //* comment */ 2 
        + 3;

En C ++, todo, desde // al final de la línea hay un comentario, por lo que esto funciona como:

int a = 10 + 3;

Dado que C90 no tiene comentarios de una sola línea, solo /* comment */ es un comentario. El primero / y 2 son ambas partes de la inicialización, por lo que resulta en:

int a = 10 / 2 + 3;

Entonces, un compilador C ++ correcto dará 13, pero un compilador C90 estrictamente correcto 8. Por supuesto, elegí números arbitrarios aquí - puede usar otros números como mejor le parezca.

respondido 27 nov., 18:05

¡¡Vaya, esto es alucinante !! De todas las cosas posibles, nunca hubiera pensado que los comentarios podrían usarse para cambiar el comportamiento, jaja. +1 - user541686

incluso sin el 2, se leería como 10 / + 3 que es válido (unario +). - Benoit

Ahora, por diversión, modifíquelo para que C y C ++ calculen diferentes expresiones aritméticas y evalúen el mismo resultado. - ryan c thompson

@RyanThompson Trivial. s / 2/1 / - seje

@Mehrdad ¿Me equivoco o los comentarios están relacionados con el preprocesador? Por lo tanto, ¡deberían excluirse como una posible respuesta a su pregunta! ;-) - Cerveza inglesa

C90 frente a C ++ 11 (int vs double):

#include <stdio.h>

int main()
{
  auto j = 1.5;
  printf("%d", (int)sizeof(j));
  return 0;
}

C ª auto significa variable local. En C90 está bien omitir el tipo de variable o función. Por defecto es int. En C ++ 11 auto significa algo completamente diferente, le dice al compilador que infiera el tipo de variable del valor usado para inicializarla.

Respondido 27 Feb 14, 20:02

@SethCarnegie: Sí, es una clase de almacenamiento; es lo que sucede de forma predeterminada cuando lo omites, por lo que nadie lo usó y cambiaron su significado. creo que es int por defecto. ¡Esto es inteligente! +1 - user541686

@KeithThompson ¿Eh? C11 todavía tiene auto, que ha estado en C desde su concepción. - jim balter

C11 no tiene implícito-int. - R .. GitHub DEJA DE AYUDAR A ICE

@KeithThompson Ah, supongo que te refieres al inferido int. Aún así, en el mundo real, donde hay toneladas de código heredado y el líder del mercado aún no ha implementado C99 y no tiene la intención de hacerlo, hablar de "una versión obsoleta de C" es absurdo. - jim balter

"Cada variable DEBE tener una clase de almacenamiento explícita. Atentamente, alta gerencia". - bciudad

Otro ejemplo que no he visto mencionado todavía, este destaca una diferencia de preprocesador:

#include <stdio.h>
int main()
{
#if true
    printf("true!\n");
#else
    printf("false!\n");
#endif
    return 0;
}

Esto imprime "falso" en C y "verdadero" en C ++. En C, cualquier macro indefinida se evalúa como 0. En C ++, hay 1 excepción: "verdadero" se evalúa como 1.

Respondido 17 Oct 12, 03:10

Interesante. ¿Alguien conoce la razón de ser de este cambio? - antraído

porque "verdadero" es una palabra clave / valor válido, por lo que se evalúa como verdadero como cualquier "valor verdadero" (como cualquier número entero positivo). Todavía puede hacer #define true false para imprimir "false" en C ++ también;) - CaféDesarrollador

#define true false ಠ_ಠ - Bryan Boettcher

@DarioOO, ¿tal redefinición no resultará en UB? - Ruslan

@DarioOO: Sí, estás equivocado. No se permite la redefinición de palabras clave, el castigo se deja al destino (UB). No obstante, el preprocesador es una fase separada de compilación. - Desduplicador

Según el estándar C ++ 11:

a. El operador de coma realiza la conversión lvalue a rvalue en C pero no en C ++:

   char arr[100];
   int s = sizeof(0, arr);       // The comma operator is used.

En C ++ el valor de esta expresión será 100 y en C será sizeof(char*).

b. En C ++, el tipo de enumerador es su enumeración. En C, el tipo de enumerador es int.

   enum E { a, b, c };
   sizeof(a) == sizeof(int);     // In C
   sizeof(a) == sizeof(E);       // In C++

Esto significa que sizeof(int) puede no ser igual a sizeof(E).

c. En C ++, una función declarada con una lista de parámetros vacía no tiene argumentos. En C, la lista de parámetros vacía significa que se desconoce el número y tipo de parámetros de función.

   int f();           // int f(void) in C++
                      // int f(*unknown*) in C

Respondido el 04 de enero de 17 a las 07:01

El primero también está definido por la implementación como el de Alexey. Pero +1. - Seth Carnegie

@Seth, Todo el material anterior se tomó directamente del Anexo C.1 del estándar C ++ 11. - Kirill Kobelev

Sí, pero todavía está definido por la implementación. sizeof(char*) podría ser 100, en cuyo caso el primer ejemplo produciría el mismo comportamiento observable en C y C ++ (es decir, aunque el método de obtención s sería diferente, s terminaría siendo 100). El OP mencionó que este tipo de comportamiento definido por la implementación estaba bien, ya que solo quería evitar las respuestas del abogado del lenguaje, por lo que el primero está bien por su excepción. Pero el segundo es bueno en cualquier caso. - Seth Carnegie

Hay una solución fácil: simplemente cambie el ejemplo a: char arr[sizeof(char*)+1]; int s = sizeof(0, arr); - Mankarse

Para evitar diferencias definidas por la implementación, también puede usar void *arr[100]. En este caso, un elemento tiene el mismo tamaño que un puntero al mismo elemento, por lo que siempre que haya 2 o más elementos, la matriz debe ser mayor que la dirección de su primer elemento. - finlandés

Este programa imprime 1 en C ++ y 0 C ª:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    int d = (int)(abs(0.6) + 0.5);
    printf("%d", d);
    return 0;
}

Esto sucede porque hay double abs(double) sobrecarga en C ++, entonces abs(0.6) devoluciones 0.6 mientras que en C vuelve 0 debido a la conversión implícita de doble a int antes de invocar int abs(int). En C, tienes que usar fabs trabajar con double.

Respondido el 17 de diciembre de 12 a las 18:12

Tuve que depurar el código de otra persona con ese problema. Oh, cómo me encantó eso. De todos modos, su programa también imprime 0 en C ++. C ++ tiene que usar el encabezado "cmath", vea la comparación primero, devuelve 0 ideone.com/0tQB2G 2do regresando 1 ideone.com/SLeANo - CaféDesarrollador

Me alegro / lamento saber que no soy el único que encuentra esta diferencia a través de la depuración. Recientemente probado en VS2013, un archivo vacío con solo este contenido generará 1 si la extensión es .cpp y 0 si la extensión es .c. Parece se incluye indirectamente en VS. - pavel chikulaev

Y parece en VS C ++, incluye cosas de C ++ en el espacio de nombres global, mientras que en GCC no lo es. Sin embargo, no estoy seguro de cuál es el comportamiento estándar. - pavel chikulaev

Esta muestra de código en particular depende de la implementación: stdlib.h solo define abs(int) y abs(long); la versión abs(double) es declarado por math.h. Así que este programa aún puede llamar al abs(int) versión. Es un detalle de implementación si stdlib.h también causa math.h ser incluido. (Creo que sería un error si abs(double) fueron llamados, pero otros aspecs de math.h no fueron incluidos). - MM

Un problema secundario es que, aunque el estándar C ++ parece decir que incluir <math.h> también incluye las sobrecargas adicionales; en la práctica, resulta que todos los compiladores principales no incluyen esas sobrecargas a menos que el formulario <cmath> se utiliza. - MM

#include <stdio.h>

int main(void)
{
    printf("%d\n", (int)sizeof('a'));
    return 0;
}

En C, imprime cualquiera que sea el valor de sizeof(int) está en el sistema actual, que normalmente es 4 en la mayoría de los sistemas que se utilizan habitualmente en la actualidad.

En C ++, esto debe imprimir 1.

Respondido 27 Feb 14, 21:02

Sí, en realidad estaba familiarizado con este truco, ya que 'c' es un int en C y un char en C ++, pero aún así es bueno tenerlo en la lista aquí. - Sean

Eso sería una pregunta de entrevista interesante, especialmente para las personas que incluyen experto en c / c ++ en sus CV. Martín Beckett

Sin embargo, es un poco deshonesto. El propósito de sizeof es que no necesite saber exactamente qué tan grande es un tipo. - Dana la cuerda

En C, el valor está definido por la implementación y 1 es una posibilidad. (En C ++ tiene que imprimir 1 como se indica). Programador de Windows

En realidad tiene un comportamiento indefinido en ambos casos. %d no es el especificador de formato correcto para size_t. - R .. GitHub DEJA DE AYUDAR A ICE

Otra sizeof trap: expresiones booleanas.

#include <stdio.h>
int main() {
    printf("%d\n", (int)sizeof !0);
}

Es igual a sizeof(int) en C, porque la expresión es de tipo int, pero normalmente es 1 en C ++ (aunque no es obligatorio). En la práctica, casi siempre son diferentes.

Respondido 16 Oct 12, 00:10

Una ! debería ser suficiente para un bool. - alexey frunze

!! es el operador de conversión de int a booleano :) - EvilTeach

sizeof(0) is 4 tanto en C como en C ++ porque 0 es un rvalue entero. sizeof(!0) is 4 en C y 1 en C ++. El NOT lógico funciona con operandos de tipo bool. Si el valor int es 0 se convierte implícitamente en false (un valor bool), luego se invierte, lo que da como resultado true. Ambos true y false son valores bool en C ++ y el sizeof(bool) is 1. Sin embargo en C !0 evalúa a 1, que es un rvalue de tipo int. El lenguaje de programación C no tiene ningún tipo de datos bool por defecto. - galaxia

El lenguaje de programación C ++ (tercera edición) da tres ejemplos:

  1. sizeof ('a'), como mencionó @Adam Rosenfield;

  2. // comentarios que se utilizan para crear código oculto:

    int f(int a, int b)
    {
        return a //* blah */ b
            ;
    }
    
  3. Estructuras, etc. que esconden cosas dentro de los ámbitos, como en su ejemplo.

Respondido el 03 de diciembre de 17 a las 15:12

Una vieja castaña que depende del compilador de C, que no reconoce los comentarios de final de línea de C ++ ...

...
int a = 4 //* */ 2
        +2;
printf("%i\n",a);
...

respondido 19 nov., 12:02

Otro listado por el estándar C ++:

#include <stdio.h>

int x[1];
int main(void) {
    struct x { int a[2]; };
    /* size of the array in C */
    /* size of the struct in C++ */
    printf("%d\n", (int)sizeof(x)); 
}

Respondido 24 Feb 09, 00:02

¿Entonces obtienes diferencias de relleno? - v.oddou

ah lo siento, lo tengo, hay otro x en la cima. pensé que habías dicho "la matriz a". - v.oddou

Las funciones en línea en C tienen un alcance externo por defecto, mientras que las de C ++ no lo hacen.

Compilar los siguientes dos archivos juntos imprimiría "Estoy en línea" en el caso de GNU C pero nada para C ++.

archivo 1

#include <stdio.h>

struct fun{};

int main()
{
    fun();  // In C, this calls the inline function from file 2 where as in C++
            // this would create a variable of struct fun
    return 0;
}

archivo 2

#include <stdio.h>
inline void fun(void)
{
    printf("I am inline\n");
} 

Además, C ++ trata implícitamente cualquier const global como static a menos que se declare explícitamente extern, a diferencia de C en el que extern es el predeterminado.

contestado el 09 de mayo de 16 a las 05:05

Realmente no lo creo. Probablemente te has perdido el punto. No se trata de la definición de struct st, que simplemente se usa para hacer que el código sea válido en C ++. El punto es que destaca el comportamiento diferente de las funciones en línea en c vs c ++. Lo mismo se aplica a extern. Ninguno de estos se discute en ninguna de las soluciones. - fkl

¿Cuál es el comportamiento diferente de las funciones en línea y extern que se demuestra aquí? - Seth Carnegie

Está escrito con bastante claridad. "Las funciones en línea en c tienen un alcance externo por defecto, mientras que las de c ++ no lo son (el código lo muestra). Además, C ++ trata implícitamente cualquier const global como ámbito de archivo a menos que se declare explícitamente extern, a diferencia de C en el que extern es el valor predeterminado. se puede crear un ejemplo para eso ". Estoy perplejo: ¿no es comprensible? - fkl

@fayyazkl El comportamiento que se muestra se debe solo a la diferencia de búsqueda (struct fun vs fn) y no tiene nada que ver si la función está en línea. El resultado es idéntico si elimina inline Calificatorio. - Alex B

En ISO C este programa está mal formado: inline no se agregó hasta C99, pero en C99 fun() no se puede llamar sin un prototipo en el alcance. Así que supongo que esta respuesta solo se aplica a GNU C.- MM

struct abort
{
    int x;
};

int main()
{
    abort();
    return 0;
}

Devuelve con el código de salida 0 en C ++ o 3 en C.

Este truco probablemente podría usarse para hacer algo más interesante, pero no pude pensar en una buena manera de crear un constructor que fuera aceptable para C. Intenté hacer un ejemplo igualmente aburrido con el constructor de copia, que dejaría un argumento pasar, aunque de una manera bastante no portátil:

struct exit
{
    int x;
};

int main()
{
    struct exit code;
    code.x=1;

    exit(code);

    return 0;
}

Sin embargo, VC ++ 2005 se negó a compilar eso en modo C ++, quejándose de cómo se redefinió el "código de salida". (Creo que esto es un error del compilador, a menos que de repente haya olvidado cómo programar). Sin embargo, salió con un código de salida de proceso de 1 cuando se compiló como C.

Respondido 24 Feb 09, 00:02

Su segundo ejemplo usando exit, no se compila en gcc o g ++, desafortunadamente. Sin embargo, es una buena idea. - Sean

exit(code) es una declaración válida de una variable code De tipo exit, aparentemente. (Ver "análisis más molesto", que es un tema diferente pero similar). - user253751

#include <stdio.h>

struct A {
    double a[32];
};

int main() {
    struct B {
        struct A {
            short a, b;
        } a;
    };
    printf("%d\n", sizeof(struct A));
    return 0;
}

Este programa imprime 128 (32 * sizeof(double)) cuando se compila con un compilador de C ++ y 4 cuando se compila con un compilador de C.

Esto se debe a que C no tiene la noción de resolución de alcance. En C, las estructuras contenidas en otras estructuras se colocan en el alcance de la estructura exterior.

Respondido 18 Feb 16, 16:02

¡Este es interesante! (Creo que quieres decir 32*sizeof(double) en lugar de 32 :)) - user1354557

tenga en cuenta que obtiene UB por impresión size_t con %d - phuclv

No olvide la distinción entre los espacios de nombres globales de C y C ++. Suponga que tiene un foo.cpp

#include <cstdio>

void foo(int r)
{
  printf("I am C++\n");
}

y foo2.c

#include <stdio.h>

void foo(int r)
{
  printf("I am C\n");
}

Ahora suponga que tiene un C Principal y main.cpp que ambos se ven así:

extern void foo(int);

int main(void)
{
  foo(1);
  return 0;
}

Cuando se compila como C ++, utilizará el símbolo en el espacio de nombres global de C ++; en C usará el C uno:

$ diff main.cpp main.c
$ gcc -o test main.cpp foo.cpp foo2.c
$ ./test 
I am C++
$ gcc -o test main.c foo.cpp foo2.c
$ ./test 
I am C

Respondido 17 Oct 14, 10:10

¿Te refieres a la especificación de vinculación? - user541686

nombre destrozando. Los nombres de C ++ tienen prefijos y sufijos, mientras que C no - CaféDesarrollador

La alteración de nombres no forma parte de la especificación de C ++. ¿Está prohibido en C? - Rey del cielo

Este es un comportamiento indefinido (definición múltiple de foo). No hay "espacios de nombres globales" separados. - MM

int main(void) {
    const int dim = 5; 
    int array[dim];
}

Esto es bastante peculiar porque es válido en C ++ y en C99, C11 y C17 (aunque opcional en C11, C17); pero no válido en C89.

En C99 + crea una matriz de longitud variable, que tiene sus propias peculiaridades sobre las matrices normales, ya que tiene un tipo de tiempo de ejecución en lugar de un tipo de tiempo de compilación, y sizeof array no es una expresión constante entera en C. En C ++ el tipo es completamente estático.


Si intenta agregar un inicializador aquí:

int main(void) {
    const int dim = 5; 
    int array[dim] = {0};
}

es C ++ válido pero no C, porque matrices de longitud variable no puede tener un inicializador.

Respondido 19 Abr '19, 06:04

Esto se refiere a lvalues ​​y rvalues ​​en C y C ++.

En el lenguaje de programación C, tanto los operadores de preincremento como los de posincremento devuelven valores r, no valores l. Esto significa que no pueden estar en el lado izquierdo del = operador de asignación. Ambas declaraciones darán un error de compilación en C:

int a = 5;
a++ = 2;  /* error: lvalue required as left operand of assignment */
++a = 2;  /* error: lvalue required as left operand of assignment */

Sin embargo, en C ++, el operador de preincremento devuelve un lvalor, mientras que el operador de incremento posterior devuelve un valor r. Significa que una expresión con el operador de incremento previo se puede colocar en el lado izquierdo del = operador de asignación!

int a = 5;
a++ = 2;  // error: lvalue required as left operand of assignment
++a = 2;  // No error: a gets assigned to 2!

Ahora, ¿por qué es esto así? El post-incremento incrementa la variable y devuelve la variable tal como estaba antes el incremento ocurrió. En realidad, esto es solo un valor r. El valor anterior de la variable a se copia en un registro como temporal y luego se incrementa a. Pero el valor anterior de a es devuelto por la expresión, es un rvalue. Ya no representa el contenido actual de la variable.

El preincremento primero incrementa la variable y luego devuelve la variable tal como se convirtió en después de el incremento ocurrió. En este caso, no necesitamos almacenar el valor anterior de la variable en un registro temporal. Simplemente recuperamos el nuevo valor de la variable después de que se haya incrementado. Entonces, el preincremento devuelve un valor l, devuelve la variable a en sí. Podemos usar asignar este lvalue a otra cosa, es como la siguiente declaración. Esta es una conversión implícita de lvalue en rvalue.

int x = a;
int x = ++a;

Dado que el preincremento devuelve un valor l, también podemos asignarle algo. Las siguientes dos declaraciones son idénticas. En la segunda asignación, primero se incrementa a, luego su nuevo valor se sobrescribe con 2.

int a;
a = 2;
++a = 2;  // Valid in C++.

Respondido el 22 de junio de 18 a las 04:06

No hay "válido en C" aquí. - o11c

Las estructuras vacías tienen tamaño 0 en C y 1 en C ++:

#include <stdio.h>

typedef struct {} Foo;

int main()
{
    printf("%zd\n", sizeof(Foo));
    return 0;
}

respondido 07 nov., 18:15

No, la diferencia es que C lo hace no tienen estructuras vacías, excepto como una extensión del compilador, es decir, este código no coincide "es válido tanto en C como en C ++" - antti haapala

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.