%d 10진수
%x 16진수
%p 포인터 값을 16진수로 출력
내 코드
#include<stdio.h>
int main(void)
{
int arr[] = {3,5,2,11,10};
int i = 0;
int imax = arr[0];
for (i = 0; i < 4; i++)
{
if (arr[i] < arr[i + 1])
{
imax = arr[i + 1];
}
}
printf("%d", imax);
}
struct student
{
char name[10];
int age;
int height;
}
struct 키워드는 구조체라는 자료형을 의미
student는 내가 만든 구조체의 이름
name, age, height는 구조체 멤버
모든 사람이 아는 개념 ⇒ 구체적
나만 알고 있는 개념 ⇒ 추상적
strcpy(대상문자열, 원본문자열);
strcpy 함수는 문자열을 다른 곳으로 복사하며 함수 이름은 string copy에서 따왔다. (string.h 헤더 파일에 선언되어 있음).
struct student
{
char name[10];
int age;
int height;
};
int main(void)
{
student str1;
student str2;
...
}
struct(구조체) student(자료형) str1(변수 이름);
struct student
{
char name[10];
int age;
int height;
}str1,str2;
구조체를 간단하게 선언하는 방법
str1과 str2는 각 18바이트로, 구조체 멤버들의 크기 값의 합이다.
구조체 변수를 통해 구조체 멤버의 값을 참조해야 한다.
[구조체 변수명].[구조체 멤버]
ex) st1.name, st1.age, st1.height
멤버에 접근 시 .(점)을 사용하는데, 이를 직접 접근이라고 한다.
#include <stdio.h>
#include <string.h>
struct student
{
char name[10];
int age;
int height;
}st1;
int main(void)
{
strcpy_s(st1.name, "김혜진");
st1.age = 2;
st1.height = 15;
printf("이름 = %s, 나이 = %d, 키 = %d\n", st1.name, st1.age, st1.height);
return 0;
}
출력결과
이름 = 김혜진, 나이 = 2, 키 = 15
int a, *pA;
pA = &a;
#include <stdio.h>
#include <string.h>
struct student
{
char name[10];
int age;
int height;
}st, * pSt;
int main(void)
{
pSt = &st;
strcpy_s(pSt->name, "김혜진");
pSt->age = 2;
pSt->height = 15;
printf("이름 = %s, 나이 = %d, 키 = %d\n", pSt->name, pSt->age, pSt->height);
return 0;
}
*pSt와 st의 타입이 동일하므로 값에 접근 가능함
포인터 변수에서 구조체 멤버를 참조할 시 ->를 사용하였는데 이는 포인터의 간접 참조 연산자를 의미한다.
-> Arrow(애로우)라고 부르기도 함.
#include <stdio.h>
#include <string.h>
struct object
{
char name[10];
int height;
int weight;
}obj;
int main(void)
{
printf("물건의 이름 : ");
fgets(obj.name, 9, stdin);
obj.name[strlen(obj.name)-1] = '\0';
printf("물건의 높이(cm) : ");
scanf_s("%d", &obj.height);
printf("물건의 무게(kg) : ");
scanf_s("%d", &obj.weight);
printf("\n");
printf("보낼 물건의 정보 : %s, %dcm, %dkg", obj.name, obj.height, obj.weight);
}
출력결과
물건의 이름 : 책
물건의 높이(cm) : 20
물건의 무게(kg) : 30
보낼 물건의 정보 : 책, 20cm, 30kg
gets(obj.name)
입력받는 문자보다 더 많은 문자를 받을 수 있는 문제점이 있음.
fgets(obj.name, 9 ,stdin)
입력하는 타겟 지정, 입력받을 수 (그 이상으로 입력받으면 뒤를 자름)
fgets() 함수는 scanf()나 gets()와는 달리 뒤에 개행문자가 붙는다.
str[strlen(str) - 1] = '\0'; 개행문자 제거
fgets 함수는 실행 후 마지막 인덱스에 \n을 삽입하기 때문에 자동 개행이 된다.
일반적 초기화 방법
[구조체 변수].[구조체 멤버] = 데이터 값
선언과 동시에 초기화 방법
struct student
{
char name[10];
int age;
int height;
}st = {"김혜진", 2, 15};
또는
student st = {"김혜진", 2, 15};
출력결과
이름 = 김혜진, 나이 = 2, 키 = 15
구조체는 자료형이므로 배열형으로 선언할 수 있다.
여러 사람의 정보를 관리하고 싶다면 구조체 변수를 배열로 선언하면 된다.
#include <stdio.h>
#include <string.h>
struct student
{
char name[10];
int age;
int height;
}st[5];
또는
student st[5];
struct student
{
char name[10];
int age;
double height;
}st[5] = { {"김혜진", 2, 15},
{"이주성", 3, 125.8},
{"송유흠", 4, 15.0},
{"김민성", 5, 145.6},
{"김민준", 6, 152.7}
};
int main(void)
{
int i;
printf("st[i] 구조체 멤버의 초기값 출력\n");
for (i = 0; i < sizeof(st) / sizeof(st[0]); i++)
{
printf("이름 = %s, 나이 = %d, 키 = %f\n", st[i].name, st[i].age, st[i].height);
}
st[2].height = 146;
printf("\nst[2] 구조체 멤버의 데이터 변경\n");
printf("이름 = %s, 나이 = %d, 키 = %f\n", st[2].name, st[2].age, st[2].height);
return 0;
}
출력결과
st[i] 구조체 멤버의 초기값 출력
이름 = 김혜진, 나이 = 2, 키 = 15.000000
이름 = 이주성, 나이 = 3, 키 = 125.800000
이름 = 송유흠, 나이 = 4, 키 = 15.000000
이름 = 김민성, 나이 = 5, 키 = 145.600000
이름 = 김민준, 나이 = 6, 키 = 152.700000
st[2] 구조체 멤버의 데이터 변경
이름 = 송유흠, 나이 = 4, 키 = 146.000000
구조체는 자료형이므로 포인터형으로 선언할 수 있다.
앞서 사용했던 student 구조체를 다음과 같이 포인터형으로 선언할 수 있다.
struct student *pSt;
예제
#include <stdio.h>
#include <string.h>
struct student
{
char name[10];
int age;
double height;
}st[5] = { {"김혜진", 2, 15},
{"이주성", 3, 125.8},
{"송유흠", 4, 15.0},
{"김민성", 5, 145.6},
{"김민준", 6, 152.7}
};
int main(void)
{
int i;
struct student* pSt;
pSt = st;
printf("st[i] 구조체 멤버의 초기값 출력\n");
for (i = 0; i < 5; i++)
{
printf("이름 = %s, 나이 = %d, 키 = %.2f\n", st[i].name, st[i].age, st[i].height);
}
printf("\nst[2] 구조체 포인터를 이용한 출력\n");
for (i = 0; i < 5; i++)
{
printf("이름 = %s, 나이 = %d, 키 = %.2f\n", (pSt+i)->name, (pSt + i)->age, (pSt + i)->height);
}
return 0;
}
출력결과
st[i] 구조체 멤버의 초기값 출력
이름 = 김혜진, 나이 = 2, 키 = 15.00
이름 = 이주성, 나이 = 3, 키 = 125.80
이름 = 송유흠, 나이 = 4, 키 = 15.00
이름 = 김민성, 나이 = 5, 키 = 145.60
이름 = 김민준, 나이 = 6, 키 = 152.70
st[2] 구조체 포인터를 이용한 출력
이름 = 김혜진, 나이 = 2, 키 = 15.00
이름 = 이주성, 나이 = 3, 키 = 125.80
이름 = 송유흠, 나이 = 4, 키 = 15.00
이름 = 김민성, 나이 = 5, 키 = 145.60
이름 = 김민준, 나이 = 6, 키 = 152.70
열거형은 enumeration의 약자로, enum(이넘)이라고 읽는다.
데이터들을 열거한 집합이다.
컴파일러는 열거형 멤버들을 정수형 상수로 취급한다.
키워드는 enum 을 사용하여 정의한다.
enum Week
{
sun = 0;
mon
tue
wed
thu
fri
sat
}
열거형의 멤버들은 각 요일을 나타낸다.
첫 번째 멤버 sun을 0으로 설정하면 다음 멤버 mon은 1씩 증가하여 1, tue는 2...
enum Week
{
sun = 1,
mon,
tue,
wed,
thu,
fri,
sat
};
int main(void)
{
enum Week day;
printf("요일을 입력하세요(1.일 2.월 3.화 4.수 5.목 6.금 7. 토 : ");
scanf_s("%d", &day);
switch (day)
{
case sun:
printf("일요일엔 짜파게티\n");
break;
case mon:
printf("월요일엔 스트레스\n");
break;
case tue:
printf("화요일에 만나요\n");
break;
case wed:
printf("수요일엔 빨간 장미를\n");
break;
case thu:
printf("목요일엔 뭐하지?\n");
break;
case fri:
printf("금요일엔 달걀 후라이\n");
break;
case sat:
printf("토요일엔 나들이\n");
break;
default:
printf("잘못 입력하셨습니다.\n");
break;
}
return 0;
}
열거형으로 사용자 정의 타입 구현 가능
실무에 자주 적용됨!
메모리는 크게 코드영역, 스택영역, 힙영역, 데이터영역 총 4가지로 구분
힙 영역이 동적 영역에 해당한다.
코드(code)영역
소스코드가 저자오디는 영역으로, 실행할 명령어들이 순서대로 쌓인다.
CPU가 이 영역에서 명령어들을 하나씩 가져다가 처리한다.
스택(stack)영역
스택이란 모든 원소들의 삽입 삭제를 한쪽 방향에서만 수행하도록 하는 선형 자료구조이다.
이를 후입 선출방식(LIFO)이라고 한다. 나중에 들어온 녀석이 먼저 빠져나간다는 뜻
우리가 지금껏 사용한 지역변수 및 매개변수 등은 모두 스택 메모리를 사용
void Test(int a)
{
char b = 'A',
int c = 1;
double d = 3.14;
}
이러한 매개 변수 및 지역 변수가 스택 메모리에 어떠한 구조로 저장되는지 보자.
힙(heap)영역
힙은 컴퓨터 메모리의 일부가 할당되었다가 회수되는 일들의 반복을 의미
힙은 컴파일 시가 아닌 실행(런타임) 시 사용자로부터 할당 메모리를 입력 받음
데이터 영역
전역변수와 static 변수가 저장되는 메모리 영역
이 메모리는 프로그램 종료 시 소멸
객체지향에서는 동적메모리를 꼭 사용함. (중요)
int a;
double b;
메모리 할당은 컴파일 타임(compile-time)에 이루어진다.
int studnet[10];
int student[100];
이는 미봉책에 불과하다. 학생 수가 어떻게 변할 지 모른다. 어떤 학교는 1000명 이상이 될 수도 있다.
int num;
fputs("학생 수를 입력하세요: , stdout);
scanf_s("%d", &num);
int student[num];
문제점
scanf는 런타임(run-time)에 실행
int student[num]은 컴파일타임(compile-time)에 실행
런타임에 입력받은 변수를 컴파일 타임에 대입하는 형태는 논리에 맞지 않는다.
소스코드⇒컴파일(obj)(컴파일 타임)⇒링크(lib)⇒실행(exe)(0과 1)(런타임)
scanf는 런타임에 입력을 받아야 하는데, 배열의 길이는 컴파일 타임에 결정이 되어야하므로 시점이 맞지 않아 오류가 남.
해결방법
실행 중에 학생 수를 알아야하는 경우, 동적 메모리 할당 기법을 통해 문제 해결이 가능하다.
void* malloc(size_t size);
엠얼록 또는 말록이라고 읽는다.
전달인자 size는 바이트 단위로 입력
메모리 할당이 되면 메모리의 주소값을 리턴
메모리 부족 시 NULL 포인터 리턴
리턴형이 void*인데, 타입이 지정되어 있지 않는 포인터를 리턴 ( 난 당신이 원하는 메모리 크기만큼 할당해줄테니, 메모리는 당신이 원하는 형태로 정해서 사용하세요.)
void free(void* memblock);
메모리 사용 후 반드시 해제해주어야 한다.
전달인자로 메모리를 가리키는 포인터를 대입한다. (이 메모리는 자유예요)
free를 안해주면 메모리 누수(memory leak)가 일어난다.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int num;
int* student; // 스택메모리 할당 (4바이트) 컴파일 시점 할당
fputs("학생 수를 입력하세요 : ", stdout);
scanf_s("%d", &num);
student = (int*)malloc(sizeof(int) * num); // 런타임 시점 할당
if (student == NULL)
{
printf("메모리가 부족하여 메모리를 할당할 수 없습니다.\n");
return 0;
}
printf("할당된 메모리의 크기는 %d 입니다\n", sizeof(int) * num);
free(student);
return 0;
}
정적메모리 <-> 동적메모리 차이
사용자로부터 입력받는 값이 있음.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int num, i, total = 0;
int* student;
fputs("학생 수를 입력하세요 : ", stdout);
scanf_s("%d", &num);
student = (int*)malloc(sizeof(int) * num);
if (student == NULL)
{
printf("메모리가 부족하여 메모리를 할당할 수 없습니다.\n");
return 0;
}
for (i = 0; i < num; i++)
{
printf("%d번째 학생의 성적 입력 : ", i + 1);
scanf_s("%d", &student[i]);
}
for (i = 0; i < num; i++)
{
total += student[i];
}
printf("총점: %d, 평균 : %d \n", total, total / num);
free(student);
return 0;
}
실시간 메모리를 할당하여 사용한다 해도 사용중에 메모리 크기를 더 늘려야 하는 경우가 발생할 수 있다.
malloc 함수로 할당된 메모리를 다시 동적으로 재할당해주는 함수가 realloc이다.
void *realloc(void* memblock, size_t size);
기존 동적 메모리의 값을 모두 가지면서 크기만 변경된 동적 메모리를 만들어낸다.
기존 메모리와 새로 확장할 메모리를 모두 포함한 크기로 설정한다.
realloc을 통해 힙메모리를 재할당하되, malloc을 통해 할당한 메모리 arr에 메모리를 덧붙여 할당한다.