포인터는 하나만 알면 된다. p는 선언할 때를 제외하고 무조건 값을 가리킨다고 생각하면 되고 &는 주소를 가리킨다고 생각하면 된다.
포인터는 메모리 주소를 저장하는 변수입니다. 이 주소는 다른 변수의 위치나 어떤 함수의 시작 주소, 배열의 원소 등을 참조할 수 있습니다.
int *ptr; // int 형 변수의 주소를 저장할 수 있는 포인터
&
는 주소 연산자입니다. 변수 앞에 이 연산자를 사용하면 해당 변수의 메모리 주소를 얻을 수 있습니다.
출처 : [C언어] scanf()함수에서 &(ampersand)를 쓰는 이유 (tistory.com)
*는 역참조 연산자로서, 포인터가 가리키는 주소에 저장된 값을 접근할 때 사용됩니다.
int x = 10;
int *ptr = &x; // x의 주소를 ptr 포인터에 저장
printf("%d", *ptr); // 10을 출력
포인터를 증가하거나 감소시키면 포인터가 가리키는 주소도 변경됩니다. 포인터의 자료형에 따라 주소의 증감 크기가 결정됩니다.
int arr[3] = {10, 20, 30};
int *ptr = arr; // 배열의 첫 번째 원소의 주소를 가리킴
ptr++; // arr[1]을 가리키게 됨, 20 출력
배열 이름은 배열의 첫 번째 원소의 주소를 가리킵니다. 따라서 포인터를 사용하여 배열의 원소에 접근할 수 있습니다.
int arr[3] = {10, 20, 30};
int *ptr = arr;
printf("%d", *(ptr+1)); // 20을 출력
문제1: 포인터를 활용하여 두 정수 값을 교환하기
문제 설명:
두 개의 정수 변수
a
와b
가 주어졌을 때, 이 두 변수의 값을 포인터를 사용하여 교환하는 함수swap
을 작성하세요.해결:
포인터를 사용하여 주어진 두 정수의 메모리 주소에 접근하고, 임시 변수를 활용하여 값을 교환합니다.
#include <stdio.h> void swap(int *x, int *y) { int temp = *x; *x = *y; *y = temp; } int main() { int a = 5, b = 10; printf("Before swapping: a = %d, b = %d\n", a, b); swap(&a, &b); printf("After swapping: a = %d, b = %d\n", a, b); return 0; }
이 코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다:
Before swapping: a = 5, b = 10 After swapping: a = 10, b = 5
문제2: 포인터 배열을 사용하여 문자열 여러 개 출력하기
문제 설명:
다음은 문자열의 포인터 배열입니다:
char *strArr[] = {"apple", "banana", "cherry"};
. 포인터 배열을 사용하여 각 문자열을 한 줄씩 출력하는 함수printStrings
를 작성하세요.해결:
문자열의 포인터 배열은 각 문자열의 첫 번째 문자를 가리키는 포인터들의 배열입니다. 따라서 포인터를 사용하여 배열의 각 원소에 접근하고, 해당 포인터를 사용하여 문자열을 출력합니다.
#include <stdio.h> void printStrings(char *strArr[], int length) { for(int i = 0; i < length; i++) { printf("%s\n", strArr[i]); } } int main() { char *strArr[] = {"apple", "banana", "cherry"}; int length = sizeof(strArr) / sizeof(strArr[0]); printStrings(strArr, length); return 0; }
이 코드를 실행하면 다음과 같은 결과를 얻을 수 있습니다:
apple banana cherry
#include <stdio.h>
int main() {
int value = 10;
int *p = &value; // p는 value의 주소를 저장합니다.
int **pp = &p; // pp는 p의 주소를 저장합니다.
printf("value = %d\n", value);
printf("*p = %d\n", *p);
printf("**pp = %d\n", **pp);
return 0;
}
//3개다 10으로 출력됨
사칙연산자는 기본적인 수학 연산을 수행하기 위한 연산자입니다.
int a = 5, b = 3;
int sum = a + b; // sum은 8
int a = 5, b = 3;
int difference = a - b; // difference는 2
int a = 5, b = 3;
int product = a * b; // product는 15
int a = 5, b = 3;
int quotient = a / b; // quotient는 1
int a = 5, b = 3;
int remainder = a % b; // remainder는 2
증감연산자는 변수의 값을 1씩 증가시키거나 감소시키는 연산자입니다.
int a = 5;
int b = ++a; // a는 6, b는 6
int a = 5;
int b = a++; // a는 6, b는 5
int a = 5;
int b = --a; // a는 4, b는 4
int a = 5;
int b = a--; // a는 4, b는 5
증감연산자를 사용할 때 전위와 후위의 차이에 주의해야 합니다. 전위 연산자는 연산을 먼저 수행하고 값을 반환하며, 후위 연산자는 값을 먼저 반환하고 연산을 수행합니다.
문제: 복합 증감 및 사칙연산 표현식 해석하기
문제 설명:
아래의 C 코드에서 주어진 변수와 표현식들이 있습니다.
result
변수에 어떤 값이 저장되는지 예측하세요.#include <stdio.h> int main() { int a = 5, b = 3, c = 4; int result = a++ + --b * c-- - ++a; printf("Result: %d\n", result); return 0; }
해석:
표현식을 단계별로 분석하면 다음과 같습니다.
a++
:a
의 현재 값인 5를 반환하고,a
를 1 증가시킨 후a
는 6이 됩니다.
-b
:b
를 1 감소시킨 후 감소된 값인 2를 반환합니다.
c--
:c
의 현재 값인 4를 반환하고,c
를 1 감소시킨 후c
는 3이 됩니다.
++a
: 이 연산은a++
연산 후에 수행되므로,a
는 현재 6입니다.a
를 1 증가시킨 후 증가된 값인 7을 반환합니다.따라서 주어진 표현식은 다음과 같이 계산됩니다:
5 + 2 * 4 - 7 = 5 + 8 - 7 = 6
따라서
result
의 값은 6입니다.
두 비트 모두 1일 때만 결과가 1이며, 그렇지 않으면 결과는 0입니다.
int a = 5; // 0101 (이진)
int b = 3; // 0011 (이진)
int result = a & b; // 0001 (이진) -> 1 (십진)
int a = 5; // 0101 (이진)
int b = 3; // 0011 (이진)
int result = a & b; // 0001 (이진) -> 1 (십진)
두 비트 중 하나 이상이 1일 때 결과는 1이며, 그렇지 않으면 결과는 0입니다.
int a = 5; // 0101 (이진)
int b = 3; // 0011 (이진)
int result = a | b; // 0111 (이진) -> 7 (십진)
두 비트가 서로 다를 때 결과는 1이며, 같을 때는 0입니다.
int a = 5; // 0101 (이진)
int b = 3; // 0011 (이진)
int result = a ^ b; // 0110 (이진) -> 6 (십진)
비트의 값을 반전시킵니다 (0은 1로, 1은 0으로).
int a = 5; // 0101 (이진)
int result = ~a; // 1010 (이진의 반전) -> -6 (2의 보수 표현으로 인해)
지정된 비트 수만큼 왼쪽으로 이동시킵니다. 오른쪽에는 0이 채워집니다.
int a = 5; // 0101 (이진)
int result = a << 1; // 1010 (이진) -> 10 (십진)
지정된 비트 수만큼 오른쪽으로 이동 시킵니다. 양수의 경우 왼쪽에 0이 채워지며, 음수의 경우 왼쪽에 1이 채워집니다.
int a = 5; // 0101 (이진)
int result = a >> 1; // 0010 (이진) -> 2 (십진)
if
문은 주어진 조건이 참일 경우 해당 코드 블록을 실행합니다.
int a = 10;
if (a > 5) {
printf("a is greater than 5\n");
}
if-else
문은 주어진 조건이 참이면 if
블록을, 그렇지 않으면 else
블록을 실행합니다.
int a = 3;
if (a > 5) {
printf("a is greater than 5\n");
} else {
printf("a is not greater than 5\n");
}
여러 조건을 확인할 때 사용됩니다. 조건은 위에서부터 차례대로 평가되며, 첫 번째로 참인 조건의 코드 블록만 실행됩니다.
int a = 5;
if (a > 10) {
printf("a is greater than 10\n");
} else if (a == 5) {
printf("a is 5\n");
} else {
printf("a is not greater than 10 and a is not 5\n");
}
//출력
//a is 5
특정 변수의 값에 따라 다양한 경우를 처리하려면 switch
문을 사용합니다. 각 경우는 case
키워드로 표시되며, default
키워드는 주어진 경우와 일치하지 않는 경우에 실행됩니다.
int day = 3;
switch (day) {
case 1:
printf("Monday\n");
break;
case 2:
printf("Tuesday\n");
break;
case 3:
printf("Wednesday\n");
break;
default:
printf("Another day\n");
}
//출력
//Wednesday
//만약 break 문이 없다면 아래와 같음
//Wednesday
//Another day
break
문은 현재의 switch
블록을 종료하고, 프로그램의 제어를 switch
문 이후의 코드로 이동시킵니다. break
를 생략하면, 해당 case
이후의 모든 코드(다음 case
나 default
포함)가 실행됩니다. 이를 통해 여러 case
에 대해 동일한 코드를 실행하도록 할 수 있습니다.
for
문은 초기화, 조건, 그리고 반복 후 작업의 세 부분으로 구성되며, 주로 명확한 반복 횟수를 알고 있을 때 사용됩니다.
for (int i = 0; i < 10; i++) {
printf("%d ", i);
}
//출력
//0 1 2 3 4 5 6 7 8 9
위의 코드는 0부터 9까지의 숫자를 출력합니다.
반복문에서 전치연산과 후위연산, ++i, i++
1. 전위 연산 (
++i
)전위 연산에서는 변수의 값을 먼저 증가시킨 후 그 값을 평가합니다. 예를 들어:
int i = 5; int j = ++i; // i는 먼저 증가하고, 그 후에 그 값을 j에 할당합니다.
이 경우
i
는 6이 되고,j
도 6이 됩니다.2. 후위 연산 (
i++
)후위 연산에서는 변수의 현재 값을 먼저 평가한 다음에 값을 증가시킵니다. 예를 들어:
int i = 5; int j = i++; // i의 현재 값을 먼저 j에 할당하고, 그 후에 i를 증가시킵니다.
이 경우
j
는 5가 되고,i
는 6이 됩니다.
for
문에서의 사용
for
문에서 전위 연산과 후위 연산을 사용하는 것은 일반적으로 큰 차이가 없습니다. 왜냐하면for
문의 증감 부분에서는 연산의 결과만 중요하고, 그 값의 평가 순서는 중요하지 않기 때문입니다.for (int i = 0; i < 10; ++i) { printf("%d\n", i); }
와
for (int i = 0; i < 10; i++) { printf("%d\n", i); }
는 동일한 출력을 합니다.
그러나,
for
문의 제어 부분에서 후위 연산과 전위 연산을 혼합하게 되면 예상치 못한 동작을 할 수 있으므로 주의해야 합니다.
while
문은 주어진 조건이 참인 동안 코드 블록을 계속 실행합니다.
int i = 0;
while (i < 10) {
printf("%d ", i);
i++;
}
//출력
//0 1 2 3 4 5 6 7 8 9
이 코드는 for
문의 예제와 동일한 출력을 생성합니다.
do-while
문은 코드 블록을 먼저 한 번 실행한 다음, 주어진 조건이 참인 동안 계속 실행합니다.
int i = 0;
do {
printf("%d ", i);
i++;
} while (i < 10);
//출력
//0 1 2 3 4 5 6 7 8 9
이 코드 역시 위의 두 예제와 동일한 출력을 생성합니다.
do-while
의 주요 특징은 조건의 참/거짓 여부와 관계없이 코드 블록이 최소 한 번은 실행된다는 것입니다.
반복문 내에서 사용될 수 있는 추가적인 제어문으로 break
와 continue
가 있습니다.
break
: 현재의 반복문을 종료하고, 반복문 뒤의 코드를 실행합니다.continue
: 반복문의 나머지 부분을 건너뛰고 다음 반복으로 넘어갑니다.예제:
for (int i = 0; i < 10; i++) {
if (i == 5) {
break; // i가 5일 때 반복문을 종료합니다.
}
printf("%d ", i);
}
//출력
//0 1 2 3 4
for (int i = 0; i < 10; i++) {
if (i % 2 == 0) {
continue; // i가 짝수일 때, 나머지 코드를 건너뜁니다.
}
printf("%d ", i);
}
//출력
//1 3 5 7 9
정수 배열이 있을 때:
**int arr[5] = {1, 2, 3, 4, 5};**
arr
는 이 배열의 첫 번째 원소인 arr[0]
을 가리키는 포인터로 사용될 수 있습니다.
**int *p = arr; // arr는 &arr[0]의 주소와 동일합니다.**
p
는 이제 arr[0]
의 주소를 가지게 됩니다. 이를 이용하여 배열의 원소에 접근할 수 있습니다:
**printf("%d\n", *p); // 1 출력, arr[0]의 값
printf("%d\n", *(p+1)); // 2 출력, arr[1]의 값**
배열을 가리키는 포인터를 사용하면 포인터 연산을 통해 배열의 원소에 접근할 수 있습니다.
**p++; // 이제 p는 arr[1]을 가리킵니다.**
다차원 배열도 포인터로 표현할 수 있습니다. 예를 들어 2차원 배열의 경우:
**int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};**
포인터를 사용하여 이 배열을 가리키려면 포인터의 타입도 맞게 설정해야 합니다:
**int (*p_matrix)[3] = matrix;**
int
포인터의 경우 p+1
은 주소에서 sizeof(int)
만큼 증가한 위치를 가리킵니다.#include<stdio.h> int a[5][5]; int main() { int i, j, k = 1; for (i = 0; i < 5; i++) { for (j = 4; j >= 0; j--) { a[i][j] = k; k++; } } for (i = 0; i < 5; i++) { for (j = 0; j < 5; j++) { printf("%3d", a[i][j]); } printf("\n"); } return 0; }
C언어에서 함수에 대한 기본적인 아이디어와 사용법을 살펴보겠습니다.
반환형 함수이름(매개변수1, 매개변수2, ...)
{
// 함수 본체
// 수행할 작업
return 반환값;
}
void
를 사용합니다.void
타입의 함수는 반환값이 없으므로 return
문 없이 종료될 수 있습니다.#include <stdio.h>// 함수 정의
int add(int a, int b) {
return a + b;
}
int main() {
int result = add(5, 3); // 함수 호출
printf("The sum is: %d\n", result);
return 0;
}
위의 예제에서 add
함수는 두 정수를 인수로 받아 그 합계를 반환합니다. main
함수에서는 add
함수를 호출하여 결과를 얻고 출력합니다.
지역 변수 (Local Variables)
#include <stdio.h>
void displayMessage() {
// 이 함수 내에서만 사용할 수 있는 지역 변수
int localVar = 100;
printf("Local variable value: %d\n", localVar);
}
int main() {
displayMessage();
// 여기에서는 localVar에 접근할 수 없습니다.
// 다음 줄은 오류를 발생시킵니다.
// printf("%d", localVar);
// 이것은 main 함수의 지역 변수입니다.
int anotherLocalVar = 200;
printf("Another local variable value: %d\n", anotherLocalVar);
return 0;
}
전역 변수 (Global Variables)
정적 변수 (Static Variables)
static
키워드로 선언된 변수입니다.레지스터 변수 (Register Variables)
register
키워드로 선언된 변수입니다.외부 변수 (Extern Variables)
extern
키워드로 선언된 변수입니다.extern
키워드가 없는 위치에서 이루어집니다.#include <stdio.h>int globalVar = 10; // 전역 변수
void function1() {
int localVar = 20; // 지역 변수
static int staticVar = 30; // 정적 변수
printf("Global variable: %d\n", globalVar);
printf("Local variable: %d\n", localVar);
printf("Static variable: %d\n", staticVar);
staticVar++;
}
int main() {
function1();
function1();
return 0;
}
위의 예제에서 function1
을 두 번 호출했을 때, staticVar
는 값이 증가된 상태로 유지되는 것을 확인할 수 있습니다.