9-1-라.배열명

배열을 구성하는 각각의 요소는 배열 타입의 변수와 완전히 동등하다. int ar[5]; 로 선언된 배열의 요소인 ar[0], ar[1] 등은 정수값 하나를 저장할 수 있는 일반적인 정수형 변수인 것이다. 다만 ar이라는 배열에 같이 모여 있을 뿐이다. 그렇다면 배열 그 자체는 문법적으로 어떤 자격을 가질까? 다음 문장을 일단 암기하도록 하자. 만약 이 말이 정 외우기 어려우면 단 세 단어라도 외우도록 하자. "배열명은 포인터 상수다."

 

배열명이 단독으로 사용되면 배열의 시작번지값을 가지는 포인터 상수이다.

 

이 문장이 선뜻 이해가 가지 않겠지만 배열의 본질을 이해하는 아주 핵심적인 문장이므로 음절 하나 틀리지 않고 그대로 외워야 할 정도로 중요하다. 배열명이 단독으로 사용된다는 말은 첨자없이 배열의 이름만 적는다는 뜻이다. ar[0], ar[1]과 같이 첨자와 함께 쓰면 배열 요소 변수지만 ar과 같이 배열명만 쓰면 이 값은 배열의 시작번지를 가리키는 포인터값이 된다. 배열명이 포인터라는 것을 확인해 보기 위해 다음 문장을 실행해 보자.

 

char str[6]={'K','o','r','e','a'};

printf("%s\n",str);

 

크기 6의 문자형 배열 str을 선언하되 "Korea"라는 문자열을 가지도록 초기화했다. 그리고 이 배열에 저장된 문자들을 printf의 %s 서식으로 출력해 보았다. 화면에 "Korea"가 출력될 것이다. 이 예제에서 보다시피 str이라는 배열명 자체는 "Korea"라는 문자열이 들어 있는 배열의 시작 번지를 가리키며 그래서 str 배열명을 printf의 %s서식에 대응시키면 이 번지에 들어 있는 문자열이 출력된다.

배열도 변수이므로 어딘가에 메모리를 차지하며 모든 배열 요소들이 연속적으로 배치되어 있다. 배열명은 이 메모리의 시작 번지를 가리키는 포인터가 된다. 그래서 배열명 그 자체는 항상 첫 번째 배열 요소의 번지와 같으며 str의 다른 표현은 &str[0]라고 할 수 있다. 즉 위 그림에서 K자가 저장되어 있는 번지가 바로 str이 가리키는 번지가 된다.

배열명은 배열의 시작을 가리키는 포인터이되 배열이 선언될 때 메모리가 할당되므로 이 포인터는 변할 수 없는 상수값이다. 즉, 배열명은 포인터 변수가 아니라 포인터 상수이다. str은 언제까지고 str의 시작 번지를 가리킬 뿐이지 다른 번지를 가리킬 수 없다. 따라서 str이라는 배열을 다른 값으로 바꾸는 것은 허용되지 않는다. 왜냐하면 상수는 좌변값이 아니기 때문이다.

배열명이 포인터 상수이기 때문에 배열끼리는 대입할 수 없다. 설사 좌, 우변의 배열이 타입과 크기가 완전히 일치하더라도 대입은 허용되지 않는다. 그래서 다음과 같이 배열의 사본을 만들 수 없다.

 

int ar[5]={1,2,3,4,5};

int ar2[5];

 

ar2=ar;

 

ar2 배열이 ar 배열과 완전히 동일한 형태의 배열이지만 ar 배열의 내용을 대입받을 수 없다. 왜냐하면 ar2라는 배열명은 자신의 시작 번지를 가리키는 포인터 상수이며 좌변값이 아니기 때문에 값을 바꿀 수 없는 것이다. 만약 ar2와 ar을 완전히 같게 만들고 싶다면 루프를 돌면서 배열 요소를 개별적으로 대입해야 한다.

 

for (i=0;i<sizeof(ar)/sizeof(ar[0]);i++) {

     ar2[i]=ar[i];

}

 

이렇게 하면 ar2는 ar과 같아진다. 배열 요소는 상수가 아니며 좌변값이므로 다른 값을 대입할 수 있다. 또는 memcpy같은 메모리 복사 함수를 사용하여 배열 요소를 통째로 복사할 수 있는데 이 방법은 다음에 배우기로 하자. 배열명이 포인터 상수이지만 다음과 같이 쓰는 것은 허용되는데 왜 그런지 생각해 보자.

 

char str[10];

scanf("%s",str);

 

str이라는 배열명이 그 자체로 포인터이므로 scanf의 인수로 넘길 때 &연산자를 붙이지 않는다. scanf 함수는 키보드로부터 입력된 문자열을 이 배열에 복사하므로 배열의 내용이 바뀔 것이다. str은 상수라고 했는데 어떻게 scanf는 str의 내용을 바꿀 수 있는가? 그 이유는 str 자체는 상수이지만 str 배열의 내용은 상수가 아니기 때문이다. scanf가 바꾸는 것은 str의 내용이지 str의 시작 번지를 다른 곳으로 옮기는 것이 아니다.

배열명은 포인터 상수이되 단, 예외가 있다. sizeof 연산자의 피연산자로 사용될 때만은 포인터로 취급되지 않고 배열 그 자체로 취급된다. 그래서 sizeof(ar)은 포인터의 크기인 4를 리턴하지 않고 배열의 전체 크기를 리턴한다. 만약 이 예외가 인정되지 않는다면 배열의 총 크기를 구할 수 없을 것이고 배열의 개수를 조사할 수도 없을 것이다.