전처리기 구문은 다른 프로그램 영역과 독립적으로 처리되고 소스코드 파일 단위로 효력이 존재한다.

 

파일 포함 전처리기

  #include는 전처리기에서 가장 많이 사용되는 문법이고 특정한 파일을 라이브러리로서 포함시키기 위해 사용한다.

  #include 구문으로 가져올 수 있는 파일에는 제약이 없다.

  즉, 어떤 파일이던 현재 프로그램의 소스코드로 가져올 수 있는 것이다.

  구문 자체를 그 파일에 적혀있떤 소스코드 자체로 대체할 수 있다.

 

  #include <파일이름> 과 같이 선언하면 시스템 디렉토리에서 파일을 검색한다.

  운영체제마다 시스템 디렉토리가 존재하는 경로가 다를 수 있으며 대표적으로 stdio.h와 같은 헤더파일등이

  시스템 디렉토리에 존재한다.

 

  #include "파일이름" 과 같이 선언하면 현재폴더에서 파일을 먼저 검색한다.

  만약 현재 폴더에 파일이 없다면 시스템 디렉토리에서 파일을 검색한다.

 

나만의 헤더를 만드는 방법

  visual studio에서 소스코드를 생성할때와 마찬가지로 헤더파일을 생성할 수 있다.

  프로젝트 내 소스파일 - 추가 - 새항목 - 헤더파일

  000.h로 만들어 주면 #pragma once라는 visual studio에서 제공해주는 일종의 전처리 구문이 들어가게 되는데

  이것은 C보다는 C++에 가까운 요소이다.

  #pragma once를 지우고

int add(int a, int b){
  return a + b;
}

  이렇게 작성해준다. 헤더파일은 temp.h로 만들어 주었기 때문에 main.c에서 #include "temp.h" 로 선언을 해준뒤

  메인 함수 내에서

  printf("%d\n", add(3, 7)); 형태로 출력해주게 되면 temp.h에 적어둔 add에서 연산처리를 하여

  10이라는 결과값을 출력하게 된다.

 

  헤더 파일을 사용할때는 같은 이름의 함수가 중첩되지 않게 주의해야 한다.

  예를들어 위와같은 헤더파일을 만들고 main.c 내에서 또다시 add함수로 만들어서 사용한다면 겹치기 때문에

  조심해야 한다.

  #include "temp.h"로 선언을 하고 아래에 또 add함수를 만든다는 것은

int add(int a, int b){ //temp.h
  return a + b;
}

int add(int a, int b){//main.c 에서 새로 작성
  return a + b;
}

int main(void){
  printf("%d\n", add(3, 7));
}

  이런 형태의 코드를 작성한것이나 마찬가지인 게 되는것이다.

  

매크로 전처리기

  프로그램내에서 사용되는 상수나 함수를 매크로 형태로 저장하기 위해 사용하며

  #define 문법을 사용해 정의할 수 있다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define PI 3.1415926535

int main(void){
  int r = 10;
  printf("원의 둘레 : %.2f\n", 2 * PI * r);
  system("pause");
  return 0;
}

  위 코드에서 #define PI 3.1415926535 가 매크로 전처리기다.

  메인함수에서 보면 출력문에서 2 * PI * r의 연산을 출력하도록 되는데 이 연산은 결국

  2 * 3.1415926535 * 10 이 되는것이다.

  전역변수처럼 PI라는 이름으로 불러다가 편하게 사용할 수 있는 것이다.

  

  함수 형태로 사용될 수 있도록 하기 위해서 인자가 포함 될 수도 있다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define POW(x) (x * x)

int main(void){
  int x = 10;
  printf("x의 제곱 : %d\n", POW(x));
  system("pause");
  return 0;
}

  제곱값을 구하기 위해 POW함수를 사용하였고 x로 값을 받아서 사용했다.

  POW함수는 제곱을 계산하는 함수이며 x의 값인 10의 제곱을 연산해 100이라는 값을 출력한다.

  처음 인강을 보면서는 함수라길래 무언가 했지만 수학관련함수같은것을 뜻하는 것 같다.

  #define은 점점 배워갈수록 더 많이 사용하게 될것이며 소스코드의 양을 획기적으로 줄일 수 있다고 한다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define ll long long
#define ld long double

int main(void){
  ll a = 98654321000;
  ld b = 100.5054;
  printf("%.1f\n", a * b);
  system("pause");
  return 0;
}

  이런 형태로 많이 사용한다고 한다.

  만약 이런 매크로 전처리기를 사용하지 않았다면

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void){
  long long a = 98654321000;
  long double b = 100.5054;
  printf("%.1f\n", a * b);
  system("pause");
  return 0;
}

  이런형태로 작성해야 했을 것이다. 몇글자 차이이긴 하지만 만약 long long과 long double을 많이 사용하는

  장문의 코드였다면 짧게 사용할 수 있는게 더 깔끔해보이고 개발자 입장에서도 더 편할 수 있게 된다.

 

조건부 컴파일

  조건부컴파일은 컴파일이 이루어지는 영역을 직접 매크로 형태로 지정할 수 있는 기법이다.

  일반적으로 디버깅과 소스코드 이식을 목적으로 하여 작성되고 C언어로 시스템 프로그램을 작성할 때에는

  운영체제에 따라서 소스코드가 달라질 수 있다.

  이때 운영체제에 따라서 컴파일이 수행되는 소스코드를 다르게 할 수 있다.

  즉, 운영체제에 맞는 컴파일 구문을 넣어주는 방법으로 코딩을 해야할 때가 있는데 그럴 때 조건부 컴파일을

  이용해서 어떤 상황에서는 어떤 컴파일을 할것인지 상황에 따른 컴파일 방법을 달리할 때 사용한다.

 

  #ifndef ~ #endif 문법은 대표적인 조건부 컴파일 문법이다. 흔히 헤더파일의 내용이 중복되어 사용되지 않도록

  하기 위해 적용한다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "temp.h"
#include "temp.h"

int main(void){
  system("pause");
  return 0;
}

  위와 같이 같은 헤더파일을 여러번 불러오게 되면 오류가 발생한다.

  이렇게 여러번 불러오더라도 단 한번만 불러오도록 처리할 수 있는 것이 ifndef ~ endif 구문이다.

