2차원 배열같은 동적할당으로 아파트의 층과 가구를 사용자로부터 입력받아 메모리를 동적으로 할당한다.

그리고 인원을 입력하여 총 인원수를 출력하는 프로그램.

#include <stdio.h>
#include <stdlib.h>
main()
{
     int i,j;
     int height, width;         //2차원 배열의 인덱스를 위한 변수.
      int sum=0;                 //층당 인원 수.
      int total=0;                 //아파트 총 인원 수.
      int **arr;                     //2차원 배열의 동적할당을 위한 더블포인터.

      printf("아파트 총 층수: ");
      scanf("%d", &height);
      arr = (int**)malloc(sizeof(int)*height);             //arr의 열의 입력. arr[height][];

      printf("층당 가구수: ");
      scanf("%d", &width);
      for(i=0; i<height; i++)
           arr[i] = (int*)malloc(sizeof(int)*width);     //이렇게하면 arr[height][width]와 같은 것이 된다.

      for(i=0; i<height; i++)
      {
           printf("-----  %d층 -----\n", i+1);
           for(j=0; j<width; j++)
           {
                printf("%d층 %d호: ", i+1, j+1);
                scanf("%d", &arr[i][j]);

                sum += arr[i][j];
           }
           printf("\n%d층 총 인원: %d\n\n", i+1, sum);
           total += sum;
           sum = 0;
      }

      printf("아파트 총 인원: %d\n", total);

      printf("arr[0][0]의 주소: %d\n", &arr[0][0]);
      printf("arr[0][1]의 주소: %d\n", &arr[0][1]);
     printf("arr[1][0]의 주소: %d\n", &arr[1][0]);
     printf("arr[1][1]의 주소: %d\n", &arr[1][1]);
     printf("arr[2][0]의 주소: %d\n", &arr[2][0]);
     printf("arr[2][1]의 주소: %d\n", &arr[2][1]);

     for(i=0; i<height; i++)
           free(arr[i]); //메모리할당 했으면 반드시 해제도 해주어야 한다!!
     free(arr);  
      return 0;
}



정확히 말하면 2차원배열이 아니다. 왜냐하면,
배열은 메모리가 순차적으로 있어야 하는데.. 각 원소들이 메모리공간에 일정하게 있는야 하는데
동적할당으로 메모리를 할당하면 메모리 공간이 일정하지 않다. 
그래서, 정확히 2차원적 배열은 아니다.

결론, 2차원 배열처럼 활용할 수 있지만, 2차원 배열은 아니다!
그러면... 함수 전달할때는..?
2차원 배열이 아니니까 int**로 전달해주면 된다.

ex) void extendSize(int** pArr, ..., ...)

'Programming > C' 카테고리의 다른 글

수학 관련 함수  (0) 2013.02.15
ftell 함수와 rewind 함수  (0) 2013.02.15
fprintf와 문자열 형식  (0) 2013.02.15
조건부 컴파일  (0) 2013.02.15
모듈화 프로그래밍 (Modular Programming)  (0) 2013.02.15
Posted by scii
:

사용자로부터 이름을 입력받아 이것을 깔끔하게 name.txt 파일에 기록한다.


#include<stdio.h>

main()
{
     int i;
      char name[100];
      FILE* file;

      if((file = fopen("c:\\name.txt", "wt")) == NULL){
           printf("file open error!\n");
           return -1;
      }

      for(i=0; i<3; i++)
      {
           fputs("문자열 입력: ", stdout);
           gets(name);

           fprintf(file, "Name %d: [%-10.10s]\n", i, name);
      }

      if((fclose(file)) != 0){
           printf("file close error!\n");
           return -1;
      }

      return 0;
}

fprintf에서 사용한 문자열 형식.

%-10.10s  =>  s는 데이터를 문자열로 출력하라는 것. 맨 앞의 -는 왼쪽 정렬하라는 의미.
그 뒤에 10은 최소 10개의 문자를 출력하라는 뜻으로 길이가 10 미만인 문자열은 공백으로 길이를 맞춘다.
그래서 Jeon이나 Taylor를 보면 뒤에 공백으로 채워져 길이가 맞춰져있다.

