JINTBEAT Design Life

C언어 - Struct와 -> 연산자 본문

🖥️ - C language

C언어 - Struct와 -> 연산자

jintbeat_design 2025. 5. 9. 02:21
반응형

1.  struct란 ?

struct는 여러개의 다른 타입의 변수들을 하나의 그룹(구조체)로 묶어주는 사용자 정의 자료형이다.

주로 관련된 데이터를 하나로 묶어서 관리할 때 사용한다.

 

예시) 

struct Point {
    int x;
    int y;
};

 

이렇게 정의하면 Point라는 구조체를 선언한 것.

 

struct Point p1;
p1.x = 10;
p1.y = 20;

 

2. -> 연산자란 ?

->는 구조체 포인터를 통해 구조체 멤버에 접근할 때 사용하는 간접 멤버 접근 연산자이다.

 

struct Point {
    int x;
    int y;
};

struct Point p = {10, 20};
struct Point *ptr = &p;

printf("%d\n", ptr->x);  // p.x에 접근

 

여기서 ptr->x 는 사실 (*ptr).x와 같은 뜻이지만, ->를 쓰는 게 훨씬 간편하고 직관적이다.

 

3. FW 코드에서 -> 많이 쓰나 ? 

펌웨어 코드에서는 하드웨어 레지스터 맵 구조체에 접근하거나, 드라이버 코드에서 동적 메모리로 할당된 구조체 포인터를 다룰 때

-> 는 거의 필수적으로 사용된다.

 

typedef struct {
    uint32_t CTRL;
    uint32_t STATUS;
} UART_TypeDef;

 

- unt32_t가 뭐지 ?

uint32_t는 C에서 사용하는 정수형 데이터 타입이고, 의미는 다음과 같다.

  • u → unsigned (부호 없음)
  • int → 정수
  • 32 → 32비트
  • _t → 타입(type)이라는 뜻의 접미사

즉, 32비트 크기의 부호 없는 정수형이다.

범위는 **0 ~ 4,294,967,295 (2³² - 1)**까지.

 

- 왜 uint32_t 를 쓰는가?

int, long 같은 기본 타입은 컴파일러나 플랫폼(32비트 vs 64비트)에 따라 크기가 다를 수 있다.

하지만 uint32_t는 항상 32비트로 고정되어 있어서, 하드웨어 제어, 펌웨어, 네트워크, 파일 포맷 등 정확한 크기가 중요한 상황에서 주로 사용해.

 

정의 위치는  <stdint.h> 헤더 파일에 정의돼 있다.

 

- 이 구조체는 UART 하드웨어 블록의 제어 레지스터(CTRL)와 상태 레지스터(STATUS)를 나타냄

- typedef를 통해 구조체 이름을 간단하게 UART_TypeDef로 쓸 수 있게 해줌


#define UART0 ((UART_TypeDef *)0x40004000)

 

- UART0은 주소 0x40004000에 위치한 UART 하드웨어 블록을 가리킴.

- UART_TypeDef *로 캐스팅해서, 구조체 멤버 접근이 가능하도록 함.

- 즉 이 주소는 하드웨어의 레지스터가 있는 실제 메모리 주소이다.

UART0->CTRL = 0x01;  // UART0의 제어 레지스터에 접근

- 구조체 포인터 UART0을 통해 CTRL 레지스터에 0x01을 씀.

- 이는 실제로는 주소 0x40004000에 4바이트 쓰기 연산이 수행되는 것과 같다.

- 왜 4byte냐면... CTRL이 uint32_t로 정의되어 있으니까이다.

- 즉, 하드웨어 제어 명령이 직접 내려가는 코드이다. 

 

- MCU나 SoC에서는 하드웨어 블록(UART, SPI 등)의 레지스터들이 특정 주소에 배치되어 있다.

- 예를 들어, UART0 컨트롤러의 레지스터는 0x40004000부터 시작할 수 있다.

- (UART_TypeDef *) : 형 변환(type cast)이다.

- C에서는 어떤 숫자를 포인터로 바꿔줄 때 이렇게 쓴다.

- 지금은 0x40004000이라는 주소를 UART_TypeDef *라는 구조체 포인터로 바꾸는 것이다.

 

#define UART0 ((UART_TypeDef *)0x40004000)

- UART0은 이제 UART_TypeDef 구조체를 가리키는 포인터가 된다.

- 실제로는 UART0->CTRL 처럼 접근하면, 주소 0x40004000을 기준으로, CTRL 필드는 그 주소, 

STATUS는 0x40004004가 됨. 왜냐하면 CTRL이 4byte이기 때문이다. 

 

4.  왜 형 변환을 해줘야 하지 ?

[1] -> 연산자는 "구조체 포인터"에서만 쓸 수 있다.

some_struct->member;

이 문법은 some_struct가 구조체 포인터일 때만 사용 가능하다.

 

typedef struct {
    int x;
} Point;

Point p = {5};
Point *ptr = &p;

ptr->x = 10;  // OK
p->x = 10;    // ERROR (p는 구조체, 포인터가 아님)

 

0x40004000은 그냥 정수일 뿐이다. 그냥은 멤버 접근(->CTRL)이 불가능하다.

왜나면 C언어에서 정수는 구조체가 아니니까!

 

5. 형 변환을 하면 뭐가 바뀌는가 ?

(UART_TypeDef *)0x40004000

 

이렇게 하면 컴파일러에게 "이 주소는 UART_TypeDef 구조체가 시작하는 주소야"라고 알려주는 것이다.

 

즉, 해당 주소에 있는 데이터를 구조체처럼 해석해도 된다는 의미이다.

 

이제부터 UART0->CTRL처럼 쓸 수 있다. 이건 결국 

 

*((uint32_t *)0x40004000) = 0x01;

 

와 같은 저수준 동작으로 바뀐다. (하지만 훨씬 보기 쉽고 관리하기 좋아진다)

 

- 예시 코드

#define MY_REG_ADDRESS 0x40000000

*( (uint32_t *)MY_REG_ADDRESS ) = 0x12345678;

 

[1] 0x40000000라는 주소를

[2] uint32_t * 형 변환해서

[3] 거기에 0x12345678을 쓰기(write) 하라는 뜻

 

 

 

 

반응형

'🖥️ - C language' 카테고리의 다른 글

C언어 - 포인터 배열  (0) 2025.05.12
C언어 - 포인터 연산  (0) 2025.05.12
C언어 - 포인터와 배열  (0) 2025.05.11
C언어 - 포인터  (0) 2025.05.10
C언어 - 포인터의 포인터  (0) 2025.05.09