배열이란?
다수의 데이터를 저장하고 처리하는 경우에 유용하게 사용할 수 있는 것이 배열이다.
배열의 선언 방법
자료형 배열이름 길이정보 순이다
즉
int arr[20]; //int형 변수 20개로 이뤄진 배열, 이름은 arr이다. 4 * 20개 byte
double arr2[10]; // 길이가 10인 double형 1차원 배열 8*10개 byte
이렇게 배열을 선언 가능하다 .
각 배열마다 각 자료형의 크기만큼 바이트가 할당되어 있다.
배열의 접근 방법
int arr[3]; //길이가 3인 int형 1차원 배열
위의 배열로 각 배열에 접근을 하는 방법은 ..
arr[0]= 1; // 배열 arr에 첫 번째 요소에 1을 저장
arr[1]= 2; // 배열 arr에 두 번째 요소에 2을 저장
arr[3]= 3; // 배열 arr에 세번째 요소에 3을 저장
즉
arr[idx] = 20; //배열 arr에 idx+1 번째 요소에 20을 저장
배열의 위치 정보를 명시하는 인덱스 값(idx)은 0부터 시작한다.
배열의 모든 요소에 접근할 때는 반복문을 이용하여 순차적으로 접근하는 것이 가능하다.
배열은 선언과 동시에 초기화가 가능하다.
즉.
int arr1[5]= {1,2,3,4,5}; //순차적으로 1,2,3,4,5로 초기화
오른쪽 중괄호로 묶인 부분을 '초기화 리스트'라 한다.
인덱스 값이 공백이어도 초기화 리스트가 존재하면 그에 맞게 알아서 길이정보가 삽입이 된다.
또한
int arr[3] = {1, 2} ; //나머지 배열요소는 0으로 초기화 된다.
sizeof(배열이름) : 바이트 단위의 배열 크기 반환
sizeof(배열이름)/ sizeof(동일한자료형) : 배열의 길이 반환
char형 배열로 문자열의 저장뿐만 아니라 문자열의 변경도 가능하다.
C언어는 큰 따옴표를 이용해 문자열을 표현한다.
char str[14] = "Good morning!";
위의 선언으로 메모리 공간에 char 배열이 할당이 가능하다.
문자열의 끝에는 '\0'문자가 자동으로 삽입된다. 이를 가리켜 널(NULL)문자라 한다.
널 문자의 아스키 코드 값은 0이다. 그리고 이를 출력할 경우 아무런 출력이 발생하지 않는다.
널과 공백문자는 다르다. 공백문자의 아스키 코드는 32이다.
scanf함수를 이용해 배열에 문자열을 입력할 수 있다.
char str[30];
scanf("%s", str); 문자열을 입력 받아서 str에 저장
scanf함수를 통해서 입력받은 문자열의 끝에도 널문자가 삽입된다.
C언어에서 표현하는 모든 문자열의 끝에는 널 문자가 자동으로 삽입된다.
널문자가 필요한 이유는 컴퓨터는 널문자를 기준으로 문자열을 구분하기 때문이다.
scanf함수의 입력은 공백을 포함하지 않는다.
포인터 : 주소 값의 저장을 목적으로 선언된다.
int num=7;
int *pnum; //포인터 변수 pnum선언
pnum =# //num의 주소 값을 포인터 변수 pnum에 저장
&기호는 'the address of'라고 읽을 수 있다. 변수를 대신해 메모리 주소를 되돌려 준다.
즉 위의 코드에서는 num의 메모리 주소를 pnum에 저장하겠다(대입하겠다)라는 의미이다.
pnum에는 num의 주소가 들어있는 것이다.
우리가 num의 주소를 참조해서 값을 읽거나 변경하기를 원하면 pnum을 이용해 변경할 수 있다.
pnum은 num의 주소를 가리키고 있기 떄문이다.
메모리에 저장된 값을 참조하기 원한다면 *pnum을 사용하면 된다.
즉..
type *ptr;
"type형 변수의 주소 값을 저장하는 포인터 변수 ptr의 선언"
int형, double형과 같이 int*도 int형 포인터, double*은 double형 포인터라고 읽는다.
즉 '포인터 형' 도 '자료형'의 범주에 포함시킨다.
*는 포인터가 가리키는 메모리를 참조하는 연산자이다.
*pnum= 20; //포인터 변수 pnum이 가리키는 메모리 공간인 변수 num에 20을 저장
printf("%d", *pnum); //포인터 변수 pnum이 가리키는 메모리 공간인 변수 num을 출력
int *ptr; //포인터 변수 ptr은 쓰레기값으로 초기화
*ptr =200; //ptr이 가리키는 주소를 모르기 때문에 함부로 변경 하면 안됨
========================================================================
int *ptr1= 15; //15번지가 어디인지 모르기때문에 함부로 값을 변경하면 안됨
*ptr1=10;
=========================================================================
int *ptr=0; //선언만 하고 나중에 주소값을 채울라면 이렇게 초기화를 해야함
int *ptr =NULL; //NULL == 0 //이것을 가리켜 '널포인터'라 함.
/* 널 포인터는 "어느 곳도 가리키지 않음"의 뜻을 지닌다. */
배열의 이름은 포인터라 할 수 있다. 값이 변경 불가한 '상수 형태의 포인터''이다.
즉..
int arr[3] = {1, 2, 3}
printf("%p", arr);
printf("%p", &arr[0]);
위의 예제는 같은 주소 값을 나타낸다.
즉 배열의 이름과 배열의 첫번째 요소의 주소값은 같다.
"int형 배열요소간 주소 값의 차이는 4 바이트이다."
즉 배열의 이름을 상수 형태의 포인터 라 한다 . '포인터 상수'라고도 한다.
배열의 이름도 포인터 이기 때문에 배열의 이름을 피연산자로 하는 *연산이 가능하다.
int arr1[5]; //arr1은 int형 포인터 상수
double arr2[3]; //arr2는 double형 포인터 상수
*arr1 += 100 ; //이 코드를 대입할 시 arr1의 배열의 첫번째 요소 값에 100을 증가 시키는 것이다.
int arr[3] = {10 , 11, 12};
int *ptr =&arr[0]; // int *ptr = arr; 와 동일한 문장
위의 코드로 인해 ptr[0~3] == arr[0~3]의 값은 같다.
포인터를 대상으로 메모리의 접근을 위한 *연산 이외에 증가 및 감소 연산도 가능하다.
int *ptr = ... ; //적절한 값으로 초기화 할시
ptr+1; //4증가(즉 포인터 형에 따라 값이 증가한다)
ptr++; //4증가
즉
tpye형 포인터를 대상으로 n의 크기만큼 값을 증가 및 감소시키면, n x sizeof(type)의 크기만큼 주소 값이 증가 및 감소한다.
*(++ptr) = 20; //ptr에 저장된 값 자체를 변경
*(ptr+1) = 20; //ptr에 저장된 값은 변경되지 않음
즉 arr[i] == *(arr+i)로 나타낼 수 있다.
포인터는 문자열을 가리킬 수 도 있다.
char str1[] = "Simple" ; //배열의 길이는 자동 계산 마지막 널문자도 삽입
char *str2 = "Simple";
이렇게 선언시 차이점은 ..
첫번째는 배열에 저장했기 때문에 문자열 전체를 저장하는 배열이고
두번째는 메모리 공간에 "Simple"이 저장되고, 문자열의 첫 번째 문자(S)의 주소값이 반환된다. 그리고 그 반환 값이 포인터 변수 str2에 저장이 된다.
첫번째도 배열의 이름 str1이 의미하는 것도 첫번째 문자인 S의 주소값이다.
하지만 둘이 차이점이 있다면, 배열로 선언할시 계속 문자 S가 저장된 위치를 가리켜야 하지만 ,
포인터 변수는 다른 위치를 가리킬 수 있다.
또 다른 차이점은
str1은 배열안의 문자열의 내용을 변경할 수 있다. 즉 '변수 형태의 문자열' 이라 하고
str2는 가리키는 문자열을 내용을 변경할 수 없기 때문에 '상수 형태의 문자열'이라 한다.
포인터 변수로 이루어진, 즉 주소 값의 저장이 가능한 배열을 가리켜 '포인터 배열'이라 한다.
int *arr[20]; //길이가 20인 int형 포인터 배열 arr
dobule *arr[30]; //길이가 30인 double형 포인터 배열 arr
int num1=10, num2 =20, num3=30;
int *arr[3] = {&num1, &num2, &num3};
for(int i=0;i<3; i++)
{
printf("%d \n", *arr[i]);
}
=================================실행 결과 ================================
10
20
30
char *strArr[3]; //길이가 3인 char형 포인터 배열
char형 포인터 배열은 문자열의 주소 값을 저장할 수 있는 배열이다.
char *strArr={"Simple", "ABC", "DEF"}; 이렇게 값을 초기화 할 수 있다.
"큰따옴표로 묶여서 포연되는 문자열은 그 형태에 상관없이 메모리 공간에 저장된 후 그 주소값이 반환된다"
반환된 주소 값은 문자열의 첫 번째 문자의 주소 값이다.
인자전달의 기본방식은 값의 복사이다. 함수가 호출되고 나면 전달인자와 매개변수는 별개이고 값만 복사되는 것이다.
배열을 매개변수로 선언할 수 없으므로 배열을 전달하고 싶다면 포인터를 사용해야한다.
int arr[3]={..};
void function(int *param){ ...};
function(arr); .// 포인터변수로 배열의 형태로 접근이 가능하다 .
함수 안에서 배열을 접근할라면
param[1], param[2] .. 이런식으로 접근하면 된다.
배열의 주소값만 안다면 어디서든 배열에 접근하여 저장된 값을 참조하거나 변경가능하다.
int *param 대신 int param[]으로 선언이 가능하다.(오직 매개변수의 선언에서만 같은 것이다)
Call-by-value : 단순히 값만 전달하는 형태의 함수 호출
Call-by-reference : 메모리의 접근에 사용되는 주소값을 전달하는 형태의 함수 호출
#include <stdio.h>
void Swap_value(int n1, int n2)
{
int temp = n1;
n1= n2;
n2 = temp;
}
void Swap_reference(int *n1, int *n2)
{
int temp =*n1;
*n1 = *n2;
*n2 = temp;
}
int main()
{
int num1=10;
int num2=20;
printf("num1 num2 : %d %d \n", num1, num2);
Swap_value(num1, num2);
printf("num1 num2 : %d %d \n", num1, num2);
Swap_reference(&num1, &num2);
printf("reference> num1 num2 : %d %d \n", num1, num2);
return 0;
}
=============실행결과=========
num1 num2 : 10 20
num1 num2 : 10 20
reference> num1 num2 : 20 10
위 예제에서 보이듯이 call-by-value형식의 함수 호출은 매개변수와 전달인자가 별개이므로 값의 변화가 일어나지 않는다 .하지만 call-by-reference 형식의 함수호출은 매개변수의 주소값을 전달해주기 때문에 매개변수의 값이 바뀐다.
위의 예제를 이해하면 call-by-reference 와 call-by-value를 쉽게 이해할 것이다.
포인터를 대상으로 const 선언이 가능하다.
int num =20;
const int *ptr = #
*ptr= 30; //에러
num = 30; //성공
const를 맨 앞에선언 하면 포인터 변수 ptr을 이용해 ptr이 가리키는 변수의 값을 변경하는것을 허용하지 않게 하는것이다.
int num=20;
int num= 10;
int *const ptr = #
ptr =&num2; //컴파일 에러
*ptr =40; //컴파일 성공
이렇게 선언되면 포인터 변수 ptr은 상수가 된다. 포인터 변수 ptr이 상수라는 것은 주소값이 저장되면 이제 그 값의 변경이 불가능 하다는 것이다. 가리키는 변수를 계속 가리켜야 한다는 뜻이다.
즉 값은 변경 가능하지만 , ptr변수가 가리키는 변수를 변경을 못한다.
const int * const ptr 이렇게도 선언이 가능한데
위의 두방식이 모두 적용된 것이다 . 즉 주소값도 변경 불가하고, 변수를 이용해 값을 변경도 불가하다.
const는 안정성을 위해 붙인다.
'휴지통 > C 언어' 카테고리의 다른 글
구조체 (0) | 2021.02.04 |
---|---|
문자열 입력 함수 (0) | 2021.02.02 |
문자와 문자열 (0) | 2021.02.01 |
함수 포인터, void 포인터 (0) | 2021.01.31 |
다차원 배열과 포인터, 다중 포인터 (0) | 2021.01.30 |