그리고 .10은 최대 10대 문자를 출력하라는 뜻이다. 10문자가 넘으면 잘리게 된다.
입력할 때, Martin Guitar를 입력했는데 최대 10문자라서 tar은 짤린것을 볼 수 있다.



'Programming > C' 카테고리의 다른 글

ftell 함수와 rewind 함수  (0) 2013.02.15
2차원 배열 동적할당.  (2) 2013.02.15
조건부 컴파일  (0) 2013.02.15
모듈화 프로그래밍 (Modular Programming)  (0) 2013.02.15
전처리기 (Pre-Processor)  (0) 2013.02.15
Posted by scii
:

조건부 컴파일

Programming/C 2013. 2. 15. 01:55 |

#if, #elif, #else, #endif 를 이용한 조건부 컴파일


전처리 지시자 #if, #elif, #else, #endif 는 전처리기에게 조건을 제시하는 경우에 사용된다.

ex) #if CONDITION1

          expression1

     #elif CONDITION2

          expression2

     #else

          expression3

     #endif

"CONDITION1이 만족되면 expression1을, CONDITION2가 만족되면 expression2를, 이도 저도 아니면 expression2을 컴파일하라"


if~ else if 문장과 상당히 유사한 형태를 지니고 있음을 알 수 있다. 다만 위의 문장은 전처리기에 의해 처리되기 때문에

컴파일 해야 할 대상을 지정할 때 사용하게 된다.


※ #endif 는 꼭! 빼먹지 말고 넣어주어야 한다. "여기까지입니다." 라는 뜻이니!!




#ifndef, #endif 를 이용한 조건부 컴파일


ifndef == if not define(정의하지 않았다면)


: 헤더 파일을 마구마구 만들어서 포함(#include)해 가다 보면 문제가 발생하기 마련이다.






위의 코드는 count.h의 int 형 count를 중복 선언해서 일어나는 에러이다. (헤더 자체를 중복  포함하는 것이 문제가 되는 것은 아니다.)



해 결 책


첫 번째 방법

: 헤더 파일을 중복해서 포함하는 것을 막는 것이다.

└ 이것을 적용하기 위해서는 신경 쓸 일이 한둘이 아니다. 파일의 배치 자체를 바꿔야 할 수도 있다.

 

두 번째 방법

: 중복 포함하더라도 내부에 선언된 내용들이 한번만 선언 및 정의되게 하는 것이다.

└ 아주 간단한 처리를 통해서 적용이 가능하다. 실제로 많은 프로그래머들이 사용하는 방법이다.


● 헤더 파일의 포함 관계에서 발생하는 문제의 해결

: 조건부 컴파일은 헤더 파일을 중복 포함하더라도 내부에 선언된 내용들이 한번만 선언 및 정의되게 하는 것이다.


ex) #ifndef _COUNT_H_    → _COUNT_H_를 정의하지 않았다면

     

     #define _COUNT_H_   → _COUNT_H_를 정의하고

     int  count = 0;            → int형 변수 count도 선언하고


     #endif                      → 끝낸다.


#ifndef _COUNT_H_ ~ #endif 는 다음과 같은 의미는?

: 만약에 _COUNT_H_ 를 정의하지 않았다면 컴파일 시 #endif 이전까지의 내용을 컴파일해라.


count.h헤더 파일을 이렇게 선언하면 위에 에러문제는 해결된다.

왜냐하면, 내부에 존재하는 내용은 한번만 선언 및 정의되기 때문이다.


그리고, #defile _COUNT_H_를 정의하는 목적은 오로지 조건부 컴파일을 위한 것이다.


더불어, _COUNT_H_ 는 헤더 파일 이름을 이용하여 만든 것이다. 조건부 컴파일을 위한 매크로를

정의하는 경우 어떤한 이름을 주건 상관 없지만 이름이 중복되어서는 안 될 것이다.

파일의 이름은 중복될 일이 없으므로, 이를 이용하는 것은 좋은 방법이고, 또 가장 보편화되어 있다.



지금까지의 내용을 종합해 보면 헤더 파일 선언 시 파일 내에 존재하는 모든 내용을 #ifndef와 #endif 문장으로

