C프로그래밍 간단정리


정수를 다른 진법으로 입출력

출력형태
10진수
8진수
16진수

변환문자열
%d
%o
%x

의미
decimal
octal
hexa-decimal

표현예
12
014
0xc


변환문자열의 가운데에 # 기호를 사용하면 접두어와 함께 출력된다.
+ 플래그를 사용하면 '+' 기호와 함께 출력된다.
- 플래그는 왼쪽으로 정렬시킨다
예) %-10.3lf 화면에서 10자리를 확보하고, 소수점 이하 3째자리까지 출력, 그리고 왼쪽정렬한다.





서식문자
서식문자 의미 기능
\n newline 출력 위치를 다음 줄의 첫번째 칸으로 옮긴다
\t tab 출력 위치를 다음 탭 위치로 옮긴다
\r carriage return 출력 위치를 현재 줄의 첫번째 칸으로 옮긴다
\b backspace 출력위치를 한 칸 뒤로 옮긴다
\a alert 경고음을 낸다

줄바꿈 문자는 아스키 코드값이 10으로, \012(8진수, 접두어 없어도 8진수 해석), \x0a(앞의 x로 16진수임을 확인)으로도 가능하나, 쉽게 사용할 수 있도록 \n을 사용하게 만든것이다.





자료형에 따른 여러가지 변환 문자열
구분 변환문자열 출력형태 대상 자료형
정수형 %d 부호있는 10진수 signed형의 정수형 변수, 상수
정수형 %u 부호없는 10진수 unsigned형의 정수형 변수, 상수
정수형 %o, %x 부호없는 8,16진수 signed,unsigned 구분없이 각 진법에 따라 출력
실수형 %lf 부호 있는 소수점 형태 실수형 변수, 상수
실수형 %le 부호 있는 지수(e)형태 실수형 변수, 상수
문자형 %c 하나의 문자 char형 변수, 문자상수
문자열 %s 문자열 char형 배열의 이름, 문자열 상수


정수값은 4바이트의 크기, 1바이트는 8비트, 즉 2의 32승만큼 표현가능하다




0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, ... 처럼 최상위 비트만 1이고 뒤로 0이 따라붙는 수와 10진수와의 관계를 외워놓는 것도 유용합니다.




0x1 = 1 뒤에 0이 하나도 없기 때문에 2의 0승 = 1
0x10 = 1 뒤에 0이 4비트 있으므로 2의 4승 = 16
0x100 = 1 뒤에 0이 8비트 있으므로 2의 8승 = 256
0x1000 = 1 뒤에 0이 12비트 있으므로 2의 12승 = 4096
0x10000 = 2의 16승 = 65536
0x100000 = 2의 20승 = 1048576 ==> 1MB
0x1000000 = 2의 24승 = 16777216
0x10000000 = 2의 28승 = (외울 필요없음)
0x100000000 = 2의 32승 = 4294967296 ==> 4GB




위의 각 수에서 1을 빼면




0 = 0x1 - 1
0xF = 0x10 - 1
0xFF = 0x100 - 1
0xFFF = 0x1000 - 1
0xFFFF = 0x10000 - 1
0xFFFFF = 0x100000 - 1
0xFFFFFF = 0x1000000 - 1
0xFFFFFFF = 0x10000000 - 1
0xFFFFFFFF = 0x100000000 - 1




이 되겠습니다. 이 일련의 0xFF...F들이 의미하는 것은, 0xF는 4비트로 표현가능한 16진수중에 제일 큰 수, 0xFF는 8비트로 표현가능한 16진수중에 제일 큰 수, 0xFFF는 12비트, 0xFFFF는 16비트, ... 등이 되겠습니다.




결론적으로 %d,%u가 표현가능한 값의 범위는 0x100000000 = 2의 32승 = 4294967296 개로, signed int는 -2147483648 ~ 2147483647 이고, unsigned int는 0부터 4294967295(0xFFFFFFFF)까지입니다.




문자열의 입력


scanf("%c", &ch1); scanf(" %c", &ch2);
두번때 scanf는 " %c" 로 앞에 한칸을 비우는 이유?
첫번째에서 입력후 enter을 누를때 버퍼(buffer)에는 \n이 들어가서 ch2로 들어가기 때문이다.
그래서 두번째에서 앞에 한칸을 비워서 \n을 받고 그다음 입력으로 ch2에 넣는다.





scanf("%s", str);
배열명에는 &를 붙이지 않는다.




선택문

switch(정수값){ case 정수값1 : 실행할문장1 break; case 정수값2 : 실행할문장2 break; default : 실행할문장3 break; }
함수
함수선언, 함수호출(main안에서), 함수정의의 순
특히 함수선언을 잊지말것(main함수에서 선언이 없으면 호출을 하지 못한다)

포인터

주소연산자(&) - 특정 변수의 포인터를 구할 수 있다.
참조연산자(*) - 포인터를 통해서 기억공간을 사용




char ch; *&ch = 'P'; printf("%c", ch);
포인터 &ch가 가리키는 기억공간에 'P'를 저장한다.



배열표현 포인터표현

ary[0] *(ary+0)
ary[1] *(ary+1)
ary[2] *(ary+2)




