C에서 화살표 연산자 (->) 사용법


257

"21 일 안에 C를 가르치십시오"라는 책을 읽고 있습니다 (이미 Java와 C #을 배웠으므로 훨씬 빠른 속도로 움직이고 있습니다). 나는 포인터에 관한 장을 읽고 있었고 ->(화살표) 연산자 는 설명없이 나타났습니다. 멤버와 함수를 호출하는 데 사용된다고 생각합니다 .(점 연산자와 동일하지만 멤버 대신 포인터). 그러나 나는 완전히 확신하지 못한다.

설명과 코드 샘플을 얻을 수 있습니까?


90
더 나은 책을 얻으십시오. norvig.com/21-days.html
joshperry

9
qrdl은 정확합니다. "Y 일에 X 학습"책은 일반적으로 쓰레기입니다. K & R 외에도 Prata의 "C Primer Plus"를 추천합니다. K & R보다 깊이가 더 좋습니다.
J. Taylor

3
@Steve 그 질문은 C ++을 다룹니다. C와 관련이없는 다른 답변에서 연산자 오버로드에 대해 읽기 시작했을 때 혼동을 일으켰습니다.
Johann

1
@Belton 어려운 길 시리즈는 좋지 않습니다. 그 사람은 책을 쓸 때도 관련이 없었으며 좋은 관행에 관심이 없다고 말합니다.
Bálint 2016 년

1
Peter Norvig의 "10 년 안에 자신에게 프로그래밍을 가르치십시오"링크는 제가 가장 좋아하는 것 중 하나입니다. 다음은 21 일 동안이 작업을 수행하는 방법을 설명하는 만화 버전입니다. 불행히도 XKCD로 기억되었지만 잘못되었습니다 : abstrusegoose.com/249
Ted

답변:


462

foo->bar와 같습니다 (*foo).bar, 그것은라는 멤버 얻을 즉 bar그 구조체에서 foo포인트를.


51
파스칼에서와 같이 역 참조 연산자를 접미사로 만들었 ->으면 훨씬 더 읽기 쉬운 연산자가 필요하지 않았을 것 foo*.bar입니다. 모든 추가 괄호가있는 타입 정의 함수의 혼란은 피할 수있었습니다.
Lorne의 후작

1
그래서 것입니다 foo*.bar(*foo).bar동등 모두 foo->bar? 무엇에 대해 Foo myFoo = *foo; myFoo.bar?
Aaron Franke

9
아니, 그는 단지 말하는 경우 C의 창조자 대신 접두사의 후위 연산자로 참조 연산자 다음 더 쉬웠을 것입니다 만들었을 것입니다. 그러나 그것은 C의 접두사 연산자입니다.
reichhart

@ user207421 당신이 언급 한 "추가 괄호와 함께 타입 정의 함수"에 대한 간단한 설명이나 링크를 제공해 주시겠습니까? 감사.
RoG

1
@ user207421 nah, 그것은 더 많은 부모를 유발할 것입니다. 지금까지, 왼쪽의 오른쪽 위의 *에 우선 순위가 있습니다. 그들이 모두 한쪽에 있다면 더 많은 부모를 두게 될 것입니다. 곱셈 연산자와의 충돌로 인해 표현식에서 동일합니다. Pascal ^는 옵션이 될 수 있지만 비트 작업을 위해 예약되었지만 여전히 더 많은 부모가 있습니다.
스위프트-금요일 파이

129

그래 그거야.

참조 대신 포인터 인 구조체 / 클래스의 요소에 액세스하려는 경우 도트 버전 일뿐입니다.

struct foo
{
  int x;
  float y;
};

struct foo var;
struct foo* pvar;
pvar = malloc(sizeof(pvar));

var.x = 5;
(&var)->y = 14.3;
pvar->y = 22.4;
(*pvar).x = 6;

그게 다야!


3
pvar은 초기화되지 않았으므로 pvar이 새로운 구조체를 가리 키도록하려면 어떻게 초기화 pvar = &var합니까?
CMCDragonkai

문제는 구체적으로 클래스 또는 참조 변수가없는 C에 관한 것입니다.
Oak

1
흠 pvar struct foo * pvar에 쓰기 전에 malloc을하지 말아야합니다. ?? 할당되지 않은 공간에 pvar-> y 쓰기!
지브리

pvar 초기화 : 모든 멤버를 원하는 기본값으로 수동으로 초기화하거나 0으로 채워도 괜찮다면 calloc ()와 같은 것을 사용하십시오.
reichhart

2
안됩니다 : pvar = malloc (sizeof (struct foo)) 또는 malloc (sizeof (* pvar)) ??
유리 Aps

33

a->b(*a).b모든면에서 짧습니다 (함수와 동일 : a->b()는 짧음 (*a).b()).


1
방법에 대해서도 작동한다는 문서가 있습니까?
AsheKetchum

28

나는 왜 "왜?"라는 답에 덧붙일 것입니다.

. 보다 높은 우선 순위를 갖는 표준 멤버 액세스 연산자입니다. *포인터 연산자 .

구조체의 내부에 액세스하려고 할 때 *foo.bar컴파일러는 foo의 'bar'요소 (메모리의 주소)를 원한다고 생각하며 분명히 주소에는 멤버가 없습니다.

따라서 당신은 아 파크 첫번째 역 참조로 컴파일러 요청해야 (*foo): 다음 멤버 요소에 액세스 (*foo).bar: 좋은의 사람들이 속기 버전으로 올라와있다, 그래서 쓰기에 조금 서투른는 foo->bar일종의 포인터 운영자가 멤버 액세스입니다.



10
struct Node {
    int i;
    int j;
};
struct Node a, *p = &a;

다음은이 값에 액세스 할 수 ij우리는 변수에 사용할 수있는 a포인터를 p다음과 같이 a.i, (*p).i그리고 p->i모두 동일합니다.

다음 .은 "직접 선택기"이며 ->"간접 선택기"입니다.


2

글쎄, 나는 또한 무언가를 추가해야합니다. 배열은 포인터이고 구조는 아니기 때문에 구조는 배열과 약간 다릅니다. 그러니 조심해!

이 쓸모없는 코드를 작성한다고 가정 해 보겠습니다.

#include <stdio.h>

typedef struct{
        int km;
        int kph;
        int kg;
    } car;

int main(void){

    car audi = {12000, 230, 760};
    car *ptr = &audi;

}

여기서 포인터 는 구조 변수 ptr의 주소 ( ! )를 가리 키지 audi만 주소 구조 옆에도 데이터 덩어리 ( ! )가 있습니다! 데이터 청크의 첫 번째 멤버는 구조 자체와 주소가 동일하며 이와 같은 포인터 만 참조하여 데이터를 가져올 수 있습니다 *ptr (중괄호 없음) .

첫 번째보다 다른 멤버를 ACESS하려면, 당신은 같은 부호를 추가 할 필요가 .km, .kph, .kg더의 기본 주소에 오프셋보다 더 아무것도없는 데이터의 덩어리 ...

그러나의 때문에 preceedence 당신은 쓸 수 없습니다 *ptr.kg액세스 연산자 .참조 연산자 전에 평가 *하고 당신은 얻을 것 *(ptr.kg)포인터가 회원이 없습니다으로 불가능하다! 그리고 컴파일러는 이것을 알고 있으므로 다음과 같은 오류를 발생시킵니다.

error: ptr is a pointer; did you mean to use ‘->’?
  printf("%d\n", *ptr.km);

대신 당신이 이것을 사용 (*ptr).kg하면에 컴파일러를 강제로 1 포인터 역 참조와에 ACESS 수 있도록 데이터의 덩어리2 멤버를 선택하면 오프셋 (지정자)를 추가합니다.

내가 만든이 이미지를 확인하십시오.

여기에 이미지 설명을 입력하십시오

중첩 된 멤버있을 것입니다하지만이 구문은 읽을 수 있으므로 될 것입니다 ->도입되었다. 나는 가독성이 ptr->kg쓰기보다 훨씬 쉽기 때문에 그것을 사용할 수있는 유일한 이유라고 생각 (*ptr).kg합니다.

이제 연결을보다 명확하게 볼 수 있도록 다르게 작성하겠습니다. (*ptr).kg(*&audi).kgaudi.kg. 여기에 내가 먼저 실제로 사용 ptr이다 "주소 audi" , 즉 &audi것을 사실 "참조" &"역 참조" * 사업자가 서로 아웃을 취소합니다.


1

Jack의 프로그램을 실행하려면 약간 변경해야했습니다. 구조체 포인터 pvar을 선언 한 후 var의 주소를 가리 킵니다. C에서 Stephen Kochan 's Programming의 242 페이지 에서이 솔루션을 찾았습니다.

#include <stdio.h>

int main()
{
  struct foo
  {
    int x;
    float y;
  };

  struct foo var;
  struct foo* pvar;
  pvar = &var;

  var.x = 5;
  (&var)->y = 14.3;
  printf("%i - %.02f\n", var.x, (&var)->y);
  pvar->x = 6;
  pvar->y = 22.4;
  printf("%i - %.02f\n", pvar->x, pvar->y);
  return 0;
}

다음 명령으로 vim에서이를 실행하십시오.

:!gcc -o var var.c && ./var

출력합니다 :

5 - 14.30
6 - 22.40

3
vim tip : %현재 파일 이름을 나타내는 데 사용 하십시오. 그래서처럼!gcc % && ./a.out
jibberia

1
#include<stdio.h>

int main()
{
    struct foo
    {
        int x;
        float y;
    } var1;
    struct foo var;
    struct foo* pvar;

    pvar = &var1;
    /* if pvar = &var; it directly 
       takes values stored in var, and if give  
       new > values like pvar->x = 6; pvar->y = 22.4; 
       it modifies the values of var  
       object..so better to give new reference. */
    var.x = 5;
    (&var)->y = 14.3;
    printf("%i - %.02f\n", var.x, (&var)->y);

    pvar->x = 6;
    pvar->y = 22.4;
    printf("%i - %.02f\n", pvar->x, pvar->y);

    return 0;
}

1

->운영자는보다 코드를 더 쉽게 읽을 *일부 상황에서 운영자입니다.

예 : EDK II 프로젝트 에서 인용 )

