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
i
en 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
struct
antes 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
f
se crea y destruye un temporal , pero en C90 imprimiráhello
porque las funciones se pueden invocar sin haber sido declaradas.En caso de que se esté preguntando si el nombre
f
se usará dos veces, los estándares C y C ++ lo permiten explícitamente, y para hacer un objeto que tiene que decirstruct f
para desambiguar si desea la estructura, o dejarlostruct
si 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 los2
dos 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 / + 3
válido (unario +).C90 vs. C ++ 11 (
int
vs.double
):En C
auto
significa variable local. En C90 está bien omitir el tipo de variable o función. Por defecto esint
. En C ++ 11auto
significa algo completamente diferente, le dice al compilador que infiera el tipo de la variable a partir del valor utilizado para inicializarla.fuente
auto
?int
por 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óns
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 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
1
en C ++ y0
en C:Esto sucede porque hay una
double abs(double)
sobrecarga en C ++, por lo queabs(0.6)
regresa0.6
mientras que en C regresa0
debido a la conversión implícita de doble a int antes de invocarint abs(int)
. En C, tienes que usarfabs
para trabajardouble
.fuente
stdlib.h
solo 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.h
también hacemath.h
que se incluya. (Creo que sería un error siabs(double)
se llamaran, peromath.h
no 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 generalmente4
se usa en la mayoría de los sistemas de hoy en día.En C ++, esto debe imprimir 1.
fuente
%d
no es el especificador de formato correcto parasize_t
.Otra
sizeof
trampa: 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á4
en C y C ++ porque0
es un valor entero r.sizeof(!0)
está4
en C y1
en C ++. NO lógico opera en operandos de tipo bool. Si el valor int es0
que se convierte implícitamente enfalse
(un valor bool r), entonces se invierte, lo que da como resultadotrue
. Ambostrue
yfalse
son valores bool en C ++ y elsizeof(bool)
is1
. Sin embargo, en C se!0
evalú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
x
en 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
const
global como astatic
menos que se declare explícitamenteextern
, a diferencia de C, queextern
es el valor predeterminado.fuente
extern
que se demuestra aquí?struct fun
vsfn
) y no tiene nada que ver si la función está en línea. El resultado es idéntico si elimina elinline
calificador.inline
no 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 variablecode
de 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 ++ y4
cuando 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_t
con%d
No 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 array
no 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