C의 포인터 : 앰퍼샌드와 별표를 언제 사용해야합니까?


298

포인터로 시작하고 약간 혼란 스럽습니다. 나는 &변수의 주소를 의미 *하며 포인터 변수가 가리키는 포인터의 값을 얻기 위해 포인터 변수 앞에 사용될 수 있습니다. 그러나 배열, 문자열로 작업하거나 변수의 포인터 복사본으로 함수를 호출 할 때 상황이 다르게 작동합니다. 이 모든 것 안에 논리 패턴을보기가 어렵습니다.

언제 사용해야 &하고 *?


5
때때로 다르게 작동하는 모습을 설명하십시오. 그렇지 않으면 혼란스러운 것이 무엇인지 추측해야합니다.
Drew Dormann

1
닐 버터 워스에 동의하십시오. 아마도 책에서 직접 정보를 얻는 데 훨씬 더 많은 정보를 얻을 수 있으며 K & R 설명은 분명합니다.
Tom

144
SO에 대해 이러한 유형의 질문을하는 것은 좋지 않다고 말하는 모든 여러분의 의견에 동의하지 않습니다. Google에서 검색 할 때 SO가 최고의 리소스가되었습니다. 당신은이 답변에 충분한 신용을주지 않습니다. Dan Olson의 답변을 읽으십시오. 이 답변은 참신한 통찰력과 초보자에게 매우 도움이됩니다. RTFM도움이되지 않으며 솔직히 매우 무례합니다. 대답 할 시간이 없다면 시간을내어 이러한 질문에 대답하는 사람들을 존중하십시오. 나는 이것을 @ "anon"으로 할 수 있기를 바라지 만 분명히 그는 그녀가 의미있는 방식으로 기여할 시간이 없다.
SSH 이번

18
무엇 SSH이 말했다 절대적으로 사실입니다. 어떤 사람들은 "Just it it"이라고 외치지 만 요즘에는 "StackOverflow를 살펴보십시오." 이 질문은 많은 사람들에게 유용합니다. (따라서 upvotes와 downvotes는 없습니다.)
MC Emperor

답변:


610

포인터와 값이 있습니다 :

int* p; // variable p is pointer to integer type
int i; // integer value

다음을 사용하여 포인터를 값으로 바꿉니다 *.

int i2 = *p; // integer i2 is assigned with integer value that pointer p is pointing to

다음을 사용하여 값을 포인터로 바꿉니다 &.

int* p2 = &i; // pointer p2 will point to the address of integer i

편집 : 배열의 경우 포인터와 매우 유사하게 취급됩니다. 그것들을 포인터로 생각하면 *위에서 설명한대로 그 안의 값을 얻는 데 사용 하지만 []연산자를 사용하는 또 다른 더 일반적인 방법도 있습니다 .

int a[2];  // array of integers
int i = *a; // the value of the first element of a
int i2 = a[0]; // another way to get the first element

두 번째 요소를 얻으려면 :

int a[2]; // array
int i = *(a + 1); // the value of the second element
int i2 = a[1]; // the value of the second element

따라서 []인덱싱 연산자는 특수한 형식의 *연산자이며 다음과 같이 작동합니다.

a[i] == *(a + i);  // these two statements are the same thing

2
어떻게 작동하지 않습니까? int aX[] = {3, 4}; int *bX = &aX;
Pieter

5
배열은 특별하며 포인터로 투명하게 변환 할 수 있습니다. 이것은 포인터에서 값을 얻는 또 다른 방법을 강조합니다. 위의 설명에 추가하겠습니다.
Dan Olson

4
내가 이것을 올바르게 이해하면 ...이 이미 ( ) 주소를 반환 int *bX = &aX;하기 때문에 예제 가 작동하지 않으므로 의미가없는 주소의 주소를 얻습니다. 이 올바른지? aXaX[0]&aX[0]&aX
Pieter

6
실제로 주소의 주소를 원하는 경우가 있지만 정확합니다. 이 경우 int ** bX = & aX로 선언하지만보다 고급 주제입니다.
Dan Olson

3
@Dan는 주어진 int aX[] = {3,4};, int **bX = &aX;에러이다. "포인터를 가리키는 포인터"가 아니라 " &aX의 배열을 가리키는 포인터 [2] "입니다. 특히, 배열의 이름은 unary의 첫 번째 요소에 대한 포인터로 취급되지 않습니다 . 당신은 할 수 있습니다 :intint&int (*bX)[2] = &aX;
알록 Singhal이

