C언어 배열 / 포인터

김지원·2023년 3월 4일

C언어(독학사)

목록 보기
5/7

배열

배열의 개념

: 동일한 자료형의 데이터들을 연속적인 기억공간에 저장해 한꺼번에 관리할 수 있게 해주는 변수공간을 의미.

  • 여러 개의 자료를 묶어 하나의 이름을 부여하기에 배열 내 데이터를 구분하기 위해서는 인덱스(첨자)를 이용한다. (0부터 시작)
  • 인덱스를 이용해 접근할 수 있는 배열내 각각의 데이터를 배열의 원소 또는 배열의 요소 라고 한다.

1차원 배열

1) 배열 선언
: 데이터의 자료형과 배열의 이름, 배열에 포함시킬 데이터의 개수를 명시한다.

자료형 배열명 [배열의 크기];
int Ary [n];    → 원소 5개를 가진 배열 Ary 선언
// 배열 요수 Ary [0], Ary [n+1], ..., Ary[n-1]

=> int형 배열 Ary는 4byte 크기의 연속적인 변수공간 n개가 모인 (4 x n)byte 크기의 기억공간을 할당받는다.

2) 배열 초기화

  • 선언 후 필요할 때 값을 부여할 수 있지만 선언과 동시에 값을 부여해 초기화 시킬 수도 있다.
  • 배열의 개수를 반드시 명시하는데 대괄호를 이용해 배열 요소 수를 표기하거나 초기화를 이용해 배열의 개수를 자동으로 설정 할 수도 있다.
  • ❗️ 배열요수의 수가 초기화한 값보다 많다면 초기화하지 못한 나미지 요소들은 0 또는 널문자(\0) 로 채워진다. 반대의 경우엔 에러 발생.
int Ary[5] = {1,2,3,4,5};
int Ary_1[5] = {1,2};
int Ary_2[] = {1,2,3,4,5,6,7};

char형 배열과 문자열

: C언어는 " " 로 묶인 문자열을 표현하기 위해 사용할 자료형이 없어 연속된 문자를 저장할 수 있는 문자형 배열을 선언해 문자열로 사용한다.

  • ❗️ 문자열의 끝을 NULL 문자 (\0) 을 이용해 알려야 하므로 배열의 크기를 문자열의 길이 + 1 만큼 지정해줘야한다.
자료형 배열명 [문자열의 길이 + 1];
char Str [6] = "HELLO";
// Str[5] = '\0'

1) char형 배열 초기화
: 문자열을 한꺼번에 배열에 넣을 떄는 " " 을 이용해 초기화하고 이때 널문자는 해당 문자열의 끝을 알리기 위해 자동으로 삽입된다.

  • 문자 하나하나 초기화 가능하지만 문자열 전체를 그대로 초기화 할 수도 있다.

다차원 배열

: 2차원 이상의 배열을 다차원 배열이라한다. (주로 2,3차원 배열 사용)

1) 2차원 배열
: 1차원 배열을 쌓아 표 형태의 구조처럼 표현해 배열 요소들을 [행][열] 로 접근하는 배열

  • 실제 주기억장치에 저장될 때는 1차원 배열과 마찬가지로 선형구조로 저장되므로 이는 논리적 형태의 기억장소라는 것을 생각해야한다.
자료형 배열명 [행의 개수][열의 개수];
int Ary [2][3];
  • 2차원 배열 선언과 초기화
    : { { } } 두 번 사용해 행단위로 구분해 초기화한다.
    행의 크기를 생략하면 열의 크기와 초기값을 이용해 자동설정을 할 수 있지만 열의 개수는 반드시 필요하므로 생략할 수 없다.

2) 3차원 배열
: 2차원 형태의 배열이 여러 개 있는 것을 가정해 사용하는 배열

  • 행, 열, 면의 개수를 더한 연속적인 기억공간에 데이터를 저장한다.
자료형 배열명 [면의 개수][행의 개수][열의 개수];
int Ary [2][3][4];

포인터

포인터란

: 포인터는 데이터가 저장된 기억공간의 주소를 의미한다.
그 주소를 저장할 수 있는 변수를 포인터 변수 라고 한다.

cf) 모든 변수는 실제 저장되는 데이터와 변수가 위치한 기억공간의주소를 가지고 있다.

