배열 이름이 포인터입니까?


203

배열의 이름이 C의 포인터입니까? 그렇지 않은 경우 배열의 이름과 포인터 변수의 차이점은 무엇입니까?


4
그러나 배열 은 동일합니다 & array [0]

36
@pst : &array[0]배열이 아닌 포인터를 생성한다;)
jalf

28
@Nava (및 pst) : array& array [0] 은 실제로 동일하지 않습니다. 적절한 예 : sizeof (array)sizeof (& ar [[]]) 는 다른 결과를 나타냅니다.
Thomas Padron-McCarthy

1
@Thomas는 동의하지만 포인터와 관련하여 array와 & array [0]을 역 참조 할 때 array [0]의 동일한 값을 생성합니다. 즉 * array == array [0]. 아무도이 두 포인터가 동일하다는 것을 의미하지는 않았지만이 특별한 경우 (첫 번째 요소를 가리키는) 배열의 이름을 사용할 수도 있습니다.
Nava Carmon

1
이것도 이해하는데 도움이 될 것입니다 : stackoverflow.com/questions/381542 , stackoverflow.com/questions/660752
Dinah

답변:


255

배열은 배열이고 포인터는 포인터이지만 대부분의 경우 배열 이름은 포인터 로 변환 됩니다. 자주 사용되는 용어는 포인터로 쇠퇴 한다는 것 입니다.

배열은 다음과 같습니다.

int a[7];

a 7 개의 정수를위한 공간을 포함하며, 다음과 같이 대입으로 값 중 하나에 값을 넣을 수 있습니다.

a[3] = 9;

다음은 포인터입니다.

int *p;

p정수를위한 공백을 포함하지 않지만 정수를위한 공백을 가리킬 수 있습니다. 예를 들어, 배열 a에서 첫 번째 장소 와 같은 장소 중 하나를 가리 키도록 설정할 수 있습니다 .

p = &a[0];

혼란스러운 점은 다음과 같이 쓸 수도 있다는 것입니다.

p = a;

이것은 배열의 내용을 포인터에 복사 하지 않습니다 (의미 한 것은 무엇이든). 대신 배열 이름 이 첫 번째 요소에 대한 포인터로 변환됩니다. 할당은 이전과 동일합니다.apa

이제 p배열과 비슷한 방식으로 사용할 수 있습니다 .

p[3] = 17;

이것이 작동하는 이유는 C의 배열 역 참조 연산자 [ ]가 포인터로 정의되기 때문입니다. x[y]수단 : 포인터로 시작 x, 단계 y에 어떤 포인터 포인트 후 앞으로 요소를, 다음이 무엇이든 걸릴. 포인터 산술 구문을 사용하여로 x[y]도 쓸 수 있습니다 *(x+y).

이것이 우리와 같은 일반 배열과 함께 작동 하려면 먼저 in a의 이름 aa[3]포인터로 변환해야합니다 (의 첫 번째 요소로 a). 그런 다음 3 단계 요소를 앞으로 진행하고 모든 요소를 ​​가져옵니다. 다시 말해, 배열에서 3 번째 위치에있는 요소를 가져옵니다. (첫 번째 요소의 번호는 0이므로 배열의 네 번째 요소는 어느 것입니까?)

따라서 요약하면 C 프로그램의 배열 이름은 (대부분의 경우) 포인터로 변환됩니다. 예외는 sizeof배열 에서 연산자를 사용할 때 입니다. a이 문맥에서 포인터로 변환 된 경우 sizeof a실제 배열이 아닌 포인터의 크기를 지정하면 쓸모가 없으므로 a배열 자체를 의미합니다.


5
모두 - 유사한 자동 변환은 함수 포인터에 적용 functionpointer()하고 (*functionpointer)()이상하게도, 같은 일을 의미한다.
Carl Norum

3
그는 배열과 포인터가 같은지 묻지 않았지만 배열의 이름이 포인터인지
Ricardo Amores

32
배열 이름은 포인터가 아닙니다. 배열 유형의 변수에 대한 식별자이며 요소 유형의 포인터로 암시 적으로 변환됩니다.
Pavel Minaev

29
또한 sizeof()배열-> 포인터 붕괴가없는 다른 컨텍스트는 연산자입니다 &-위의 예제 에서 단일 &a배열에 int대한 포인터가 아닌 7 배열에 대한 포인터가됩니다 int. 즉, 형식은로 int(*)[7]묵시적으로 변환 할 수 없습니다 int*. 이런 식으로 함수는 실제로 특정 크기의 배열에 대한 포인터를 가져 와서 형식 시스템을 통해 제한을 적용 할 수 있습니다.
Pavel Minaev