28

배열과 함수를 다룰 때 패턴이 있습니다. 처음에는 조금보기 힘들다.

배열을 다룰 때 다음을 기억하는 것이 유용합니다. 대부분의 상황에서 배열식이 나타날 때 식의 유형은 암시 적으로 "T의 N- 요소 배열"에서 "포인터에서 T"로 변환되고 그 값이 설정됩니다 배열의 첫 번째 요소를 가리 킵니다. 이 규칙의 예외는 배열 표현식이 &또는 sizeof연산자 의 피연산자로 표시되거나 선언에서 초기화 자로 사용되는 문자열 리터럴 인 경우입니다.

따라서 배열 식을 인수로 사용하여 함수를 호출하면 함수가 배열이 아닌 포인터를받습니다.

int arr[10];
...
foo(arr);
...

void foo(int *arr) { ... }

따라서 "% s"에 해당하는 인수에 연산자를 사용 하지 않는 이유는 다음 &scanf()같습니다.

char str[STRING_LENGTH];
...
scanf("%s", str);

암시 적 변환 으로 인해 배열 의 시작을 가리키는 값을 scanf()받습니다 . 이는 배열 표현식을 인수로 호출 한 함수 ( 함수 및 함수 등)에 해당됩니다. char *strstr**scanf*printf

실제로 다음과 같이 &연산자를 사용하여 배열 표현식으로 함수를 호출하지 않을 것입니다 .

int arr[N];
...
foo(&arr);

void foo(int (*p)[N]) {...}

이러한 코드는 흔하지 않습니다. 함수 선언에서 배열의 크기를 알아야하며 함수는 특정 크기의 배열에 대한 포인터에서만 작동합니다 (T의 10 요소 배열에 대한 포인터는 11 요소 배열에 대한 포인터와 다른 유형입니다) T).

배열 표현식이 &연산자 의 피연산자로 나타나는 경우 결과 표현식의 유형은 "T의 N- 요소 배열에 대한 포인터"또는 T (*)[N]이며, 이는 포인터 배열 ( T *[N]) 및 기본 유형에 대한 포인터 ( T *).

함수와 포인터를 다룰 때 기억해야 할 규칙은 다음과 같습니다. 인수 값을 변경하고 호출 코드에 반영하려면 수정하려는 항목에 포인터를 전달해야합니다. 다시 말하지만, 배열은 약간의 멍키 렌치를 작동에 넣지 만 일반적인 경우를 먼저 처리합니다.

C는 모든 함수 인수를 값으로 전달 합니다 . 공식 매개 변수는 실제 매개 변수의 값 사본을 수신하며 공식 매개 변수의 변경 사항은 실제 매개 변수에 반영되지 않습니다. 일반적인 예는 스왑 함수입니다.

void swap(int x, int y) { int tmp = x; x = y; y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);

다음과 같은 결과가 나타납니다.

스왑 전 : a = 1, b = 2
스왑 후 : a = 1, b = 2

형식 매개 변수 x와는 y별개의 개체 ab변경 있도록, x그리고이 y에 반영되지 않습니다 ab. aand 의 값을 수정하고자하므로 스왑 함수에 대한 포인터b전달해야합니다 .

void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);

이제 출력은

스왑 전 : a = 1, b = 2
스왑 후 : a = 2, b = 1

swap 함수에서 xand 의 값을 변경하지 않고 ywhat xy point 의 값을 변경합니다 . 글을 *x쓰는 것은 글을 쓰는 것과 다릅니다 x. 우리는 값 x자체를 업데이트하지 않고 위치를 가져와 해당 위치 x의 값을 업데이트합니다.

포인터 값을 수정하려는 경우에도 마찬가지입니다. 우리가 쓰면

int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);

우리는 입력 매개 변수의 값을 수정하고 stream,하지 무엇을 stream 가리키는 때문에 변경 stream의 값에 영향을주지 않습니다 in; 이것이 작동하려면 포인터에 대한 포인터를 전달해야합니다.

int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);

다시 말하지만, 배열은 약간의 원숭이 렌치를 작품에 넣습니다. 배열 표현식을 함수에 전달할 때 함수가받는 것은 포인터입니다. 배열 첨자 정의 방법에 따라 배열에서 첨자 연산자를 사용하는 것과 같은 방식으로 포인터에 첨자 연산자를 사용할 수 있습니다.

int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}