#ifndef _TEMP_H_
#define _TEMP_H_
int add(int a, int b){
  return a + b;
}
#endif

  temp.h를 위와같이 변경해주면 된다.

  #ifndef _TEMP_H_는 temp.h파일이 정의가 되어있지 않다면 #endif까지의 아래 코드를 처리한다는 것이다.

  그리고 ifndef안에서 #define _TEMP_H_로 정의하도록 하면 된다.

  ifndef가 정의되어 있지 않다면 이라는 의미이기 때문에 한번이라도 temp.h가 정의되었다면 그 다음부터는

  if문 안에 있는게 처리될 수 없기 때문에 중첩되서 오류가 발생하는것이 방지되는 것이다.

 

파일분할 컴파일

  일반적으로 직접 라이브러리를 만들때에는 c언어 파일과 헤더파일을 모두 작성해야 한다.

  

  main.c

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include "temp.h"

int main(void){
  printf("%d\n", add(3, 7));
  system("pause");
  return 0;
}

 

  temp.h

#ifndef _TEMP_H_
#define _TEMP_H_
int add(int a, int b);
#endif

 

  temp.c

#include "temp.h"

int add(int a, int b){
  return a + b;
}

  위와 같이 나눠서 작성한다.

  C파일로 만들때는 헤더파일과 동일한 파일명을 갖고 있어야 하며 헤더파일은 기본적인 선언만 하고 반환과

  연산처리는 C파일에서 해주면 된다. 그리고 C파일에는 헤더파일을 정의한다는 의미에서

  #include "temp.h"로 불러와줘야 한다.

 

 

 

레퍼런스

패스트캠퍼스 올인원 패키지 - 소프트웨어 베이직

'C' 카테고리의 다른 글

파일 입출력  (0) 2020.12.30
구조체  (0) 2020.12.30
함수포인터  (0) 2020.12.29
동적메모리 할당  (0) 2020.12.29
포인터 배열의 구조  (0) 2020.12.28

파일 입출력 변수

  파일 입출력 변수는 FILE형식의 포인터 변수로 선언한다.

  파일을 열때는 fopen()를 사용하고 닫을때는 fclose()를 사용한다.

 

FILE *fp;
fp = fopen(파일경로, 접근방식);
//파일관련 처리.
fclose(fp);

  파일열기 함수인 fopen()에는 파일경로와 접근방식을 설정할 수 있으며 기본경로는 현재 프로그램의 경로이고

  가장 많이 사용되는 접근방식은 다음과 같다.

r

파일에 접근하여 데이터를 읽는다

w

파일에 접근하여 데이터를 기록한다(이미 존재하면 덮어쓰기)

a

파일에 접근하여 데이터를 뒤에서부터 기록한다.

  기본적인 입출력을 위해서 printf와 scanf를 사용했었는데 파일 입출력에서는 앞에 f만을 붙인

  fprintf와 fscanf가 사용된다.

  fprintf(파일포인터, 서식, 형식지정자);

  fscanf(파일포인터, 서식, 형식지정자);

  형태로 사용한다.

 

  파일 입출력은 열고, 읽고/쓰고, 닫기의 과정을 철저히 따라야 한다.

  파일을 열때는 파일포인터가 사용되며, 이는 동적으로 할당된것이기 때문에 파일 처리 이후에 파일을 닫아주지 않으면

  메모리 누수가 발생하게 된다.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int main(void){
  char s[20] = "Hello World";
  FILE *fp;
  fp = fopen("temp.txt", "w");
  fprintf(fp, "%s\n", s);
  fclose(fp);
  return 0;
}

  위 코드를 실행하면 프로젝트의 폴더 내에 temp.txt라는 파일이 생성되고 그 파일에는 Hello World라는 내용이

  들어간다.

 

 

레퍼런스

패스트캠퍼스 올인원 패키지 - 소프트웨어 베이직

'C' 카테고리의 다른 글

전처리기  (0) 2020.12.30
구조체  (0) 2020.12.30
함수포인터  (0) 2020.12.29
동적메모리 할당  (0) 2020.12.29
포인터 배열의 구조  (0) 2020.12.28

구조체

  구조체란 여러개의 변수를 묶어 하나의 객체를 표현하고자 할때 사용한다.

 

정의와 선언

struct 구조체명 {
  자료형1 변수명1;
  자료형2 변수명2;
  ...
}

  기본적으로 구조체의 변수에 접근할때는 '.'을 사용한다.

#include <stdio.h>

struct Student{
  char studentId[10];
  char name[10];
  int grade;
  char major[10];
};

int main(void){
  struct Student s;
  strcpy(s.studentId, "20201230");
  strcpy(s.name, "홍길동");
  s.grade = 4;
  strcpy(s.major, "컴퓨터공학");
  printf("학번 : %s\n", s.studentId);
  printf("이름 : %s\n", s.name);
  printf("학년 : %d\n", s.grade);
  printf("학과 : %s\n", s.major);
  system("pause");
  return 0;
}

  위 예제처럼 사용한다.

  s.000은 s로 정의한 Student라는 구조체를 사용하는 것이고 그 안에 studentId, name, grade, major 를 사용할 수 있게

  struct로 정의한다.

  학번 : 20201230

  이름 : 홍길동

  학년 : 4

  학과 : 컴퓨터공학

  이 형태로 출력된다.

 

  하나의 구조체 변수만 사용하는 경우 정의와 동시에 선언을 할 수도 있다.

  이 경우 변수는 전역변수로 사용된다.

struct Student{
  char studentId[10];
  char name[10];
  int grade;
  char major[100];
} s;

  이런 형태로 중괄호가 끝난 뒤에 변수명을 적어주면 된다.

  그럼 위의 예제에서처럼 main 함수내에서 굳이 struct Student s; 의 형태로 정의할 필요가 없어지는 것이다.

 

  그리고 구조체의 변수를 한번에 초기화 할 수도 있다.

struct Student s = { "20201230", "홍길동", 4, "컴퓨터공학" };

  이런 형태로 사용이 가능하고 구조체의 정의와 선언을 동시에 하면서도 값을 바로 넣어줄 수 있다.

  하지만 거의 사용하지 않는 방식이라고 한다.

 

  typedef 키워드를 이용하면 임의의 자료형을 만들 수 있기 때문에 선언이 더 짧아진다.

#include <stdio.h>

typedef struct Student{
  char studentId[10];
  char name[10];
} stu;