int ary[5] = {10, 20, 30, 40, 50}; int *ap = ary; int i; for(i=0; i<5;> str2 이고, -1이면 그 반대이다. 0인경우는 str1 = str2이다.
char * strcat(char *st1, char *str2); // 문자열 붙이기, string concatenation, 두번째 전달인자로 받은 문자열을 첫번째 문자열의 뒤에 붙이는 기능을 수행, 즉 첫번째 문자열은 반드시 두번째 문자열도 함께 저장할 수 있도록 넉넉한 배열을 사용해야 한다.



문자열의 입출력

scanf("%s", str); 을 사용하면 문자열의 중간에 빈칸이 있으면 그 이후의 문자열은 입력되지 않는다.
gets(str); 을 사용하면 빈칸까지 입력을 받아 엔터를 칠때까지 입력된 모든 문자열을 입력한다.



puts(str); 을 사용하면 문자열을 출력한다. printf와 기능이 같지만, 문자열을 출력한다는 의미가 포함되므로 가독성이 증가한다. 그리고 문자열을 모두 출력한뒤에 줄 바꿈 기능까지 수행한다. 즉 printf("%s\n", str)과 같다.

문자 입출력
int getchar(); // 하나의 문자를 입력
int putchar(int); // 하나의 문자를 출력
전달인자와 리턴값이 모두 int형이라는 것에 유의
gerchar의 경우 키보드로부터 ctrl+z 가 입력되면 -1(EOF)을 리턴하도록 설계되어 있다.
(즉 입력 중간에 입력 종료시에 ctrl+z를 누르면 종료하게 만들 수 있다.)





변수의 영역과 데이터의 전달
중첩되어 있는 블록에서 같은 이름의 변수가 선언되어 있을 때는 가장 가까운 블록에서 선언된 변수를 사용하게 된다.





* 하나의 함수에서 사용되는 자동변수


선언되는 위치 함수의 시작 부분(블록의 시작부분)
사용 범위 함수 내부에서만 사용(블록 내부에서만 사용)
생존 기간 함수가 리턴될 때(블록이 끝날 때) 사라진다
메모리의 위치 스택 영역
자동 초기화 자동 초기화가 안 되고 쓰레기 값이 존재한다


* 자료형 앞에 static 을 붙여서 선언하면 정적변수가 된다.
정적변수는 기억공간의 생성과 초기화가 함수의 호출 횟수에 영향을 받지 않는다.
프로그램이 시작될때 기억공간이 할당되며, 프로그램이 종료될때까지 그 기억공간을 유지하게 된다.
즉 이후에 함수의 재호출로 인해, 초기화가 되지 않고, 그대로 값이 유지된다.



* 외부변수 - 자동변수는 사용 범위가 하나의 함수로 제한되지만, 외부변수는 어느 함수에서나 자유롭게 사용가능하다. 데이터의 공유는 쉽지만, 안전성을 보장하기 힘들다.
외부변수와 같은 이름의 변수를 함수 내에서 선언하는 경우, 함수 내에서 선언된 변수를 우선적으로 참조하게 된다.





2차원 배열
int score [3][4]; // int형 변수 4개짜리 1차원 배열이 2차원 배열의 배열 요소가 된다.
int score [3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; // 중괄호로 표기, 중괄호를 표기하지 않아도 같다. 중간 중괄호를 사용하여, 행 단위로 초기값을 생략가능하다. {{1,2},{5,6,7,8},{9,10,11,12}} 식으로 하면 3,4 자리가 초기화된다.





선언시 행 첨자를 생략할 수도 있다.
int nums[][4] = {{1},{2,3},{4,5,6}}; // 행이 3개가 된다. 중괄호를 사용하지 않고, [4] 즉 4개씩 값을 끊어서 행첨자가 선언되게 할 수도 있다.
당연할말이지만 [4] 즉, 열 첨자는 생략 불가능하다.





2중 포인터
int val = 10; int *ip = &val;
일때 &ip 은 이중포인터이다.
10을 참조하기 위해서 **&ip는 10을 참조하게 된다.
포인터변수는 변수이므로 주소연산자(&)를 사용하여 다중포인터를 구할 수 있지만, 포인터 상수는 불가능합니다. 주소연산자는 변수형태의 기억공간만 피연산자로 사용할 수 있기 때문입니다.



int **ipp; *ipp = &ip;
이중포인터(&ip)를 이중포인터변수(**ipp)에 대입한다.





배열포인터
int ary[5]
일때 ary + 1과 &ary + 1의 차이는 ary + 1은 다음행 ary[1]을 가리키지만, &ary + 1은 배열 전체의 다음행, 즉 아직 할당되지 않은 기억공간을 가리킨다.





int ary[3][4] = {{1,2,3,4},{5,6,7,8,},{9,10,11,12}}; int (*ap)[4]; // int형 변수 네 개의 배열을 가리키는 배열포인터 변수 ap = ary; // ap는 2차원 배열의 배열명 처럼 쓰일 수 있다.
즉 ap[3][4] 로 출력가능하다. *(*(ap+3)+4) 로도 가능


&ary // 2차원 배열 전체를 가리키는 포인터

ary // 첫번째 부분배열을 가리ㅣ는 포인터

&ary[0] // 첫번째 부분배열을 가리키는 포인터

ary[0] // 첫번째 부분배열의 첫번째 배열요소를 가리키는 포인터

&ary[0][0] // 첫번째 부분배열의 첫번째 배열요소를 가리키는 포인터

즉 2,3번과 4,5번만 서로 같다.
정확히 말하면 2번과 3번도 차이가 있는데, 2번은 배열명이고, 3번은 단순한 포인터이다.
배열명은 포인터뿐만 아니라 논리적으로 변수의 기능도 가지고 있다. 즉 sizeof 계산하면 2번은 배열 전체의 크기(48바이트)가 나오고, 3번은 포인터의 크기(4바이트)가 나온다.
마찬가지로 4번과 5번도 다른데, 4번은 부분배열명(16바이트)이고, 논리적으로 변수의 기능을 가지지만, 5번은 포인터(4바이트)이다.





함수포인터
int (*fp)(int,int); // 함수포인터변수 선언 fp = sum; // 함수명을 함수 포인터 변수에 저장 fp(10, 20); // 함수포인터변수로 함수 호출 (*fp)(10, 20) 도 사용가능
동적 할당 함수
#include "stdlib.h" 해야함





void * malloc(unsigned int); // 메모리 할당





int *ip; ip = (int*)malloc(4); *ip = 10; printf("%d\n", *ip); // ip가 가리키는 기억공간의 값을 출력 free(ip);
힙(heap)영역의 공간 할당, 동적할당 메모리영역





void free(void *); // 동적할당 된 기억공간을 반환한다.





void * calloc(unsigned int, unsigned int);
메모리를 동적으로 할당받은 뒤, 초기화까지한다.
첫번째는 기억공간 배열요소의 개수, 두번째는 할당 받고자 하는 기억공간의 크기(바이트)



void * realloc(void *, unsigned int);
기억공간의 크기를 조절
첫번째는 이미 할당받은 기억공간의 포인터를 넘겨준다. 두번째는 새로 할당 받고자 하는 크기를 바이트 단위로 입력한다.





메인함수의 전달인자를 사용하는 방법
첫번째 전달인자는 문자열의 개수이며, 두번째 전달인자는 포인터배열의 배열명입니다.
int main(int argc, char **argv)



구조체
struct student{ int num; double grade; }; // 세미콜론을 반드시 붙인다.

구조체 선언후 사용할 때에도 앞에 struct를 꼭 붙여줘야 한다.(c++은 안 붙여줘도 됨)
이후 구조체형의 변수를 선언한 후에, 변수 뒤에 멤버참조 연산자(.)뒤에 멤버명을 붙여서 접근가능하다.





구조체 변수
struct student a = {20, 19.4};
struct student *sp = &a;



간접멤버참조연산자(->)
(*sp).kor
sp->kor


공용체
구조체와 같은 방법으로 union 으로 선언한다.
공용체는 모든 멤버가 하나의 기억공간을 공유하므로, 공용체변수의 크기는 멤버중에서 크기가 가장 큰 멤버로 결정된다.





열거형
enum season {spring, summer, fall, winter};
열거형을 선언하면 컴파일러는 실제로 멤버들을 정수형 상수로 취급한다.
처음의 멤버를 0으로 시작해서 차례로 하나씩 큰 값을 가지도록 번역한다.





tpyedef를 사용한 형 재정의
typedef 기존의자료형 새로운자료형의이름
typedef struct student Student;
이후
Student s1; 식으로 사용가능





재정의되기 전의 자료형을 굳이 사용할 필요가 없다면 형 선언과 동시에 재정의하는 방법을 사용할 수도 있다.


typedef student{ int num; double grade; } Student;
파일 열기&닫기
프로그램과 입출력 장치 사이에서 다리 역할을 하는 파일 - 스트림 파일





파일개방
FILE * fopen(char *, char *);
첫번째 전달인자는 개방할 파일의 이름, 두번째 전달인자는 개방모드를 적어준다.


개방모드 파일이 있을 때 파일이 없을 때
r 읽기 위해 개방 널 포인터 리턴
w 파일의 내용을 삭제하고 쓰기 위해 개방 새로운 파일 생성
a 파일의 끝에 추가하기 위해 개방 새로운 파일 생성


FILE *fp;
fp = fopen("a.txt", "w");
fp가 NULL 이 아니면 파일이 열린것이다.





개방한 파일은 fclose 함수로 닫는다.
int fclose(FILE *);
리턴값은 성공적으로 닫았을 때는 0, 오류 발생시 -1을 리턴한다.




파일 포인터를 이용한 파일 입출력
int fgetc(FILE *); // 파일에서 하나의 문자를 가져온다.
리턴값(문자의 int값) char형이 아닌 이유는 만일 문자가 없을 시 -1(EOF)이 리턴되는 상황도 있기 때문이다.


스트림 파일이 하드디스크에 있는 파일의 입력이 끝났음을 확인하는 방법은 디스크 파일의 크기와 현재까지 읽어 들인 데이터의 크기를 비교하여 판단하는 것입니다. 디스크 파일의 끝에는 파일의 끝을 표시하는 어떤 정보도 포함되지 않습니다.

int fputc(int, FILE *); // 하나의 문자를 파일로 출력한다.
첫번째는 파일로 출력할 문자를 넘겨준다. 두번째 전달인자로 주어지는 파일에 그 문자를 쓴다.

스트림 파일의 이름 스트림 파일의 용도 연결된 입출력 장치
stdin 표준 입력 스트림 키보드
stdout 표준 출력 스트림 모니터
stderr 표준 에러 스트림 모니터




int main() { char ch; while(1){ ch = fgetc(stdin); if(ch==EOF) break; fputc(ch, stdout); } return 0; }
별도의 파일 개방없이 운영체제가 개방하는 스트림 파일을 사용하여서 문자를 입출력하는 방법이다.





문자열 파일 입출력
char * fgets(char *, int, FILE *); // 파일에서 문자열을 읽어 들인다.
첫번째는 문자열을 저장할 char배열의 배열명, 두번째는 읽어들일 문자열의 길이, 세번째는 입력받을 파일의 파일포인터
fgets 함수는 문자열의 끝을 표시하는 널 문자(\0)뿐 아니라, 새줄 문자(\n)도 입력된다.



fgets 함수가 호출되었을때 더이상 읽어 들일 데이터가 없으면 널 포인터(NULL)를 리턴합니다.
fgetc 는 EOF(-1)이지만, fgets는 NULL임을 기억해두자.



int fputs(char *, FILE *); // 파일로 문자열을 출력
파일입출력이 아닌 puts 함수는 문자열을 출력된 후에 자동으로 줄을 바꾸지만, fputs 함수는 문자열이 출력된 후에 줄을 바꾸지 않습니다.
리턴값은 성공하면 음수가 아닌값(0 또는 출력한 문자의 개수, 컴파일러에 따라 다름), 실패시 -1을 리턴합니다.





gets와 puts 대신 fgets와 fputs 함수를 사용하기
gets 함수는 데이터를 입력할 때 새줄 문자를 제거하지 않아도 되며,(자동으로 제거), puts 함수는 출력할 때 자동으로 줄을 바꿔주므로 나름대로 편리하다.
하지만 gets 함수는 입력 받을 기억공간의 크기를 지정해 줄 수 없으므로 데이터를 입력할 때 할당되지 않은 기억공간을 침범할 가능성이 있습니다. 또 puts 함수는 항상 줄을 바꾸므로, 문자열을 이어서 출력하는 경우에는 사용이 불가능하다.





그래서 fgets와 fputs 함수를 대신 사용하는 방법이 유용하다.
fgets(str, sizeof(str), stdin); // 키보드로 부터 문자열을 입력한다.
fputs(str, stdout); // 모니터로 문자열을 출력한다.


다양한 자료형의 파일 입출력
텍스트 파일의 데이터는 모두 아스키코드값으로 저장된 문자열이므로 숫자 형태로 저장된 파일이라도 모두 문자열로 입출력이 수행된다.
int fscanf(FILE *,char *, ...); // 파일에서 형식에 따라 데이터 입력, 데이터입력 종료시 -1(EOF)리턴
int fprintf(FILE *,char *, ...); // 파일로 형식에 따라 데이터 출력





예) fscanf(ifp, "%s %d %lf", name, &age, &height);


스트림 파일의 버퍼 비우기
int fflush(FILE *); // 버퍼를 비운다. 리턴값은 0, 버퍼를 비우기 실패시 -1(EOF) 리턴



전처리기
#include
#define 매크로명 확장문자열
#define 매크로함수명(전달인자) 확장문자열



조건부 컴파일 전처리 명령어
기존의 if~else if~else와 처리되는 방식이 같다. 조건식에 따라 컴파일이 된다.



#if 조건식1
컴파일 할 문장1
#elif 조건식2
컴파일 할 문장2
#else
컴파일 할 문장3
#endif


매크로명이 정의되어 있으면 문장1을 컴파일 그렇지 않으면 조건식을 평가해서 문장 2를 컴파일하거나 문장3을 컴파일 하게 된다.



#ifdef 매크로명
컴파일 할 문장 1
#elif 조건식
컴파일 할 문장 2
#else
컴파일 할 문장 3
#endif


앞과는 반대로 매크로명이 정의되어 있지 않은 경우에 문장을 선택적으로 컴파일 한다.



#ifndef 매크로명
컴파일 할 문장1
#elif
컴파일 할 문장 2
#else
컴파일 할 문장 3
#endif



const를 사용한 상수처리
const는 변수를 상수로 만든다.
포인터변수를 상수화 시키면 포인터변수의 값이 아니라 가리키는 기억공간의 값을 고정시킨다.





외부 변수의 선언과 정의
분할 컴파일시, extern 예약어를 사용하여 사용하는 변수가 다른 모듈에 있음을 컴파일러에게 알려줘야 한다.
(즉, 실제로 기억공간이 할당되지 않고, 변수가 다른 모듈에 있음을 알리는 역할을 한다.)





두개의 모듈에서 같은 이름의 외부 변수를 사용하면 변수의 중복선언으로 에러가 난다.
즉 필요시에는 하나의 모듈에서만 사용할 수 있도록 외부변수를 정의할 수 있는데, 이것을 외부 정적변수라고 한다.(extern은 다른 모듈의 변수를 공유, static은 다른 모듈의 변수와 별개로 하나의 모듈에서만 사용)
static 예약어를 사용





연산자 정리
& 비트별 논리곱
비트별 논리합
~ 비트부정 연산자
<<, >> 비트이동 연산자(시프트)





조건연산자( ? : )
피연산자1 ? 피연산자2 : 피연산자 3
피연산자1의 조건을 확인하여 참이면 피연산자2를 결과로, 거짓이면 피연산자3를 결과로 남기는 기능을 수행한다.
C 프로그래밍 정리


정수를 다른 진법으로 입출력
출력형태
10진수
8진수
16진수

변환문자열
%d
%o
%x

의미
decimal
octal
hexa-decimal

표현예
12
014
0xc


변환문자열의 가운데에 # 기호를 사용하면 접두어와 함께 출력된다.
+ 플래그를 사용하면 '+' 기호와 함께 출력된다.
- 플래그는 왼쪽으로 정렬시킨다
예) %-10.3lf 화면에서 10자리를 확보하고, 소수점 이하 3째자리까지 출력, 그리고 왼쪽정렬한다.