감싸는 것이 좋다는 결론을 내리게 된다. 

언제 어떻게 중복 포함될지 모르는 일이기 때문이다.


'Programming > C' 카테고리의 다른 글

2차원 배열 동적할당.  (2) 2013.02.15
fprintf와 문자열 형식  (0) 2013.02.15
모듈화 프로그래밍 (Modular Programming)  (0) 2013.02.15
전처리기 (Pre-Processor)  (0) 2013.02.15
C언어에서의 Token  (0) 2013.02.15
Posted by scii
:

모듈(Module)이란?

: 소프트웨어에서 말하는 모듈이란 프로그램의 일부분을 의미하는 것이다.

C언어를 이용하여 계산기에 관련된 함수들을 구현했다면, 이러한 함수들의 집합도 하나의 모듈이 될 수 있다.


※ 일반적으로 소프트웨어 공학에서 이야기하는 모듈(module)이라는 것은 크게는 하나의 파일이 될 수도 있지만,

작게는 하나의 함수가 될 수도 있다.




외부 변수의 사용: 키워드 extern (External = 외부의, 밖의)의 의미.

: ex) extern int i;

이렇게 선언을 하면, 컴파일러에게 변수 i는 외부에(다른 파일에) 선언되어 있음을 알려주는 것이다.

그래서 컴파일러는 그냥 컴파일을 한다.


※ 외부(다른 파일)에 정의된 함수를 호출하기 위해서 함수를 선언할 때 extern이라는 키워드를 반드시 써야 하는 것은 아니다.

왜냐하면, 함수 선언 자체가 extern의 의미를 포함하고 있기 때문이다.


ex) extern double add(double a, double b);

               double add(double a, double b);  // 이 둘은 같은 의미이다.


그러나, extern이라는 키워드를 붙여 주는 것이 나쁘지는 않다. 왜냐하면 함수의 정의가 외부에 존재한다는 의미를 부여할 수 있기 때문이다.




외부 접근 금지: 전역 변수 선언시, 키워드 static의 의미. 

:전역 변수로 변수 앞에 static 선언을 하면 외부의 접근을 금지한다는 뜻이 된다.(정확히는 변수의 접근 영역을 파일로 제한한다는 뜻.)

ex) static int a;  ←   extern int a; 접근 불가!!!

     int a;  ←   extern int a; 접근 가능!!!




링크(Link)에 대한 이해

: 컴파일을 하면 오브젝트 파일이 생긴다.(obj파일은 단지 기계어로 변환된 내용만을 지닌 파일이다.)

만약, 모듈화 프로그래밍을 해서 두개의 파일이 있다면 두개의 obj파일이 생성되어있을것이다.

링크는 이 파일 두개를 서로 연결시켜주는 작업을 한다. 그래서 하나의 완성된 실행 파일을 생성한다.


: 최종적인 실행 파일의 생성을 위해서 접근하는 변수가 어디에 존재하는지, 호출하는 함수가 어디에 존재하는지 

연결해 주는 작업을 가리켜 링크라 한다.




헤더파일(Header File)의 구현과 유용성

: ANSI 표준에서 정의하고 있는 헤더 파일을 "표준 헤더 파일"이라 한다.

이러한 헤더 파일들을 모아놓은 곳이 "표준 디렉토리"라고 한다.


: 확장자가 .h로 끝나는 파일을 헤더 파일이라고 한다. 헤더 파일은 #include 전처리기 지시자에 의해서 다른 파일 내에 포함된다.


※ 일반적으로 함수의 정의를 헤더 파일에 포함시키지는 않는다. 선언만 헤더 파일에 포함한다.




- 헤더 파일을 포함하는 두 가지 방법 -


ex) #include <abc.h>                  // 표준 디렉토리에서 abc.h를 찾아서 포함.

     #include "c:/include/abc.h"    // c:/include 에서 abc.h를 찾아서 포함.

     #include "/include/lib/plus.h" // /include/lib/plus.h에 존재하는 plus.h 포함.(유닉스 계열 컴퓨터)


 < >를 이용한 헤더 파일의 포함.