int main(void){
  stu s;
  strcpy(s.studentId, "20201230");
  strcpy(s.name, "홍길동");
  printf("학번 : %s\n", s.studentId);
  printf("이름 : %s\n", s.name);
  system("pause");
  return 0;
}

  위 예제처럼 사용한다. 구조체 구문 제일 앞에 typedef를 적어주고 중괄호 바깥에 사용할 이름을 적어준다.

  이렇게 사용한다면 main함수에서 struct Student s; 라고 할 필요 없이 stu s; 로 사용해도 된다는 것이다.

 

  구조체가 포인터 변수로 사용되는 경우에는 내부 변수에 접근할 때 화살표(->)를 사용한다.

#include <stdio.h>

typedef struct Student {
  char studentId[10];
  char name[10];
  int grade;
} stu;

int main(void) {
  stu* s = malloc(sizeof(stu));
  strcpy(s->studentId, "20201230");
  strcpy(s->name, "홍길동");
  s->grade = 4;
  printf("학번 : %s\n", s->studentId);
  printf("이름 : %s\n", s->name);
  printf("학년 : %d\n", s->grade);
  system("pause");
  return 0;
}

  이런 형태로 사용된다. '.'대신 '->'를 사용한다고 생각하면 편하다.

 

 

레퍼런스

패스트캠퍼스 올인원 패키지 - 소프트웨어 베이직

  

'C' 카테고리의 다른 글

전처리기  (0) 2020.12.30
파일 입출력  (0) 2020.12.30
함수포인터  (0) 2020.12.29
동적메모리 할당  (0) 2020.12.29
포인터 배열의 구조  (0) 2020.12.28

함수포인터

  C언어에서는 함수의 이름을 이용해 특정한 함수를 호출한다.

  함수 이름은 메모리 주소를 반환한다.

#include <stdio.h>

void function(){
  printf("It's my function.");
}

int main(void){
  printf("%d\n", function);
  system("pause");
  return 0;
}

  이렇게 함수 이름을 넣어서 출력하도록 하면 함수의 주소값을 확인할 수 있다.

  function 함수에 있는 It's my function 이 출력되는 것이 아닌 fucntion 함수의 주소값만이 출력되게 된다.

  만약 It's my function 이라는 결과값을 얻고 싶다면 출력문이 아닌 function(); 이렇게 function함수 자체를 호출하면

  된다.

 

  함수 포인터를 이용하면 형태가 같은 서로 다른 기능의 함수를 선택적으로 사용할 수 있다.

  반환자료형(*이름)(매개변수) = 함수명; 형태로 사용하면 된다.

 

  매개변수 및 반환자료형이 없는 함수 포인터

#include <stdio.h>

void myFunction(){
  printf("It's my Function.\n");
}

void function(){
  printf("It's Function.\n");
}

int main(void){
  void(*fp)()=myFunction();
  fp();
  fp = function;
  fp();
  system("pause");
  return 0;
}

  위 예제에서 main함수를 보면 void(*fp)()=myFunction() 형태로 반환자료형이 void이며 매개변수는 ()로 비워두었다.

  결과값은

  It's my Function.

  It's Function.

  이렇게 출력된다. 

  위 예제는 fp라는 함수 포인터에 myFunction을 넣어준 뒤 fp(); 형태로 myFunction함수를 호출한 뒤에

  fp = function 으로 function함수를 정의한 뒤 다시 fp();형태로 function함수를 호출해 출력한 것이다.

 

  매개변수 및 반환자료형이 있는 함수 포인터

#include <stdio.h>

int add(int a, int b){
  return a + b;
}

int sub(int a, int b){
  return a - b;
}

int main(void){
  int(*fp)(int, int) = add;
  printf("%d\n", fp(10, 3));
  fp = sub;
  pritnf("%d\n", fp(10, 3));
  system("pause");
  return 0;
}

  위 예제는 반환자료형, 매개변수를 모두 사용한 경우이다.

  정수 연산이기때문에 int로 반환자료형을 선언하고 각 함수에서 받는 변수들이 int이므로 매개변수 역시 int로

  작성한다.

  add함수를 처음 넣어준 뒤 출력문에서 fp(10,3)으로 add함수에 10과 3을 넘겨준다. 그럼 add함수에서는

  a + b인 10 + 3의 결과값 13을 리턴해주고 그 리턴된 값을 main함수의 출력문에서 출력해주는 것이다.

  그다음 fp를 sub함수로 정의한 뒤 같은 방식으로 처리하여 출력한다.

  return으로 연산을 반환함으로 main함수내의 출력문에서 바로 출력할 수 있다.

 

  함수포인터를 반환하여 사용

#include <stdio.h>

int add(int a, int b){
  return a + b;
}

int(*process(char *a))(int, int){
  printf("%s\n", a);
  return add;
}

int main(void){
  printf("%d\n", process("10과 20의 합")(10, 20));
  system("pause");
  return 0;
}

  main함수에서 출력문 내에 process함수를 호출함으로써 process로 넘어가게 된다. process함수에서는 

  "10과 20의 합"이라는 문자열을 *a로 받고 그 뒤에 있는 int, int로 10 과 20을 받게 된다.

  그럼 process에서는 a만을 출력하라고 했기에 "10과 20의 합"을 출력하게 되고 add로 넘어가는데 이때

  받아둔 10 과 20을 add함수로 넘겨주게 된다.

  그럼 add함수에서는 return 에 작성한 연산으로 두 값을 합하여 return하게 되고 그 반환 값은 main함수의 출력문으로

  넘어와서 출력되게 된다.

 

  C의 모든 함수는 내부적으로 포인터 형태로 관리할 수 있고 함수포인터는 자주 사용되지 않지만 알고있으면

  컴퓨터 구조를 이해하는데 도움이 된다고 한다.

 

 

레퍼런스

패스트캠퍼스 올인원 패키지 - 소프트웨어 베이직

'C' 카테고리의 다른 글

파일 입출력  (0) 2020.12.30
구조체  (0) 2020.12.30
동적메모리 할당  (0) 2020.12.29
포인터 배열의 구조  (0) 2020.12.28
전역변수, 지역변수, 정적변수, 레지스터변수, 함수의 매개변수처리  (0) 2020.12.27

동적메모리 할당의 개념

  일반적으로 c에서 배열의 경우 사전에 적절한 크기만큼 할당해주어야 한다.

  우리가 원하는 만큼만 메모리를 할당해서 사용하고자 한다면 동적메모리 할당을 사용한다.

  동적이라는 말의 의미는 '프로그램 실행 도중에'라는 의미이다.

  c에서는 malloc()을 이용해 원하는 만큼의 메모리 공간을 확보할 수 있는데 malloc()은 메모리할당에 성공하면

  주소를 반환하고 그렇지 않으면 null을 반환한다.

  malloc()은 <stdlib.h>에 정의되어 있다.

  malloc(할당할 바이트 크기) 형태로 사용하며 동적메모리 할당을 수행할 때마다 할당되는 포인터의 주소는 변칙적이다.

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  int *a = malloc(sizeof(int));
  printf("%d\n", a);
  a = malloc(sizeof(int));
  printf("%d\n", a);
  system("pause");
  return 0;
}

  sizeof(int)는 4바이트가 반환되는데 위 코드에서는 malloc(4)와 같게 된다.

  할당에 성공했다면 포인터변수 a에 들어가고 실패했다면 null값이 들어가게 된다.

  실행해보면 서로 다른값이 출력되게 되며 실행시마다 값이 달라진다.

  인강에서는 두번째 a를 *a로 만들어 주어도 다른값이 나온다고 했지만 여러번 해봤지만 계속 동일한 값이 출력되었다.

 

  동적으로 할당된 변수는 힙 영역에 저장되며 기본적인 C언어에서는 스택에 선언된 변수는 따로 메모리 해제를 해주지

  않아도 된다고 한다. 반면에 동적으로 할당된 변수는 반드시 free()로 메모리 해제를 해주어야 한다.

  메모리 해제를 하지 않으면 메모리내의 프로세스 무게가 더해져 언젠가는 오류가 발생하게 된다.

  프로세스의 무게가 더해진다는 것은 메모리가 계속 중첩되어 사용공간이 증가하고 사용가능한 공간이 줄어든다는

  것이다. 메모리누수(Memory Leak)방지는 코어 개발자의 핵심역량이라고 한다.

 

#include <stdio.h>
#include <stdlib.h>

int main(void) {
  int *a = malloc(sizeof(int));
  printf("%d\n", a);
  free(a);
  a = malloc(sizeof(int));
  printf("%d\n", a);
  free(a);
  system("pause");
  return 0;
}

  이런 형태로 사용하면 된다

  malloc()과 free()는 한쌍으로 무조건 붙는다고 생각하면 된다고 한다.

  이렇게 작성하게 되면 같은값으로 출력되는 것을 확인할 수 있는데

  처음 할당받은 주소를 받은 뒤에 free()로 메모리를 비워주고 다시 할당받게 되니 처음 할당받았던 주소를 받을 확률이

  높아졌기 때문이다. 거의 100%에 가까울 정도로 높은 확률로 같은값을 받게 된다고 한다.

 

동적으로 문자열 처리

  일괄적인 범위의 메모리를 모두 특정한 값으로 설정하기 위해서는 memset을 사용하며

  memset(포인터, 값, 크기); 형태로 사용한다.

  한 byte씩 값을 저장하므로 문자열 배열의 처리방식과 흡사하다.

  그래서 memset()은 <string.h>에 정의되어 있다.

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
  char *a = malloc(100);
  memset(a, 'A', 100);
  for(int i = 0; i < 100; i++){
    printf("%c ", a[i]);
  }
  system("pause");
  return 0;
}

  위 예제에서는 malloc(100)으로 100만큼의 공간을 할당한 뒤에 memset(a, 'A', 100)으로 a배열에 'A'를 100개 채우게

  된다.

 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void) {
  int **p = (int**)malloc(sizeof(int*) * 3);
  //**는 이중포인터.
  //즉 2차원 배열 생성 후 3개의 행을 할당.
  for(int i = 0; i < 3; i++){
    *(p + i) = (int*)malloc(sizeof(int) * 3);
    //각각의 행에 들어갈 열을 3개씩 할당.
  }
  
  for(int i = 0; i < 3; i++){
    for(int j = 0; j < 3; j++){
      *(*(p + i) + j) = i * 3 + j;
      //*(*(0) + 0) = 0 * 3 + 0 = 0
      //*(*(0) + 1) = 0 * 3 + 1 = 1
      //*(*(0) + 2) = 0 * 3 + 2 = 2
      //*(*(1) + 0) = 1 * 3 + 0 = 3
      //*(*(1) + 1) = 1 * 3 + 1 = 4
      //*(*(1) + 2) = 1 * 3 + 2 = 5
      //*(*(2) + 0) = 2 * 3 + 0 = 6
      //*(*(2) + 1) = 2 * 3 + 1 = 7
      //*(*(2) + 2) = 2 * 3 + 2 = 8
    }
  }//각 행, 열에 값을 입력
  
  for(int i = 0; i < 3; i++){
    for(int j = 0; j < 3; j++){
      printf("%d ", *(*(p + i) + j));
    }//출력
    printf("\n");
  }
  system("pause");
  return 0;
}

  위 예제는

  0 1 2 

  3 4 5

  6 7 8  의 결과값을 출력한다.

  **는 이중포인터 이며 2차원 배열을 생성한 후에 3개의 행만큼 들어갈 수 있도록 만들어준 것이다.

  하나의 포인터가 한개의 1차원 배열을 처리할 수 있기 때문에 sizeof(int*)가 1차원 배열의 시작점을 가리키는

  포인터가 된다. 그리고 3을 곱해줌으로써 총 3개의 행이 만들어지는 것이다.

  그리고 첫 for문에서 각각의 행에 들어갈 열을 3개씩 할당해준다.

  *(p + i) 는 각각의 행을 의미하는데 각 행마다 3개의 열을 추가해주는 것이다.

  그다음 이중for문에서는 각각의 행과 열에 값을 입력하게 되는데

  *(p + i)는 행 j는 열을 의미한다.

  마지막 for문은 출력문이다. 마찬가지로 이차원배열이기 때문에 이중 for문으로 출력해준다.

  이 예제에서는 free를 사용해서 메모리해제를 하지 않았는데 한번 전체적으로 실행되고 나면 종료되는 코드이기

  때문에 굳이 사용하지 않은것이지만 만약 장시간 실행되며 처리하는 코드라면 꼭 free()를 사용해서 메모리를

  해제해주어야 한다.

 

 

 

레퍼런스

패스트캠퍼스 올인원 패키지 - 소프트웨어 베이직

'C' 카테고리의 다른 글

구조체  (0) 2020.12.30
함수포인터  (0) 2020.12.29
포인터 배열의 구조  (0) 2020.12.28
전역변수, 지역변수, 정적변수, 레지스터변수, 함수의 매개변수처리  (0) 2020.12.27
C언어 기초  (0) 2020.12.27

구조분석

  배열은 포인터와 동일한 방식으로 동작하며 배열의 이름은 배열의 원소의 첫번째 주소가 된다.

  유일한 차이점이라고 한다면 포인터는 변수이며 배열의 이름은 상수이다.

#include <stdio.h>

int main(void){
  int a = 10;
  int b[10];
  b = &a;
}

  위 예제에서는 배열인 b에 a의 주소값을 넣어주겠다는 것인데 에러가 발생한다.

  배열의 이름은 상수이기 때문에 변경할 수 없는데 a의 주소값으로 변경하겠다고 하니 에러가 발생하는 것이다.

 

  하지만 반대로 포인털르 배열처럼 사용하는 것은 문제가 없다.

#include <stdio.h>

int main(void){
  int a[5] = {1, 2, 3, 4, 5};
  int *b = a;
  printf("%d\n", b[2]);
}

  이렇게 작성하게 된다면 a배열의 3번째 값인 3이 출력되게 된다.

  a라는 배열을 포인터변수 b에 넣어주고 포인터 변수 b에서의 2번 인덱스의 값을 가져오라고 하니 포인터변수의 값인

  a 배열의 값 중에서 2번인덱스의 값을 가져오는 것이다.

 

  배열의 이름은 배열의 첫번째 원소의 주소이다.

#include <stdio.h>

int main(void) {
  int a[5] = {1, 2, 3, 4, 5};
  int *b = &a[0];
  printf("%d\n", b[2]);
}

  이 코드와 위의 코드의 차이점은 a를 주소값으로 불러왔는지 그냥 배열이름으로 불러왔는지의 차이다.

  '배열의 이름은 배열의 첫번째 원소의 주소' 라는 말이 이 말이다.

  배열의 이름을 써도 a배열의 0번인덱스의 주소가 되는 것이고 주소값으로 &a[0]으로 사용해도 똑같은 값을 찾는다는

  것이다.

  

  포인터는 연산을 통해 자료형의 크기만큼 이동한다는 특징이 있다.

  따라서 int 포인터는 4byte씩 이동한다.

#include <stdio.h>

int main(void) {
  int a[5] = {1, 2, 3, 4, 5};
  int i;
  for(i = 0; i < 5; i++){
    printf("%d ", a + i);
  }
}

  위 코드를 출력하면 4만큼씩 증가하는 값을 확인할 수 있다.

  출력문에서 *(a + i)로 바꿔준다면 값을 출력할 수 있게 되며 1 2 3 4 5 라는 결과값이 출력되는데 a[i]와 동일하다.

 

 

#include <stdio.h>

int main(void) {
  int a[5] = {1, 2, 3, 4, 5}
  int *p = a;
  printf("%d\n", *(p++));
  printf("%d\n", *(++p));
  printf("%d\n", *(p + 2));
}

  위 예제의 경우 1 3 5가 출력이 된다. 포인터 변수 p에 배열 a를 넣어주었다. p는 a[0]이 되는것이기 때문에 출력했을때

  1이 나오게 되는데  첫 출력문에서는 p가 후치증가이기 때문에 1을 출력한 뒤에 p가 1증가하여 a[1]이 된다.

  그 다음 출력문은 전치증가로 먼저 증가한다음 출력하기 때문에 a[2]의 값을 출력하게 되므로 3이 출력되고

  마지막 출력문은 p에 2를 더한 뒤 출력하라고 했기 때문에 a[4]의 값을 출력하게 되고 5가 출력되는 것이다.

 

#include <stdio.h>

int main(void) {
  int a[2][5] = { {1, 2, 3, 4, 5}, {6, 7, 8, 9, 10} };
  int (*p)[5] = a[1];
  int i;
  for(i = 0; i < 5; i++){
    printf("%d ", p[0][i]);
  }
}

  위 예제의 결과값은 6 7 8 9 10이다.

  포인터 변수 p는 크기는 5이며 a배열의 1번 인덱스의 행 주소를 갖고 있게 된다.

  a의 1번 행은 6, 7, 8, 9, 10이기 때문에 출력값 역시 6 7 8 9 10이 되는 것이며 출력시에는 이중포인터이기 때문에

  p[0][i]로 처리해야 하는 것이다.

 

 

레퍼런스

패스트캠퍼스 올인원 패키지 - 소프트웨어 베이직

'C' 카테고리의 다른 글

구조체  (0) 2020.12.30
함수포인터  (0) 2020.12.29
동적메모리 할당  (0) 2020.12.29
전역변수, 지역변수, 정적변수, 레지스터변수, 함수의 매개변수처리  (0) 2020.12.27
C언어 기초  (0) 2020.12.27

전역변수(Global Variable)

  프로그램의 어디서든 접근 가능한 변수이다.

  말 그대로 전체를 총괄하는 변수이며 main함수가 실행되기 전에 프로그램의 시작과 동시에 메모리에 할당된다.

  프로그램의 크기가 커질수록 전역변수로 인해 프로그램이 복잡해질 수 있고 메모리의 데이터영역에 적재(load)된다.

#include<stdio.h>

int a = 5;

void ChangeValue(){
  a = 10;
}

int main(void){
  printf("%d\n", a);
  changeValue();
  printf("%d\n", a);
}

  위 코드는 전역변수의 가장 기본적인 사용방법이며 int a = 5; 이것이 전역변수이다.

  전역변수이기 때문에 main함수가 아닌 changeValue 함수에서도 바로 사용이 가능하다.

  함수가 여러개 있는 코드일 때 a의 값이 계속 필요하다면 전역변수로 사용한다면 편해진다.

 

지역변수(Local Variable)

  프로그램에서 특정한 블록에서만 접근할 수 있는 변수이다.

  함수가 실행될때마다 메모리에 할당되어 함수가 종료되면 메모리에서 해제되고 메모리의 스택영역에 기록된다.

#include<stdio.h>

int main(void){
  int a = 7;
  if(1) {
    int a = 5;
  }
  printf("%d", a);
}

  위 코드에서 결과값은 7이다.

  5라고 혼동 될 수 있지만 지역변수의 특성상 5라는 값은 if문 안에서만 영향을 끼치고 if문을 벗어나는 순간 다시 a의

  값은 7이 되기 때문이다.

  7의 값을 갖고 있는 a와 5의 값을 갖고 있는 a는 다르다고 생각하는게 좀 더 편할 것 같다.

  자바 공부할때는 같은 변수명으로 사용한 적이 없어서 이 코드를 보자마자 어? 이랬었는데

  'if문 안에 있는 a는 if문이 종료되자마자 사라지는 것'이라고 생각하니 좀 이해가 편했다.

  if문 안에 새로운 변수를 만들었다고 생각하면 된다.

  만약 7을 5로 바꿔서 출력하고 싶은것이라면 if문안에 있는 a의 자료형을 빼고 a = 5; 이렇게 작성한다면

  출력값은 5가 된다.

 

정적변수(Static Variable)

  특정한 블록에서만 접근할 수 있는 변수이다.

  프로그램이 실행될 때 메모리에 할당되어 프로그램이 종료되면 메모리에서 해제되며 데이터영역에 적재(load)된다.

#include<stdio.h>

void process(){
  static int a = 5;
  a = a + 1;
  printf("%d\n", a);
}

int main(void){
  process();
  process();
  process();
}

  결과값은 6 7 8이 출력된다.

  정적변수로 처음 한번 메모리에 적재되고 나면 그 다음 호출 시 부터는 값이 변경되지 않고 기존 연산이 처리된 a의

  값을 계속해서 갖고 있게 되는 것이다.

  그래서 메인함수에서 첫 process로 넘어왔을 때 5의 값을 갖고 시작해서 연산이 끝난 후 6이 되고

  두번째 process에서는 a의 값이 6이 되고 연산처리해서 7이 출력되는 것이다.

 

레지스터변수(Register Variable)

  메인 메모리 대신 CPU의 레지스터를 사용하는 변수이다.

  레지스터는 매우 한정되어 있으므로 실제로 레지스터에서 처리될지는 장담할 수 없다.

#include<stdio.h>

int main(void){
  register int a = 10, i;
  for(i = 0; i < a; i++){
    printf("%d ", i);
  }
}

  위 코드의 출력값은 0 1 2 3 4 5 6 7 8 9이다.

  레지스터 변수를 사용했기 때문에 일반변수를 사용했을때 보다 더 빠를거라는 기대를 할 수 있게 된다.

  위 예제는 큰 연산이 없어서 체감은 별로 없고 알아두었다가 나중에 복잡한 연산을 할 때 사용해야 체감할 것 같다.

 

함수의 매개변수 처리

  함수를 호출 할 때 함수에 필요한 데이털르 매개변수로 전달한다.

  전달 방식은 값에 의한 전달 방식과 참조에 의한 전달방식이 있다.

  값에 의한 전달 방식은 단지 값을 전달하므로 함수 내에서 변수가 새롭게 생성된다.

  참조에 의한 전달 방식은 주소를 전달하므로 원래의 변수 자체에 접근할 수 있다.

#include<stdio.h>

void add(int a, int b){
  a = a + b;
}

int main(void){
  int a = 7;
  add(a, 10);
  printf("%d\n", a);
}

  위 코드의 결과값은 7이다.

  처음 봤을 때 add함수의 a에 7을 b에 10을 넣어주니 17이 나올거라고 생각할 수 있지만

  add함수 내에서만 적용이 된 것이기 때문에 기존에 있던 a의 값인 7이 그대로 출력되는 것이다.

  만약 add함수에서 출력을 했다면 17이 출력이 되었겠지만 메인에서는 그게 안된다.

 

  이럴때 17을 출력하고 싶다면 참조에 의한 전달 방식을 사용해야 한다.

  참조에 의한 전달 방식은 함수의 매개변수로 값을 전달하는 것이 아니라 변수의 주소를 전달한다.

  이러한 방식을 이용하면 원래 변수의 값에 접근하여 값을 변경할 수 있다.

 

#include<stdio.h>

void add(int *a, int b){
  *a = (*a) + b;
}

int main(void){
  int a = 7;
  add(&a, 10);
  printf("%d\n", a);
}

  이 결과값은 17이 나오게 된다.

  두 코드의 차이점은 매개변수로 포인터 변수를 보냈느냐 아니냐의 차이일 뿐이다.

  a는 7이라는 값을 갖고 있고

  add함수에 보낼때 &a로 a의 주소값을 보내게 된다. 그럼 add함수에서는 포인터 변수를 받아서 b에 넘겨준 10을 더한

  값인 17을 만들고 이것을 a의 값을 참조해서 더한 값이 되기 때문에 a가 17로 변하게 되는 것이다.

  첫 코드는 7을 단순히 가져와서 연산처리한거고 두번째 코드는 7이 있는데로 가서 연산처리 했다고 이해하면 좀 더

  편할듯하다.

 

 

레퍼런스

패스트캠퍼스 올인원 패키지 - 소프트웨어 베이직

'C' 카테고리의 다른 글

구조체  (0) 2020.12.30
함수포인터  (0) 2020.12.29
동적메모리 할당  (0) 2020.12.29
포인터 배열의 구조  (0) 2020.12.28
C언어 기초  (0) 2020.12.27

Visual Studio 2019 에서의 프로젝트 생성.

  파일 - 새로만들기 - 프로젝트 - 빈프로젝트 - 솔루션 탐색기 - 소스파일 오른쪽클릭 - 추가 - 새항목.

  기본적으로 .cpp로 나와있지만 .c로 바꿔서 생성한다면 c로 생성이 가능하다.

  C++이 C를 기반으로 한 것이기 때문에 확장자 변경만으로 C파일로 변경이 가능하다.

  

프로젝트 빌드 결과

  솔루션 탐색기 오른쪽클릭 - 파일탐색기에서 폴더 열기 - debug로 들어가면 실행파일이 보인다.

 

라이브러리

  C/C++에서는 #include 명령어를 이용해 다양한 라이브러리를 불러올 수 있다.

  제일 많이 봤던 stdio는 Standard Input output 으로 입출력 라이브러리다.

 