3
@ onmyway133, 여기 에 간단한 설명과 추가 인용이 있는지 확인 하십시오 .
Carl Norum

36

배열을 값으로 사용하면 해당 이름은 첫 번째 요소의 주소를 나타냅니다.
배열을 값으로 사용하지 않으면 이름이 전체 배열을 나타냅니다.

int arr[7];

/* arr used as value */
foo(arr);
int x = *(arr + 1); /* same as arr[1] */

/* arr not used as value */
size_t bytes = sizeof arr;
void *q = &arr; /* void pointers are compatible with pointers to any object */

20

배열 유형의 표현식 (예 : 배열 이름)이 더 큰 표현식에 나타나고 &또는 의 피연산자가 아닌 경우sizeof 그 배열 식의 종류가 "T의 N 소자 어레이"로 변환된다 연산자 "pointer to T"이고 표현식의 값은 배열에서 첫 번째 요소의 주소입니다.

간단히 말해서 배열 이름은 포인터가 아니지만 대부분의 상황에서 마치 .

편집하다

의견의 질문에 대답 :

sizeof를 사용하면 배열 요소의 크기 만 계산합니까? 그런 다음 배열 "head"는 길이와 포인터에 대한 정보로 공간을 차지합니다 (그리고 이것은 일반적인 포인터보다 공간이 더 많이 필요하다는 것을 의미합니까?).

배열을 만들 때 할당 된 유일한 공간은 요소 자체를위한 공간입니다. 별도의 포인터 또는 메타 데이터를 위해 스토리지가 구체화되지 않습니다. 주어진

char a[10];

기억에 남는 것은

   +---+
a: |   | a[0]
   +---+ 
   |   | a[1]
   +---+
   |   | a[2]
   +---+
    ...
   +---+
   |   | a[9]
   +---+

표현은 a 전체 어레이를 지칭하지만 없다 객체 a 배열 요소 자체는 별개. 따라서 sizeof a전체 배열의 크기 (바이트)를 제공합니다. 이 표현식 &a은 배열 의 주소를 제공하며 이는 첫 번째 요소의 주소와 동일합니다 . 차이 &a&a[0]그 결과의 유형 1 - char (*)[10]첫번째 경우와 char *두번째로.

상황이 이상하게 여기는 위치는 개별 요소에 액세스하려는 경우입니다. 표현식 a[i]*(a + i)주소 값 의 결과로 정의되며 해당 주소 a에서 오프셋 i요소 ( 바이트가 아님)를 정의하고 결과를 역 참조합니다.

문제는 a포인터 또는 주소가 아니라 전체 배열 객체라는 것입니다. 따라서 C의 규칙은 컴파일러가 배열 유형의 표현식 (예 : a유형이있는 char [10])을 보고 해당 표현식이 sizeof단항 &연산자 의 피연산자가 아니 거나 해당 표현식의 유형이 변환 되는 규칙 ( "감쇠")입니다. 포인터 유형 ( char *)에 대한 표현식의 값은 배열의 첫 번째 요소의 주소입니다. 따라서 표현식 a표현식 과 유형 및 값이 동일 &a[0]하며 확장에 따라 표현식 *a은 유형 및 값과 동일합니다 a[0].

C는 B라는 이전 언어에서 파생되었으며 B 에서는 배열 요소 와 별도의 포인터 객체 a 였습니다 .a[0]a[1] 등 리치는 B의 배열의 의미를 유지하고 싶어하지만, 그는 별도의 포인터 객체를 저장 엉망 싶지 않았다. 그래서 그는 그것을 제거했다. 대신, 컴파일러는 필요에 따라 변환 중에 배열 표현식을 포인터 표현식으로 변환합니다.

배열은 크기에 대한 메타 데이터를 저장하지 않는다는 것을 기억하십시오. 배열 표현식이 포인터로 "부패"하자마자 단일 요소에 대한 포인터입니다. 해당 요소는 일련의 요소 중 첫 번째 요소이거나 단일 객체 일 수 있습니다. 포인터 자체를 기반으로 알 방법이 없습니다.

배열 표현식을 함수에 전달할 때받는 모든 함수는 첫 번째 요소에 대한 포인터입니다. 배열이 얼마나 큰지 알 수 없습니다 (이것이 gets함수가 그러한 위협이었고 결국 라이브러리에서 제거 된 이유입니다 ). 함수가 배열의 요소 수를 알기 위해서는 센티넬 값 (예 : C 문자열의 0 종결 자)을 사용하거나 요소 수를 별도의 매개 변수로 전달해야합니다.


  1. 주소 값 해석 방법에 어떤 영향을 미칠 수 있는지는 기계에 따라 다릅니다.

