User Tools

Site Tools


blog:pointers_and_memory_efficiency

Pointers and Memory Efficiency (포인터와 메모리 절약)

1. 포인터란?
  • 메모리 주소를 저장하는 변수
  • 예시:
int a = 10;
int* p = &a;  // p는 a의 주소를 저장
  • `p` 자체에는 주소가 들어있음
  • `*p`를 하면 그 주소가 가리키는 값(예: 10)을 읽거나 수정할 수 있음
2. 배열과 포인터의 관계
  • 배열 이름은 배열 첫 원소의 주소와 같음
  • 예시:
char str[] = "Piano";
char* p = str;  // p == &str[0]
  • 차이점:
    • `str`은 상수 주소 (다른 곳을 가리킬 수 없음)
    • `p`는 다른 문자열을 가리킬 수 있음
3. 문자열 저장 방식 비교

(1) 2차원 배열 (비효율적)

char names[3][12] = {"Piano", "Guitar", "Violin"};
  • 모든 문자열이 고정 크기(12바이트)를 차지
  • 남는 공간은 낭비
  • 전체가 SRAM에 저장됨 → 아두이노에서 메모리 부족 위험

(2) 포인터 배열 (효율적)

const char* names[3] = {"Piano", "Guitar", "Violin"};
  • RAM에는 문자열 주소(포인터)만 저장 (2바이트씩)
  • 실제 문자열은 플래시 메모리에 저장됨
  • 훨씬 효율적
4. 다차원 배열 vs 포인터 배열
  • 다차원 배열: 데이터가 메모리에 연속적으로 배치됨
  • 포인터 배열: 주소 목록만 있고, 실제 데이터는 흩어져 있음
  • 포인터 배열은 유연하지만 관리가 까다로울 수 있음
5. PROGMEM과의 관계
  • Arduino(AVR)에서는 문자열 리터럴이 플래시에 저장됨
  • `const char*`만으로도 대부분 플래시에 저장됨
  • 확실히 플래시에만 보관하려면 `PROGMEM` 사용:
const char string_0[] PROGMEM = "Piano";
const char* const names[] PROGMEM = { string_0 };
  • 꺼낼 때는 `pgm_read_word()` 필요 → 코드가 복잡해짐
6. 잘못 쓸 때의 문제
  • 포인터가 잘못된 주소를 가리키면 → 세그멘테이션 오류 / 오동작
  • `const char*` 문자열은 읽기 전용
  • `strcpy()` 같은 함수로 덮어쓰면 에러 발생
7. Arduino에서의 결론
  • 문자열은 포인터 배열(`const char*`)로 관리하는 것이 가장 효율적
  • GM 악기명: `const char* gmNames[128]`
  • 드럼킷 이름: `const char* drumKitNames[]`
  • 필요시 `struct`로 확장 (MSB, LSB, PC 함께 관리 가능)

8. 실제 예시: gm_gs_names.h (배열 버전)
// GM 악기 이름 (128개)
const char* gmNames[128] = {
  "AcouPiano1", "AcouPiano2", "ElecGrand", "HonkyTonk",
  "ElecPiano1", "ElecPiano2", "Harpsi", "Clavi",
  // ... (중략) ...
  "Gunshot"
};
 
// 드럼킷 이름 (9개, CleanWave32 기준)
const char* drumKitNames[] = {
  "Standard", "Room", "Power", "Electronic",
  "TR-808", "Jazz", "Brush", "Orchestra", "SFX"
};
 
// LCD 표시 예시
void renderLCD(uint8_t channel, uint8_t pc) {
    char line2[17];
    if (channel == 10) {
        snprintf(line2, sizeof(line2), "D:%-10s", drumKitNames[pc]);
    } else {
        snprintf(line2, sizeof(line2), "%-12s %03d", gmNames[pc], pc);
    }
    lcd.setCursor(0, 1);
    lcd.print(line2);
}

9. 확장 예시: 드럼킷 구조체 버전

드럼킷은 이름뿐 아니라 MSB/LSB/Program Change 번호도 필요할 수 있음. 이 경우 구조체를 정의해서 더 체계적으로 관리 가능.

// 드럼킷 정보 구조체
struct DrumKit {
    const char* name;
    uint8_t msb;
    uint8_t lsb;
    uint8_t pc;
};
 
// 드럼킷 배열 (CleanWave32 기준)
const DrumKit drumKits[] = {
    {"Standard",   121, 0, 0},
    {"Room",       121, 0, 1},
    {"Power",      121, 0, 2},
    {"Electronic", 121, 0, 3},
    {"TR-808",     121, 0, 4},
    {"Jazz",       121, 0, 5},
    {"Brush",      121, 0, 6},
    {"Orchestra",  121, 0, 7},
    {"SFX",        121, 0, 8}
};
 
// 드럼킷 선택 함수
void selectDrumKit(uint8_t index) {
    midiSendControlChange(0, drumKits[index].msb, 10);   // CC#0 = MSB
    midiSendControlChange(32, drumKits[index].lsb, 10);  // CC#32 = LSB
    midiSendProgramChange(drumKits[index].pc, 10);       // PC
    lcd.setCursor(0,1);
    lcd.print(drumKits[index].name);
}

요약: 포인터 배열을 쓰면 문자열 이름을 플래시에 두고 RAM은 주소만 저장하기 때문에 메모리를 크게 절약할 수 있다. 드럼킷은 단순히 이름만 관리할 수도 있지만, MSB/LSB/PC까지 구조체에 넣어 관리하면 MIDI 제어가 훨씬 깔끔해진다.


저자 및 이용 안내

이 문서는 정해영의 아이디어와 지시에 따라 AI 도구(ChatGPT)의 도움을 받아 작성되었습니다.

본 문서는 Creative Commons CC0 1.0 Universal Public Domain Dedication에 따라 누구나 자유롭게 복제, 수정, 배포, 활용할 수 있으며, 출처 표시도 필요하지 않습니다. 다만, 내용의 정확성은 보장되지 않았으며, 정해영은 본 문서의 내용에 대해 어떠한 법적 책임도 지지 않습니다.

Authorship and Usage Notice

This document was written with the assistance of an AI tool (ChatGPT), based on the ideas and direction provided by Haeyoung Jeong.

It is released under the Creative Commons CC0 1.0 Universal Public Domain Dedication. Anyone may freely copy, modify, distribute, and use the content, with no requirement for attribution. However, the accuracy of the content is not guaranteed, and Haeyoung Jeong assumes no legal responsibility for its use.

blog/pointers_and_memory_efficiency.txt · Last modified: by hyjeong