C/C++ 배열 알아보기
배열은 연속된 공간을 받아 사용하는 데이터이다.
배열 정의하기
char, int, float 형 배열 정의하는 사용법을 살펴본다.
형식은 다음과 같다.{타입} {변수명}{[배열크기]};
배열을 정의한 경우 배열 크기와 타입을 곱한 만큼의 메모리 공간을 차지한다.
char
int main()
{
char character[5];
}
int
int main()
{
int integer[5];
}
float
int main()
{
float floating[5];
}
배열 할당하기
배열을 정의하고 사용 하지 않는 경우 쓰레기 값이 출력하여 예상치 않은 동작을 유발할 수 있으므로 값을 할당한다.
배열 할당은 변수 명과 배열 번호를 입력하여 직접 값을 할당한다.
char 배열 할당
int main()
{
char character[5];
character[0] = 'a';
character[1] = 'b';
character[2] = 'c';
character[3] = 'd';
character[4] = 'e';
}
int 배열 할당
int main()
{
int integer[5];
integer[0] = 0;
integer[1] = 1;
integer[2] = 2;
integer[3] = 3;
integer[4] = 4;
}
float 배열 할당
int main()
{
int floating[5];
floating[0] = 0;
floating[1] = 1.1;
floating[2] = 2.2;
floating[3] = 3.3;
floating[4] = 4.4;
}
배열 초기화하기
배열을 직접 할당해서 사용한 경우 개발자 실수로 할당을 누락할 수 있다.
이를 방지하는 방법으로 배열을 정의한 함께 초기화하는 것이다.
char 초기화
int main()
{
char character[5] = { 'a', 'b', 'c', 'd', 'e' };
}
int 초기화
int main()
{
int integer[5] = { 0, 1, 2, 3, 4 };
}
float 초기화
int main()
{
float floating[5] = { 0, 1.1, 2.2, 3.3, 4.4 };
}
배열이 많은 경우 모두 초기화 값을 넣기에는 부담될 수 있다.
값 입력하는 부분에서 쉼표(,)를 기입한 다음 생략하면 그 이후의 배열은 자동으로 0 값으로 채워준다. (char 타입도 0 값으로 채운다.)
char 초기화
int main()
{
char character[5] = { 'a', };
}
int 초기화
int main()
{
int integer[5] = { 0, };
}
flaot 초기화
int main()
{
float floating[5] = { 0, };
}
배열 값 출력하기
배열 값을 출력한 경우 어떻게 표시되는지 확인해보도록 한다.
출력할 포맷은 다음과 같이 확인해보고 싶었다.
- 배열 변수 직접 호출하기
- 첫 번째 배열 변수 출력하기
- 배열 메모리 주소 값 출력하기
- 포인터 형태로 출력하기
char 출력하기
int main()
{
char character[5] = { 'a', 'b', 'c', 'd', 'e'};
printf("Direct: %d\n", character);
printf("Array: %c\n", character[0]);
printf("Reference: %d\n", &character);
printf("Pointer %c\n", *character);
printf("Type Size: %d\n", sizeof(character));
}
Direct: 1890581652
Array: a
Reference: 1890581652
Pointer a
Type Size: 5
int 출력하기
int main()
{
int interger[5] = { 1, 2, 3, 4, 5 };
printf("Direct: %d\n", interger);
printf("Array: %d\n", interger[0]);
printf("Reference: %d\n", &interger);
printf("Pointer %d\n", *interger);
printf("Type Size: %d\n, sizeof(integer)");
}
Direct: 2014573896
Array: 1
Reference: 2014573896
Pointer 1
Type Size: 20
float 출력하기
int main()
{
float floating[5] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
printf("Direct: %d\n", floating);
printf("Array: %f\n", floating[0]);
printf("Reference: %d\n", &floating);
printf("Pointer %f\n", *floating);
printf("Type Size: %d\n, sizeof(floating)");
}
Direct: 1407055304
Array: 1.100000
Reference: 1407055304
Pointer 1.100000
Type Size: 20
char 주의점
배열 문제는 아니나 복습 차원에서 char 형을 알아본다.
int main()
{
char character[5] = { 'a', 'b', 'c', 'd', 'e' };
printf("%s", character[0]); // 오류 발생!!
}
char 변수로 %c 출력이 아닌 %s 출력으로 한 경우 오류가 발생될 것이다.
왜냐하면 %s 는 문자열 포인터를 기대한다. char 형으로 기입한 경우 값을 메모리 주소로 해석하여 오류가 발생한다.
반대 상황도 마찬가지다. %c 는 문자열을 기대하고 있는데, 포인터 메모리 주소를 받은 경우 오류를 내보낸다.
만약 char 배열로 스트링 형태로 %s 출력하고 싶다면 포인터인 것처럼 메모리 주소를 붙여준다.
int main()
{
char character[5] = { 'a', 'b', 'c', 'd', 'e' };
printf("%s", character); // 또는 &character
}
abcde儆儆儆儆儆儆儆儆儆儆儆儆儆儆儆?
그러면 위와 같이 문자열은 출력하나 의미 없는 값까지 출력한다. 이는 종료 시그널 널 문자열을 붙여주도록 한다.
int main()
{
char character[5] = { 'a', 'b', 'c', 'd', 'e' };
character[4] = '\0'; // 문자열 e 값 대신 NULL 값으로 치환
printf("%s", character);
}
abcd
배열 크기 구하기
선언한 배열의 갯수를 구해 반복문으로 사용하는 케이스가 많다.
배열의 크기를 다음과 같이 구한다.
char 배열 크기
int main()
{
char character[5] = { 'a', 'b', 'c', 'd', 'e' };
printf("Size %d\n", sizeof(character));
}
Size: 5
int 및 기타 배열 크기
int main()
{
int integer[5] = { 0, 1, 2, 3, 4 };
printf("Size %d\n", sizeof(integer)/sizeof(integer[0]));
}
Size: 5
char 타입은 1바이트로 바로 구할 수 있으나,
int 타입과 float 타입은 4바이트임으로 전체 크기와 타입 크기로 나누어서 구한다.
나누기 연산 비용이 높아 프레임단위로 호출하는 경우 리터럴 값으로 할당하는 것이 좋다.
함수로 배열 인자로 전달하기
함수로 배열 인자 값으로 전달하도록 한다.
func 이름의 함수를 만들고 매개변수은 다음과 같다
char arr[]
: 이 파라미터는char* arr
같으며 포인터를 받겠다는 것이다.char& p
: 이 파라미터는char p
선언한 것과 같으며 문자열로 받겠다는 것이다.
호출 부에서는 인자 값으로
배열의 포인터 주소(character 또는 *(&character))과
배열의 일부 문자열(*character 또는 character[0])
값을 전달하였다.
void func(char arr[], char& p);
int main()
{
char character[5] = { 'a', 'b', 'c', 'd', 'e' };
character[4] = '\0'; // 문자열 e 값 대신 NULL 값으로 치환
func(character, *character);
}
void func(char arr[], char& p) {
printf("char Array %s\n", arr);
printf("char %c\n", p);
printf("Type Size Pointer %d\n", sizeof(arr));
printf("Type Size %d\n", sizeof(p));
}
char Array abcd
char a
Type Size Pointer 8
Type Size 1
인자로 받은 배열의 크기를 알고 싶은 경우가 생길 수 있는데,
여기서 문제점이 발생한다.
인자로 전달 받은 배열의 크기를 구할 수가 없다.
첫 번째 인자는 Pointer 값의 크기로 64bit 환경에서 포인터 크기 8 출력하고, 두 번째 인자는 일반 문자열 크기로 값 1이 출력한다.
결국 우리가 알고 싶은 전체 배열의 크기는 구할 수 없다.
별도의 length 매개변수를 만들어 전달 받도록 하거나, 함수 스코프내에서 배열을 선언하고 배열 크기를 내보내는 멤버 함수를 만든다.
번외
C 언어에서는 배열 전체의 주소를 &array
형태로 얻을 수 있으며, 이를 함수에 T (*ptr)[N]
형태로 전달하면 배열의 전체 크기를 타입 정보에 포함시켜 함수 내부에서 안전하게 처리 가능하다.
void func(char arr[], char& p, char (*pp)[5]);
int main()
{
char character[5] = { 'a', 'b', 'c', 'd', 'e' };
character[4] = '\0'; // 문자열 e 값 대신 NULL 값으로 치환
func(character, *character, &character);
}
void func(char arr[], char& p, char (*pp)[5])
{
// Array or Pointer
printf("char Array %s\n", arr);
// char Type
printf("char: %c\n", p);
// DoublePointer
printf("char Reference: %c\n", (*pp)[0]);
printf("char Reference : %c\n", (*pp)[3]);
// Check Size
printf("Type Size Pointer: %d\n", sizeof(arr));
printf("Type Size: %d\n", sizeof(p));
printf("Type Size DoublePointer: %d\n", sizeof(pp[0]));
printf("Type Size DoublePointer Value: %d\n", sizeof(pp[0][0]));
}
char Array: abcd
char: a
char Reference: a
char Reference : d
Type Size Pointer: 8
Type Size: 1
Type Size DoublePointer: 5
Type Size DoublePointer Value: 1
이중 포인터를 사용할 때 char* pp[]
대신 char (*pp)[size]
형태로 배열의 크기를 유지할 수 있지만, 이 경우 함수 매개변수에 배열 크기를 명시해야 하므로 상위 스코프에 상수를 따로 정의가 필요하다. 그 뿐 아니라 문법도 복잡해 가독성이 떨어진다.
따라서 배열 크기를 구하는 멤버 함수나 템플릿을 사용하는 것이 더 생산적이다.