typedef
EFI_STATUS
(EFIAPI *EFI_BLOCK_READ)(
  IN EFI_BLOCK_IO_PROTOCOL          *This,
  IN UINT32                         MediaId,
  IN EFI_LBA                        Lba,
  IN UINTN                          BufferSize,
  OUT VOID                          *Buffer
  );


struct _EFI_BLOCK_IO_PROTOCOL {
  ///
  /// The revision to which the block IO interface adheres. All future
  /// revisions must be backwards compatible. If a future version is not
  /// back wards compatible, it is not the same GUID.
  ///
  UINT64              Revision;
  ///
  /// Pointer to the EFI_BLOCK_IO_MEDIA data for this device.
  ///
  EFI_BLOCK_IO_MEDIA  *Media;

  EFI_BLOCK_RESET     Reset;
  EFI_BLOCK_READ      ReadBlocks;
  EFI_BLOCK_WRITE     WriteBlocks;
  EFI_BLOCK_FLUSH     FlushBlocks;

};

_EFI_BLOCK_IO_PROTOCOL구조체는 4 명 함수 포인터 멤버가 포함되어 있습니다.

variable이 struct _EFI_BLOCK_IO_PROTOCOL * pStruct있고 좋은 이전 *연산자를 사용하여 멤버 함수 포인터를 호출 한다고 가정하십시오 . 다음과 같은 코드로 끝납니다.