1) 변수와 주소
: 변수가 시작되는 곳을 해당 변수의 주소라고 하고 주소연산자(&)를 이용해 접근한다.

  • 컴퓨터의 메모리공간은 byte단위로 나누어져 있으며 각 byte는 각각의 주소를 가지고 있다. 변수가 선언되면 컴파일러와 운영체제는 적절한 메모리 위치에 정해진 크기만큼의 공간을 할당해준다.
int Num_x = 30;
// 4byte 4칸을 차지하고 있고 Num_x의 주소는 1002이다. (실제 값은 다름)

🙆‍♀️ 여기 중요합니다 ~

포인터 변수

: 일반 변수처럼 사용하지만 기억장소의 주소만 저장할 수 있다는 특징을 가진다.

  • 기계어나 어셈블리어처럼 메모리에 직접 접근해 조작할 수 있고 함수의 호출 시 주소를 전달해 기억공간을 절약할 수 있다.

1) 포인터 변수의 선언
: 변수명 앞에 * (Asterisk Mark) 을 사용해 일반 변수와 구분한다.
❗️포인터 변수 앞에 선언하는 자료형은 포인터 변수가 가리키는 기억장소에 저장될 변수의 자료형이라는 점이다. 따라서 포인터 변수가 차지하는 크기와 관계없이 4byte가 할당된다.

2) 포인터 변수를 이용한 간접 참조
: 이미 선언된 포인터 변수 앞에 *을 사용하면 해당 변수각 가리키는 주소에 저장된 데이터를 참조하는 간접 참조 연산자로 사용된다.

#include<stdio.h>
int main(void) {
    int a = 10, b;
    int *p;
    p = &a;                                    → 포인터 변수 p에 a의 주소값 저장
    printf("변수 a의 값: %d\n", a);
    printf("포인터 변수 p의 직접 참조 : %d\n", p);  → 즉, a의 주소값 출력
    printf("포인터 변수 p의 간접 참조 : %d\n", *p); → a 주소값을 따라가보니 있던 값 출력
    b = *p + 30;                              // 10 + 30 
    printf("변수 b의 값 : %d\n", b);
}
<output>
변수 a의 값: 10
포인터 변수 p의 직접 참조 : 1876947628
포인터 변수 p의 간접 참조 : 10
변수 b의 값 : 40
  • 포인터 p가 가르키는 내용을 읽으려면 *p (포인터(주소))를 통해 메모리를 간접참조한다.

포인터와 기억공간의 표현

1) 포인터와 데이터형별 기억공간의 대응관계
: 지정한 자료형에 따라 현재 포인터가 접근할 기억공간의 크기와 다음 번지가 결정되기 때문에 포인터 변수 선언 시 자료형은 포인터가 가르키는 기억공간에 저장되는 데이터의 자료형으로 지정한다.

각각 포인터 변수의 자료형에 따라 1byte / 4byte 씩 공간이 할당이 되어있는 걸 확인할 수 있다. 
char 은 번지수가 1씩 증가하고 int 는 4씩 증가하고 있다.

포인터와 배열

  • 배열선언 시 부여된 배열의 이름은 배열의 주소를 의미하므로 배열이름을 이용하면 포인터와 동일한 기능을 수행할 수 있고 포인터 변수의 가감 연산은 배열의 인덱스에서 유용하게 사용할 수 있다.

1) 배열주소를 나타내는 배열의 이름

  • 배열명과 인덱스를 이용해 배열의 각 요소에 접근한다.
p = &num[0] / p+1 / p+2 는 배열의 주소를 의미하고 있고
*p = num[0] / *(p+1) / *(p+2) 는 배열의 주소가 가르키고 있는 값을 의미하고 있다.

포인터 배열을 이용한 문자열 표현

  • 여기서 c[0] 은 메모리 105번지의 값을 가리키는 포인터로써 있다.
    그렇다면 *(c[0]) 의 값은 'E'를 *(c[0]+1) 은 'B'를 의미하게 된다.

이중 포인터

: 포인터를 가르키는 포인터라는 의미 : 데이터가 있는 기억공간을 이중으로 가르키는 포인터

  • 이중 포인터가 가르키는 주소에는 데이터가 값이 아닌 주소! 가 또 들어있고 그 주소로 이동하게 되면 실제로 사용할 데이터를 참조할 수 있다.
p는 주소값을 가지게 되고 *p가 가르키는 주소엔 실제값이 아닌 p가 참조할 데이터가 들어있는 주소를 가지고 있다. 
따라서 **p 변수는 주소 변수에 대한 주소를 의미하는 이중 포인터 역할을 하게 된다.

0개의 댓글