2012-01-18 17 views
20

He estado programando c/C++ durante muchos años, pero el descubrimiento accidental de hoy me hizo algo curioso ... ¿Por qué ambas salidas producen el mismo resultado en el siguiente código? (arr es por supuesto la dirección de arr[0], es decir, un puntero a arr[0] lo que habría esperado &arr ser la dirección de ese puntero, pero tiene el mismo valor que arr.)¿Por qué arr y & arr son iguales?

int arr[3]; 
    cout << arr << endl; 
    cout << &arr << endl; 

Observación: Esta cuestión fue cerrado, pero ahora se abre de nuevo. (Gracias?)

sé que &arr[0] y arr evalúa al mismo número, pero que es no mi pregunta! La pregunta es por qué &arr y arr se evalúa con el mismo número. Si arr es un literal (software no almacenado), entonces el compilador debe quejarse y decir que arr no es un valor l. Si la dirección del arr está almacenada en alguna parte, entonces &arr debe darme la dirección de esa ubicación. (Pero este no es el caso)

si escribo

const int * arr2 = arr;

entonces arr2[i]==arr[i] para cualquier entero i, pero .

+0

¿Qué significa "la dirección de ese puntero"? El puntero no está almacenado en una variable. –

+1

@David Schwartz, si escribiera int * arr2 = new int [3], entonces & arr2 se habría almacenado en una variable. Quizás arr es una constante literal? Pero si uno toma la dirección de una constante (por ejemplo, & 123), entonces el compilador se queja (123 no es un valor l). El compilador no se quejó cuando tomé la dirección de arr. – ragnarius

+6

¡No creo que sea un duplicado! – ragnarius

Respuesta

13

No son lo mismo. Simplemente están en la misma ubicación de memoria. Por ejemplo, puede escribir arr+2 para obtener la dirección de arr[2], pero no (&arr)+2 para hacer lo mismo.

Además, sizeof arr y sizeof &arr son diferentes.

+4

Es bueno tener en cuenta que '(& arr) + 2' compilará, y tal vez incluso ejecutará, pero es un comportamiento indefinido, y _no_ le dará' arr [2] '. –

+0

No se compilará aquí! 'no se puede convertir 'int (*) [3]' a 'int *' en la inicialización' –

+1

' int (*) [3] 'se puede convertir a' int ** 'Un puntero a una matriz se puede convertir a un puntero a un puntero. –

5

La dirección es la misma pero ambas expresiones son diferentes. Simplemente comienzan en la misma ubicación de memoria. Los tipos de ambas expresiones son diferentes.

El valor de arr es del tipo int * y el valor de &arr es del tipo int (*)[3].

& es el operador de dirección y la dirección de un objeto es un puntero a ese objeto. El puntero a un objeto del tipo int [3] es del tipo int (*)[3]

+0

Como sucede, la dirección de la matriz en sí y la dirección de su primer elemento son numéricamente iguales. Pero tienen diferentes tipos y se comportan de manera diferente en otros aspectos. –

+0

El valor de 'arr' es del tipo' int (&) [3] 'que se" promociona "a' int * 'ya que' cout' no tiene una sobrecarga para las matrices. –

17
#include <cassert> 

struct foo { 
    int x; 
    int y; 
}; 

int main() {  
    foo f; 
    void* a = &f.x; 
    void* b = &f; 
    assert(a == b); 
} 

Por la misma razón las dos direcciones a y b anterior are the same. La dirección de un objeto es la misma que la de su primer miembro (sin embargo, sus tipos son diferentes).

      arr 
         _______^_______ 
        /    \ 
        | [0] [1] [2] | 
--------------------+-----+-----+-----+-------------------------- 
     some memory |  |  |  |  more memory 
--------------------+-----+-----+-----+-------------------------- 
        ^
        | 
      the pointers point here 

Como se puede ver en este diagrama, el primer elemento de la matriz está en la misma dirección que la propia matriz.

