[ 배열 ] |
배열의 선언
int array[10];
배열 요소 자료형 int 배열 이름 array 배열 길이 10 |
배열은 선언과 동시에 초기화
int arr1[5] = {1, 2, 3, 4, 5}; // 1,2,3,4,5 int arr2[ ] = {1, 3, 5, 7, 9}; // 1,3,5,7,9 int arr3[5] = {1, 2}; // 1,2,0,0,0 int arr4[5] = {0, }; // 0,0,0,0,0 실무에서 많이들 배열 초기화할때 사용한다.
char str1[5] = "Good"; // G o o d \0 모든 문자열 끝에는 NULL문자가 자동 삽입 char str2[] = "morning"; // m o r n i n g \0 |
위의 선언에서 중요한건 문자열이다
예를 들어
char str[10] = "Hello";
이렇게 코드를 입력하면, 자동으로 \0 널문자가 삽입되지만,
10개의 char 형 변수를 잡아논 상태에서 초기화가 이루어지지 않으면,
char str[10];
[0] = 'H';
[1] = 'e';
[2] = 'l';
[3] = 'l';
[4] = 'o';
나머지 [5] ~ [9] 까진 쓰레기 값이 들어간다.
결국 printf 함수는 문자열의 끝인 NULL문자가 없으므로 어디가 마지막 문자인지 알수가 없어진다.
그래서 쓰레기문자까지 출력하게 된다.
해결할려면?
#include <string.h>
int main() { char str[10]; memset( str, 0, sizeof(char) * 10 ); //초기화 return 0; } |
문자열 배열을 memset() 함수를 통해 초기화를 하던가, 아니면 문자열입력후 \0문자를 직접 넣어주면 된다. 아무래도 초기화가 더 편하겠죠. 문자열을 다룰때는 좀 더 신경쓰자.
여기서 정적 배열의 길이선언은 무조건 상수로 한다.
왜 상수로 해야할까?
힙영역의 필요성과도 결합되는데, 예를 한번 들어보자
void func( void ) { int i = 10; int array[i]; // error } |
위의 코드를 그냥 봐서는 문제가 없을꺼같다. 하지만, 컴파일러 입장에서는 에러를 내뿝는다.
왜냐면, 컴파일러는 변수의 대입된 값을 런-타임(프로그램이 실행되는 동안)에 결정하자 하고 넘어가 버린다. 그래서 int array[i]; 문장을 만나면, 컴파일러는 i값이 먼지 모르니 에러가 나는것이다.
즉 변수 i의 값이 결정되는 시기는 '컴파일-타임'이 아니라 '런-타임' 이다.
조금만 생각해보면, 왜 힙영역이 필요한지 바로 나온다.
나는 프로그램 실행도중 즉 런-타임동안 메모리를 할당하고 싶은데, 이때 유용하게 사용되는 메모리공간이 바로 힙(Heap) 영역이다.
[ 포인터 ] |
"포인터란 메모리의 주소 값을 저장하기 위한 변수이다."
이 말이 가장 맞는말 같다.
포인터의 선언 및 연산자
여기서 한가지더!
포인터는 반드시 선언과 동시에 초기화 하는것이 좋다. 초기화를 하지 않으면 쓰레기값이 들어가 어디를 포인터변수가 가르킬지 모르니 항상 선언과 동시에 초기화 해놓는 습관을 길러야 한다.
int main(void)
{
int * pA; //초기화 안됐으니, 대박 쓰레기값
*pA = 10; //error
return 0;
}
─────────────────────────────────────────────
포인터에 다양한 타입이 있는 이유
포인터는 주소값을 저장하는 4바이트로 구성되어있다. 하지만 위의 코드처럼 다양한 포인터형이 있는건 이유가 있다. 포인터는 주소값을 가르키고 타입에 따라 몇바이트를 읽어 들어야 하는지를 알아낸다
즉!
포인터의 타입은 메모리를 참조하는 방법을 알려주는 역할을 한다.
[ 포인터와 배열 ] |
"배열 이름도 포인터, 단 그값을 바꿀 수 없는 상수라는 점이 일반적인 포인터와의 유일한 차이점이다."
"배열의 이름은 배열의 첫번째 요소의 주소 값을 나타낸다."
왜? 위의 코드에서 보면 배열 a의 이름값이 배열 &a[0] 주소값과 같기 때문에
포인터 연산
포인터 연산은 바이트 단위이고, 바이트수는 포인터 자료형에 따라서 달라진다.
여기서! 중요한 결론
arr[i] == *(arr+i) => 즉 arr[3] == *(arr+3) 과 같다.
풀어서 읽어보면, 배열 arr의 [3]요소는 arr을 가르키는 포인터변수에 즉 arr[0] 에 +3 포인터연산으로 이동된 주소에서 배열 arr의 [3]요소의 참조값과 같다. 값은 위의 코드에서 보면 4다.
─────────────────────────────────────────────
문자열 상수를 가르키는 포인터
char *str1 = "abcd";
char * str2 = "ABCD"; // 문자열 상수 선언하고 바로 char* str2 가 가르키고 있다.
이게 가능한이유는?
"문자열 상수는 메모리 공간에 저장이 되면, 그 순간에 문자열 상수의 주소값이 반환된다."
즉 ABCD의 문자열 첫번째 문자('A')의 주소를 str2가 가르키게 된다.
─────────────────────────────────────────────
배열 요소로 포인터를 지니는 포인터 배열
포인터 배열이란?
"메모리 주소 값을 저장할 수 있는, 즉 포인터를 요소로 지니는 배열"
[코드]
[결과]
하나만 읽어보자
printf("%d \n"', *(*(arr+1)));
배열 arr[0] 요소에서 +1 포인터연산 4byte이동되고 arr[1] (*)참조값이니 &b의 주소값이 되고, (&b)의 (*)참조값이니 20의 값을 출력하게 된다.
[ 함수 인자로 배열 전달하기 ] |
입력한 두값을 바꿔주는 swap() 함수를 만들어 보면서 Call-By-Value와 Call-By-Reference를 충분히 이해해 보자
함수 호출방식
1. Call-By-Value ( 값의 복사 )
void swap( int a, int b ) { int temp = a; a = b; b = temp;
printf("a : %d \n", a); printf("b : %d \n", b); } |
코드는 간단하다. 임시변수 temp를 통해 a를 저장하고 b를 a에 옮기고 a의 값을 임시로 저장했던 temp를 다시 b로 복사해주는것이다. 바로 printf() 호출도 하고있다.
이함수에서 매개변수로 넘어오는 int a, int b 값은 복사되어 넘어온다.
즉! 매개변수로 입력했던 값은 이함수안에서 a와 b를 수정한다해서 바뀌지 않는다는 말.
2. Call-By-Reference ( 참조의 의한 호출 )
void swap( int* a, int* b ) // void swap( int a[], int b[] ) { int temp = *a; *a = *b; *b = temp; } |
참조의 의한 호출
즉!
매개변수를 포인터로 받아, 매개변수로 전달된 변수도 swap함수에서 변경하면 바뀌게 된다는점이
가장큰 Call-By-Value 랑은 차이점이다.
"인자로 전달된 주소가 가리키는 변수의 조작을 함수 내에서 가능하게 하는것!"
'0x0001 > C, C++' 카테고리의 다른 글
[C언어] 파일 입출력, 매크로 (0) | 2019.02.07 |
---|---|
[C언어] 문자열 함수, 구조체, 공용체, 열거형 (0) | 2019.02.07 |
[C언어] 전역변수, 지역변수, static, extern 키워드 (0) | 2019.02.07 |
[C언어] goto문, 연산자(증감,대입,sizeof) (0) | 2019.02.07 |
[C언어] 변수, 데이터 표현, 기본자료형, 서식문자 (0) | 2019.02.07 |