안녕하세요! 지난 글들에서 C언어의 배열, 포인터 내용을 학습하셨을텐데요.
이어서 변수의 영역과 함수의 데이터 공유 내용과 더불어 다차원 배열, 포인터 배열, 응용 포인터에 대해 정리해보았습니다.
그래서 앞선 글들에서 언급드린 바와 같이 굉장히 중요한 내용이라 이전 글에서 다룬 기초적인 개념부터 확실히 알고 가시길 권장드립니다.
1. 변수의 영역과 데이터 공유
변수 사용 영역
- 지역 변수는 함수나 블록 내에서 선언되고 사용되는 변수로, 해당 블록이 종료되면 메모리에서 사라진다.
- 지역 변수는 함수나 코드 블록 내에서만 접근할 수 있어 데이터의 가시성을 제한하는 데 유용하다.
void example() {
int localVar = 10; // 지역 변수
printf("지역 변수: %d\n", localVar);
}
- 지역 변수는 {}로 감싸진 블록 안에서만 유효하다.
- 중첩된 블록에서 동일한 이름의 변수를 선언하면 블록 안의 변수가 우선적으로 사용된다.
void nestedBlockExample() {
int num = 5;
{
int num = 10; // 중첩된 블록 내의 지역 변수
printf("중첩 블록 내 num: %d\n", num); // 출력: 10
}
printf("외부 블록 내 num: %d\n", num); // 출력: 5
}
- 전역 변수는 함수 외부에서 선언된 변수로, 프로그램 전체에서 접근이 가능하다.
- 전역 변수는 메모리에서 프로그램이 종료될 때까지 유지된다.
int globalVar = 20; // 전역 변수
void printGlobalVar() {
printf("전역 변수: %d\n", globalVar);
}
- 정적 지역 변수는 static 키워드를 사용하여 선언되며, 함수가 호출될 때마다 초기화되지 않고 이전 값을 유지한다.
- 이러한 변수는 블록 내에서만 접근할 수 있지만, 메모리 상에서는 프로그램 종료 시까지 유지된다.
void staticExample() {
static int count = 0; // 정적 지역 변수
count++;
printf("count: %d\n", count);
}
- 레지스터 변수는 CPU의 레지스터에 저장되기를 요청하는 변수로, 빠른 접근이 필요할 때 사용한다.
- 컴파일러는 레지스터에 저장할지 여부를 결정하므로, 반드시 레지스터에 저장된다고 보장할 수는 없다.
void registerExample() {
register int i = 0;
for (; i < 10; i++) {
printf("%d ", i);
}
}
함수의 데이터 공유 방법
- 함수에 인수를 전달할 때 기본적으로 값이 복사되어 전달된다.
- 이 경우 함수 내에서 매개변수의 값을 변경해도 원본 변수에는 영향을 주지 않는다.
void passByValue(int a) {
a = 20;
}
int main() {
int num = 10;
passByValue(num);
printf("num: %d\n", num); // 출력: 10 (값이 변경되지 않음)
return 0;
}
- 주소를 전달하면 함수에서 직접 원본 변수의 값을 변경할 수 있다.
- 이를 포인터를 통해 구현한다.
void passByReference(int *p) {
*p = 20;
}
int main() {
int num = 10;
passByReference(&num);
printf("num: %d\n", num); // 출력: 20 (값이 변경됨)
return 0;
}
- 함수에서 주소를 반환하여, 다른 함수에서도 해당 메모리의 값을 참조할 수 있다.
- 단, 함수 내부에서 선언된 지역 변수의 주소를 반환하면 안 된다.
- 이는 함수가 종료되면서 메모리에서 사라지기 때문이다.
int* returnPointer(int *a) {
*a += 10;
return a;
}
int main() {
int num = 30;
int *p = returnPointer(&num);
printf("num: %d\n", *p); // 출력: 40
return 0;
}
2. 다차원 배열과 포인터 배열
다차원 배열
- 2차원 배열은 행과 열로 이루어져 있으며, 선언할 때 각 차원의 크기를 지정해야 한다.
int matrix[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
matrix[i][j] = i * j;
printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j]);
}
}
- 2차원 배열을 선언과 동시에 초기화할 수 있다.
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
- 2차원 char 배열은 여러 개의 문자열을 저장할 수 있다.
char names[3][10] = {
"Alice",
"Bob",
"Charlie"
};
- 문자열을 2차원 배열에 초기화하여 여러 문자열을 관리할 수 있다.
for (int i = 0; i < 3; i++) {
printf("%s\n", names[i]);
}
- 3차원 배열은 3개의 차원을 가지며, 주로 여러 개의 2차원 배열을 관리할 때 사용된다.
int cube[2][3][4]; // 2층, 3행, 4열의 3차원 배열
포인터 배열
- 포인터 배열은 여러 개의 포인터를 저장하는 배열로, 각 포인터는 다른 변수나 배열을 가리킬 수 있다.
int a = 10, b = 20, c = 30;
int *arr[3] = {&a, &b, &c};
for (int i = 0; i < 3; i++) {
printf("arr[%d]가 가리키는 값: %d\n", i, *arr[i]);
}
- 포인터 배열을 사용하면 2차원 배열처럼 데이터를 관리할 수 있다.
char *messages[3] = {
"Hello",
"World",
"C Language"
};
for (int i = 0; i < 3; i++) {
printf("%s\n", messages[i]);
}
3. 응용 포인터
이중 포인터와 배열 포인터
- 이중 포인터는 포인터를 가리키는 포인터로, 포인터의 주소를 저장한다.
- 이중 포인터는 다차원 배열이나 동적 메모리 할당 시 유용하게 사용된다.
int num = 10;
int *p = #
int **pp = &p;
printf("num: %d, *p: %d, **pp: %d\n", num, *p, **pp); // 출력: 10, 10, 10
- 이중 포인터를 사용하면 함수 내에서 포인터 변수 자체의 값을 변경할 수 있다.
void changePointer(int **pp) {
static int newValue = 50;
*pp = &newValue;
}
int main() {
int value = 10;
int *p = &value;
changePointer(&p);
printf("p가 가리키는 값: %d\n", *p); // 출력: 50
return 0;
}
- 이중 포인터를 사용하여 포인터 배열을 함수의 매개변수로 전달할 수 있다.
void printMessages(char **messages, int size) {
for (int i = 0; i < size; i++) {
printf("%s\n", messages[i]);
}
}
int main() {
char *messages[] = {"Hello", "World", "C Programming"};
printMessages(messages, 3);
return 0;
}
- 배열의 요소와 배열 자체는 다른 주소를 가진다.
- 배열의 이름은 배열의 첫 번째 요소의 주소이지만, 배열의 주소와는 다르다.
int arr[5] = {1, 2, 3, 4, 5};
printf("arr: %p, &arr: %p\n", (void *)arr, (void *)&arr); // 출력: 서로 다른 주소
- 2차원 배열을 가리키는 포인터는 배열 포인터를 사용하여 선언할 수 있다.
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*pMatrix)[3] = matrix;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("%d ", pMatrix[i][j]);
}
printf("\n");
}
- 2차원 배열에서 요소를 참조할 때는 행과 열의 인덱스를 사용하며, 배열 포인터를 통해 쉽게 접근할 수 있다.
함수 포인터와 void 포인터
- 함수 포인터는 함수의 주소를 저장하는 포인터로, 함수 호출을 동적으로 제어할 수 있다.
- 함수 포인터를 사용하면 코드의 유연성을 높일 수 있다.
int add(int a, int b) {
return a + b;
}
int main() {
int (*funcPtr)(int, int) = add;
printf("두 수의 합: %d\n", funcPtr(5, 7)); // 출력: 12
return 0;
}
- 함수 포인터는 콜백 함수나 동적 함수 호출 등에 사용된다.
- 이를 통해 함수의 호출 방식을 유연하게 구성할 수 있다.
- void 포인터는 어떤 자료형의 포인터든 가리킬 수 있는 범용 포인터이다.
- 하지만 역참조를 위해서는 명시적으로 형변환이 필요하다.
void printValue(void *ptr, char type) {
switch (type) {
case 'i':
printf("정수 값: %d\n", *(int *)ptr);
break;
case 'f':
printf("실수 값: %f\n", *(float *)ptr);
break;
}
}
int main() {
int num = 42;
float fnum = 3.14;
printValue(&num, 'i'); // 출력: 정수 값: 42
printValue(&fnum, 'f'); // 출력: 실수 값: 3.140000
return 0;
}
혹시 이해가 어려우시다면, 이전 글을 먼저 참고해보시면 좋을 것 같습니다.
C 언어 함수, 배열, 포인터 사용법과 예제
안녕하세요! 오늘은 C언어의 핵심이자 꼭 알아야 할 내용인 함수, 배열, 포인터를 정리해보았습니다. 무엇보다 배열과 포인터는 C언어에서 몰라서는 안될 핵심과도 같기 때문에 반드시 이해하고
actshiny.com
C 언어 배열, 포인터, 문자열 활용 심화
안녕하세요! 지난 글에서 C언어 함수, 배열, 포인터에 대해 간단하게 다뤄보았습니다. 혹시나 해당 내용에 대해 기초적인 부분을 모르시는 분들은 바로 이전 글을 보고 오시면 좋을 것 같습니다.
actshiny.com
반응형
'IT & AI > AI 지식' 카테고리의 다른 글
JAVA 변수, 연산자, 데이터 입출력 (1) | 2024.11.20 |
---|---|
C 언어 메모리 동적 할당, 구조체, 파일 입출력, 전처리 (1) | 2024.11.20 |
C 언어 배열, 포인터, 문자열 활용 심화 (0) | 2024.11.19 |
C 언어 함수, 배열, 포인터 사용법과 예제 (0) | 2024.11.18 |
C 언어 연산자와 제어문(반복문, 조건문) (0) | 2024.11.18 |