서식문자
서식문자 의미 기능
\n newline 출력 위치를 다음 줄의 첫번째 칸으로 옮긴다
\t tab 출력 위치를 다음 탭 위치로 옮긴다
\r carriage return 출력 위치를 현재 줄의 첫번째 칸으로 옮긴다
\b backspace 출력위치를 한 칸 뒤로 옮긴다
\a alert 경고음을 낸다

줄바꿈 문자는 아스키 코드값이 10으로, \012(8진수, 접두어 없어도 8진수 해석), \x0a(앞의 x로 16진수임을 확인)으로도 가능하나, 쉽게 사용할 수 있도록 \n을 사용하게 만든것이다.





자료형에 따른 여러가지 변환 문자열
구분 변환문자열 출력형태 대상 자료형
정수형 %d 부호있는 10진수 signed형의 정수형 변수, 상수
정수형 %u 부호없는 10진수 unsigned형의 정수형 변수, 상수
정수형 %o, %x 부호없는 8,16진수 signed,unsigned 구분없이 각 진법에 따라 출력
실수형 %lf 부호 있는 소수점 형태 실수형 변수, 상수
실수형 %le 부호 있는 지수(e)형태 실수형 변수, 상수
문자형 %c 하나의 문자 char형 변수, 문자상수
문자열 %s 문자열 char형 배열의 이름, 문자열 상수