이 답변에 꽤 오랫동안 찾고 있습니다. 감사합니다! 그리고 당신이 알고 있다면, 배열 표현이 무엇인지 조금 더 말할 수 있습니까? sizeof를 사용하면 배열의 요소 크기 만 계산합니까? 그런 다음 배열 "head"는 길이와 포인터에 대한 정보로 공간을 차지합니다 (그리고 이것은 일반적인 포인터보다 더 많은 공간을 차지한다는 것을 의미합니까?).
Andriy Dmytruk

그리고 하나 더. 길이 5의 배열은 int [5] 유형입니다. 그래서 우리가 sizeof (array)를 호출 할 때 길이를 아는 곳에서 시작합니다. 그리고 이것은 길이가 다른 배열이 다른 유형의 상수와 같다는 것을 의미합니까?
Andriy Dmytruk

@AndriyDmytruk : sizeof는 연산자이며 피연산자 의 숫자 바이트 (객체를 나타내는 표현식 또는 괄호 안의 유형 이름)로 평가됩니다. 따라서 배열 sizeof의 경우 요소 수에 단일 요소의 바이트 수를 곱한 값으로 평가됩니다. 의 int너비가 4 바이트 인 경우 5 요소 배열은 int20 바이트 를 차지합니다.
John Bode

운영자도 [ ]특별하지 않습니까? 예를 들어, int a[2][3];에 대해 x = a[1][2];로 다시 작성할 수는 있지만 x = *( *(a+1) + 2 );여기서는 a포인터 유형으로 변환되지 않습니다 int*( a함수의 인수 인 경우 로 변환되어야 함 int*).
Stan

2
@Stan : 표현식의 atype은 int [2][3]"부패"합니다 int (*)[3]. 이 표현식 *(a + 1)에는 유형 int [3]이 있으며 "부패"합니다 int *. 따라서 *(*(a + 1) + 2)type을 갖습니다 int. a의 제 3 소자 어레이 포인트 int, a + 1의 제 3 소자 어레이 포인트 int, *(a + 1) 의 제 3 소자 어레이 int, *(a + 1) + 2의 제 2 어레이의 세 번째 요소 점 int때문에 *(*(a + 1) + 2) 의 제 2 어레이의 세 번째 요소는 int. 그것이 기계 코드에 매핑되는 방법은 전적으로 컴파일러에 달려 있습니다.
John Bode

5

이와 같이 선언 된 배열

int a[10];

10 int초 동안 메모리를 할당합니다 . 당신은 수정할 수 a없지만 포인터 산술을 할 수 있습니다a .

이와 같은 포인터는 포인터에 대해서만 메모리를 할당합니다 p.

int *p;

ints를 할당하지 않습니다 . 수정할 수 있습니다 :

p = a;

다음과 같이 배열 첨자를 사용하십시오.

p[2] = 5;
a[2] = 5;    // same
*(p+2) = 5;  // same effect
*(a+2) = 5;  // same effect

2
배열이 항상 스택에 할당되는 것은 아닙니다. Yhat은 컴파일러마다 다른 구현 세부 사항입니다. 대부분의 경우 정적 또는 전역 배열은 스택과 다른 메모리 영역에서 할당됩니다. const 유형의 배열은 또 다른 메모리 영역에서 할당 될 수 있습니다
Mark Bessey

1
나는 Grumdrig이 할당 10 개 "라고하는 의미 생각 int자동 스토리지 duration`에들.
궤도의 밝기 경주

4

배열 이름 자체는 메모리 위치를 생성하므로 배열 이름을 포인터처럼 취급 할 수 있습니다.

int a[7];

a[0] = 1976;
a[1] = 1984;

printf("memory location of a: %p", a);

printf("value at memory location %p is %d", a, *a);

그리고 포인터에 대해 할 수있는 다른 멋진 것들 (예 : 오프셋 추가 / 빼기)은 배열에 할 수도 있습니다.

printf("value at memory location %p is %d", a + 1, *(a + 1));

C가 배열 을 "포인터"의 일종 으로 노출시키지 않은 경우 언어 적으로 메모리 위치입니다. 메모리 위치 일뿐입니다. 메모리의 임의 위치를 ​​가리킬 수 없으며 프로그래머가 제어 할 수도 없습니다. 우리는 항상 이것을 코딩해야합니다 :

printf("value at memory location %p is %d", &a[1], a[1]);

1

이 예제는이 문제에 대해 약간의 지적을합니다.

