12. 배열과 문자열
배열
- 배열은 자료형이 같은 변수들을 그룹으로 묶어서 관리할 때 사용한다.
- 색인(인덱스)는 0부터 시작한다.
void main(){
short student[20]; //short 타입 변수 20개 만들어라
student[1] = 10; // 배열의 두번째 요소에 10 대입
}
배열을 사용할 때 주의점
[]안에 반드시 상수를 적어야 한다.
short student[20]; //20은 무조건 상수!
int CNT = 30;
int arr[CNT]; //오류!!! 변수 안된다.
배열 초기화
배열의 각 요소에 일정한 값을 대입하여 초기화해서 사용해야 한다.
쉼표를 사용한 배열 초기화
void main(){
short student[20] = {0, };
short data[5] = {3, }; // 결과 -> 3, 0, 0, 0, 0 처럼 된다
}
배열 크기를 생략하여 사용할 수 있음
short data[] = {1, 2, 1, 2, 1};
void main(){
char data[5] = {1, 2, 3, 4, 5};
int result = 0, i;
for(i = 0; i < 5; i++){
result = result + data[i];
}
printf("data 배열의 각 요소의 합은 %d입니다.", result);
}
// 결과 -> data 배열의 각 요소의 합은 15입니다.
문자열
- C언어에서 문자를 저장하는 데 가장 적합한 자료형은 char형
- 문자의 끝에 NULL(널) 문자 0을 추가로 입력해서 ‘이 배열에 저장된 정보는 문자열이다’ 라고 컴파일러에게 알려줘야 한다. (문자의 끝에 0을 넣어주는것은 컴파일러가 왠만하면 알아서 해줌. 굳이 내가 안써도 된다)
- NULL 문자는 0이라고 적어도 되고 ‘\0’이라고 적어도 된다. (왠만하면 역슬래시 써주는게 좋음)
- 배열의 크기와 문자열의 길이는 다르다 ex) HELLO = 배열의 크기:6, 문자열의 길이:5
char data[6] = {'h', 'a', 'p', 'p', 'y', 0}; //문자개수는 5개이고 끝에 0을 덧붙여야 하므로 배열의 크기는 6
char data[6] = "happy"; //이렇게 쓰면 문자열의 끝에 NULL 문자 0이 자동으로 포함된다
- strlen(문자열이 저장된 변수 이름)
- strcpy(복사해서 저장할 변수 이름, 복사할 기존 변수 이름)
- strcat(기존 문자열이 저장된 변수 이름, 새로 덧붙일 문자열)
예제) 문자열의 길이 구하기
#include <string.h> //문자열 표준 함수를 사용하기 위해 추가함
void main(){
int data_length;
char data[10] = {'h','a','p','p','y',0};
data_length = strlen(data);
printf("data length = %d", data_length);
}
//결과 -> data length = 5
예제) 두 개의 문자열 합치기
#include <string.h> //문자열 표준 함수를 사용하기 위해 추가함
void main(){
char data[10] = "abc";
char result[16];
strcpy(result, data);
strcat(result, "def");
printf("%s + \"def\" = %s", data, result);
}
//결과 -> abc + "def" = abcdef
2차원 배열
- 대괄호 []를 두 번 사용해서 선언하는 배열이 2차원 배열이다.
- []연산자는 동일한 우선순위를 가질 때 왼쪽에서 오른쪽으로 연산을 수행한다.
- 즉 왼쪽에 있는 []연산자를 먼저 처리한다
2차원배열 형식 들어가면 오류가 남…………..
예제) 1차원 배열의 바둑판
void main(){
char data[12] = {0,0,2,0,1,1,0,0,2,1,0,2};
int i, x, y;
for(i = 0; i < 12; i++){
x = i % 4 + 1; //열번호 구함
y = i / 4 + 1; //행번호 구함
printf("%d행 %d열에", y, x);
if(data[i] == 1){
printf("검은 돌이 놓여있습니다\n");
}else if(data[i] == 2){
printf("흰 돌이 놓여있습니다\n");
}else{
printf("돌이 없습니다.\n");
}
}
}
//결과 -> 1행 1열에돌이 없습니다.
// 1행 2열에돌이 없습니다.
// ...
// 3행 3열에돌이 없습니다.
// 3행 4열에흰 돌이 놓여있습니다
예제) 2차원 배열의 바둑판
void main(){
char data[3][4] = { {0,0,2,0,}, {1,1,0,0,}, {2,1,0,2} };
int x, y;
for(y = 0; y < 3; y++){
for(x = 0; x < 4; x++){
printf("%d행 %d열에 ", y+1, x+1);
if(data[y][x] == 1){
printf("검은 돌이 놓여있습니다.\n");
}else if(data[y][x] == 2){
printf("흰 돌이 놓여있습니다.\n");
}else{
printf("돌이 없습니다.\n");
}
}
}
}
//결과 -> 1행 1열에돌이 없습니다.
// 1행 2열에돌이 없습니다.
// ...
// 3행 3열에돌이 없습니다.
// 3행 4열에흰 돌이 놓여있습니다
13. 포인터
운영체제의 메모리 관리 방식
운영체제와 프로그래밍
- C언어 소스코드에서 사용한 변수들은 컴파일 작업 후 기계어로 번경되면 모두 메모리 주소로 바뀌어서 적용된다.
- 결국 기계어에서는 변수 이름보다 변수가 위치한 메모리의 주소가 중요하다.
32비트 운영체제와 64비트 운영체제
- 실제 개발 현장에는 32비트로 개발하는 경우가 많다. 32비트 방식으로 개발해도 64비트 운영체제에서 모두 동작하기 때문.
- 32비트 운영체제는 우리가 메모리라고 부르는 RAM을 4GB(기가바이트, 230)밖에 사용하지 못하지만 64비트 운영체제에서는 16EB(엑사바이트, 260)까지 사용할 수 있다.
- 따라서 자신의 시스템이 RAM을 4GB이상 사용한다면 64비트 운영체제를 설치해야 메모리를 100% 다 사용 할 수 있다.
- 64비트의 단점은 기본적으로 메모리 사용량이 많으며 낮은 사양의 컴퓨터에 64비트 운영체제를 설치하는 것은 오히려 손해이다.
메모리 주소 지정 방식
- 메모리를 사용하려면 반드시 사용할 주소를 지정해야하고, 메모리가 1바이트 단위로만 사용되는 것은 아니기 때문에 프로그래머가 메모리를 사용할 때 한 번에 읽거나 저장할 크기를 명시해야 한다.
- 메모리 주소번지까지 컨트롤 하는 경우는 거의 없다.
*컴퓨터는 오직 2진수(0, 1)를 사용한다 *C는 보통 16진수를 활용하며 컴파일러가 알아서 2진수로 변환해준다.
직접 주소 지정 방식
- C언어의 '변수' 문법과 같다.
- 번역기의 도움을 받아서 내부적으로 변수가 주소로 변환되어 결과적으로는 직접 주소 지정 방식을 사용하게 되는 것이다.
- 직접 주소 지정 방식의 한계 : 각 함수의 지역 변수는 해당 함수 안에서만 사용 가능
간접 주소 지정 방식
- 32비트 운영체제에서는 크기를 4바이트로 고정해야 한다.(64비트는 8바이트)
포인터
- c언어에서 직접 주소 지정 방식은 변수 문법이라고 했다.
-
간접 주소 지정 방식은 값을 저장할 '주소'를 메모리에 저장하는 것이다.
- 포인터 문법을 사용해 선언한 포인터 변수는 메모리 주소**만**을 저장하기 위해 탄생한 특별한 변수이다.
- 포인터 변수는 크기가 무조건 4바이트로 정해져있기 때문에 변수의 크기를 적을 필요가 없다.
short birthday;
short *ptr; //포인터 변수 선언
ptr = &birthday; //birthday 변수의 주소를 ptr 변수에 대입
*ptr = 1042 //ptr에 저장된 주소에 가서 값 1042를 대입함. 즉 birthday = 1042
short *ptr = &birthday; //포인터 변수를 선언!
*ptr = 1042; //ptr 포인터가 가리키는 대상에 가서 1042 값을 대입하겠다는 의미. 정의!
//위와 아래는 같지 않다.
예제) call by value. 일반변수는 값 변동x
void Test(short data){
short soft = 0;
soft = data; //soft = 5
tips = 3; //오류 발생
}
void main(){
short tips = 5;
Test(tips);
}
//data = tips;
예제) call by reference. 포인터 사용시 원본 값 변경된다.
void Test(short *ptr){
short soft = 0;
soft = *ptr; //soft = tips;
*ptr = 3; //tips = 3;
}
void main(){
short tips = 5;
Test(&tips);
}
//ptr = &tips;
예제) 간접 주소 지정 방식(포인터)으로 변수 값 교환하기
void Swap(int *pa, int *pb){
int temp = *pa; //참조
*pa = *pb; //복사
*pb = temp; //참조
}
void main(){
int start = 96, end = 5;
if(start > end){
Swap(&start, &end);
}
printf("start : %d, end : %d", start, end);
}
// 결과 -> start : 5, end : 96
인자값이 2개라서 return값은 하나만 되기 때문에 포인터 사용한다. C에서만 return값을 하나만 사용할 수 있다.
포인터와 const 키워드
주소 변경 실수를 방지하기 위해 const 키워드를 사용한다.
- int * const p; : 주소를 변경하면 오류 발생
- const int *p; : 대상의 값 변경시 오류 발생
- const int * const p; : 둘 다 잠금
포인터 변수의 주소 연산
배열에서 사용시 좋다!!
포인터 변수에 +1을 하면 자신이 가리키는 대상의 크기만큼 증가한다.
short data = 0;
short *p = &data;
p = p+1; //포인터 변수에 저장된 주소 값을 1만큼 증가시킴
char *p1 = (char *)100; //p1에 100번지를 저장함
short *p2 = (short *)100;
int *p3 = (int *)100;
double *p3 = (double *)100;
p1++; //1바이트이기 때문에 p1에 저장된 주소값이 101
p2++; //2바이트라서 102
p3++; //4바이트라서 104
p4++; //8바이트라서 108
포인터가 가리키는 대상의 크기
변수 p에 일반 변수 data의 주소 값을 저장하고 포인터 변수 p를 이용하여 data 변수의 값을 변경하는 경우에는 두 변수의 자료형을 같게 지정하는것이 일반적이다.
int data = 0;
short *p = (short *)&data;
//앞에 두칸만 갖다 사용
예제) int형 변수에 저장된 값을 1바이트 단위로 출력
void main(){
int data = 0x12345678, i;
char *p = (char *)&data;
for(i = 0; i < 4; i++){
printf("%x, ", *p);
p++;
}
}
// 결과 -> 78, 56, 34, 12,
위 예제는 p가 가지고 있는 주소 값을 옮기는 방식으로 작업했는데, p의 주소 값을 변경하지 않고 data 변수 값을 1바이트씩 출력하고 싶다면 반복문 코드만 다음과 같이 변경하면 된다.
for(i = 0; i < 4; i++){
printf("%x, ", *(p + i) );
}
void *형 포인터
- 주소 값을 저장할 수는 있지만 해당 주소에서 값을 읽거나 저장할 때 사용하는 크기는 정해져 있지 않다.
- 즉 사용할 메모리의 시작 주소만 알고 끝 주소를 모를 때 사용하는 포인터 형식이다.
- void * 는 주소를 사용할 때 반드시 ‘사용할 크기’를 표기해야 한다.
int data = 0;
void *p = &data; //data의 시작 주소를 저장함
*p = 5 //오류 발생. 대상 메모리의 크기가 지정되지 않음.
int data = 0;
void *p = &data;
*(int *)p = 5; //형 변환 문법을 사용하여 대상의 크기를 4바이트로 지정하므로 data 변수에 5가 저장됨
예제) void *를 사용항 대상 메모리의 크기 조절하기
int GetData(void *p_data, char type){
int result = 0;
if(type == 1){
result = *(char *)p_data;
}else if(type == 2){
result = *(short *)p_data;
}else if(type == 4){
result = *(int *)p_data;
}
return result;
}
void main(){
int data = 0x12345678;
printf("%x", GetData(&data, 2));
}
//결과 -> 5678
Comments