정수값은 4바이트의 크기, 1바이트는 8비트, 즉 2의 32승만큼 표현가능하다




0x1, 0x10, 0x100, 0x1000, 0x10000, 0x100000, ... 처럼 최상위 비트만 1이고 뒤로 0이 따라붙는 수와 10진수와의 관계를 외워놓는 것도 유용합니다.




0x1 = 1 뒤에 0이 하나도 없기 때문에 2의 0승 = 1
0x10 = 1 뒤에 0이 4비트 있으므로 2의 4승 = 16
0x100 = 1 뒤에 0이 8비트 있으므로 2의 8승 = 256
0x1000 = 1 뒤에 0이 12비트 있으므로 2의 12승 = 4096
0x10000 = 2의 16승 = 65536
0x100000 = 2의 20승 = 1048576 ==> 1MB
0x1000000 = 2의 24승 = 16777216
0x10000000 = 2의 28승 = (외울 필요없음)
0x100000000 = 2의 32승 = 4294967296 ==> 4GB




위의 각 수에서 1을 빼면




0 = 0x1 - 1
0xF = 0x10 - 1
0xFF = 0x100 - 1
0xFFF = 0x1000 - 1
0xFFFF = 0x10000 - 1
0xFFFFF = 0x100000 - 1
0xFFFFFF = 0x1000000 - 1
0xFFFFFFF = 0x10000000 - 1
0xFFFFFFFF = 0x100000000 - 1




이 되겠습니다. 이 일련의 0xFF...F들이 의미하는 것은, 0xF는 4비트로 표현가능한 16진수중에 제일 큰 수, 0xFF는 8비트로 표현가능한 16진수중에 제일 큰 수, 0xFFF는 12비트, 0xFFFF는 16비트, ... 등이 되겠습니다.




결론적으로 %d,%u가 표현가능한 값의 범위는 0x100000000 = 2의 32승 = 4294967296 개로, signed int는 -2147483648 ~ 2147483647 이고, unsigned int는 0부터 4294967295(0xFFFFFFFF)까지입니다.




문자열의 입력


scanf("%c", &ch1); scanf(" %c", &ch2);
두번때 scanf는 " %c" 로 앞에 한칸을 비우는 이유?
첫번째에서 입력후 enter을 누를때 버퍼(buffer)에는 \n이 들어가서 ch2로 들어가기 때문이다.
그래서 두번째에서 앞에 한칸을 비워서 \n을 받고 그다음 입력으로 ch2에 넣는다.





scanf("%s", str);
배열명에는 &를 붙이지 않는다.




선택문
switch(정수값){ case 정수값1 : 실행할문장1 break; case 정수값2 : 실행할문장2 break; default : 실행할문장3 break; }
함수
함수선언, 함수호출(main안에서), 함수정의의 순
특히 함수선언을 잊지말것(main함수에서 선언이 없으면 호출을 하지 못한다)

포인터
주소연산자(&) - 특정 변수의 포인터를 구할 수 있다.
참조연산자(*) - 포인터를 통해서 기억공간을 사용




char ch; *&ch = 'P'; printf("%c", ch);
포인터 &ch가 가리키는 기억공간에 'P'를 저장한다.



배열표현 포인터표현
ary[0] *(ary+0)
ary[1] *(ary+1)
ary[2] *(ary+2)