#include <stdio.h>
int main()
{
        int a[3] = {9, 10, 11};
        int **b = &a;

        printf("a == &a: %d\n", a == b);
        return 0;
}

gcc 4.9.2에서 잘 컴파일되고 (경고 2 개) 다음을 인쇄합니다.

a == &a: 1

죄송합니다 :-)

따라서 결론은 아니오입니다. 배열은 포인터가 아니며 &처럼 연산자를 사용하여 주소를 얻을 수 있기 때문에 메모리처럼 (읽기 전용이 아닌) 포인터로 저장되지 않습니다. . 그러나-죄송합니다-그 연산자는 작동하지 않습니다 :-)), 당신은 어느 쪽이든 경고를 받았습니다 :

p.c: In function main’:
pp.c:6:12: warning: initialization from incompatible pointer type
  int **b = &a;
            ^
p.c:8:28: warning: comparison of distinct pointer types lacks a cast
  printf("a == &a: %d\n", a == b);

C ++은 컴파일 타임에 오류가있는 그러한 시도를 거부합니다.

편집하다:

이것이 내가 설명하려는 것입니다.

#include <stdio.h>
int main()
{
    int a[3] = {9, 10, 11};
    void *c = a;

    void *b = &a;
    void *d = &c;

    printf("a == &a: %d\n", a == b);
    printf("c == &c: %d\n", c == d);
    return 0;
}

비록 ca같은 메모리에 "포인트", 당신의 주소를 얻을 수 있습니다 c포인터를, 그러나 당신은의 주소를 얻을 수 없습니다 a포인터를.


1
"정확하게 컴파일됩니다 (2 경고)." 그건 좋지 않아 를 추가하여 gcc에 적절한 표준 C로 컴파일하도록 지시 -std=c11 -pedantic-errors하면 잘못된 C 코드 작성에 대한 컴파일러 오류가 발생합니다. 이유는 int (*)[3]의 변수에 a를 할당하려고하기 때문입니다.이 변수는 int**서로 전혀 관련이없는 두 가지 유형입니다. 이 예가 무엇을 증명해야할지 모르겠습니다.
Lundin

의견을 보내 주셔서 감사합니다. 많은 표준이 있다는 것을 알고 있습니다. 나는 편집에서 의미하는 바를 명확히하려고 노력했다. int **유형은 하나 더를 사용한다,이 점없는 void *이를 위해.
Palo

-3

배열 이름은 포인터처럼 동작하며 배열의 첫 번째 요소를 가리 킵니다. 예:

int a[]={1,2,3};
printf("%p\n",a);     //result is similar to 0x7fff6fe40bc0
printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0

두 인쇄 문 모두 기계에 대해 정확히 동일한 출력을 제공합니다. 내 시스템에서 그것은 주었다 :

0x7fff6fe40bc0

-4

배열은 메모리에있는 연속적이고 연속적인 요소의 모음입니다. C에서 배열의 이름은 첫 번째 요소의 색인이며 오프셋을 적용하면 나머지 요소에 액세스 할 수 있습니다. "제 1 요소에 대한 인덱스"는 실제로 메모리 방향에 대한 포인터이다.

포인터 변수와의 차이점은 배열의 이름이 가리키는 위치를 변경할 수 없으므로 const 포인터와 유사합니다 (유사하지 않습니다. 마크 주석 참조). 또한 포인터 aritmetic을 사용하는 경우 값을 얻기 위해 배열 이름을 역 참조 할 필요가 없습니다.

char array = "hello wordl";
char* ptr = array;

char c = array[2]; //array[2] holds the character 'l'
char *c1 = ptr[2]; //ptr[2] holds a memory direction that holds the character 'l'

답은 '예'입니다.


1
배열 이름은 const 포인터와 다릅니다. 주어진 : int a [10]; int * p = a; sizeof (p)와 sizeof (a)는 동일하지 않습니다.
Mark Bessey

1
다른 차이점이 있습니다. 일반적으로 C 표준에서 사용하는 용어 인 "변환"이라고하는 용어를 사용하는 것이 가장 좋습니다. 인용구 : "sizeof 연산자 나 단항 & 연산자의 피연산자이거나 배열을 초기화하는 데 사용되는 문자열 리터럴 인 경우를 제외하고 ''array of type ''형식의 식은 ' 배열 객체의 초기 요소를 가리키고 lvalue가 아닌 '포인터를 가리키는 포인터'입니다. 배열 객체에 레지스터 스토리지 클래스가 있으면 동작이 정의되지 않습니다. "
Pavel Minaev

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.