(*pStruct).ReadBlocks(...arguments...)

그러나 ->연산자를 사용하면 다음과 같이 작성할 수 있습니다.

pStruct->ReadBlocks(...arguments...).

어느 것이 더 좋아 보입니까?


1
90 년대의 AOL 채팅에서 십대가 입력 한 것과 같이 모든 대문자가 아닌 코드는 더 읽기 쉽습니다.
thang

1
#include<stdio.h>
struct examp{
int number;
};
struct examp a,*b=&a;`enter code here`
main()
{
a.number=5;
/* a.number,b->number,(*b).number produces same output. b->number is mostly used in linked list*/
   printf("%d \n %d \n %d",a.number,b->number,(*b).number);
}

출력은 5 5 5


0

도트는 역 참조 연산자이며 특정 구조 레코드에 대한 구조 변수를 연결하는 데 사용됩니다. 예 :

struct student
    {
      int s.no;
      Char name [];
      int age;
    } s1,s2;

main()
    {
      s1.name;
      s2.name;
    }

이런 식으로 점 연산자를 사용하여 구조 변수에 액세스 할 수 있습니다


6
이것이 어떤 가치를 더합니까? 이 예제는 실제로 다른 답변과 비교하여 약간 열악합니다 ->. 또한이 질문은 4.5 년 동안 이미 답변되었습니다.
EWit
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.