[C] C언어 스터디 5주차
Review
- Quiz
// 1. 크기 5의 int형 배열 arr를 선언하기
// 2. for문을 돌려 arr에 1부터 5까지 숫자를 순차적으로 입력받기
// 3. for문을 돌려 arr의 모든 요소들을 출력하기
/*
코드 수행 예시)
입력 : 1,2,3,4,5
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
*/
배열
- 동일한 타입의 변수들의 집합
- int, char, double 섞인게 아니라
- 자료형 배열이름 [배열 크기] 로 선언
int arr[8] ; // 선언 double values[3] = {99.8, 87.5, 100.0}; // 선언과 동시에 초기화
- 인덱스로 접근!
- 접근 범위는 0부터 크기-1 까지 (범위를 벗어나면 ‘읽기 전용’)
int nums[3] = {10, 20, 30}; printf("%d", nums[0]); // 10 printf("%d", nums[1]); // 20 printf("%d", nums[2]); // 30
- 동일한 타입의 변수들의 집합
- 위 그림은 byte 크기를 기준으로 조금 더 구체적으로 그린 그림.
- nums[3] 읽는것 자체는 가능. 할당되지 않은 메모리라 ‘쓰레기값 출력’
- (IDE 발전하면서 변함, 원래는 읽는것도 에러)
- 하지만 값을 대입하면 Run time Error 발생!
- ‘주소’ 개념 매우 중요! 배열의 이름은 배열의 첫 번째 주소이다.
- int arr[10];
- arr == &arr == &arr[0];
이차원 배열
- 행과 열 구조, 대괄호 두 개 필요
- x축 하나만 있다가, x축 y축 두 개로 늘어난 것! 1차원 -> 2차원!!
- 자료형 배열이름 [배열 행크기] [배열 열크기]
int arr[2][4]; // 선언 char str[4][10] = {"hello", "test", "string", "hi"}; // 선언과 동시에 초기화
- 배열의 사용 일차원과 똑같다!
- 위의 경우 접근 가능한 범위는 // 범위를 벗어나면 out of range Error!
arr2[0][0] 부터 ~ arr2[1][3] 까지 str[0][0] 부터 ~ str[3][9] 까지
메모리적인 관점
- 0x0039… 는 컴퓨터 메모리 어딘가의 ‘주소’
int value = 10; &value; // 0x0055.. 처럼 메모리 어딘가에 4byte 만큼 할당 된 것. value // 그 주소에 해당하는 공간에 10이라는 값이 들어있는 것.
함수에 배열을 넘기는 방법
- 배열을 함수에 넘길 수 있다.
- 매개변수로 받는 선언부
int function1(int a); // int형 변수를 매개변수로 int function2(char ch); // char형 변수를 매개변수로 int sumOfNumbers(int[]); // int형 일차원 배열을 매개변수로 int sumOfNumbers2(int [5][]); // int형 이차원 배열을 매개변수로
- 함수에 인자로 넘길 때
int arr[5] = ... ; int arr2[5][5] = ... ; sumOfNumbers(arr); // 배열의 '이름'을 넘겨준다. sumOfNumbers2(arr2); // 배열의 이름은 배열의 첫번째 주소이다!! // 다르게 표현하면? sumOfNumbers(__); // &arr 도 되고, &arr[0]도 되고 sumOfNumbers2(__); // &arr2도 되고, &arr2[0]도 되고, &arr2[0][0]도 되고
——————————————————–
포인터
포인터의 이해
포인터란?
- 포인터 == 주소
- ‘변수 A의 포인터’ 라는 문장 == ‘변수 A의 주소’
- 따라서 포인터 변수란, 주소를 담는 변수
- int형 변수, int형 자료를 담는 변수
- char형 - char을 담는..
int num = 10;
int* ptr = # // & : 앰퍼샌드. 주소.
- 10이라는 값을 담고있는 int형 변수 num
- int 형 데이터를 자료로 담는 변수 num
- 주소 0x1154를 담고있는 int형 포인터 변수 ptr
- int* 형 데이터를 자료로 담는 변수 ptr
- int형 변수의 주소를 자료로 담는 변수 ptr
- 포인터 변수 ptr이 num을 가리킨다 라고 표현하기도 함.
- num 은 10
- ptr은 &num, 즉 0x1154
- *ptr은 num, 즉 10
- 역참조 연산자
- num에는 10이라는 값이 들어있음.
- int* ptr 에는 num의 주소인 0x1154 가 들어있음.
- 즉, ptr은 num을 가리키고 있다. == ptr은 변수 num의 주소를 담고 있다.
- ptr은 변수 num이 메모리의 어디에 할당 되어있는지 알고 있는 것.
- 특정 메모리에 접근할 수 있는 것
포인터 변수의 크기
- 모든 포인터 변수의 크기는 4byte 이다? -> 정확한 표현이 아님.
- 포인터 변수의 크기는, 주소를 나타내는 크기에 따라 다르다.
- 즉, 컴파일 되는 컴파일러의 bit 방식. 32bit / 64bit냐? 에 따라서 달라진다.
- 우리는 32bit 형식으로 코딩하다 보니까 4byte.
포인터 자료형
- int*
- char*
- double*
- 똑같은 ‘주소’인데 왜 자료형이 다를까?
- 해당 주소에 접근해서 가져올 때 그 값이 자료형마다 다르니까…
포인터 사용
int *ptr = NULL; // 처음에 초기화를 안해줄 땐, NULL로 초기화 하는게 일반적
int num = 10;
ptr = # // 포인터 변수 ptr에 num의 주소를 대입
int *ptr2 = ptr;
ptr2 -> num;
ptr -> num;
- 메모리를 다루는 것이기 때문에, 조심해야함!! 그래서 안쓸땐 NULL로 초기화
포인터 응용1
// 1. 값에 의한 호출 (Call by value)
void changeValue(int n);
void main() {
int num = 10;
printf("%d", num); // 10
changeValue(num);
printf("%d", num); // 10
}
void changeValue(int n) {
n = 20;
}
- 결과는?
// 2. 주소에 의한 호출 (Call by reference)
void changeValue(int* n);
void main() {
int num = 10;
printf("%d", num); // 10
changeValue(&num); // num = 20 과 결과가 같아진다
printf("%d", num); // 20
}
void changeValue(int* n) {
*n = 20;
}
- 결과는?
주소를 넘겨줄거냐? 값을 넘겨줄거냐?
★★★★ **Call by reference? Call by value? **★★★★
- 주소를 넘겨주면, 해당 메모리의 주소에 직접 접근 할 수 있는 것!!
- 다시 복습. 배열의 이름은 배열의 첫 번째 주소
- 다시 말하면, 배열의 이름은 배열의 포인터
포인터 응용2
// 주소 출력하여 비교하기
void print_reference(int n);
void main() {
int num = 100;
int* ptr = #
print_reference(num);
print_reference(ptr);
printf("num 16진수 : %p\t\t\tnum의 주소 : %p\n", num, &num);
printf("num 10진수 : %d\t\t\tnum의 주소 : %d\n", num, &num);
return 0;
}
void print_reference(int n) {
printf("넘겨받은 값 16진수 : %p\t\t매개변수 n의 주소 : %p\n", n, &n);
printf("넘겨받은 값 10진수 : %d\t\t매개변수 n의 주소 : %d\n\n", n, &n);
}
응용 예시 swap
#include <stdio.h>
void swap(int*, int*);
void main() {
int a = 10;
int b = 20;
int* ptr;
printf("a:%d, b:%d\n",a,b);
swap(&a, &b);
printf("a:%d, b:%d\n",a,b);
}
void swap(int* n1, int* n2) {
int temp;
// n1 == &a
// *n1 == a == 10
temp = *n1;
*n1 = *n2;
*n2 = temp;
}
배열과 포인터
#include <stdio.h>
void main() {
int arr[3] = { 10,20,30 };
int* ptr = arr;
int anyVal;
printf("arr\t: %p\n", arr);
printf("&arr\t: %p\n\n", &arr);
printf("arr[0]\t: %d\n", arr[0]);
printf("arr[1]\t: %d\n", arr[1]);
printf("arr[2]\t: %d\n\n", arr[2]);
printf("&arr[0]\t: %p\n", &arr[0]);
printf("&arr[1]\t: %p\n", &arr[1]);
printf("&arr[2]\t: %p\n\n", &arr[2]);
printf("ptr\t: %p\n", ptr);
printf("&ptr\t: %p\n", &ptr);
printf("&anyVal\t: %p\n\n", &anyVal);
printf("*ptr\t: %d\n", *ptr);
printf("*ptr+1\t: %d\n", *ptr+1);
printf("*ptr+2\t: %d\n\n", *ptr+2);
printf("*ptr\t: %d\n", *ptr);
printf("*(ptr+1): %d\n", *(ptr+1));
printf("*(ptr+2): %d\n\n", *(ptr+2));
printf("ptr[0]\t: %d\n", ptr[0]);
printf("ptr[1]\t: %d\n", ptr[1]);
printf("ptr[2]\t: %d\n\n", ptr[2]);
printf("&ptr[0]\t: %p\n", &ptr[0]);
printf("&ptr[1]\t: %p\n", &ptr[1]);
printf("&ptr[2]\t: %p\n\n\n", &ptr[2]);
for (int i = 0; i < 3; i++) {
printf("&arr[%d] : %p\tarr[%d] : %d\n", i, &arr[i], i, arr[i]);
}
for (int i = 0; i < 3; i++) {
printf("&ptr[%d] : %p\tptr[%d] : %d\n", i, &ptr[i], i, ptr[i]);
}
}
int arr[3] = {1, 2, 3};
int* ptr = arr;
- 위 코드를 선언 했을 때 할당된 메모리 상의 변화 및 각 변수, 인덱스, 주소가 의미하는 값
- arr (배열의 이름)
- &arr
- arr[0]
- arr[1]
- arr[2]
- &arr[0]
- &arr[1]
- &arr[2]
- ptr
- &ptr
- *ptr
- ptr[0], ptr[1], ptr[2]
자주 나는 에러
NullPointerException
- Null 비어있다. 0 NULL 가리키는게 아무것도 없는 포인터 변수를 참조할 때.
int *p = NULL printf ("%d", *p);
IndexOutOfRange (Bound) // 배열에서
- 인덱스를 벗어났을 때
포인터의 다른 자료형들
포인터 배열
- char 형 배열 / int 형 배열 / double 형 배열
- char*형 배열 / int*형 배열
- int *p[3]
- 배열의 요소로 ‘포인터 값’, 즉… 주소를 값으로 가지는 배열
int* arr[5]; int* int* int* int* int*
배열 포인터
- int (*p)[5]
- 특정 크기 형식의 배열을 가리키는 포인터
- char형 **, **int형 **, **struct ***, … **int [5] 형 포인터!
- 참고
함수 포인터
- 반환타입 (*함수이름)(매개변수)
- 참고
구조체 포인터
연습문제
- 호출한 함수에서 선언한 지역변수의 주소를 반환해 출력해보는 코드
#include <stdio.h>
int* add(int x, int y);
int main() {
int a = 30, b = 50;
int* sum;
sum = add(a, b);
printf("sum=%d\n", *sum); // 원칙적으로는 오류가 나야 한다!
// 함수 호출 이후 없어져버리는 주소이기 때문
return 0;
}
int* add(int x, int y) {
int result = x + y;
return (&result);
}
- 배열을 함수에 넘겨, 포인터 접근 방식으로 순차적으로 출력해보는 코드
#include <stdio.h>
void printArray(int*, int);
void main() {
int nums[5] = {1,2,3,4,5};
printArray(nums, sizeof(nums)/sizeof(nums[0])); // (&nums) 또는 (&nums[0])
}
void printArray(int* arr, int size) {
// 인덱스 접근 방식
for (int i=0; i<size; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
// 주소 접근 방식
for (int i=0; i<size; i++) {
printf("*arr+%d = %d\n", i, *arr+i);
}
}
- 포인터의 주소 출력, 배열의 주소출력, 인덱스별로 추소 증가시키며 출력해보기