배열 객체는 할당되지 않을 수 있습니다. 즉, 당신은 같은 것을 할 수 없습니다

int a[10], b[10];
...
a = b;

따라서 배열에 대한 포인터를 다룰 때주의해야합니다. 같은

void (int (*foo)[N])
{
  ...
  *foo = ...;
}

작동하지 않습니다.


16

간단히 말해서

  • &address-of를 의미하므로 C에서와 같이 매개 변수를 수정하는 함수의 자리 표시 자에서 매개 변수는 참조로 전달하는 앰퍼샌드 수단을 사용하여 값으로 전달됩니다.
  • *는 포인터 변수 의 역 참조 를 의미하며, 이는 해당 포인터 변수의 값을 얻는 것을 의미합니다.
int foo(int *x){
   *x++;
}

int main(int argc, char **argv){
   int y = 5;
   foo(&y);  // Now y is incremented and in scope here
   printf("value of y = %d\n", y); // output is 6
   /* ... */
}

위의 예제는 foopass-by-reference를 사용하여 함수를 호출하는 방법을 보여줍니다.

int foo(int x){
   x++;
}

int main(int argc, char **argv){
   int y = 5;
   foo(y);  // Now y is still 5
   printf("value of y = %d\n", y); // output is 5
   /* ... */
}

역 참조 를 사용하는 예는 다음과 같습니다.

int main(int argc, char **argv){
   int y = 5;
   int *p = NULL;
   p = &y;
   printf("value of *p = %d\n", *p); // output is 5
}

위는 주소 y 를 가져 와서 포인터 변수에 할당 한 방법을 보여줍니다 p. 다음 우리는 역 참조 p 첨부하여 *값을 얻기 위해 그 전면 p, 즉 *p.


10

*C / C ++에서 여러 가지 다른 용도로 사용 되므로 상당히 복잡 할 수 있습니다 .

경우 *이미 선언 된 변수 / 함수 앞에 표시, 그것도 것을 의미한다 :

  • a) *해당 변수의 값에 액세스 할 수 있습니다 (해당 변수의 유형이 포인터 유형이거나 *연산자에 과부하가 걸린 경우 ).
  • b) *곱하기 연산자의 의미를 가지며,이 경우 왼쪽에 다른 변수가 있어야합니다.*

경우 *변수 또는 함수 선언 나타난다는 그 변수 포인터 것을 의미

int int_value = 1;
int * int_ptr; //can point to another int variable
int   int_array1[10]; //can contain up to 10 int values, basically int_array1 is an pointer as well which points to the first int of the array
//int   int_array2[]; //illegal, without initializer list..
int int_array3[] = {1,2,3,4,5};  // these two
int int_array4[5] = {1,2,3,4,5}; // are identical

void func_takes_int_ptr1(int *int_ptr){} // these two are identical
void func_takes int_ptr2(int int_ptr[]){}// and legal

경우 &변수 또는 함수 선언에 나타나는, 일반적으로 그 변수는 그 유형의 변수에 대한 참조임을 의미한다.

경우 &이미 선언 된 변수의 앞에 나타납니다, 그 변수의 주소를 반환

또한 배열을 함수에 전달할 때 배열이 0으로 끝나는 cstring (char 배열)과 같은 경우를 제외하고는 항상 해당 배열의 배열 크기도 전달해야합니다.


1
@akmozo s / func_takes int_ptr2 / func_takes_int_ptr2 / (잘못된 공간)
PixnBits

4

포인터 변수 또는 함수 매개 변수를 선언 할 때는 *를 사용하십시오.

int *x = NULL;
int *y = malloc(sizeof(int)), *z = NULL;
int* f(int *x) {
    ...
}

주의 : 선언 된 각 변수에는 고유 한 *가 필요합니다.

값의 주소를 가져 오려면 &를 사용하십시오. 포인터로 값을 읽거나 쓰려면 *를 사용하십시오.

int a;
int *b;
b = f(&a);
a = *b;

a = *f(&a);

배열은 보통 포인터처럼 취급됩니다. 함수에서 배열 매개 변수를 선언하면 포인터임을 쉽게 선언 할 수 있습니다 (같은 의미). 배열을 함수에 전달하면 실제로 첫 번째 요소에 포인터를 전달합니다.

함수 포인터는 규칙을 따르지 않는 유일한 것입니다. &를 사용하지 않고 함수의 주소를 사용할 수 있으며 *를 사용하지 않고 함수 포인터를 호출 할 수 있습니다.


