2009-10-22 12 views
5

¿Por qué este ejemplo de código se comporta de manera diferente en C++ y C#.El índice, la asignación y el incremento en una instrucción se comporta de manera diferente en C++ y C#. ¿Por qué?

[C Ejemplo ++]

int arr[2]; 
int index = 0; 
arr[index] = ++index; 

El resultado de lo cual será arr [1] = 1;

[C# Ejemplo]

int[] arr = new int[2]; 
int index = 0; 
arr[index] = ++index; 

El resultado de lo cual será arr [0] = 1;

Me parece muy extraño. Seguramente debe haber alguna razón para que ambos idiomas lo implementen de manera diferente. Me pregunto qué sería la salida C++/CLI?

+0

Bueno, jynx, chicos. ;) – hobbs

Respuesta

11

Como han notado otros, el comportamiento de este código es undefined en C/C++. Puedes obtener cualquier resultado.

El comportamiento de su código C# es estrictamente definido por el estándar C#.

Seguramente debe haber alguna razón para que ambos idiomas lo implementen de manera diferente?

Bueno, supongamos que está diseñando C#, y desea que el lenguaje sea fácil de aprender para los programadores de C++. ¿Elegirías copiar el enfoque de C++ a este problema, es decir, dejarlo indefinido? ¿Realmente desea facilitarles a los desarrolladores perfectamente inteligentes que escriban código accidentalmente para que el compilador pueda inventar cualquier significado que quiera?

Los diseñadores de C# no creen que el comportamiento indefinido de las expresiones simples sea algo bueno, y por lo tanto, hemos definido estrictamente lo que significan expresiones como esta. No podemos estar de acuerdo con lo que hace cada compilador de C++ porque diferentes compiladores de C++ le dan diferentes resultados para este tipo de código, por lo que no podemos estar de acuerdo con todos ellos.

En cuanto a por qué los diseñadores de C++ creen que es mejor dejar expresiones simples como esta para tener un comportamiento indefinido, bueno, tendrás que preguntarle a uno de ellos. Ciertamente podría hacer algunas conjeturas, pero esas solo serían conjeturas educadas.

He escrito una serie de artículos de blog sobre este tipo de problema; el más reciente fue casi exactamente el código que mencionas aquí.Algunos de los artículos es posible que desee leer:

Cómo el diseño de C# estimula la eliminación de errores sutiles:

http://blogs.msdn.com/ericlippert/archive/2007/08/14/c-and-the-pit-of-despair.aspx

exactamente cuál es la relación entre la precedencia, la asociatividad, y el orden de ejecución en C#?

http://blogs.msdn.com/ericlippert/archive/2008/05/23/precedence-vs-associativity-vs-order.aspx

¿En qué orden los efectos secundarios de la indexación, misiones y el incremento suceda?

http://blogs.msdn.com/ericlippert/archive/2009/08/10/precedence-vs-order-redux.aspx

+0

Gracias. Leeré los artículos. –

+0

No está definido en C++ porque tampoco está definido en C. Aparentemente, diferentes compiladores de C hicieron cosas diferentes antes de que se adoptara el estándar, y dejarlo indefinido fue la única opción razonable, en mi humilde opinión. –

3

El comportamiento de usar índice y índice de ++ dentro de la misma asignación no está especificado en C++. Simplemente no puede hacer eso: escriba arr[index] = index + 1 e incremente su variable después de eso. Para el caso, con mi compilador de C++ en mi máquina veo arr [0] = 1, y arr [1] no ha sido tocado.

+6

Es peor que no especificado, en realidad es un comportamiento indefinido. –

+0

Por supuesto, eso es lo que debería haber escrito (está * especificado * como * indefinido * ;-) No puedo tener una idea clara cuando pasas toda la mañana depurando ... (¡Incluso encontré un error en gdb!) –

4

Su código C++ podría, de hecho, hacer cualquier cosa. arr[index] = ++index; invoca comportamiento indefinido.