코드의 시작

  자바와 마찬가지로 main함수로부터 시작한다.

  함수는 return 값이 없을 수 있으나 메인함수에서는 항상 0을 반환하는 것이 일반적이라고 한다.

  C에서의 기본 출력함수는 printf이다.

  system함수는 운영체제의 기본적인 기능을 이용할 수 있다.

  공부 중에는 system("pause"); 를 가장 많이 사용하였고 pause명령어는 키보드를 입력하기 전까지 대기하는 기능을

  수행한다.

 

기본적인 자료형

  int, string, char, double, long long, bool.

  int는 정수, string은 문자열, char은 문자, double은 실수, long long은 숫자가 긴 정수형, bool은 boolean이다.

  string은 C 문법에는 포함되지 않지만 C++에서는 사용한다.

  C에서 문자열을 포현하기 위해서는 char을 배열형태로 만들어서 사용하면 된다.

  char a[20] = "HELLO WORLD" 이렇게 사용하면 문자열 형태처럼 사용할 수 있다.

 

입력

  C에서 값을 입력받기 위해서는 scanf를 사용한다. scanf(%d, &a);

  보통 실무에서는 많이 사용하지 않고 처음 이해하고 배울때만 사용을 많이 한다고 한다.

  scanf는 C에서 취약한 함수로 분류되기에 visual studio에서는 바로 사용할 수 없도록 막아두었다.

  그래서 #define _CRT_SECURE_NO_WARNINGS 를 include위치에 정의해줘야 사용할 수 있다.

  scanf에 &를 사용하는 이유는 특정한 메모리 주소에 접근하여 데이터를 수정할 수 있도록 하기 위해서이다.

  C언어에서는 포인터를 사용할 때 &를 사용한다.

  메모리주소에 얼마만큼의 크기로 데이터를 쓸 지 경정해야 하는데 그럴때 사용하는 것이 형식지정자이다.

int(4byte)

%d를 이용해서 정수형 데이터를 입력 및 출력

long long(8byte)

%lld 를 이용해서 큰 정수형 데이터를 입력 및 출력

double(8byte)

입력시 %lf 출력시 %f로 큰 실수형 데이터를 처리

float(4byte)

%f를 이용해서 실수형 데이터를 입력 및 출력

string(제한없음)

%s를 이용해서 문자열 데이터를 입력 및 출력

char(1byte)

%c를 이용해서 문자 데이터를 입력 및 출력

  double에서 입출력시 사용하는 형식지정자가 다른 이유는 입력할 때는 크기를 지정해줘야 하지만 출력할때는

  값 자체를 이용해서 출력하는 것이므로 구체적인 크기를 지정하지 않아도 되는 것이다.

  만약 %자체를 문자로 출력하고 싶다면 %%로 입력해줘야 문자로 인식하고 % 한개가 출력되게 된다.

  실수형에서는 .0으로 소수점 자리수를 지정해준다.

  %.2f 이런식으로 표시하는데 .은 소수점을 의미하고 2는 소수점 자리 수, f는 실수라는 의미이다.

  

  한자리씩 끊어서 입력하는 방법도 있는데

  scanf("%1d%1d%1d", &a, &b, &c);

  printf("%d %d %d",a,b,c);

  이렇게 코드를 작성하고 576을 입력한다면 5 7 6 이렇게 출력되게 된다.

  한자리씩 끊어서 변수에 입력된다고 생각하면 된다.

 

이스케이프시퀀스(Escape Sequence)

  특정한 표현을 출력하기 위해 사용하는 문법이다.

\n

줄바꾸기

\t

수평 탭 넣기

\\

백슬레시 넣기

\”

큰 따옴표 넣기

\b

백스페이스 넣기

 

포인터

  C에 대해 얘기를 들을 때 마다 나왔던 포인터이다. 굉장히 중요하다는 얘기만 많이 들었다.

  포인터 변수는 특이한 변수로 메모리 주소를 저장한다.

  int a = 5;

  int *b = &a;

  이러한 코드가 있다면 b가 포인터 변수다. b는 &a라는 값을 갖고 있는데 &는 주소값을 의미하는 주소연산자이기

  때문에 a의 주소값을 포인터 변수인 b가 갖고 있게 되는 것이다.

  C에서는 그 주소값으로 해당 주소의 값을 가져오기 때문에 포인터 변수 b를 이용해 5라는 a의 값을 가져오는 것이

  가능하다.

  포인터는 특정 메모리의 주소값에 직접적으로 접근할 수 있기 때문에 사용할 때 조심해야 한다.

 

포인터 관련 연산자

 

주소연산자(&)

변수 앞에 붙어서 변수의 메모리 시작 주소값을 구한다

포인터(*)

포인터 변수를 선언할 때 사용한다

간접 참조 연산자(*)

선언된 포인터 변수가 가리키는 변수를 구한다

 

문자 입출력

  char은 아스키 코드를 기준으로 받아 들인다.

  a = 65;

  printf("%c\n", a);

  이 코드를 출력하게 되면 아스키코드표의 65에 해당하는 A가 출력되게 된다.

  그래서 문자 입출력함수가 따로 있는데 getchar()이다.

  이것은 단 하나의 문자를 입력 받는다.

 

  입력 버퍼로 인해 흔히 발생하는 오류가 있다.

int main(){
  int a;
  char c;
  scanf("%d", &a);
  printf("%d\n", a);
  scanf("%c", &c);
  printf("%c\n", c);
}

  위 코드에서 숫자를 입력하고 엔터를 치게 되면 그대로 입력이 끝나게 된다.

  숫자는 a에 들어가게 되지만 엔터 또한 문자로 입력되면서 엔터가 c에 들어가면서 입력이 끝나게 되는 것이다.

  char은 아스키코드로 관리하다보니 엔터도 문자로 인식하기에 문자로 입력을 받게 되는 것이다.

 

  이때는 남아있는 입력버퍼를 지워줘야 한다.