: 표준 디렉토리는 컴파일러마다 유지하고 있는 위치가 다르다.

: 표준 디렉토리에 존재하는 헤더 파일을 포함하기 위해서는 < >를 사용한다.


 " "를 이용한 헤더 파일의 포함.

: " "를 이용해서 헤더 파일을 포함할 경우 헤더 파일이 존재하는 디렉트로의 위치를 직접 지정하는 것이 가능하다.

: 보통 사용자가 정의하는 헤더 파일은 위치를  정해서 따로 관리하기 마련인데, 이렇게 임의의 위치에 존재하는

헤더 파일을 포함하는 경우, 다시 말하면 사용자가 정의하는 헤더 파일을 포함하는 경우 " "를 이용하게 된다.



※ main 함수를 지니고 있는 파일을 메인 모듈(main module)이라 한다.





여기서는 file.c라는 파일을 만들어 함수의 "정의"를 해두었다. 

그리고 메인 모듈로 가선 함수의 "선언"을 해주었다.


자 그런데.. 만약 저 함수들의 인자받는 것이 바뀐다면... 이를테면 void 형으로 바뀐다면...


메인모듈의 있는 함수 원형 선언문 모두를 void로 바꾸어야 한다.

지금은 4개밖에 없지만.. 저것이 10~20개면.. 그리고 다른 파일들도 저 선언을 가지고 있다면..

파일을 돌아다니며 다 바꿔주어야 한다. 참 귀찮을 것이다.


그런데, 헤더파일을 만들어서 거기에 함수 원형을 선언하고 메인 모듈이나 다른 파일들이 #include하는 식이라면...

헤더파일에 있는 함수 선언만 바꿔주면 끝이다.





이러한 번거로운 일들을 편히 하기 위해서 헤더 파일이라는 것을 만든다.


'Programming > C' 카테고리의 다른 글

fprintf와 문자열 형식  (0) 2013.02.15
조건부 컴파일  (0) 2013.02.15
전처리기 (Pre-Processor)  (0) 2013.02.15
C언어에서의 Token  (0) 2013.02.15
메모리 관리와 동적 할당  (0) 2013.02.15
Posted by scii
:

전처리기 

#define으로 시작하는 전처리기 지시자(Preprocess or Directive)
: #문자로 시작하는 문장을 가리켜 '전처리기 지시자'라 한다. 이러한 문장은 컴파일러가 아니라 
전처리기에게 적절한 처리를 요구할 때 사용된다. (단순 치환 작업을 요청할 때 사용되는 지시자)
ex)   #define    PI    3.1415
                      ↓                  ↓                   ↓
           전처리기 지시자       매크로        대체 리스트

이것은 전처리기 지시자로 인해 컴파일 하기 전 PI를 3.1415로 치환한다.
※PI는 상수처럼 사용되고 있다. 그래서 매크로 상수를 이용한 배열 선언을 할 수 있다.
ex) #define NUM 10
      int arr[NUM];


매크로 함수

: 매크로를 이용하면 함수 같은 매크로를 만들 수 있다. 
매크로는 매크로인데 함수의 기능을 하는 매크로란 뜻이다. 함수처럼 인자를 전달받을 수 있다.

장점: 1. 매크로 함수는 자료형에 독립적이다. int형이든 float형이든 다 받을 수 있다.

즉, 매크로 함수는 전처리기에 의한 단순 치환 방식으로 구현되므로, 전달 인자의 자료형을 명시할 필요가 없고,
또 어떠한 자료형 변수를 인자로 전달해도 잘 동작한다.

2. 실행 속도가 향상된다. 

단!! 함수 호출 문장이 전부 매크로 함수 몸체 부분으로 완전히 치환되어 버리기 때문에 코드의 크기는 커질 수 밖에 없다.
따라서 모든 함수를 매크로로 정의하는 것은 좋지 않다. 더불어 매크로 함수를 정의하는 것이 일반 함수를 정의하는 것보다 
어렵고, 오류 발생의 소지가 높다. (오류 발생의 소지가 왜 높냐면, 컴파일러가 하는 것이 아니고 전처리기가 하는 것인데
전처리기는 오류를 못잡는다.)

