본문으로 바로가기

Day-6 배열과 문자열, 포인터

category Programming/TIPS 17기 2017. 7. 16. 15:32

 

 

몫과 나머지의 사용

 

나눗셈    =>    몫(/)    나머지(%)    

 

char  data[3];

index(색인)은 0,1,2이다. 색인이 0, 1, 2 이외의 수가 들어가면 오류가 발생 할 수 있다. 따라서 색인의 범위를 제한할 필요가 생긴다.

if(key>=0&&key<=2) { } // 단, 0,1,2 이외의 값이 들어왔을때도 처리해주어야 한다.

data[key%3] 사용자가 무슨 값을 넣든지 프로그램이 작동하고 죽지 않는다.

 

 

 

rand 함수 ( 0 ~ MAX int (약 40억) )

0 ~ 5 사이의 난수가 필요하다.

rand() % 6을 하면 결과는  0 ~ 5 사이의 값이 발생한다.

 

7 ~ 10까지 난수가 필요하다

7 + (rand() % 4)

 

2차원 배열

 

2차원 공강에 있는 한 점(대상)은 자신의 위치를 좌표로 나타낼 때 (x축 좌표 값, y축 좌표 값) 형태로 사용한다.

바둑판에 놓인 바둑돌의 위치 정보도 2차원 형식의 데이터이다. 실제로 내부적 메모리는 1차원 배열이다.

메모리 배열 자체는 char data[5][4]와 char data[20]과 같다. 컴퓨터는 x,y가 아니라 y,x라고 한다.

char data[4][5] = 5개를 가진 항목들이 4개 있다.

 

 

2차원 배열 선언하기

 

같은 크기의 변수를 나열해서 적는 것이 불편하기 때문에 배열을 사용해서 크기가 같은 변수들끼리 묶어서 사용한다.

 

2차원 배열을 선언하다 보면 아래와 같은 의문이 생긴다.

 

char data[4][5];로 적어야 될지 char data[5][4];로 적어야 할지 헷갈릴 수 있다. 위와 같은 구조는 char data[5][4]이다.

[]연산자는 동일한 우선순위를 가질 때 왼쪽에서 오른쪽으로 연산을 수행한다.

즉, char (data[5])[4];이다.

 

1단계 char (data[5])[4]; data[5]라는 배열을 만들겠다.

2단계 char (data[5])[4]; data 배열의 각 요소 (data[0], data[1]..)는 char[4] 크기를 가진다.

 

2차원 배열 선언하기 - 행(Y)축 단위로 묶음

 

 

 

2차원 배열 선언하기 - 열(X축) 단위로 묶음

 

 

 

2차원 배열이 실제 메모리에 저장되는 형태

 

컴퓨터가 사용하는 메모리는 2차원 개념을 제공하지 않는다.

배열 문법으로 선언된 메모리는 1차원 형태로 메모리에 저장된다.

 

2차원 배열 초기화 하기

 

char temp1[3] = {1, 2, 3};

char temp2[3] = {4, 5, 6};

char temp[2][3] = { {1, 2, 3}, {4, 5, 6} };

 

2차원 데이터를 1차원 데이터로 변환하기

 

5행 4열 크기로 줄인 바둑판에서 1행 1열부터 순서대로 돌을 9개 놓는다.

돌을 놓을 때마다 0번부터 1씩 증가시키면서 돌에 번호를 적어둔다.

 

 

 

1차원 데이터를 2차원 데이터로 변환하기

 

1차원 데이터를 2차원 데이터로 변환하려면 행 번호와 열 번호를 계산해야 한다.

 

 

 

2차원 배열은 메모리상에 1차원 형태로 데이터를 저장하면서 프로그래머가 이 메모리를 사용할 때 컴파일러가 수학 공식을 적용하여 2차원 개념을 제공한다. 즉, char temp[5][4]; char test[20];의 메모리 형태는 동일하다.

Ex) char temp[M][N]; char test[L];

1. test[L] 항목과 temp[L / N][a % N] 항목은 위치가 같다.

2. temp[b][c] 항목과 test[b x N + c] 항목은 위치가 같다.

 

포인터 (기계어로 간접주소지정방식)

 

C언어에서 새로 정의한 문법이 아니라 기계어에 이미 존재하는 문법이다. 따라서 기계어의 특징을 가지고 있다.

 

운영체제와 프로그래밍

 

기계어에서는 변수가 위치한 메모리 주소를 통해 변수 값을 읽거나 변경한다. 즉, 변수라는 개념은 기계어로 바뀌면 주소로 바뀐다.

컴퓨터 시스템의 메모리는 운영체제가 관리한다. 메모리는 주소로 이루어져 있다. 운영체제는 RAM을 1Byte 단위로 이루어져 있다.

운영체제가 메모리를 관리하는 방법에 따라 프로그래밍 방법도 달라진다. 메모리에 값을 사용하고 싶으면 쓰고 싶은 메모리의 주소를 적어야 한다.

 

고급언어에서는 변수명을 컴파일러가 번역해 주면 주소로 바꾸기 때문에 변수명으로 주소값을 대신한다.

주소번지에 데이터를 넣을때 단지 주소번지만 필요한것이 아니라 메모리는 영역이기 때문에 시작번지와 끝번지를 알아야 한다.

시작번지와 끝번지를 계속 기억하고 있기는 힘들기 때문에 주로 시작번지와 크기를 기억한다.

프로그래머는 일반적으로 크기를 정해 놓고 쓰고 있기 때문에 시작번지와 크기로 기억하는것이 좋다.

 

명령어가 기계어로 바뀔때 아래와 같이 명시되어야 한다.

 

동일한 데이터가 크기를 다르게 쓰면 오류가 발생한다. 따라서 고급언어 에서는 사용할 크기 대신 type이라는 자료형이 등장했다.

int x=5; x의 주소와 x는 4바이트의 크기를 같이 가지고 있다. 값은 5이다.

 

포인터의 편리점과 공부법

 

포인터의 사용법만 익히지 말고 '왜?' 라고 질문을 하면서 원리를 이해해야 한다.

직접 명칭을 쓰게 되면 변화가 왔을때 일일히 다 바꾸어야 하지만 간접 명칭을 쓰게 되면 변화가 왔을때 한번만 바꾸어주면 된다.

C언어 문법중에서 유일하게 대상이 정해져 있지 않아도, 실체가 정해져 있지 않아도 사용할 수 있다.

 

메모리 주소 지정 방식

 

운영체제는 메모리 주소를 1바이트 단위로 관리한다.

32비트 윈도우 운영체제는  0 ~ 4,294,967,295번까지의 주소를 사용할 수 있다.

메모리를 사용하려면 반드시 사용할 메모리 주소를 지정해야 한다.

 

직접 주소 지정 방식

 

메모리를 사용할 때 프로그래머가 사용할 메모리 주소를 직접 적는 방식이다.

102번지에 1042라는 값을 2바이트 크기로 저장하겠다.

102번지를 16진수로 변환하면 0x00000066이고 1042를 16진수로 변환하면 0x00000418이다.

 

직접 주소 지정 방식은 C언어의 '변수' 문법과 같다

 

C언어는 '변수'라는 개념으로 직접 주소 지정 방식을 사용한다.

C언어에서 변수를 사용하면 변수에 값을 대입하는 소스코드가 어셈브리 형태의 기계어로 번역된다.

C언어의 직접 주소 지정 방식은 변수 개념을 사용하기 때문에 문법 구조상 서로 다른 함수에 존재하는 변수 참조가 불가능하다.

 

C언어(구조화된 언어)

 

함수를 기반으로 해서 프로그램을 쪼갠다. 직접 주소 지정 방식은 함수 속에서만 사용이 가능하다.

독립화, 모듈화 부분으로 사용할 수 있다. 직접 주소 지정 방식의 한계가 발생한다. 따라서 구조적 문제가 발생한다.

함수가 호출할때 넘기는 매개변수값은 제한이 없다. 그러나 함수가 복귀할때는 한 개만 가지고 복귀할 수 있다.

 

직접 주소 지정 방식은 주소를 직접 명시한다.

간접 주소 지정 방식은 사물함 이라는 매개체를 이용해 주소를 간접적으로 명시한다.

 

간접 주소 지정 방식 : 나의 친구는 홍길동 // 나 = 포인터 , 친구 = 대상

직접 주소 지정 방식 : 홍길동

 

간접 주소 지정 방식

 

102번지에 4바이트 크기의 '주소'가 저장되어 있는데 이 주소에 가서 '값' 1042를 2바이트 크기로 대입하라.

이 명령어에는 대상의 주소가 정해져 있지 않다. 융통성이 커졌다는 이야기 이다.

명령의 대상이 정해져 있지 않고 실체에 대한 표현도 없다. 행위만 제시 되어 있다.

 

 

포인터(Pointer)

 

일반 변수도 주소를 저장할 수 있으나, 저장된 주소의 메모리에 가서 값을 읽거나 저장할 수 있는 기능은 없다.

C언어는 간접 주소 지정 방식으로 동작하는 포인터(Pointer) 문법을 제공한다.

자신이 사용하고 싶은 메모리의 '주소'를 저장하고 있는 메모리가 포인터이다.

포인터 변수는 자료형을 선언하지 않아도 무조건 크기가 4바이트이다.

즉, 포인터의 장점은 융통성이 매우 크다는 것이다. 단점은 메모리를 4바이트 더 사용하고 행위도 한번 더한다. 따라서 속도가 비교적 느리다.

 

ptr은 포인터이다. ptr은 다른 변수의 주소를 저장한다. 다른 변수의 주소를 가진다는 것은 포인터가 가리키는 대상은 일반적으로 주소라는 것이다. 또한  32비트 운영체제에서 주소의 크기는 4바이트이다. 포인터가 시작주소만 가지고 있기 때문에 앞에 붙은 자료형은 작업할 크기를 말한다. short를 변위라고 부르기도 한다.

 

변수가 저장된 메모리 공간의 주소 얻기

 

변수의 주소는 프로그램이 실행될 때마다 달라지기 때문에 포인터 변수에 주소를 직접 입력하는 것보다 프로그램 안에 선언한 다른 변수의 주소를 받아와 사용하는 것이 안전하다. 또한 운영체제가 시작할 때마다 변수의 주소가 계속 바뀌기 때문에 절대 번지(상수)를 사용하면 프로그램에 문제가 발생한다.

따라서 &를 사용하여 번지를 가르킨다.

 

short birthday;  //  short형 변수 birthday를 선언한다.

short * ptr;  //  포인터 변수를 선언한다.

ptr = &birthday;  // birthday 변수의 주소를 ptr 변수에 대입한다. birthday의 주소가 ptr에 저장된다. ptr에 원래 쓰레기값이 들어 있는데 값 자체가 바뀐다.

 

* 키워드의 또 다른 이름, 번지 지정 연산자

 

 

short * ptr = &birthday  //  포인터가 가리키는 대상의 크기가 2바이트인 포인터 변수를 선언하는데 birthday 변수의 주소를 ptr변수에 대입하라

*ptr = 1042;                  //  ptr 포인터가 가리키는 대상에 가서 1042 값을 대입하겠다.