C y C ++ tienen muchas diferencias, y no todos los códigos C válidos son códigos C ++ válidos.
(Por "válido" me refiero a código estándar con comportamiento definido, es decir, no específico de implementación / indefinido / etc.)
¿Hay algún escenario en el que un código válido tanto en C como en C ++ produzca un comportamiento diferente cuando se compila con un compilador estándar en cada idioma?
Para hacer 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
#ifdef __cplusplus, pragmas, etc.) - Cualquier cosa definida por la implementación es la misma 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.

Respuestas:
Lo siguiente, válido en C y C ++, va a resultar (muy probablemente) en diferentes valores
ien C y C ++:Consulte Tamaño del carácter ('a') en C / C ++ para obtener una explicación de la diferencia.
Otro de este artículo :
fuente
structantes de los nombres de estructura.struct sz { int i[2];};significaría que C y C ++ tienen que producir valores diferentes. (Mientras que un DSP con sizeof (int) == 1, podría producir el mismo valor).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 a funciones no declaradas:
En C ++, esto no imprimirá nada porque
fse crea y destruye un temporal , pero en C90 imprimiráhelloporque las funciones se pueden invocar sin haber sido declaradas.En caso de que se esté preguntando si el nombre
fse usará dos veces, los estándares C y C ++ lo permiten explícitamente, y para hacer un objeto que tiene que decirstruct fpara desambiguar si desea la estructura, o dejarlostructsi desea la función.fuente
Para C ++ vs. C90, hay al menos una forma de obtener un comportamiento diferente que no está definido en 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 ++.
En C ++, todo desde el
//final de la línea es un comentario, por lo que esto funciona como:Como C90 no tiene comentarios de una sola línea, solo el
/* comment */es un comentario. El primero/y los2dos son partes de la inicialización, por lo que se trata de:Por lo tanto, un compilador C ++ correcto dará 13, pero un compilador C90 estrictamente correcto 8. Por supuesto, acabo de elegir números arbitrarios aquí; puede usar otros números como mejor le parezca.
fuente
2, se leería como10 / + 3válido (unario +).C90 vs. C ++ 11 (
intvs.double):En C
autosignifica variable local. En C90 está bien omitir el tipo de variable o función. Por defecto esint. En C ++ 11autosignifica algo completamente diferente, le dice al compilador que infiera el tipo de la variable a partir del valor utilizado para inicializarla.fuente
auto?intpor defecto. Esto es inteligente! +1int.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 intención de hacerlo, hablar de "una versión obsoleta de C" es absurdo.Otro ejemplo que no he visto mencionado aún, este destaca una diferencia de preprocesador:
Esto imprime "falso" en C y "verdadero" en C ++: en C, cualquier macro indefinida se evalúa en 0. En C ++, hay 1 excepción: "verdadero" se evalúa en 1.
fuente
#define true falseಠ_ಠPor estándar C ++ 11:
a. El operador de coma realiza la conversión de valor a valor en C pero no en C ++:
En C ++ el valor de esta expresión será 100 y en C será
sizeof(char*).si. En C ++, el tipo de enumerador es su enumeración. En C, el tipo de enumerador es int.
Esto significa que
sizeof(int)puede no ser igual asizeof(E).C. En C ++, una función declarada con una lista de parámetros vacía no toma argumentos. En C la lista de parámetros vacíos significa que se desconoce el número y el tipo de parámetros de función.
fuente
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ónssería diferente,sterminarí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 de los abogados de idiomas, por lo que el primero está bien por su excepción. Pero el segundo es bueno en cualquier caso.char arr[sizeof(char*)+1]; int s = sizeof(0, arr);void *arr[100]. En este caso, un elemento tiene el mismo tamaño que un puntero al mismo elemento, por lo tanto, siempre que haya 2 o más elementos, la matriz debe ser más grande que la dirección de su primer elemento.Este programa imprime
1en C ++ y0en C:Esto sucede porque hay una
double abs(double)sobrecarga en C ++, por lo queabs(0.6)regresa0.6mientras que en C regresa0debido a la conversión implícita de doble a int antes de invocarint abs(int). En C, tienes que usarfabspara trabajardouble.fuente
stdlib.hsolo defineabs(int)yabs(long); La versiónabs(double)es declarada pormath.h. Entonces este programa aún puede llamar a laabs(int)versión. Es un detalle de implementación sistdlib.htambién hacemath.hque se incluya. (Creo que sería un error siabs(double)se llamaran, peromath.hno se incluyeron otras especificaciones ).<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 se use el formulario<cmath>.En C, esto imprime cualquier valor que
sizeof(int)tenga el sistema actual, que generalmente4se usa en la mayoría de los sistemas de hoy en día.En C ++, esto debe imprimir 1.
fuente
%dno es el especificador de formato correcto parasize_t.Otra
sizeoftrampa: expresiones booleanas.Es igual a
sizeof(int)en C, porque la expresión es de tipoint, pero generalmente es 1 en C ++ (aunque no es obligatorio que sea). En la práctica, casi siempre son diferentes.fuente
!debería ser suficiente para abool.sizeof(0)está4en C y C ++ porque0es un valor entero r.sizeof(!0)está4en C y1en C ++. NO lógico opera en operandos de tipo bool. Si el valor int es0que se convierte implícitamente enfalse(un valor bool r), entonces se invierte, lo que da como resultadotrue. Ambostrueyfalseson valores bool en C ++ y elsizeof(bool)is1. Sin embargo, en C se!0evalúa como1, que es un valor de tipo int. El lenguaje de programación C no tiene ningún tipo de datos bool por defecto.El lenguaje de programación C ++ (3a edición) ofrece tres ejemplos:
sizeof ('a'), como mencionó @Adam Rosenfield;
//comentarios utilizados para crear código oculto:Estructuras, etc., ocultando cosas en nuestros ámbitos, como en su ejemplo.
fuente
Un viejo castaño que depende del compilador de C, que no reconoce los comentarios de fin de línea de C ++ ...
fuente
Otro listado por el Estándar C ++:
fuente
xen la parte superior. Pensé que habías dicho "la matriza".Las funciones en línea en C tienen por defecto un ámbito externo donde las de C ++ no.
Compilar los dos archivos siguientes juntos imprimiría "Estoy en línea" en el caso de GNU C pero nada para C ++.
Archivo 1
Archivo 2
Además, C ++ trata implícitamente cualquier
constglobal como astaticmenos que se declare explícitamenteextern, a diferencia de C, queexternes el valor predeterminado.fuente
externque se demuestra aquí?struct funvsfn) y no tiene nada que ver si la función está en línea. El resultado es idéntico si elimina elinlinecalificador.inlineno se agregó hasta C99, pero en C99fun()no se puede llamar sin un prototipo en su alcance. Así que supongo que esta respuesta solo se aplica a GNU C.Devuelve con el código de salida de 0 en C ++ o 3 en C.
Este truco probablemente podría usarse para hacer algo más interesante, pero no podía pensar en una buena forma de crear un constructor que fuera aceptable para C. Traté de hacer un ejemplo igualmente aburrido con el constructor de copias, que dejaría una discusión ser aprobado, aunque de una manera no portátil:
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 este 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.
fuente
exit(code)Es una declaración válida de una variablecodede tipoexit, aparentemente. (Ver "análisis más irritante", que es un problema diferente pero similar).Este programa imprime
128(32 * sizeof(double)) cuando se compila usando un compilador de C ++ y4cuando se compila usando 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 ponen dentro del alcance de la estructura externa.
fuente
32*sizeof(double)lugar de 32 :))size_tcon%dNo olvide la distinción entre los espacios de nombres globales C y C ++. Supongamos que tienes un foo.cpp
y un foo2.c
Ahora suponga que tiene un main.c y main.cpp que se ven así:
Cuando se compila como C ++, usará el símbolo en el espacio de nombres global de C ++; en C usará el C uno:
fuente
foo). No hay "espacios de nombres globales" separados.Esto es bastante peculiar, ya que 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 arrayno es una expresión constante entera en C. En C ++, el tipo es totalmente estático.Si intenta agregar un inicializador aquí:
es válido C ++ pero no C, porque las matrices de longitud variable no pueden tener un inicializador.
fuente
Esto se refiere a valores y valores en C y C ++.
En el lenguaje de programación C, tanto los operadores de incremento previo como los de incremento posterior devuelven valores, no valores. 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:Sin embargo, en C ++, el operador anterior al incremento devuelve un valor l , mientras que el operador posterior al incremento devuelve un valor r. ¡Significa que se puede colocar una expresión con el operador de pre-incremento en el lado izquierdo del
=operador de asignación!Ahora, ¿por qué es así? El incremento posterior incrementa la variable y devuelve la variable como estaba antes. ocurriera el incremento. En realidad, esto es solo un valor. 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 valor r. Ya no representa el contenido actual de la variable.
El pre-incremento primero incrementa la variable, y luego devuelve la variable tal como se convirtió después del incremento. 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 ha incrementado. Entonces, el pre-incremento devuelve un valor l, devuelve la variable a sí mismo. Podemos usar asignar este valor de l a otra cosa, es como la siguiente declaración. Esta es una conversión implícita de lvalue en rvalue.
Como el incremento previo 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.
fuente
Las estructuras vacías tienen tamaño 0 en C y 1 en C ++:
fuente