함수를 매크로로 정의해서 손해 없이 속도 향상의 이점을 얻을 수 있는 방법은?
: 함수의 크기가 작아야 한다.

#include<stdio.h>

#define PI 3.14159265355820
#define AREA(x) (x * x * PI)

int main(void)
{
      double a;

      fputs("원의 반지름을 입력하세요: ", stdout);
      scanf("%lf", &a);

      printf("원의 넓이는 = %lf \n", AREA(a));

      return 0;
}

----------------------------------------------------------------------------------------------------------------

#include<stdio.h>

#define MAX(a,b) (((a)>(b)) ? (a) : (b))

int main(void)
{
       int a,b;
       fputs("두개의 정수 입력: ", stdout);
       scanf("%d %d", &a, &b);

       printf("큰 수: %d \n", MAX(a,b));

       return 0;
}

----------------------------------------------------------------------------------------------------------------


이미 정의되어 있는 표준 매크로

: C언어에는 정의하지 않아도 기본적으로 정의되어 있는 매크로가 존재한다. 이를 가리켜 표준 매크로라 한다.
프로그래머의 편의를 위해 제공이 되는 것이다.

__FILE__   = 현재 소스 코드의 파일명을 나타내는 문자열.
__TIME__  = 컴파일 시각을 "시:분:초"의 형태로 나타내는 문자열.
__DATE__  = 컴파일 날짜를 "월 년 일"의 형태로 나타내는 문자열.
__LINE__   = 현재 처리중인 소스파일의 행 번호를 나타낸다.

'Programming > C' 카테고리의 다른 글

조건부 컴파일  (0) 2013.02.15
모듈화 프로그래밍 (Modular Programming)  (0) 2013.02.15
C언어에서의 Token  (0) 2013.02.15
메모리 관리와 동적 할당  (0) 2013.02.15
파일 복사 프로그램  (0) 2013.02.15
Posted by scii
:

C언어에서의 Token

Programming/C 2013. 2. 15. 01:44 |

토큰(Token) 이란, 


토큰이라는 단어는 사용되는 분야에 따라서 해석이 다양한데, C언어에서의 토큰이란 


컴파일러가 인식하는 의미를 지니는 문자나 문자열의 최소 단위를 뜻한다.


ex) int val = a + b;


위의 문장을 최소의 단위로 나눠 보면 "int", "val", "=", "a", "+", "b", ";" 이렇게 총 7개가 된다.


따라서 총 7개의 토큰으로  구성되어 있는 문장이라 할 수 있다.


변수나 함수의 이름부터 시작해서 연산자, 키워드 및 상수들도 모두 토큰의 기본 단위가 된다.




토큰(Token)의 결합


#define TOKEN(x,y) x ## y

↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓


TOKEN(a,b) → ab