+0

Lo probé, pero & f.x! = & F.y. Entonces la dirección de un objeto es solo la misma que la dirección de su PRIMER miembro ... – ragnarius

+1

@ragnarius: La dirección de la matriz es la misma que la dirección del primer elemento de la matriz. No tiene nada que ver con los miembros de los elementos. –

+0

@ragnarius: eso es exactamente lo que dije. –

6

Ambos tienen el mismo valor pero tipos diferentes.

Cuando se utiliza por sí mismo (no el operando de & o sizeof), arr evalúa a un puntero a int la celebración de la dirección de la primera int en la matriz. &arr se evalúa como un puntero a una matriz de tres int s, que contiene la dirección de la matriz. Dado que el primer int en el conjunto debe estar al principio de la matriz, las direcciones deben ser iguales.

La diferencia entre los dos se hace evidente si lo hace un poco de matemática de los resultados:

arr+1 será igual a arr + sizeof(int).

((&arr) + 1) será igual a arr + sizeof(arr) == arr + sizeof(int) * 3

Editar: En cuanto a cómo/por qué sucede esto, la respuesta es bastante simple: porque la norma lo dice. En particular, se dice (§6.3.2.1/3):

excepto cuando es el operando del operador sizeof o la unario & operador, o es un literal cadena que se utiliza para inicializar una matriz, una expresión que tiene tipo '' matriz de tipo '' se convierte en una expresión con el tipo '' puntero para escribir '' que apunta al elemento inicial del objeto de matriz y no es un valor l.

[nota: esta cita en particular es del estándar C99, pero creo que hay un lenguaje equivalente en todas las versiones de los estándares C y C++].

En el primer caso (arr por sí mismo), arr no está siendo utilizado como el operando de sizeof, unario &, etc., por lo que se convierte (no promovido) para el tipo "puntero a escribir" (en este caso, "puntero a int").

En el segundo caso (&arr), el nombre, obviamente, es ser utilizado como el operando de la & operador unario - de modo que la conversión no hace tener lugar.

+0

@MooingDuck: ¿Dónde crees que ves algo relacionado con la dirección de un puntero? (sugerencia: no hay tal cosa aquí). No hay promoción involucrada aquí tampoco. –

+0

indica que 'arr' produce" un puntero a 'int', que es ambiguo y posiblemente confuso. –

+0

No estoy seguro de entender su cotización.Estamos discutiendo el operador único y su cita dice que "Excepto que es el operador [...] único y ...". – ragnarius

2

Los punteros y matrices a menudo se pueden tratar de manera idéntica, pero existen diferencias. Un puntero tiene una ubicación de memoria, por lo que puede tomar la dirección de un puntero. Pero una matriz no tiene nada que lo señale, en tiempo de ejecución. Entonces, tomar la dirección de una matriz es, para el compilador, sintácticamente definida para ser la misma que la dirección del primer elemento. Lo cual tiene sentido, leyendo esa oración en voz alta.

4

No son lo mismo.

un poco más estricta explicación:

arr es un lvalue de tipo int [3]. Un intento de usar arr en algunas expresiones como cout << arr dará como resultado la conversión lvalue-r-valor que, como no hay valores de tipo de matriz, la convertirá en un valor r de int * y con el valor igual a &arr[0]. Esto es lo que puedes mostrar.

&arr es un rvalue de tipo int (*)[3], señalando el propio objeto de matriz. No hay magia aquí :-) Este puntero apunta a la misma dirección que &arr[0] porque el objeto de matriz y su primer miembro comienzan exactamente en el mismo lugar en la memoria. Es por eso que tiene el mismo resultado cuando los imprime.


Una manera fácil de confirmar que son diferentes es comparar *(arr) y *(&arr): el primero es un valor-I de tipo int y el segundo es un valor-I de tipo int[3].

Cuestiones relacionadas