int ary[5] = {10, 20, 30, 40, 50}; int *ap = ary; int i; for(i=0; i<5;> str2 이고, -1이면 그 반대이다. 0인경우는 str1 = str2이다.
char * strcat(char *st1, char *str2); // 문자열 붙이기, string concatenation, 두번째 전달인자로 받은 문자열을 첫번째 문자열의 뒤에 붙이는 기능을 수행, 즉 첫번째 문자열은 반드시 두번째 문자열도 함께 저장할 수 있도록 넉넉한 배열을 사용해야 한다.



문자열의 입출력

scanf("%s", str); 을 사용하면 문자열의 중간에 빈칸이 있으면 그 이후의 문자열은 입력되지 않는다.
gets(str); 을 사용하면 빈칸까지 입력을 받아 엔터를 칠때까지 입력된 모든 문자열을 입력한다.



puts(str); 을 사용하면 문자열을 출력한다. printf와 기능이 같지만, 문자열을 출력한다는 의미가 포함되므로 가독성이 증가한다. 그리고 문자열을 모두 출력한뒤에 줄 바꿈 기능까지 수행한다. 즉 printf("%s\n", str)과 같다.

문자 입출력
int getchar(); // 하나의 문자를 입력
int putchar(int); // 하나의 문자를 출력
전달인자와 리턴값이 모두 int형이라는 것에 유의
gerchar의 경우 키보드로부터 ctrl+z 가 입력되면 -1(EOF)을 리턴하도록 설계되어 있다.
(즉 입력 중간에 입력 종료시에 ctrl+z를 누르면 종료하게 만들 수 있다.)





변수의 영역과 데이터의 전달
중첩되어 있는 블록에서 같은 이름의 변수가 선언되어 있을 때는 가장 가까운 블록에서 선언된 변수를 사용하게 된다.





* 하나의 함수에서 사용되는 자동변수


선언되는 위치 함수의 시작 부분(블록의 시작부분)
사용 범위 함수 내부에서만 사용(블록 내부에서만 사용)
생존 기간 함수가 리턴될 때(블록이 끝날 때) 사라진다
메모리의 위치 스택 영역
자동 초기화 자동 초기화가 안 되고 쓰레기 값이 존재한다


* 자료형 앞에 static 을 붙여서 선언하면 정적변수가 된다.
정적변수는 기억공간의 생성과 초기화가 함수의 호출 횟수에 영향을 받지 않는다.
프로그램이 시작될때 기억공간이 할당되며, 프로그램이 종료될때까지 그 기억공간을 유지하게 된다.
즉 이후에 함수의 재호출로 인해, 초기화가 되지 않고, 그대로 값이 유지된다.



* 외부변수 - 자동변수는 사용 범위가 하나의 함수로 제한되지만, 외부변수는 어느 함수에서나 자유롭게 사용가능하다. 데이터의 공유는 쉽지만, 안전성을 보장하기 힘들다.
외부변수와 같은 이름의 변수를 함수 내에서 선언하는 경우, 함수 내에서 선언된 변수를 우선적으로 참조하게 된다.





2차원 배열
int score [3][4]; // int형 변수 4개짜리 1차원 배열이 2차원 배열의 배열 요소가 된다.
int score [3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; // 중괄호로 표기, 중괄호를 표기하지 않아도 같다. 중간 중괄호를 사용하여, 행 단위로 초기값을 생략가능하다. {{1,2},{5,6,7,8},{9,10,11,12}} 식으로 하면 3,4 자리가 초기화된다.





선언시 행 첨자를 생략할 수도 있다.
int nums[][4] = {{1},{2,3},{4,5,6}}; // 행이 3개가 된다. 중괄호를 사용하지 않고, [4] 즉 4개씩 값을 끊어서 행첨자가 선언되게 할 수도 있다.
당연할말이지만 [4] 즉, 열 첨자는 생략 불가능하다.





2중 포인터
int val = 10; int *ip = &val;
일때 &ip 은 이중포인터이다.
10을 참조하기 위해서 **&ip는 10을 참조하게 된다.
포인터변수는 변수이므로 주소연산자(&)를 사용하여 다중포인터를 구할 수 있지만, 포인터 상수는 불가능합니다. 주소연산자는 변수형태의 기억공간만 피연산자로 사용할 수 있기 때문입니다.



int **ipp; *ipp = &ip;
이중포인터(&ip)를 이중포인터변수(**ipp)에 대입한다.





배열포인터
int ary[5]
일때 ary + 1과 &ary + 1의 차이는 ary + 1은 다음행 ary[1]을 가리키지만, &ary + 1은 배열 전체의 다음행, 즉 아직 할당되지 않은 기억공간을 가리킨다.





int ary[3][4] = {{1,2,3,4},{5,6,7,8,},{9,10,11,12}}; int (*ap)[4]; // int형 변수 네 개의 배열을 가리키는 배열포인터 변수 ap = ary; // ap는 2차원 배열의 배열명 처럼 쓰일 수 있다.
즉 ap[3][4] 로 출력가능하다. *(*(ap+3)+4) 로도 가능


&ary // 2차원 배열 전체를 가리키는 포인터

ary // 첫번째 부분배열을 가리ㅣ는 포인터

&ary[0] // 첫번째 부분배열을 가리키는 포인터

ary[0] // 첫번째 부분배열의 첫번째 배열요소를 가리키는 포인터

&ary[0][0] // 첫번째 부분배열의 첫번째 배열요소를 가리키는 포인터

즉 2,3번과 4,5번만 서로 같다.
정확히 말하면 2번과 3번도 차이가 있는데, 2번은 배열명이고, 3번은 단순한 포인터이다.
배열명은 포인터뿐만 아니라 논리적으로 변수의 기능도 가지고 있다. 즉 sizeof 계산하면 2번은 배열 전체의 크기(48바이트)가 나오고, 3번은 포인터의 크기(4바이트)가 나온다.
마찬가지로 4번과 5번도 다른데, 4번은 부분배열명(16바이트)이고, 논리적으로 변수의 기능을 가지지만, 5번은 포인터(4바이트)이다.





함수포인터
int (*fp)(int,int); // 함수포인터변수 선언 fp = sum; // 함수명을 함수 포인터 변수에 저장 fp(10, 20); // 함수포인터변수로 함수 호출 (*fp)(10, 20) 도 사용가능
동적 할당 함수
#include "stdlib.h" 해야함





void * malloc(unsigned int); // 메모리 할당





int *ip; ip = (int*)malloc(4); *ip = 10; printf("%d\n", *ip); // ip가 가리키는 기억공간의 값을 출력 free(ip);
힙(heap)영역의 공간 할당, 동적할당 메모리영역





void free(void *); // 동적할당 된 기억공간을 반환한다.





void * calloc(unsigned int, unsigned int);
메모리를 동적으로 할당받은 뒤, 초기화까지한다.
첫번째는 기억공간 배열요소의 개수, 두번째는 할당 받고자 하는 기억공간의 크기(바이트)



void * realloc(void *, unsigned int);
기억공간의 크기를 조절
첫번째는 이미 할당받은 기억공간의 포인터를 넘겨준다. 두번째는 새로 할당 받고자 하는 크기를 바이트 단위로 입력한다.





메인함수의 전달인자를 사용하는 방법
첫번째 전달인자는 문자열의 개수이며, 두번째 전달인자는 포인터배열의 배열명입니다.
int main(int argc, char **argv)



구조체
struct student{ int num; double grade; }; // 세미콜론을 반드시 붙인다.

구조체 선언후 사용할 때에도 앞에 struct를 꼭 붙여줘야 한다.(c++은 안 붙여줘도 됨)
이후 구조체형의 변수를 선언한 후에, 변수 뒤에 멤버참조 연산자(.)뒤에 멤버명을 붙여서 접근가능하다.





구조체 변수
struct student a = {20, 19.4};
struct student *sp = &a;



간접멤버참조연산자(->)
(*sp).kor
sp->kor


공용체
구조체와 같은 방법으로 union 으로 선언한다.
공용체는 모든 멤버가 하나의 기억공간을 공유하므로, 공용체변수의 크기는 멤버중에서 크기가 가장 큰 멤버로 결정된다.





열거형
enum season {spring, summer, fall, winter};
열거형을 선언하면 컴파일러는 실제로 멤버들을 정수형 상수로 취급한다.
처음의 멤버를 0으로 시작해서 차례로 하나씩 큰 값을 가지도록 번역한다.





tpyedef를 사용한 형 재정의
typedef 기존의자료형 새로운자료형의이름
typedef struct student Student;
이후
Student s1; 식으로 사용가능





재정의되기 전의 자료형을 굳이 사용할 필요가 없다면 형 선언과 동시에 재정의하는 방법을 사용할 수도 있다.


typedef student{ int num; double grade; } Student;
파일 열기&닫기
프로그램과 입출력 장치 사이에서 다리 역할을 하는 파일 - 스트림 파일





파일개방
FILE * fopen(char *, char *);
첫번째 전달인자는 개방할 파일의 이름, 두번째 전달인자는 개방모드를 적어준다.


개방모드 파일이 있을 때 파일이 없을 때
r 읽기 위해 개방 널 포인터 리턴
w 파일의 내용을 삭제하고 쓰기 위해 개방 새로운 파일 생성
a 파일의 끝에 추가하기 위해 개방 새로운 파일 생성


FILE *fp;
fp = fopen("a.txt", "w");
fp가 NULL 이 아니면 파일이 열린것이다.





개방한 파일은 fclose 함수로 닫는다.
int fclose(FILE *);
리턴값은 성공적으로 닫았을 때는 0, 오류 발생시 -1을 리턴한다.




파일 포인터를 이용한 파일 입출력
int fgetc(FILE *); // 파일에서 하나의 문자를 가져온다.
리턴값(문자의 int값) char형이 아닌 이유는 만일 문자가 없을 시 -1(EOF)이 리턴되는 상황도 있기 때문이다.


스트림 파일이 하드디스크에 있는 파일의 입력이 끝났음을 확인하는 방법은 디스크 파일의 크기와 현재까지 읽어 들인 데이터의 크기를 비교하여 판단하는 것입니다. 디스크 파일의 끝에는 파일의 끝을 표시하는 어떤 정보도 포함되지 않습니다.

int fputc(int, FILE *); // 하나의 문자를 파일로 출력한다.
첫번째는 파일로 출력할 문자를 넘겨준다. 두번째 전달인자로 주어지는 파일에 그 문자를 쓴다.

스트림 파일의 이름 스트림 파일의 용도 연결된 입출력 장치
stdin 표준 입력 스트림 키보드
stdout 표준 출력 스트림 모니터
stderr 표준 에러 스트림 모니터




int main() { char ch; while(1){ ch = fgetc(stdin); if(ch==EOF) break; fputc(ch, stdout); } return 0; }
별도의 파일 개방없이 운영체제가 개방하는 스트림 파일을 사용하여서 문자를 입출력하는 방법이다.





문자열 파일 입출력
char * fgets(char *, int, FILE *); // 파일에서 문자열을 읽어 들인다.
첫번째는 문자열을 저장할 char배열의 배열명, 두번째는 읽어들일 문자열의 길이, 세번째는 입력받을 파일의 파일포인터
fgets 함수는 문자열의 끝을 표시하는 널 문자(\0)뿐 아니라, 새줄 문자(\n)도 입력된다.



fgets 함수가 호출되었을때 더이상 읽어 들일 데이터가 없으면 널 포인터(NULL)를 리턴합니다.
fgetc 는 EOF(-1)이지만, fgets는 NULL임을 기억해두자.



int fputs(char *, FILE *); // 파일로 문자열을 출력
파일입출력이 아닌 puts 함수는 문자열을 출력된 후에 자동으로 줄을 바꾸지만, fputs 함수는 문자열이 출력된 후에 줄을 바꾸지 않습니다.
리턴값은 성공하면 음수가 아닌값(0 또는 출력한 문자의 개수, 컴파일러에 따라 다름), 실패시 -1을 리턴합니다.





gets와 puts 대신 fgets와 fputs 함수를 사용하기
gets 함수는 데이터를 입력할 때 새줄 문자를 제거하지 않아도 되며,(자동으로 제거), puts 함수는 출력할 때 자동으로 줄을 바꿔주므로 나름대로 편리하다.
하지만 gets 함수는 입력 받을 기억공간의 크기를 지정해 줄 수 없으므로 데이터를 입력할 때 할당되지 않은 기억공간을 침범할 가능성이 있습니다. 또 puts 함수는 항상 줄을 바꾸므로, 문자열을 이어서 출력하는 경우에는 사용이 불가능하다.





그래서 fgets와 fputs 함수를 대신 사용하는 방법이 유용하다.
fgets(str, sizeof(str), stdin); // 키보드로 부터 문자열을 입력한다.
fputs(str, stdout); // 모니터로 문자열을 출력한다.


다양한 자료형의 파일 입출력
텍스트 파일의 데이터는 모두 아스키코드값으로 저장된 문자열이므로 숫자 형태로 저장된 파일이라도 모두 문자열로 입출력이 수행된다.
int fscanf(FILE *,char *, ...); // 파일에서 형식에 따라 데이터 입력, 데이터입력 종료시 -1(EOF)리턴
int fprintf(FILE *,char *, ...); // 파일로 형식에 따라 데이터 출력





예) fscanf(ifp, "%s %d %lf", name, &age, &height);


스트림 파일의 버퍼 비우기
int fflush(FILE *); // 버퍼를 비운다. 리턴값은 0, 버퍼를 비우기 실패시 -1(EOF) 리턴



전처리기
#include
#define 매크로명 확장문자열
#define 매크로함수명(전달인자) 확장문자열



조건부 컴파일 전처리 명령어
기존의 if~else if~else와 처리되는 방식이 같다. 조건식에 따라 컴파일이 된다.



#if 조건식1
컴파일 할 문장1
#elif 조건식2
컴파일 할 문장2
#else
컴파일 할 문장3
#endif


매크로명이 정의되어 있으면 문장1을 컴파일 그렇지 않으면 조건식을 평가해서 문장 2를 컴파일하거나 문장3을 컴파일 하게 된다.



#ifdef 매크로명
컴파일 할 문장 1
#elif 조건식
컴파일 할 문장 2
#else
컴파일 할 문장 3
#endif


앞과는 반대로 매크로명이 정의되어 있지 않은 경우에 문장을 선택적으로 컴파일 한다.



#ifndef 매크로명
컴파일 할 문장1
#elif
컴파일 할 문장 2
#else
컴파일 할 문장 3
#endif



const를 사용한 상수처리
const는 변수를 상수로 만든다.
포인터변수를 상수화 시키면 포인터변수의 값이 아니라 가리키는 기억공간의 값을 고정시킨다.





외부 변수의 선언과 정의
분할 컴파일시, extern 예약어를 사용하여 사용하는 변수가 다른 모듈에 있음을 컴파일러에게 알려줘야 한다.
(즉, 실제로 기억공간이 할당되지 않고, 변수가 다른 모듈에 있음을 알리는 역할을 한다.)





두개의 모듈에서 같은 이름의 외부 변수를 사용하면 변수의 중복선언으로 에러가 난다.
즉 필요시에는 하나의 모듈에서만 사용할 수 있도록 외부변수를 정의할 수 있는데, 이것을 외부 정적변수라고 한다.(extern은 다른 모듈의 변수를 공유, static은 다른 모듈의 변수와 별개로 하나의 모듈에서만 사용)
static 예약어를 사용





연산자 정리
& 비트별 논리곱
비트별 논리합
~ 비트부정 연산자
<<, >> 비트이동 연산자(시프트)





조건연산자( ? : )
피연산자1 ? 피연산자2 : 피연산자 3
피연산자1의 조건을 확인하여 참이면 피연산자2를 결과로, 거짓이면 피연산자3를 결과로 남기는 기능을 수행한다.

'공부 > C/C++' 카테고리의 다른 글

[mel] 멜주소  (0) 2010.12.29
25일 코딩소스  (0) 2010.01.25
C프로그래밍 관련 PDF  (0) 2010.01.19
19일 코드(C프로그래밍)  (0) 2010.01.19