TOKEN("aa,"bb") → "aa""bb"

TOKEN(arr,[i]) → arr[i]


##는 토큰을 결합할 때 사용된다.




## 이것을 어디에서 써 먹는 것인지 아리송하다. 


지금 당장 ##의 필요성을 알 필욘 없단다.. 다만 변수나 함수의 이름을 동적으로 작성하기 위한 용도로 사용된다고 한다.


Posted by scii
:


#include<stdlib.h>
 
void* malloc(size_t size) //malloc 함수의 원형.
void* calloc(size_t elt_count, size_t elt_size) //calloc 함수의 원형.
↓↓↓↓↓↓↓↓↓↓
int *arr = (int*)calloc(size, sizeof(int));
int *arr = (int*)malloc(size*sizeof(int)); //둘 다 같은 결과.
 
 free함수
#include<stdlib.h>
 
void free(void* ptr) //free 함수의 원형.
- Heap 영역은 컴퓨터가 알아서 올리고 내리고 해주지 않는다. 그래서 메모리를 할당하는 것과
더불어 할당된 메모리를 해제하는 것도 프로그래머가 직접해 주어야 한다.
free함수로 힙영역의 메모리를 해주지 않으면 쌓이고쌓여서 메모리 부족현상이 일어나게 된다.
 
#include<stdio.h>
#include<stdlib.h>
 
int main(void)
{
       int size, i;
       int *arr;
 
       fputs("Input number!! to memory allocate: ", stdout);
       scanf("%d", &size);
 
       arr = (int*)calloc(size, sizeof(int));
       //arr = (int*)malloc(size*sizeof(int)); //위의 것과 같다.
       if(arr == NULL)
       {
              puts("Meomory allocation filed\n");
              exit(1); //비 정상적 종료를 의미.
       }
 
       for(i=0; i<size; i++)
              scanf("%d", &arr[i]);
 
      for(i=0; i<size; i++)
              printf("%d ", arr[i]);
 
       free(arr);
 
       return 0;
}


Stack Area = 지역변수, 매개변수가 올라가는 메모리 지역. (정적으로 할당)
- 함수호출이 끝나면 메모리상에서 없어짐.
 
Data Area = 전역변수, static변수가 올라가는 메모리 직역. (정적으로 할당)
- 프로그램이 종료되어야 메모리상에서 사라짐.
 
Heap Area = 프로그래머 할당 메모리 지역. (동적으로 할당)
- 데이터 영역이나 스택과 달리 프로그래머에 의해 필요한 만큼 메모리를 할당하고 해제할 수 있다.
 
※ Stack, Data 영역은 컴파일-타임에서 할당될 메모리의 크기가 결정되어야 한다.
※ Heap 영역은 런-타임에서 할당될 메모리의 크기가 결정되어야 한다.
break, return, exit(0 or 1)
break문은 반복문이나 제어문등을 종료할 때 쓰인다.
return문은 함수를 종료할 때 쓰인다.
exit문은 프로그램을 완전히 종료할 때 쓰인다. exit(1)이면 오류로 인해 프로그램 완전종료를 알린다.
break < return < exit 순으로 exit가 가장 크다.
return이 만약 void function(void)에 쓰이면 해당 함수만 종료하고 main함수로 돌아간다.
exit이 만약 void function(void)에 쓰이면 main함수가 아니라 다른 함수에 있더라도 프로그램을 완전 종료해 버린다.

Posted by scii
:

/* strcat을 이용한 파일 복사 프로그램 */

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

int main(int argc, char* argv[])
{
      int state;
      char buf[100];
      char bufCp[100];

      //file open
      FILE * file = fopen(argv[1], "rb");
      if(file == NULL){
             puts("file open error!\n");
            return 1;
      }

      //문서의 내용을 bufCp 배열에 저장.
      strcpy(bufCp, "");
      while(1)
      {
            fgets(buf, sizeof(buf), file);
            if(feof(file) != 0)
                  break;
            strcat(bufCp, buf);
      }

      //file close
      state = fclose(file);
      if(state != 0){
            puts("file close error!\n");
            return 1;
      }

      //file open
      file = fopen(argv[2], "wb");
      if(file == NULL){
            puts("file open error!\n");
            return 1;
      }

      //bufCp배열의 내용을 main함수 세번째 인자의 이름으로 파일로 만들어서 저장.
      fputs(bufCp, file);

      //file close
      state = fclose(file);
      if(state != 0){
            puts("file close error!\n");
            return 1;
      }

      return 0;
}


----------------------------------------------------------------------------------------------------------------
위의 코드와 결과는 같다.

/* FILE 구조체 변수 2개를 이용한 파일 복사 프로그램 */

#include<stdio.h>

int main(int argc, char* argv[])
{
      char c;
      int state1, state2;
      FILE *sour, *dest;

       //file open
      sour = fopen(argv[1], "rb");
      dest = fopen(argv[2], "wb");
      if(sour == NULL || dest == NULL){
            puts("file open error!\n");
            return 1;
      }

      while(1)
      {
            c = fgetc(sour); //sour가 가리키는 문서의 단어를 읽어들여와서 c에 저장.
            if(feof(sour) != 0)
                  break;
            fputc(c, dest); //c가 가지고 있는 문자를 dest가 가리키는 문서의 쓴다.
      }

       //file close
      state1 = fclose(sour);
      state2 = fclose(dest);
      if(state1 != 0 || state2 != 0){
            puts("file close error!\n");
            return 1;
      }

      return 0;
}

Posted by scii
: