배열의 이름이 C의 포인터입니까? 그렇지 않은 경우 배열의 이름과 포인터 변수의 차이점은 무엇입니까?
&array[0]
배열이 아닌 포인터를 생성한다;)
배열의 이름이 C의 포인터입니까? 그렇지 않은 경우 배열의 이름과 포인터 변수의 차이점은 무엇입니까?
&array[0]
배열이 아닌 포인터를 생성한다;)
답변:
배열은 배열이고 포인터는 포인터이지만 대부분의 경우 배열 이름은 포인터 로 변환 됩니다. 자주 사용되는 용어는 포인터로 쇠퇴 한다는 것 입니다.
배열은 다음과 같습니다.
int a[7];
a
7 개의 정수를위한 공간을 포함하며, 다음과 같이 대입으로 값 중 하나에 값을 넣을 수 있습니다.
a[3] = 9;
다음은 포인터입니다.
int *p;
p
정수를위한 공백을 포함하지 않지만 정수를위한 공백을 가리킬 수 있습니다. 예를 들어, 배열 a
에서 첫 번째 장소 와 같은 장소 중 하나를 가리 키도록 설정할 수 있습니다 .
p = &a[0];
혼란스러운 점은 다음과 같이 쓸 수도 있다는 것입니다.
p = a;
이것은 배열의 내용을 포인터에 복사 하지 않습니다 (의미 한 것은 무엇이든). 대신 배열 이름 이 첫 번째 요소에 대한 포인터로 변환됩니다. 할당은 이전과 동일합니다.a
p
a
이제 p
배열과 비슷한 방식으로 사용할 수 있습니다 .
p[3] = 17;
이것이 작동하는 이유는 C의 배열 역 참조 연산자 [ ]
가 포인터로 정의되기 때문입니다. x[y]
수단 : 포인터로 시작 x
, 단계 y
에 어떤 포인터 포인트 후 앞으로 요소를, 다음이 무엇이든 걸릴. 포인터 산술 구문을 사용하여로 x[y]
도 쓸 수 있습니다 *(x+y)
.
이것이 우리와 같은 일반 배열과 함께 작동 하려면 먼저 in a
의 이름 a
을 a[3]
포인터로 변환해야합니다 (의 첫 번째 요소로 a
). 그런 다음 3 단계 요소를 앞으로 진행하고 모든 요소를 가져옵니다. 다시 말해, 배열에서 3 번째 위치에있는 요소를 가져옵니다. (첫 번째 요소의 번호는 0이므로 배열의 네 번째 요소는 어느 것입니까?)
따라서 요약하면 C 프로그램의 배열 이름은 (대부분의 경우) 포인터로 변환됩니다. 예외는 sizeof
배열 에서 연산자를 사용할 때 입니다. a
이 문맥에서 포인터로 변환 된 경우 sizeof a
실제 배열이 아닌 포인터의 크기를 지정하면 쓸모가 없으므로 a
배열 자체를 의미합니다.
functionpointer()
하고 (*functionpointer)()
이상하게도, 같은 일을 의미한다.
sizeof()
배열-> 포인터 붕괴가없는 다른 컨텍스트는 연산자입니다 &
-위의 예제 에서 단일 &a
배열에 int
대한 포인터가 아닌 7 배열에 대한 포인터가됩니다 int
. 즉, 형식은로 int(*)[7]
묵시적으로 변환 할 수 없습니다 int*
. 이런 식으로 함수는 실제로 특정 크기의 배열에 대한 포인터를 가져 와서 형식 시스템을 통해 제한을 적용 할 수 있습니다.
배열을 값으로 사용하면 해당 이름은 첫 번째 요소의 주소를 나타냅니다.
배열을 값으로 사용하지 않으면 이름이 전체 배열을 나타냅니다.
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 */
배열 유형의 표현식 (예 : 배열 이름)이 더 큰 표현식에 나타나고 &
또는 의 피연산자가 아닌 경우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 종결 자)을 사용하거나 요소 수를 별도의 매개 변수로 전달해야합니다.
sizeof
는 연산자이며 피연산자 의 숫자 바이트 (객체를 나타내는 표현식 또는 괄호 안의 유형 이름)로 평가됩니다. 따라서 배열 sizeof
의 경우 요소 수에 단일 요소의 바이트 수를 곱한 값으로 평가됩니다. 의 int
너비가 4 바이트 인 경우 5 요소 배열은 int
20 바이트 를 차지합니다.
[ ]
특별하지 않습니까? 예를 들어, int a[2][3];
에 대해 x = a[1][2];
로 다시 작성할 수는 있지만 x = *( *(a+1) + 2 );
여기서는 a
포인터 유형으로 변환되지 않습니다 int*
( a
함수의 인수 인 경우 로 변환되어야 함 int*
).
a
type은 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
. 그것이 기계 코드에 매핑되는 방법은 전적으로 컴파일러에 달려 있습니다.
이와 같이 선언 된 배열
int a[10];
10 int
초 동안 메모리를 할당합니다 . 당신은 수정할 수 a
없지만 포인터 산술을 할 수 있습니다a
.
이와 같은 포인터는 포인터에 대해서만 메모리를 할당합니다 p
.
int *p;
int
s를 할당하지 않습니다 . 수정할 수 있습니다 :
p = a;
다음과 같이 배열 첨자를 사용하십시오.
p[2] = 5;
a[2] = 5; // same
*(p+2) = 5; // same effect
*(a+2) = 5; // same effect
int
자동 스토리지 duration`에들.
배열 이름 자체는 메모리 위치를 생성하므로 배열 이름을 포인터처럼 취급 할 수 있습니다.
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]);
이 예제는이 문제에 대해 약간의 지적을합니다.
#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;
}
비록 c
와 a
같은 메모리에 "포인트", 당신의 주소를 얻을 수 있습니다 c
포인터를, 그러나 당신은의 주소를 얻을 수 없습니다 a
포인터를.
-std=c11 -pedantic-errors
하면 잘못된 C 코드 작성에 대한 컴파일러 오류가 발생합니다. 이유는 int (*)[3]
의 변수에 a를 할당하려고하기 때문입니다.이 변수는 int**
서로 전혀 관련이없는 두 가지 유형입니다. 이 예가 무엇을 증명해야할지 모르겠습니다.
int **
유형은 하나 더를 사용한다,이 점없는 void *
이를 위해.
배열은 메모리에있는 연속적이고 연속적인 요소의 모음입니다. 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'
답은 '예'입니다.
배열 이름은 배열의 첫 번째 요소의 주소입니다. 예, 배열 이름은 const 포인터입니다.