int main(){
  int a;
  char c;
  scanf("%d", &a);
  printf("%d\n", a);
  
  int temp;
  while((temp = getchar()) != EOF && temp != '\n'){}
  
  scanf("%c", &c);
  printf("%c\n", c);
}

  위 코드에서 EOF는 End Of File의 약자로 파일의 끝을 의미한다.

  while문을 보면 파일의 끝이나 개행 문자를 만났을 때 입력을 멈추고 버퍼를 비운다는 의미라고 한다.

  이 부분이 조금 이해가 안되는 부분이다.

  자바를 사용하면서는 !=는 not의 의미이고 &&는 AND의 의미인데 왜 파일의 끝이나 개행문자를 만났을 때 입력을

  멈추고 버퍼를 비운다는 것인지 잘 이해가 되지 않는다.

  그래서 생각해보니 temp 아래로 코드가 더 있기 때문에 'EOF가 아니다.'는 true가 되고 temp에는 값을 주지 않았기

  때문에 \n이 아닌게 되므로 입력버퍼를 비워준다는게 맞는 것 같다. 이거는 다른 C 강의도 찾아보고 기억해야

  할 것 같다.

  

문자열

  C언어에서 문자열은 메모리 구조상에서 마지막에 null값을 포함한다.

  printf함수를 실행하면 컴퓨터는 내부적으로 null을 만날때까지 출력하도록 되어있기 때문이다.

  HELLO WORLD를 출력한다고 하면

0

1

2

3

4

5

6

7

8

9

10

11

H

E

L

L

O

 

W

O

R

L

D

\0
(null)

  이렇게 들어가는 것이다.

  그래서 HELLO WORLD는 중간 공백을 포함해서 11글자에 마지막 null값까지 총 12자리의 배열 크기가 되는 것이다.

 

  문자열 형태로 포인터를 사용하면 포인터에 특정한 문자열의 주소를 넣게 된다.

int main(void) {
	char *a = "Hello World";
	printf("%s\n", a);
}

  이런식으로 배열 형태가 아닌 포인터 변수에 문자열 자체를 마치 상수처럼 넣을 수 있다.

  이때 이렇게 큰 따옴포안에 문자열이 들어가는 구조를 문자열 리터럴이라고 한다.

  이렇게 작성하게 되면 컴파일러가 알아서 문자열이 특정한 메모리 주소에 저장될 수 있도록 남아있는 메모리

  공간중에서 주소를 결정해준다.

  그리고 그 주소를 포인터인 *a가 갖고 있게 되는 것이다.

  이렇게 문자열을 리터럴 방식으로 넣어준다면 읽기전용으로 상수형태로 넣어주는 것이다.

  말 그대로 읽기전용이기 때문에 중간에 문자열을 바꿀 수 없고 굳이 변경해야 한다면 포인터에 선언한 문자열 자체를

  변경해야 한다.

  포인터로 문자열을 선언했다고 하더라고 배열처럼 처리하는 것은 가능하다.

 

int main(void){
  char *a = "Hello World";
  printf("%c\n", a[1]);
  printf("%c\n", a[4]);
  printf("%c\n", a[8]);
}

  출력문을 이렇게 변경해준다면 출력결과는

   e

   o

   r

  이렇게 출력된다.

  포인터로 선언되었지만 그 인덱스를 갖고 출력하는 것은 배열과 동일한 것이다.

 

  C에서 문자열 함수는 <string.h> 라이브러리에 포함되어 있다.

strlen()

문자열의 길이를 반환

strcmp()

문자열 1 문자열 2보다 사전적으로 앞에 있으면 -1 뒤에 있으면 1 반환

strcpy()

문자열을 복사

strcat()

문자열 1 문자열2 더한다

strstr()

문자열 1 문자열 2 어떻게 포함되어 있는지를 반환

 

  strlen은 중간 공백가지 포함해서 문자의 수를 출력해준다.

  strlen의 경우는 <string.h>를 선언하지 않아도 최근에는 사용이 가능하다고 한다.

 

  strcmp는 사전순이라고 되어있는데 오름차순이라고 생각하면 된다.

  a와 c를 비교한다면 a가 사전순으로 앞에 있기 때문에 -1이 출력되는 것이고 반대로 비교한다면 1이 출력된다.

 

  strcpy는 

int main(void){
  char a[20] = "Hello";
  char b[20] = "World";
  strcpy(a, b);
  printf("복사된 문자 : %s\n", a);
}

  이렇게 작성했을 때 World가 출력되게 된다.

  앞에 있는 문자열에 뒤에 있는 문자열을 복사해서 붙여넣기 하는 것이다.

 

  strcat은 두 문자열을 붙여준다고 생각하면 된다.

  위 코드에서 strcpy가 아닌 strcat으로 바꿔준다면

  HelloWorld가 출력되게 되는 것이다.

  여기서 주의해야 할 점은 a에 b가 합쳐지는 것이기 때문에 배열 크기를 조절해줘야 한다.

  b가 들어가도 될만큼의 여유공간을 a가 갖고 있어야 한다.

 

  strstr은 긴 문자열에서 짧은 문자열을 찾아 그 위치를 반환한다.

int main(void){
  char a[20] = "I like you";
  char b[20] = "like";
  printf("문자열 : %s\n", strstr(a, b));
}

 

  위 코드에서  b의 값인 like의 위치를 찾아 그 뒤의 문자열까지 전체 출력해주기 때문에 like you가 출력된다.

  

 

문자열 입출력 함수(gets)

  gets는 문자열 입출력을 수행하는 함수이며 scanf는 공백을 만날때까지 입력을 받지만

  gets는 공백까지 포함하여 한줄을 입력 받는다.

  scanf("%s", &a); 이렇게 사용했지만 gets는 gets(a)이렇게 사용한다.

  

  실무에서는 gets보다는 gets_s를 많이 사용한다고 한다.

  gets_s(a, sizeof(a));

  이렇게 사용하는데 버퍼의 크기를 지정해주는 방식이다.

  gets_s는 버퍼의 크기를 벗어나게 된다면 런타임 오류가 발생하기 때문에 안정성이 보장된다.

  기존의 gets함수는 버퍼 이상으로 덮어쓰기 때문에 취약점이 존재한다.

 

 

 

레퍼런스

패스트캠퍼스 올인원 패키지 - 소프트웨어 베이직

'C' 카테고리의 다른 글

구조체  (0) 2020.12.30
함수포인터  (0) 2020.12.29
동적메모리 할당  (0) 2020.12.29
포인터 배열의 구조  (0) 2020.12.28
전역변수, 지역변수, 정적변수, 레지스터변수, 함수의 매개변수처리  (0) 2020.12.27

+ Recent posts