4

내가 그렇게 대신 rescue.Here에 대한 뉴 사우스 웨일즈 대학에서 비디오로 전환하는 모든 말의 설명을 통해보고 된 간단한 설명은 다음과 같습니다 우리는 주소가있는 셀이있는 경우 x와 가치를 7값의 주소를 요청하는 간접 방법 7입니다 &7및 간접 방법은 주소 값을 요청하는 x것입니다 *x낭포 (cell: x , value: 7) == (cell: &7 , value: *x)그것으로 보는 .Another 방법은 : John에 앉아 7th seat국지적 *7th seat를 가리 킵니다 John&John줄 것이다 address/의 위치를 7th seat. 이 간단한 설명은 저에게 도움이되었고 다른 사람들에게도 도움이 되길 바랍니다. 훌륭한 비디오에 대한 링크는 다음과 같습니다 . 여기를 클릭하십시오.

또 다른 예는 다음과 같습니다.

#include <stdio.h>

int main()
{ 
    int x;            /* A normal integer*/
    int *p;           /* A pointer to an integer ("*p" is an integer, so p
                       must be a pointer to an integer) */

    p = &x;           /* Read it, "assign the address of x to p" */
    scanf( "%d", &x );          /* Put a value in x, we could also use p here */
    printf( "%d\n", *p ); /* Note the use of the * to get the value */
    getchar();
}

애드온 : 포인터를 사용하기 전에 항상 초기화 하십시오. 그렇지 않으면 포인터가 무엇이든 가리켜 서 운영 체제에서 소유하지 않은 메모리에 액세스하지 못하게되므로 프로그램이 중단 될 수 있습니다. p = &x;, 포인터에 특정 위치를 할당합니다.


3

실제로, 당신은 그것을 가볍게 두드려서 더 이상 알아야 할 것이 없습니다 :-)

다음 비트를 추가합니다.

  • 두 동작은 스펙트럼의 반대쪽 끝입니다. &변수를 *가져와 주소를 제공 하고 주소를 가져와 변수 (또는 내용)를 제공합니다.
  • 함수에 전달할 때 포인터를 "degrade"합니다.
  • 실제로 간접적으로 여러 레벨을 가질 수 있습니다 ( char **p즉, p에 대한 포인터 임을 의미합니다 char.

다르게 작동하는 것에 대해서는 실제로는 아닙니다.

  • 이미 언급했듯이 배열은 함수에 전달 될 때 포인터 (배열의 첫 번째 요소)로 저하됩니다. 그들은 크기 정보를 보존하지 않습니다.
  • C에는 문자열이 없으며 일반적으로 문자 배열은 일반적으로 영 ( \0) 문자로 끝나는 문자열을 나타냅니다 .
  • 변수의 주소를 함수에 전달하면 포인터 자체를 역 참조하여 변수 자체를 변경할 수 있습니다 (일반적으로 변수는 값을 기준으로 전달됩니다 (배열 제외)).

3

나는 당신이 약간 혼란스러워 생각합니다. 포인터에 대한 좋은 자습서 / 책을 읽어야합니다.

튜토리얼은 초보자에게 매우 유용합니다 (명확한 내용 &과 설명 *). 그리고 Kenneth Reek의 C에서 Pointers in C 책을 읽는 것을 잊지 마십시오 .

의 차이 &와는 *매우 분명하다.

예:

#include <stdio.h>

int main(){
  int x, *p;

  p = &x;         /* initialise pointer(take the address of x) */
  *p = 0;         /* set x to zero */
  printf("x is %d\n", x);
  printf("*p is %d\n", *p);

  *p += 1;        /* increment what p points to i.e x */
  printf("x is %d\n", x);

  (*p)++;         /* increment what p points to i.e x */
  printf("x is %d\n", x);

  return 0;
}

1

좋아요, 게시물이 수정 된 것 같습니다 ...

double foo[4];
double *bar_1 = &foo[0];

를 사용 &하여 배열 구조의 시작 주소를 얻는 방법을 참조하십시오 . 다음과 같은

Foo_1(double *bar, int size){ return bar[size-1]; }
Foo_2(double bar[], int size){ return bar[size-1]; }

같은 일을 할 것입니다.


질문은 C ++가 아닌 C로 태그되었습니다.
Prasoon Saurav

1
그리고 나는 불쾌한 cout을 제거했습니다 <<
wheaties
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.