1. C의 자료형
(1) C에서 제공하는 자료형
- C에서 기본적으로 제공하는 자료형(Data Type)은 크게 정수, 실수 그리고 문자로 나누어 진다.
- 이러한 기본 자료형을 32비트 운영체제를 기준으로 정리하면 다음과 같다.
2. 배열
(1) 1차원 배열
- int자료형이기 때문에 자료 1개당 크기는 4바이트(byte)이다. 이제 이러한 배열의 이름을 key라고 하였을 때, 이를 C소스로 정의하면 다음과 같다.
int key[3];
- 위의 C소스는 key라는 이름을 가지며 3개의 int자료형 원소(element)를 가지는 배열을 정의하는 소스이다. 즉, 위 소스는 아래와 같이 분석될 수 있다.
- 실제 컴퓨터의 메모리에서는 다음과 같이 할당된다.
0 (4 byte) | 1 (4 byte) | 2 (4 byte) |
---|
key[0]의 값 | key[1]의 값 | key[2]의 값 |
(2) 1차원 배열 초기화와 값 설정
1) 정수형 배열 초기화
배열을 초기화 하는 방법은 다음과 같다.
int key[3] = [3, 5, 7];
int key[ ] = [3, 5, 7];
int key[3] = {0, };
key[0] = 3;
key[1] = 5;
key[2] = 7;
- 방법1은 배열을 선엄함과 동시에 값을 초기화시키는 방법이다. 초기값 리스트(initial value list)를 이용하여 값을 설정할 수 있다.
- 단, 초기화 리스트를 사용하여 초기화하는 경우 배열의 원소 개수를 지정하지 않아도 초기화 리스트에 있는 원소 개수에 의해 자동으로 배열의 크기가 결정된다. (방법2)
- 방법3은 배열 선언 이후에 원소별로 값을 설정하는 방법이다. 여기서 배열 key를 선언할 때 초기화 리스트 {0, }를 사용하였는데, 이 초기화 리스트는 모든 원소를 0으로 초기화 시킨다. 그 후, 각 원소에 3, 5, 7값을 설정한다.
2) 문자열 배열 초기화
- 문자 배열은 1차원 배열 중 문자 자료형 char에 대한 1차원 배열을 말한다. 정수형 1차원 배열과 달리 문자열의 제일 마지막에는 반드시 특수 문자 '\0' (NULL, 널 문자)을 할당해야 한다는 점에서 차이가 있다.
- 문자열이 끝나는 것을 알려주기 위해 문자열의 제일 뒤에는 반드시 특수문자 '\0'이 있어야 한다. 따라서 문자 배열의 경우 실제 저장해야 되는 문자 개수보다 반드시 1바이트 더 크게 메모리를 할당해야 한다.
- 예를 들어 'value'라는 5개의 char를 문자 배열에 저장한다면 다음과 같은 방법이 가능하다.
- 방법1
char str[6] = "value"
char str[ ] = "value"
char str[ ] = {'v','a','l','u','e'}
char str[6] = {0, };
str[0] = 'v'
str[1] = 'a'
str[2] = 'l'
str[3] = 'u'
str[4] = 'e';
- 방법1과 방법2는 배열 변수를 선언하고 이를 초기화 할 때 값을 저장하는 방법이다. 배열 변수 선언과 동시에 값을 대입한다.
- 이때 문자 배열 str의 크기는 모두 6이라는 점에 주의해야 한다. 실제 저장되는 문자의 개수는 5개이지만, 문자열의 제일 마지막에 특수 문자'\0'이 자동으로 추가되기 때문이다.
0 (1byte) | 1 (1 byte) | 2 (1 byte) | 3 (1 byte) | 4 (1 byte) | 5 (1 byte) |
---|
v | a | l | u | e | \0 |
str[0] | str[1] | str[2] | str[3] | str[4] | str[5] |
(3) 다차원 배열(Multi-Dimensional Array)
1) 2차원 배열
- 다차원 배열은 배열의 원소가 배열 자료형인 배열을 말한다. 예를 들어 2차원 배열의 경우는 2차원 배열의 각 원소는 1차원 배열이다. 마찬가지로 3차원 배열의 경우 2차원 배열이 3차원 배열의 각 원소가 된다.
- int 자료형 2차원 배열을 C로 선언하는 방법들은 다음과 같다.
- 방법1
int values[2][3] = {{3, 5, 7}, {4, 6, 8}};
int values[2][3] = {3, 5, 7, 4, 6, 8};
int values[2][3] = {0, }
values[0][0] = 3;
values[0][1] = 5;
values[0][2] = 7;
values[1][0] = 4;
values[1][1] = 6;
values[1][2] = 8;
0 (4 byte) | 1 (4 byte) | 2 (4 byte) | 3 (4 byte) | 4 (4 byte) | 5 (4 byte) |
---|
3 | 5 | 7 | 4 | 6 | 8 |
values[0] | values[1] | values[2] | values[3] | values[4] | values[5] |
2) 3차원 배열
- 다음은 int자료형에 대한 3차원 배열을 선언하고 이를 초기화하는 거다.
- 방법1
int multi_values[2][3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24};
int multi_values[2][3][4] = { { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} },
{ {13, 14, 15, 16}, {17, 18, 19, 20}, {21, 22, 23, 24} } };
int mulyi_values[2][3][4] = {0, };
multi_values[0][0][0] = 1;
multi_values[0][0][1] = 2;
multi_values[0][0][2] = 3;
multi_values[0][0][3] = 4;
multi_values[0][1][0] = 5;
multi_values[0][1][1] = 6;
....
multi_values[1][2][1] = 22;
multi_values[1][2][2] = 23;
multi_values[1][2][3] = 24;
- 3차원 배열(2x3x4)은 두 개의 2차원 배열(3x4)로 구성되어 있다. 또한 이 2차원 배열(3x4)은 3개의 1차원 배열로 구성되어 있다.
3. 구조체
(1) 개요
- 구조체(Structure)는 서로 다른 자료형(Data Type)의 데이터를 하나의 그룹으로 묶은 자료형을 말한다.
- 배열이 서로 같은 자료형의 데이터를 하나의 자료형으로 묶은 것이라면, 구조체는 서로 다른 자료형을 묶었다는 점에서 차이가 있다.
(2) 구조체 선언
- 구조체를 사용하기 위해서는 먼저 구조체 선언(Declaration)이 필요하다.
- 즉, '구조체 이름'과 '구조체의 속성들'에 대한 자료형 정의를 통해 하나의 독립된 구조를 가지도록 선언해 주어야 한다.
- 예를 들어, 구조체 student의 선언은 다음과 같다.
struct student {
char name[20+1];
int year;
float score;
}
(3) 구조체 초기화와 값 설정
- 다음 C 소스는 실제 변수를 선언하는 예제 소스이다.
- 방법1
struct student student_lee = {"lee", 2008, 95.4};
struct student student_lee = {0, };
strcpy(student_lee.name, "lee");
student_lee.year = 2008;
student_lee.score = 95.4;
- 방법1은 구조체의 값을 초기화 할 때 그 값을 설정한다.
- 방법2는 구조체 변수가 선언된 후에 값을 설정한다.
- 구조체 변수 선언 이후의 값 설정(방법2)은 점 연산사(.)를 이용한다는 점에 주의해야 한다. 화살표 연산자(→)를 이용할 수 있다.
- 문자 배열 속성인 name의 경우 함수 strcpy()를 사용하였는데, 이는 1차원 배열과 같이 문자 배열이 선언된 이후에 문자열 값을 한 번에 대입하기 위해서이다.
- 단 다음 방법으로 구조체를 선언하면 안된다.
struct student student_lee = {0, };
strcpy(student_lee.name, "lee");
strcpy(student_lee.year, 2008);
strcpy(student_lee.score, 95.4);
- "name"은 문자열이므로, 'strcpy()'함수를 사용하여 초기화하는 것이 적절하지만, 'year', 'score'필드는 각각 'int'와 'float'타입이다. 따라서 'strcpy()'를 사용하여 초기화하면 안된다. 이 함수는 문자열 복사를 위해 설계된 함수이다.
- 또한, 같은 구조체에 대해서는 대입 연산자(=)를 통해 구조체 변수 사이에 다음과 같이 복사할 수 있다.
struct student lee_src = {"lee", 2008, 95.4};
struct student lee_dest = {0, };
lee_dest = lee_src;
- 구조체 변수 lee_src의 내용이 모두 구조체 변수 lee_dest에 그대로 복사된다. 이것을 그림으로 나타내면 다음과 같다.
- 구조체를 정의할 때 typedef를 이용하여 새로운 구조체를 하나의 데이터 타입으로 정의하기도 한다.
typedef struct student type {
char name[20+1];
int year;
float score;
} student;
student lee_src = {"lee", 2008, 95.4}
- 이는 구조체 student가 새로운 데이터 타입으로 정의되었으므로, 실제 변수 lee_src선언에서 struct가 생략되어 편리하게 사용할 수 있다.
4. 포인터
(1) 개요
- 포인터 변수(Pointer Variable)는 메모리 주소(Memory Address)값을 저장하는 변수를 말한다.
- 포인터 변수 이외의 다른 변수들을 메모리에 특정 값(Value)을 저장하는 데 비해 포인터 변수는 다른 변수들의 물리적 주소(Physical Address, 메모리 주소)를 저장한다는 점에서 차이가 있다.
배열의 이름과 포인터
배열의 이름이 있는 곳을 배열의 첫 번째 요소의 주소로 대치한다. 따라서 배열의 이름이 포인터이기 때문에 배열이 함수의 매개변수로 전달될 때에 사실은 포인터가 전달되는 것이다. 이것은 메모리 공간과 함수 호출 시간을 절약하는 기법이기도 하다. 함수 호출 시에 배열을 복사할 필요가 없기 때문이다.
(2) 포인터 선언과 값의 초기화
- 아래는 정수 변수를 가리키는 포인터 변수 ptr_int를 선언한 예제이다.
int *ptr_int = NULL;
- 즉, 위의 포인터 변수 선언에서 포인터 변수는 다음과 같은 문법에 따라 선언됨을 알 수 있다.
포인터 변수 NULL
포인터 변수 ptr_int의 값이 NULL로 초기화되어 있다는 것에 주의해야한다. 포인터 변수는 모두 메모리 주소에 직접 접근하여, 프로그램의 안정성 차원에서 가능한 변수 선언에 NULL로 그 값을 초기화 시켜주는 것이 좋다.
- 포인터변수 ptr_int에 변수 int_value의 주소를 대입하려면, 다음과 같은 소스가 가능하다.
int *ptr_int = NULL;
int int_value = 100;
ptr_int = &int_value;
- 위 소스 중에서 주소 연산자(&)는 변수의 주소 값을 얻을 때 사용된다. 따라서 위 소스는 변수 int_value의 주소를 구한 다음, 이를 포인터 변수 ptr_int에 대입한다. 결과적으로 변수 int_value와 이를 가리키는 포인터 변수는 다음과 같은 구조를 지닌다.
- 위 소스는 먼저 포인터 변수를 선언한 이후에, 포인터 변수를 설정한 경우이다.
- 아래는 포인터 변수 선언 시에 초기화를 통해서 대입하는 방법이다.
int *ptr_int = &int_value;
(3) 포인터 연산
(가) 주소 연산자 &
- 주소 연산자(&)는 변수의 주소 값을 얻을 때 사용된다. 특정 변수의 주소 값을 추출하는 주소 연산자의 사용 문법은 다음과 같다.
- 포인터 변수 = &변수;
float float_value = 5.23f;
float *ptr_float = NULL;
ptr_float = &float_value;
- 위의 C소스는 변수 float_value의 주소 값을 추출하여, 이를 포인터 변수(ptr_float)에 대입하는 소스이다. 따라서 다음과 같은 구조를 지니게 된다.
- 즉, 변수 float_value의 시작 주소가 포인터 변수 ptr_float에 저장되어('2000'), 포인터 변수 ptr_float가 변수 float_value를 가리키게 되었다.
float *ptr_float = NULL; 을 쓴 이유
포인터의 타입은 단지 메모리의 주소를 저장하는 것 이상의 의미를 가지고 있다. 포인터 타입은 그 주소에 저장된 값이 어떤 유형의 데이터인지, 그리고 그 데이터가 얼마나 많은 메모리 공간을 차지하는지에 대한 정보도 포함한다. 이러한 정보는 포인터를 통해 메모리에 접근하거나 연산을 수행할 때 중요하다.
예를 들어, int * 포인터를 사용하면 컴파일러는 그 포인터가 가리키는 메모리 위치에 정수가 저장되어 있다고 가정하고, sizeof(int) 바이트를 읽거나 쓰려고 한다. 반면에 float * 포인터를 사용하면, 해당 위치에 실수가 저장되어 있으며, sizeof(float) 바이트를 읽거나 쓴다고 가정한다.
따라서 float_value의 주소를 int * 포인터에 저장하려고 하면 타입 불일치로 인해 컴파일러는 경고 또는 오류를 발생시킬 수 있다. 이렇게 하면 int 대신 float를 가리키는 포인터를 통해 메모리를 해석하려고 시도할 때 예기치 않은 결과가 발생할 수 있다.
요약하면, 포인터의 타입은 해당 포인터가 가리키는 데이터의 타입을 나타내며, 이는 메모리에서 데이터를 읽고 쓸 때 사용되는 바이트 수를 결정합니다. 그러므로 float_value의 주소를 저장하려면 float * 타입의 포인터를 사용해야 한다.
(나) 참조 연산자 *
- 참조 연산자(*)는 포인터 변수에 저장된 주소를 이용하여 해당 주소에 있는 값을 나타낸다. 특정 포인터 변수에 저장된 변수 값을 추출하는 참조 연산자의 사용 문법은 다음과 같다.
- *포인터 변수 = 값;
char char_value_A = 'A'
char *ptr_char = &char_value_A;
- 위 소스에서 먼저 포인터 변수 ptr_char에 변수 char_value_A의 주소를 대입한다. 이는 주소 연산자를 이용하여 포인터 변수에 다른 변수의 주소를 저장하는 예와 동일하다.
- 다음 소스는 포인터 변수 ptr_char가 가리키는 주소에 저장된 값을 'X'로 변경시킨다.
*ptr_char = 'X';
- 최종적으로 다음과 같은 구조를 지니게 된다.
- 포인터 변수 ptr_char에 저장된 주소(f000)의 값이 'A'에서 'X'로 변경된다. 이는 참조 연산자를 이용하여 특성 주소에 저장된 값을 변경하는 가장 기본적인 경우이다. 또한 참조 연산자는 다음과 같은 문법도 가능하다
*포인터 변수 = 변수;
- 위의 문법은 포인터 변수에 저장된 특정 주소에 다른 변수의 값을 대입하는 경우를 의미한다.
char char_value_A = 'A';
char char_value_B = 'B';
char *ptr_char = &char_value_A;
- 위 소스는 먼저 포인터 변수 ptr_char에 변수 char_value_A의 주소를 대입한다. 이후 아래 소스가 있다 해보자.
*ptr_char = char_value_B;
- 위 소스는 포인터 변수 ptr_char가 가리키는 주소에 있는 값을 변수 char_value_B의 값으로 변경한다. 위 소스의 결과는 다음과 같은 구조를 지닌다.
- *ptr_char = char_value_B연산으로, 포인터 변수 ptr_char에 저장된 주소(f000)에 있는 값이 'A'에서 'B'로 변경된 것을 볼 수 있다. 즉, 변수 char_value_B에 저장된 값'B'가 주소 f000에 저장된 것이다.
(다) 포인터를 이용한 동적 메모리 할당
- 동적 메모리 할당(Dynamic Memory Allocation)은 프로그램 실행 도중 동적으로 메모리를 할당하는 것을 말한다. 여기서 '동적'이라는 의미는 할당될 메모리의 크기가 컴파일할 때 미리 정해져 있지 않았다는 의미이다.
- 즉, 프로그램이 실행되는 도중에 임의의 크기로 메모리(배열 혹은 구조체)를 할당할 수 있다는 점에서 '정적 메모리 할당(Static Memory Allocation)과 차이가 있다.
- 정적 메모리 할다은 프로그램이 실행되기 전에 할당해야 할 메모리 크기가 미리 정해진 경우이다.
- 방법1)정적 메모리 할당
int int_array[100] = {0, };
int *ptr_int = NULL;
int size = 100
...
size = 200;
ptr_int = (int*)malloc(sizeof(int)*size);
- 정적 메모리 할당은 소스 코드가 단순하여 간단하게 사용할 수 있다는 장점이 있다. 하지만, 미리 크기를 정해주어야 하기 때문에 대부분 여유를 가지도록 메모리를 할당하여 메모리 낭비가 쉽게 발생한다는 단점이 있다.
- 동적 메모리 할당은 소스 코드가 복잡하고, 사용 방법이 정적 메모리 할당에 비해 다소 어렵다는 단점이 있지만, 동적으로 필요한 양 만큼의 메모리를 할당할 수 있다는 장점이 있다.
- 동적 메모리 할당의 경우 메모리를 다 사용한 다음 반드시 메모리를 해제시켜 주어야 한다는 제약 사항이 있다. 메모리를 해제하지 않을 경우 운영체제(OS)로 메모리가 반환되지 않아 메모리 누수가 발생한다. (C언어 프로그래밍하는 개발자에게 힘든 작업 중 하나)
- 함수 malloc()의 반환 값은 할당된 메모리의 시작 주소이기 때문에 포인터 변수에 저장한다. 즉, 포인터 변수는 메모리 주소를 저장하는 변수이기 때문에 동적 메모리 할당된 결구를 포인터 변수에 저장하는 것이다.
- size개의 int자료형 저장할 수 있는 메모리가 할당되었고, 그 시작 주소가 int형 호인터 변수 ptr_int에 대입되었다.
ptr_int = (int*)malloc(sizeof(int)*size);
- 함수 malloc()를 호출하여 메모리를 동적으로 할당할 수 있으며, 함수 malloc() 호출 후 반환되는 호인터에 대해 다음과 같은 NULL 검사가 필요하다
if(ptr_int != NULL){
// do something
}
else {
// ERROR!!!
}
- 사용이 끝난 메모리에 대해서는 C함수 free()를 호출하여 해제시켜 주어야 한다.
- 예를 들어, 메모리가 할당된 포인터 변수 ptr_int에 대한 사용이 모두 끝났다고 가정하면 다음과 같은 소스에 의해 메모리를 해제 시켜줄 수 있다.
if(ptr_int != NULL) {
free(ptr_int)
}
(int*)
(int*)는 C 언어에서 사용하는 형 변환(Type Casting) 연산자이다.
이 연산자는 원하는 형태의 자료형으로 값을 변환하거나, 메모리의 해석 방식을 바꾸는데 사용한다. 여기서는 malloc 함수가 반환하는 void 형태의 포인터를 int 형태로 변환하고 있다.
malloc 함수는 할당한 메모리의 시작 주소를 void (즉, 어떤 형태의 데이터도 가리킬 수 있는 일반적인 포인터)로 반환한다. 그러나 C 언어에서는 명시적으로 해당 주소가 어떤 데이터 형식을 가리키는지 지정해주어야 한다. 그래서 이 예제에서는 int 로 형 변환을 해준다. 이렇게 해서 할당된 메모리를 int 형태로 해석하고 사용할 수 있게 된다.
명령어
C 언어에서는 메모리 관리를 개발자가 직접 수행해야 한다. 동적 메모리 할당이라는 개념은 이 메모리 관리에서 중요한 부분을 차지하며, 이를 통해 프로그램 실행 중에 필요한 메모리 공간을 동적으로 할당하고 해제할 수 있다. 동적 메모리 할당은 크게 메모리를 할당하는 malloc, calloc, realloc 함수와 메모리를 해제하는 free 함수로 구성된다.
- malloc : 메모리를 할당하는 함수로, malloc 함수를 호출하면 지정된 크기의 메모리를 할당하고, 그 메모리의 주소를 반환한다. 만약 메모리 할당에 실패하면 NULL을 반환한다.
int *ptr = (int*)malloc(sizeof(int) * 5);
- calloc : malloc 함수와 유사하지만, 할당된 메모리를 0으로 초기화하는 차이점이 있다. 메모리를 할당할 때 할당된 메모리 공간의 모든 비트를 0으로 설정한다. 이는 다시 말해, 할당된 메모리 영역의 모든 값을 '0'으로 초기화한다는 것이다.
int *ptr = (int*)calloc(5, sizeof(int));
- realloc : 이미 할당된 메모리의 크기를 조절하는 함수이다. 새로운 크기의 메모리를 할당하고, 원래 메모리의 데이터를 복사한 후, 원래 메모리는 해제한다.
ptr = (int*)realloc(ptr, sizeof(int) * 10);
- free : 동적으로 할당된 메모리를 해제하는 함수이다. 메모리 누수를 방지하기 위해 동적으로 할당된 메모리는 반드시 free 함수를 사용해 해제해야 한다.
free(ptr);
(라) Double Pointer
- '포인터의 포인터'란 포인터 변수를 가리키는 포인터 변수를 말한다.
- 즉, 포인터 변수가 다른 변수의 메모리 주소를 저장하는 변수라면, 포인터의 포인터(Double Pointer, 더블 포인터)변수는 다른 변수의 메모리 주소를 저장하는 포인터 변수의 주소를 가리킨다는 점에서 차이가 있다
int int_value = 500
int *ptr_int = &int_value;
int **pptr_int = & ptr_int
- 위의 C소스는 포인터 변수 ptr_int에 변수 int_value의 주소를 대입하고, 더블 포인터 변수 pptr_int에 포인터 변수 ptr_int의 주소를 대입하였다. 따라서 위 소스 결과 다음과 같은 구조를 지니게 된다.
- 포인터의 포인터 예제
printf("변수 int_vale의 값: %d\n", int_value);
printf("포인터 변수 ptr_int의 값: %x\n", ptr_int);
printf("포인터 변수 ptr_int가 가리키는 변수의 값: %d\n", *ptr_int);
printf("더블 포인터 변수 pptr_int의 값: %x\n", pptr_int);
printf("더블 포인터 변수 pptr_int가 가리키는 주소의 값: %x\n", *pptr_int);
printf("더블 포인터 변수 pptr_int가 가리키는 주소가 가리키는 값: %d\n", **pptr_int);
- 위의 소스에서, 더블 포인터 변수 pptr_int를 이용하여 변수 int_value의 값을 출력하기 위해 참조 연산자를 두 번 사용하였다.(**)하였다. 이러한 더블 포인터(포인터의 포인터)가 주로 사용되는 곳은 2차원의 다차원 배열이다.
(마) 포인터와 배열
- 배열은 행과 열의 크기가 미리 정해진 정적 메모리 할당이었다면, 여기서는 행과 열의 크기가 프로그램 실행 도중 변할 수 있는 동적 메모리 할당을 통해 2차원 정수 배열을 생성한다.
int row = 3;
int col = 4;
- 2차원 배열은 결국 1차원 배열들의 배열이므로, 다음과 같이 먼저 배열들을 가리키는 포인터의 배열을 만들어 준다.
int **pptr_int_array = NULL;
pptr_int_array = (int**)malloc(sizeof(int*)*row);
- 위 소스에 의해 더블 포인터 변수인 pptr_int_array은 포인터 변수 배열의 시작 주소를 저장하게 된다. 먼저 3개(행의 크기, 변수 row의 값)의 정수 포인터 변수(int*)를 원소로 가지는 1차원 배열을 메모리에 할당한다.
- 할당된 1차원 배열의 시작 주소를 더블 포인터 변수인 pptr_int_array에 대입한다. 여기서 포인터의 포인터인 더블 포인터 pptr_int_array는 다른 변수의 메모리 주소를 저장하는 포인터 변수의 주소를 가리킨다.
- 이제 각각의 포인터 변수에 열의 크기인 4만큼의 1차원 정수 배열을 생성하면 다음과 같다.
for(i = 0; i<row; i++) {
pptr_int_array[i] = (int*)malloc(sizeof(int)*col);
memset(pptr_int_array[i], 0, sizeof(int)*col);
}
- 각 행 별로 루프를 돌면서 열의 크기(변수 col 사용)만큼 1차원 int배열에 대한 메모리를 할당하고 그 시작 주소를 변수 pptr_int_array[i]에 저장한다. 위 소스에 의해 다음과 같은 구조를 가지게 된다.
- 더블 포인터 변수 pptr_int_array는 포인터 배열의 시작 주소(2000)을 가리키고 있다. 더블 포인터 변수 pptr_int_array가 가리키는 주소에 저장된 값(주소2000-2003에 저장된 값)은 또한 다른 메모리상의 주소 1000이다. 이 주소는 결국 4개의 int변수를 저장하는 배열의 시작주소임을 알 수 있다.
- 만약 위와 같은 2차원(3Ⅹ4)배열의 (0,0)위치에 있는 값을 1로 설정하려면 다음과 같은 방법이 있다.
pptr_int_array[0][0] = 1;
**pptr_int_array = 1;
memset
memset은 C 표준 라이브러리에서 제공하는 함수로, 메모리 블록의 내용을 주어진 값으로 설정하는데 사용된다. 이 함수는 메모리의 특정 부분을 특정 값으로 채우는 데 사용되며, 일반적으로 동적으로 할당된 메모리를 초기화하는 데 사용된다
void *memset(void *s, int c, size_t n);
(바) 포인터와 2차원 배열