0

El resultado de la versión de C++ no siempre será como usted escribe ya que está invocando comportamiento indefinido. En C++ obtendrás comportamiento indefinido si usas el valor de una variable en una expresión cuando esa variable también se modifica la misma expresión a menos que leer ese valor sea parte de la determinación del valor a escribir, o la expresión contenga una secuencia punto entre la lectura y la escritura.

En su expresión, que están leyendo el valor de index para determinar dónde asignar el resultado de la parte derecha de la =, pero la mano derecha sub-expresión también modifica index.

1

En el caso de C++, al menos, está invocando un comportamiento indefinido preincrementando y utilizando index sin un punto de secuencia en el medio. Si usted alimenta a ese código de GCC con advertencias permitido dirá:

preinc.cpp:6: warning: operation on ‘index’ may be undefined 

supongo que está indefinido, así como en C#, pero no sé el idioma. Para C y C++ como mínimo, la respuesta es que el compilador puede hacer cualquier cosa que desee sin estar equivocado porque su código es erróneo. No hay obligación para los diferentes compiladores (o incluso el mismo compilador) de producir resultados consistentes, tampoco.

+0

Solo como curiosidad, G ++ 4.4.1 se comporta como C#, configuración de matriz [0] = 1. – hobbs

+1

_Competencia no definida_ significa que podría establecer 'matriz [1] = 1' o establecer' matriz [4711] = 42', limpie su disco duro disco, o te deja calvo, dependiendo de si hay un depurador presente, se ejecuta 42 veces, dependiendo de la fase de la luna, cuánto te gusta tu novia, o si has sido desagradable con tu madre. No tiene sentido decir "compilador x, versión y, así es", porque nunca se sabe. (http://stackoverflow.com/questions/1553382/1553407#1553407) – sbi

+4

Está * DEFINIDO ESTRICTAMENTE * en C#. No entramos en este negocio loco de tener expresiones simples que no tienen ningún significado, pero el compilador las toma de todos modos y hace algo loco. Danos algo de crédito aquí! –

1

Nota: según @Eric Lippert 's answer, el comportamiento se define estrictamente para C#, así que permítanme reformular mi respuesta en este.

este código:

arr[index] = ++index; 

es difícil de leer, incluso si el compilador de C# sabe exactamente cómo evaluar y en qué orden. Por esta sola razón, debe evitarse.

El operador MSDN page en C# va tan lejos como para señalar que este comportamiento podría no estar definido, aunque Eric señala que no es así. El hecho de que las múltiples fuentes de documentación (confío en Eric, sin embargo) lo hace diferente también es una señal de que podría ser mejor dejarlo solo.

+2

NO SE INDICIA en C#, está estrictamente definido. Véase más arriba. –

+2

¿Quizás alguien debería ir y actualizar la documentación de MSDN entonces? –

+1

Santo Dios, esa página es CHOCK LLENO de errores. Tendré una conversación con el administrador de documentación de inmediato. Gracias por traer eso a mi atención. –

-1

índice en C# es un tipo de valor, lo que significa que devuelve una nueva instancia del valor cuando realiza operaciones en él.

Si se lo imagina como un procedimiento en lugar de un operador, el procedimiento sería el siguiente:

public int Increment(int value) 
{ 
    int returnValue=value+1; 
    return returnValue; 
} 

C++, sin embargo, que funciona en la referencia del objeto, por lo que el procedimiento se vería así:

int Increment(int &value) 
{ 
    value=value+1; 
    return value; 
} 

Nota: si hubiera estado aplicando el operador en un objeto (por ejemplo, sobrecargado el operador ++) entonces C# se comportaría como C++, ya que los tipos de objetos se pasan como referencias.

+0

¿Está diciendo que en el índice C# ++ no se modifica el índice? – Henrik

+0

No, solo estaba privado de sueño ya que estaba tratando de explicar y no pensar bien –

Cuestiones relacionadas