컴파일러 : 고급언어로 쓰인 프로그램을 컴퓨터에서 실행될 수 있는 형태로 번역해주는 프로그램 << GCC가 그중 하나
c언어 개발 환경 : 코딩, 디버깅, 컴파일을 한 곳에서 처리할 수 있는 소프트웨어
강의 영상에서 사용된 개발 환경 : Dev C++ << 사용중이던 visual studio로 대체
visual studio의 컴파일러는 Microsoft Visual C++ Compiler 로 추정 << 홈페이지 문구
#include <stdio.h> << #(전처리기) include(헤더 파일을 포함) <stdio.h>(STanDardInputOutput.Headerfile) << 표준 입출력 헤더 파일
int main(void){ << int(함수의 반환 자료형) main(프로그램이 최우선 실행시키는 함수 키워드) void(main이 파라미터를 받지 않음을 명시)
printf("Hello World!"); << printf(stdio.h에 포함된 표준 출력 함수) "Hello World!"(char[13])
return 0; << return 0(main함수가 반환하는 값 0 << 프로그램의 종료 여부)
}
(해석 : 표준 입출력 라이브러리를 포함하여 출력 함수를 사용 가능하게 하고, 문자열을 출력한 뒤, main 함수를 정수 0 으로 반환하여 프로그램 종료)
코드 작성 후 compile and run 을 통해 실행 << Hello World! 가 출력되는 프로그램
변수 : 프로그램이 실행되는 동안 언제든지 저장된 값이 변경될 수 있는 공간 << 변하는 수
상수 : 한번 정해지면 값이 변경되지 않는 데이터 << 항상 변함없는 수
메모리 : 프로그램이 올라가 실행되는 공간, 프로그램의 모든 변수와 상수의 값을 저장
int x; << 변수의 선언, 정수형 변수의 식별자 x 및 그 공간을 할당
x = 5; << 변수의 초기화, 정수형 변수 x의 값을 정수 5로 초기화 (대입 연산자 이용)
printf("%d", x); << 변수의 사용, printf에서 서식지정자 %d를 이용하여 정수형 변수 x 출력
printf("변수 x의 메모리 크기 : %d",sizeof(x)) << 정수형 변수의 크기, sizeof() 연산자를 사용하여 변수 x의 크기 출력 << 정수형 변수의 크기인 4byte 반환
printf("%d/n%d",x,x) << 문자열 내에서 특정 기능을 수행하는 이스케이프 문자 /n을 통해 개행
float y = 1234.1234;
printf("%.2f"); << 서식지정자 %.2f를 통해 실수형 변수 y의 값을 소수점 두번째 자리까지(1234.12) 출력
자료형의 최댓값을 넘는 일종의 오류
// limits.h 를 include하여 INT_MAX 와 같은 각 자료형의 최댓값 상수를 포함
int a = INT_MAX; << 정수형 변수 a를 선언하고 정수형의 최댓값을 초기화
printf("%d",a+1); << INT_MAX(정수형의 최댓값) + 1 을 정수형으로 출력 << 오버플로우로 인해 정수의 최솟값 출력
+ 더하기
- 빼기
* 곱하기
/ 나누기 (정수의 경우 몫)
% 나머지 (모듈러)
#define MONTHS 12 << 매크로상수, #(전처리자) MONTHS(상수의 식별자) 12(값)
double monthSalary = 1000.5;
printf("%.2f", monthSalary * MONTHS); << 실행 시 1000.5 * 12인 12006을 %.2f을 통해 12006.00 로 출력
char 는 작은따옴표를 통해 1byte 크기 안에 들어가는 문자 하나를 저장
실제로는 해당 문자에 대응하는 문자 테이블 ASCII 코드 값을 저장 (char는 내부적으로 숫자를 가진다)
그러므로
int a = 65;
printf("%c",a) << 65 가 서식지정자 %c 를 통하여 char 'A' 출력
65 == 'A' 또한 true
%d 10진수
%o 8진수
%x 16진수
1 + 2 에서 1, 2는 피연산자, +는 연산자
3강 노트의 "C의 두 숫자에 대한 연산자" 는 두 피연산자 사이에 들어가 사용되는 이항 연산자에 해당
#define SPM 60 << 메크로 상수
int input = 1000;
int minute = input / SPM; << 총합 초를 60으로 나눔(정수사이의 연산이므로 그 몫) == 총합 분
int second = input % SPM; << 총합 초를 60으로 나눈 값의 나머지 == 60(분) 이 되지 않은 초 == 실제 초
printf("%d분 %d초",minute,second"); << 총합 초를 실제 초, 분으로 바꾸어 출력
x = 5; 와 같이 사용하는 주로 변수에 값을 입력하기 위한 이항 연산자
변수, 해당 변수의 자료형 값 을 피연산자로 가지며 그 중간에 위치한다.
x = a; 의 경우 x의 값을 a로 변경(초기화) 한다.
상수를 정의할 때 쓰이기도 한다.
변수 하나를 피연산자로 두어 그 앞 또는 뒤에 붙여 사용하는 단항 연산자
연산의 경우 피연산자에 대해 ++ 는 1을 더하며 -- 는 1을 뺀다.
피연산자를 기준으로 앞에 붙이면 전위, 뒤에 붙이면 후위로 기능한다.
전위의 경우 연산을 우선 실행한 후 해당 줄을 실행하고,
후위의 경우 해당 줄의 실행을 마친 이후 연산을 실행한다.
--x, ++x 전위 연산자
x--, x++ 후위 연산자
변수의 값을 해당 변수에 기반하여 변경하는 경우
x = x + 5;
x = x * 2;
이와 같이 대입 연산자의 양쪽에 해당 변수가 포함되어야 하는데
x += 5;
x *= 2;
처럼 실행 연산자를 대입 연산자 앞에 붙여 같은 효과를 볼 수 있다.
증감 및 복합 대입 연산자는 코드의 길이를 줄일 수 있기에 효율적이다.
3강 노트의 "C의 두 숫자에 대한 연산자"의 모든 연산자가 복합 대입 연산자로 사용될 수 있다.
-x
x * -1 을 반환한다.
int x = 50;
int y = 30;
printf("x == y : %d", x == y);
3번째 줄의 x == y는 x와 y가 같은가 에 대한 관계 연산자이다. (대입 연산자와 구별하기 위함)
관계 연산자는 이와 같이 두 값 사이의 관계를 boolean 값으로 반환한다.
boolean은 true 또는 false를 가지나 %d의 형식으로 출력할 경우 true는 1, false는 0으로 출력된다.
C는 boolean 값을 필요로 할 때 0은 false로, 0이 아닌 값은 true로 자동 형변환을 하여 사용할 수도 있기에 1 또는 0 으로 boolean 값을 대체할 수 있다.
! (boolean 피연산자에 대하여) 아니다[부정]
== 같다
!= 다르다
<, > 크다, 작다
<=, >= 이상, 이하
&& (boolean 피연산자에 대하여) 그리고[논리곱]
|| (boolean 피연산자에 대하여) 또는[논리합]
(조건식) ? 값1 : 값2
의 형태로 (조건식) 이 참이면 값1을, 거짓이면 값2를 반환하는 연산자(조건문 if의 축소판)
(조건식) 의 자리에는 논리값을 가지는 관계 연산자, 혹은 boolean 값 자체가 들어간다.
하나의 식에서 짧은 조건문을 사용할 수 있기에 효율적이다.
(x > 0) ? x : -x;
조건 연산자를 이용하여 절댓값을 구할 수 있다.
//math.h 를 include하여 abs()[절댓값], pow()[거듭제곱] 와 같은 수학 함수 포함
double x = pow(2.0,20.0); << 값 초과로 인한 overflow를 막기 위해 double 사용
printf("2²⁰ = %.0f",x); << %.0f 를 통하여 실수형인 double을 정수형 처럼 출력
pow() 함수는 두개의 파라미터(a,b)를 가지며, a의 b거듭제곱을 반환한다.
조건문과 반복문은 프로그램의 실행 흐름을 조절하는 제어 함수이다.
조건문은 조건에 따라 일부 코드만 실행하여 나머지 코드의 실행을 생략하고
반복문은 조건에 따라 해당 코드를 반복한다.
반복문도 항상 조건식을 가진다는 점에서 조건문에 속한다 할 수 있으나,
조건에 따른 결정이 주 목적인 함수와, 반복 실행이 주 목적인 함수를 구별하기 위하여 따로 분리하 칭한다.
[main() 내에서 정의된 int a; 의 경우]
변수[피연산자]는 (a = 0) 과 같이 초기화하여 사용
/
[새로 지역변수를 정의 및 초기화하는 경우]
변수[피연산자]는 (int a = 0) 과 같이 정의 및 초기화하여 사용
—for(int i = 0 ; i < 5; i++){printf("%d ",i);}
위의 for문은 int i를 지역변수로 사용하고, i가 1씩 증가하여 출력 결과는
[0 1 2 3 4 ] 가 된다.
for (int i = 0; i < 5; i++) {
printf("%d", i);
}
scanf("(서식지정자)",&변수); 의 형태로 사용하며 (& 는 뒤 변수의 주소값에 해당)
서식지정자에 맞는 값을 입력받았을 때, 해당 값이 담긴 주소를 입력 변수 주소에 대입한다.
int x 의 경우
scanf("%d",&x);
getchar()
를 입력하여 쓰레기 값(공백문자) 처리 및 개행
#define _CRT_SECURE_NO_WARNINGS
전처리기를 통해 경고를 무시한다.
printf("문자열 (서식지정자)",값); 의 형태로 사용하며, (서식지정자)와 같이 값은 생략 가능하다.
char o;
int x, y;
while (1) {
printf(">> ");
scanf("%d %c %d", &x, &o, &y);
if (o == '+') {
printf("\n%d", x + y);
}
else if (o == '-') {
printf("\n%d", x - y);
}
else if (o == '*') {
printf("\n%d", x * y);
}
else if (o == '/') {
printf("\n%d", x / y);
}
else {
printf("잘못된 연산자\n");
continue;
}
break;
}
지역(함수) 바깥에서 정의되어 모든 지역에서 접근할 수 있는 변수
지금까지는 프로그램이 실행시키는 main 함수 내부에서만 코드를 작성했지만, main 함수 외부의 함수(사용자 정의 함수)에서 변수를 사용하기 위하여 전역변수를 선언한다.
int sum(int a, int b){
return a + b;
}
의 경우int a = sum(1,2) - sum(3,4);
와 같이 값 -4로 대체되어 사용할 수 있다.금액을 각 화폐의 수로 변환하여 출력하는 프로그램 (배열을 사용하지 않아 효율과 가독성이 좋지 않다.)
#include <stdio.h>
#define m1 50000
#define m2 10000
#define m3 5000
#define m4 1000
#define m5 500
#define m6 100
void m(int a) {
int a1 = a / m1;
a -= m1 * a1;
int a2 = a / m2;
a -= m2 * a2;
int a3 = a / m3;
a -= m3 * a3;
int a4 = a / m4;
a -= m4 * a4;
int a5 = a / m5;
a -= m5 * a5;
int a6 = a / m6;
printf("오만원 지폐 : %d장\n일만원 지폐 : %d장\n오천원 지폐 : %d장\n일천원 지폐 : %d장\n오백원 동전 : %d개\n일백원 동전 : %d개\n",a1,a2,a3,a4,a5,a6);
}
int main(void) {
printf(">> ");
int a;
scanf("%d", &a);
m(a);
return 0;
}
반복문(for 또는 while)을 이용하여 특정한 처리를 반복하는 함수
함수 내부에서 자기 자신을 호출하여 특정한 처리를 반복하는 함수
#include <stdio.h>
// 반복 함수
int f1(int a) {
int r = 1;
for (int i = 1; i <= a; i++) {
r *= i;
}
return r;
}
//재귀 함수
int f2(int a) {
if (a == 1) {
return 1;
}
return a * f2(a - 1);
}
int main(void) {
printf(">> ");
int a;
scanf("%d", &a);
printf("f1 : %d / f2: %d", f1(a), f2(a));
return 0;
}
자료형에 대하여 연속적인 값을 저장할 수 있는 공간
자료형 식별자[길이]; 의 형태를 가진다.
변수에 길이만 추가되었다는 듯이 선언하면 된다. (실제로 변수에 길이만 추가된거 아니다)
길이는 배열이 가지는 칸(인덱스)의 개수를 의미한다.
배열의 크기(용량)은 (자료형의 크기 x 길이)로 정해진다.
int a[3]; 의 경우 int의 크기인 4byte x 3이므로 12byte가 된다.
int a[2]; 일때 a의 값은 a[0]의 주솟값이다.
사실 a 자체는 a의 인덱스 시작점(a[0]의 주솟값)을 가지고 있으며,
실제 값을 얻기 위해서는 식별자[번호] 의 형식으로 [번호] 번째 인덱스의 값을 얻을 수 있다.
배열의 각 칸에 해당하며 각각 번호를 0부터 차례로 가진다.
인덱스가 3개면 각 칸의 번호는 [0], [1], [2] 이 된다.
배열을 이용하여 다시 만든 9강 노트의 프로그램
#include <stdio.h>
int ms[6] = { 50000,10000,5000,1000,500,100 };
char mc[6][10] = { "오만원\0","만원\0","오천원\0","천원\0","오백원\0","백원\0" };
void m(int a) {
int md[6];
for (int i = 0; i < 6; i++) {
md[i] = a / ms[i];
a -= ms[i] * md[i];
}
for (int i = 0; i < 4; i++) {
printf("%s 지폐 : %d장\n", mc[i], md[i]);
}
for (int i = 4; i < 6; i++) {
printf("%s 동전 : %d개\n", mc[i], md[i]);
}
}
int main(void) {
printf(">> ");
int a;
scanf("%d", &a);
m(a);
return 0;
}
2개 이상의 차원을 가진 배열
2차원 배열 a >> {a[0], a[1], a[2]}, 실제 값 >> a[0]의 주소
1차원 배열 a[0] >> {a[0][0],a[0][1]}, 실제 값 >> a[0][0]의 주
int a[9][9] = { {0,} };
printf(">> ");
int n = 0;
scanf("%d", &n);
for (int i = 1; i < n+1; i++) { << 입력(인덱스 개별 접근)
for (int j = 1; j < 10; j++) {
a[i-1][j-1] = i * j;
}
}
for (int i = 0; i < n; i++) { << 출력
for (int j = 0; j < 9; j++) {
printf("%d\t", a[i][j]);
}
printf("\n");
}
메모리의 주소를 가지는 변수
자료형* 식별자; 의 형태를 가지며, 자료형 뒤에 * 기호를 꼭 붙여주어야 한다.
포인터는 보통 원시 자료형의 주소를 가지므로 예시로 int a = 0; 의 주소로 초기화하려면 &a와 같이 &를 붙여 해당 변수의 주소값으로 초기화하자.
고대 기록 (이전에 들었던 다른 강의 필기, ※가독성 떨어짐 주의)
포인터 << 쉽게 설명하면 변수를 지켜보는 변수이다. int p = &a 라면 p는 a를 지켜보는 << 이때 p의 값을 a2로 변경하면 p는 a2를 지켜보게 되고, p의 값을 5로 변경하면 지켜보고 있는 변수의 값을 5로 바꾸는 것
선언 int p << 앞에 를 붙인다.
포인터 사용 printf("%p", &i); << 변수 i의 주솟값 출력
예를 들어 변수 a를 선언하는 순간 메모리의 특정한 공간에 변수 a를 저장하는데, a(포인터 a는) 변수 a가 저장된 메모리의 특정한 주소를 알려주는 것
int a = 10; 일때 int p = &a; 이후 p를 보면 저장된 메모리의 주소가 나오지만 p의 값을 보면 10이 나온다. 이는 원래 int a의 주소였던 p의 주소를 찾는것인데, 주소의 주소로 역으로 타고올라가 a의 값이 나오는 거 같다.
위 상태에서 p = 5 를 실행하면 p의 값인 &a의 주소인 a의 값이 5가 되는 것 << 한마디로 저건 a = 5
변수의 주소 = 포인터
포인터의 주소 = 변수
예를 들어 변수 a의 주소를 가진 포인터 p의 경우, p를 변경하는건 메모리에서 참조하는 주소의 위치를 변경하려는거고, p를 변경하면 해당 메모리의 위치가 가진 값, a의 변수값이 변경된다. p는 p의 주소, a의 주소의 주소가 되서 결국에 a가 된다는 것int a1 = 5; int a2 = 8; int* p; //공간이 정해지지 않은 포인터 p p = &a1; //포인터 p에 a1의 주소를 넣어줌으로서 공간을 만들어주고 int* p = &a1의 효과를 준다. << 아직 공간이 정해지지 않았는데 그 공간에 값을 넣는 것은 불가능 printf("%d \n", *p); //p는 a1의 주소를 갖고 있는데 포인터 p의 주소를 물어 p의 값 &a1의 주소인 5가 나오는 것 *p = a2; //이제 p는 공간이 있어 이렇게 바로 주소에 대입할 수 있다. << p는 아직 a1의 주소를 가지는데, 이상태에서 p의 주소를 a2로 바꾸면 &a1의 주소인 a1을 a2로 바꾸는 것이다. printf("%d", a1); //그러니까 여기선 a1이 8로 나온다. printf("%d \n", *p); //p는 아직도 a1의 주소를 담고있으니, p의 주소를 물으면 위에 대입한대로 *p = a1이니 a1인 8이 나온다.
char 배열을 부르는 말 char포인터
char 배열이 있다면 gets(배열); 을 통해 엔터 이전까지의 문자열을 입력받을 수 있다. 이번 영상으로 처음 알았다.
char str[100];
gets(str);
printf("%s", str);
#include <string.h>
char s1[6] = "Hello";
char s2[6] = "Hello";
strcmp(s1,s2);
char s3[6];
strcpy(s3,s1);
문자열의 경우 배열이기에 변수명으로 비교하는 것이 불가능하다(변수 값 : 주소)
본래 인덱스에 접근해서 개별적으로 char가 동일한지 확인해야 하지만, strcmp() 함수를 사용하여 해결할 수 있다. (두 문자열이 동일하면 0, 다르다면 사전적으로 앞설 때 1, 아닐 때 -1 반환)
동일한 이유로 문자열 복사 역시 본래 개별적으로 인덱스의 char를 복사해야 했으나, strcpy() 함수를 사용하여 해결할 수 있다. (앞 배열에 뒷 배열의 문자열을 복사)
커스텀자료형 여러 값을 하나로 묶는 틀 (클래스의 모체)
구조체 배열은 데이터베이스의 테이블과 비슷한 구조를 가지며, 체계적인 자료 관리가 가능하여 효율적.
main의 밖에서
struct 식별자 {
자료형 (개별)식별자;
};
#include <stdio.h>
typedef struct {
int x;
int y;
}point;
int main(void) {
point a[4];
for (int i = 0; i < 4; i++) {
printf("점 %d 좌표 >> ",i);
scanf("%d %d", &a[i].x, &a[i].y);
}
for (int i = 0; i < 4; i++) {
printf("%d번 좌표 : (%d,%d)\n", i+1, a[i].x, a[i].y);
}
return 0;
}
main 안에서
struct (정의된)식별자 식별자;
struct의 식별자는 int char와 같이 자료형을 쓰듯 사용하면 된다.
struct strA {int a;}; 의 경우
main에서 struct strA A; 로 선언 시
strA 안의 int a를 사용하는 경우
A.a로 사용 가능하다.
선언에서의 식별자와 정의에서의 (개별)식별자 사이의 . 기호를 붙여 사용
#include <stdio.h>
struct student {
unsigned int num;
int score;
char* name;
};
void setStu(struct student* s, int a, int b, char c[]) {
s->num = a;
s->score = b;
s->name = c;
}
void prStu(struct student s) {
printf("학번 : %d\n이름 : %s\n상점 : %d", s.num, s.name, s.score);
}
int main(void) {
struct student st;
setStu(&st, 1101, 10, "asdf");
prStu(st);
return 0;
}
처음 배우는 내용이라 적응까지 시간 필요 예상
파일은 보통 FILE의 포인터를 이용하여 접근한다.
FILE* fp = NULL; << 파일 포인터 fp 선언 후 NULL로 초기화 하여 메모리에 공간 할당
fp = fopen("output.txt", "w"); << fopen()을 통한 파일 생성
if (fp == NULL) {
printf("X");
return -1; << 파일이 열리지 않았을 때 프로그램(main 함수) 종료
}
int c; << ASCII 값을 받을 int c
FILE* fp = NULL;
fp = fopen("input.txt", "r"); << 읽기 모드
if (fp == NULL) {
printf("X");
return -1;
}
while ((c = fgetc(fp)) != EOF) { << 파일의 끝(EndOfFile)까지 읽기
putchar(c); << 파일의 char를 차례로 반환하는 함수
}
fclose(fp); << 파일에 대한 작업 종료 시, 파일을 닫아 메모리에서 제거
FILE* fp = NULL;
char data[100]; << 출력할 문자열 char[100]
printf("내용 >> ");
scanf("%s", data);
fp = fopen("output.txt", "w");
if (fp == NULL) {
fprintf(stderr, "X\n");
return -1;
}
fprintf(fp, data); << 파일 포인터 fp의 주소를 대상으로 문자열 data 출력
fclose(fp);
return 0;
18강 - 12:00의 텍스트파일을 줄 단위로 읽어 해당 줄에 특정 단어가 있는지 확인하는 프로그램
#include <stdio.h>
#include <string.h>
int main(void) {
FILE* fp = NULL;
int fname[256];
int buffer[256];
int word[256];
int line = 0;
printf("파일명 >> ");
scanf("%s", fname);
printf("단어 >> ");
scanf("%s", word);
if ((fp = fopen(fname, "r")) == NULL) {
fprintf(stderr, "%s X\n", fname);
return -1;
}
printf("%s가 있는 줄 :\n", word);
while (fgets(buffer, 256, fp)) {
line++;
if (strstr(buffer, word)) {
printf("%d ", line);
}
}
fclose(fp);
return 0;
}
고대 기록 (이전에 들었던 다른 강의 필기, ※가독성 떨어짐 주의)
c 언어의 메모리 구조
프로그램을 실행하면 "운영체제가 프로그램 실행을 위한 메모리 공간을 만든다"
CPU(중앙처리장치)가 프로그램에 필요한 연산을 하려면 데이터를 받아야 하는데
HDD(저장장치)는 너무 느리다. 그래서 CPU속도에 맞춰줄 수 있는 속도인
RAM(메모리)를 사용하는 것
저장장치가 왜 느리냐면 기계식이기 때문, 데이터를 찾고 옮길때 스스로 돌면서 데이터를 일일이 훍으면서 찾는다.
메모리는 전자식이라 빠르지만 전원을 끄면 기껏 모아둔거 다 사라져버린다는 단점이 있다.
그렇기 때문에 메모리의 크기는 작다. << 메모리 관리가 중요한 이유
메모리를 잘 쓰기 위해서 운영체제가 나눈 메모리 공간의 영역
코드 영역 << 컴파일된 코드를 저장하는 공간
데이터 영역 << 전역변수, 컴파일중인 문자열을 저장하는 공간 // 프로그램이 시작할때부터 끝날때까지 유지된다 // main() 외의 영역 << 이전에 선언한 변수 등
스택 영역 << 지역변수, 매개변수, 함수를 저장하는 공간 // main() 내의 영역
힙 영역 << 원하는 시점에 만들고 없애는 동적 할당 공간
스택이 왜 스택인가
스택은 생긴게
|ㅤ | << 이렇게 생겼는데
|ㅤ | 이 때 데이터를 저장하면
|ㅤ | 밑에서부터 차례대로 쌓인다.
|ㅤ | 그래서 데이터를 뺄때 나중에 넣은 것 부터 차례대로 빼야된다. (FILO)
|___|
프로그램 실행 도중에 동적으로 메모리를 할당받는 것 (값을 힙 영역에 할당하는 것)
필요할 때 필요한 만큼 할당하고, 원할 때 반납 가능하므로 메모리를 효율적으로 관리 가능
할당받은 메모리는 프로그램 종료 이전에 반드시 반납해야 한다.
int* pi; << 공간에 접근할 매게가 되는 포인터
pi = (int*)malloc(sizeof(int)); << sizeof(int) == 4만큼의 공간을 할당한다.
if (pi == NULL) { << 할당 실패에 대한 예외처리 << 지금까지 실패한 적은 없었지만..
printf("X");
return -1;
}
*pi = 100; << 이제 포인터는 할당받은 heap영역의 주소를 가지므로 해당 값은 특정 변수가 아닌 동적 영역에 저장된다.
printf("%d\n", *pi);
free(pi); << heap영역의 값은 프로그램 종료 이전에 무조건 반납해야 한다.
두 코드의 기능은 동일하다.(int*)malloc(sizeof(int)*2);
(int*)calloc(sizeof(int),2);
int size;
printf("크기 >> "); << 배열에서는 불가능한 동적 크기 (배열 크기는 상수여야만 한다)
scanf("%d",&size);
int* p = (int*)malloc(sizeof(int)*size); << 정수 size개 크기의 메모리 할당 (4 * size byte)
int c;
for(int i = 0; i < size; i++){
scanf("%d",(p+i)); << int 포인터이므로 주소에 +1을 더하면 다음 칸의 주소를 가지게 된다.
}
for(int i = 0; i < size; i++){
printf("%d",*(p+i));
}
free(p); << 메모리 반납
(구조체 식별자)malloc(sizeof(구조체 식별자))를 통하여 구조체 포인터에 동적 메모리 할당이 가능하다.
구조체 포인터의 경우, ' . ' 기호가 아닌 '->' 기호를 통해 내부 자료형에 접근 가능하다.
int** pp = (int**)malloc(sizeof(int*)*3);
포인터 3개의 주소를 가지는 포인터 포인터 (int*[3]..?)
pp의 각 주소는 포인터의 주소이므로, 포인터 포인터가 참조하는 포인터의 크기를 다르게 할 수 있다.
*pp = (int*)malloc(sizeof(int)*2);
*(pp+1) = (int*)malloc(sizeof(int)*4);
*(pp+2) = (int*)malloc(sizeof(int)*3);
개인적으로 자바의 2차원 배열과 같은 형식이라 생각한다.
#include <stdio.h>
#include <stdlib.h>
// 본래 목적은 class 외부에서 접근제한자가 private인 LIST를 사용하는 것 입니다.
int* LIST;
int size = 0;
int now = 0;
// LIST의 첫 공간을 할당합니다.
void set(int a) {
if (LIST != NULL) {
printf("이미 할당된 list\n");
return;
}
if (a <= 0) {
printf("잘못된 값\n");
return;
}
size = a;
LIST = (int*)malloc(sizeof(int) * a);
}
// LIST의 인덱스 요소를 가져옵니다.
int get(int a) {
if (LIST == NULL) {
printf("할당되지 않은 list\n");
return -1;
}
if (a > now-1 || a < 0) {
printf("범위 이탈\n");
return -1;
}
return LIST[a];
}
// LIST의 요소를 추가합니다. 필요에 따라 확장합니다.
void add(int a) {
if (now == size) {
int temp;
int* l = malloc(sizeof(int)*size);
for (int i = 0; i < size; i++) {
temp = LIST[i];
l[i] = temp;
}
LIST = (int*)realloc(LIST, sizeof(int));
for (int i = 0; i < size; i++) {
temp = l[i];
LIST[i] = temp;
}
size++;
LIST[size - 1] = a;
}
else {
LIST[now] = a;
}
now++;
}
// LIST의 마지막 요소를 제거합니다. << 몇일동안 시도한 오류 해결에 실패했습니다.
void del() {
if (LIST == NULL) {
printf("할당되지 않은 list\n");
return;
}
if (now == 0) {
printf("비어있는 list\n");
return;
}
now--;
}
void end() {
free(LIST);
}
// list가 class의 private 요소 일때의 app.class의 함수
int main() {
int c;
printf("크기 설정\n>> ");
scanf("%d", &c);
getchar();
set(c);
while (1) {
printf("리스트_______\n1. 추가\n2. 출력\n3. 제거\n4. 종료\n>> ");
scanf("%d", &c);
getchar();
if (c == 1) {
printf(">> ");
scanf("%d", &c);
add(c);
}
else if (c == 2) {
for (int i = 0; i < now; i++) {
printf("%d\t", get(i));
}
printf("\n");
}
else if (c == 3) {
del();
}
else if (c == 4) {
end();
break;
}
else {
printf("잘못된 값\n");
}
}
return 0;
}
본래 연결형 리스트가 목적이었으나...
struct str {
char* data;
struct str* next;
};
typedef struct str st;
void set(st* a) {
a->data = "";
a->next = NULL;
}
void read(st* a) {
if (a == NULL) {
printf("비어있는 구조체\n");
return;
}
while (a->next != NULL) {
printf("?");
printf("%s",a->data);
a = a->next;
}
}
void add(st* a, char* d) {
if (a == NULL) {
printf("비어있는 구조체\n");
return;
}
if (a->next == NULL) {
printf("!");
st* n;
n = malloc(sizeof(st));
n->data = d;
a->next == (st*)n;
}
else {
printf("a");
add(a->next, d);
}
}
int main() {
struct st* head;
head = malloc(sizeof(st));
set(head);
add(head, "hello");
add(head, "world");
read(head);
free(head);
return 0;
}
더욱 이상한 실패
#include <stdio.h>
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
#include <stdlib.h>
typedef struct {
char* data;
struct st* next;
}st;
// 연결된 모든 배열 출력하기
void f(st* a) {
while (a != NULL) {
printf("%s", a->data);
a = a->next;
}
printf("\n");
}
// 뒤쪽에 문자열을 가진 새로운 배열 추가하기
void p(st* a, char* s) {
while (a->next != NULL) {
a = a->next;
}
st* n = (st*)malloc(sizeof(st));
a->next = n;
n->data = s;
n->next = NULL;
}
// 가장 뒤쪽 배열 지우기
void d(st* a) {
if (a->next == NULL) {
return;
}
st* b = a->next;
while (b->next != NULL) {
a = a->next;
b = a->next;
}
a->next = NULL;
free(a->next);
}
// 맨 앞 제외하고 지우기
void delete(st* a) {
while (a->next != NULL) {
d(a);
}
}
int main() {
st* head = malloc(sizeof(st));
head->data = "";
head->next = NULL;
int a = 0;
char* n = NULL;
while (1) {
printf("___________\n\n연결형리스트\n1. 추가\n2. 출력\n3. 제거\n4. 전체 제거\n5. 종료\n>> ");
scanf("%d", &a);
if (a == 1) {
printf("입력 >> ");
scanf("%s", &n);
getchar();
p(head, &n);
}
else if(a == 2) {
printf("출력\n");
f(head);
}
else if (a == 3) {
d(head);
printf("제거됨\n");
}
else if (a == 4) {
delete(head);
printf("제거됨\n");
}
else if (a == 5) {
printf("종료");
break;
}
else {
printf("잘못된 값\n");
continue;
}
}
delete(head);
free(head);
return 0;
}