본문 바로가기

소프트웨어/C/C++

c] 130929 C언어 Basig Training

/*  

########130929 정인이형 C언어 BT교육#########


char - 1 byte 작은 정수로 취급

ACSII - table에 128자의 영어알파벳과 숫자를 매칭, 모양이없는 new line이나 backspace등의 확장열들도 들어있음

한글표현 - 2 byte. char형의 최상단 비트를 1로함. ex) 물 == 10111001 10110000. 즉 128이상의글자에서는 한글임

short(==short int) - 2 byte. 약 65000

int - Architecture dependent. 64비트 설정시에는 8 byte

long - 4 byte

long long(== long long int) - 8byte

__int64: 8 byte. 엄청큰숫자까지 지원 

변수의 byte는 로컬에서는 상관없지만 네트웤상에서는 주의해야한다.


signed - 최상위 비트를 부호로사용

unsigned - 최상위 비트도 값으로 사용


signed int =>   072, -072 8진수

0x2E, -0x2E 16진수

unsigned int => 100u, 072U, 0x2Eu (대소문자상관없음)

signed long int => 100l, 100L

unsigned long int => 100ul, 100UL

long long => 100ll, 100i64

u long long => 100ui64


Endian - Network에서 중요함

little endian - c에서는 little endian 사용, intel cpu에 유리함i

big endian - java에서는 big endian을 사용. Network의 기본

c->java로 Network시 byte를 한번 뒤집어 줘야함 


실수형 - cpu에서 실수와 정수의 계산방식이 다르기때문에 등장

float - 4 byte

double - 8 byte

long double - 8~12 byte. CPU dependent


float => 13.45f, -13.45F

double => 13.45, -13.45

long double => 13.45l, -13.45L


지수표현

float => 1.34e3f (== 1.34 * 10^3 float)

double => 1.34e3 (== 1.34 * 10^3 double)


float  - 부호 1 bit, 지수 8 bit, 가수 23 bit

double - 부호 1 bit, 지수 11bit, 가수 52 bit

float->double로 증가하여도 가수부의 중요성때문에 정밀도(소수점)가 증가되기때문에

딱 두배가 되지 않음

0 00000000 0000000000000000000000000000 = 0

1 00000000 0000000000000000000000000000 = -0

0 11111111 0000000000000000000000000000 = inf

1 11111111 0000000000000000000000000000 = -inf

0 11111111 0001000100010001000100010001 = Nan

1 11111111 0001000100010001000100010001 = -Nan

실수형 연산에서는 % moduler연산 불가능. 정수에서만 가능

부동소수점으로 표현하기때문에 같은값으로 보이지만 달라서 ==연산시 값이 다를수도 있음


유도형 변수

enum - 여러 정수를 순차적 정의. 주로 상수를 정의(달력 '월'과 'March' 매칭시)

enum { SUN=0,S MON, TUE, WED }

struct - 여러 타입의 묶음을 정의

union - 공용채. 같은메모리를 공유


삼항 - a? b : c

크기 - sizeof(a)

cast - (a)b, (B)a


연산자도 일종의 funtion이다. (Return값이 있고, 매개변수 개수가 정해져있음)

int a = ( b = c ); //a에는 b에받은 c값이 들어간다.

그런데 function은 아니다. (우선순위가있다)

int a = x + y * z;

가독성을위해 괄호를 적극 사용하는 것이 좋다.

int a = ( x = x++ + ++x) => a==23임 

연산자는 대체로 왼쪽부터

특이하게 오른쪽부터되는놈들이 있음

int a = 1, b = 2, c = 3;

a = b = c; //오른쪽우선 연산  a= ( b = c);

(* / % ) > ( + - )

(== != ) > ( && ) > ( || )

a *= b +1 == a * b + 1

a *= b +1 == (a * b) + 1

a *= (b +1) == ((a * b) + 1)

a *= ((b +1) == ((a * b) + 1))

(a *= ((b +1) == ((a * b) + 1)))


c += 3 & b | a && c

c += (3 & b) | a && c

c += ((3 & b) | a) && c

c += (((3 & b) | a) && c)

(c += (((3 & b) | a) && c))


입출력의 대상

사용자 <-> OS <-> (Frame Work) <-> Program

표준입출력

stdin(keyboard)

stdout(screen)

stderr(screen)

표준출력함수

printf( printf는 puts와 putchar로 이루어짐), puts, putchar

표준입력

scanf, gets, getchar

표준에러함수

perror 

특정파일에 입출력

open() -> 소통 -> close()

파일출력함수

fscanf, fgets, getc, fgetc

파일입력함수

fprintf, fputs

표준입출력파일을 close한다면 어떻게될까?

scanf("%s", str);

fclose(stdout); 하면 stdout이 닫혔기때문에 printf가 실행되지않아 아무것도안뜸

만약 fprintf( stdout, "%s\n", str); 을 해준다면 뜸


%d => "123"

%5d => "  123"

%05d => "00123"

%-5d => "123  "


%f => "3.140000"

%10f => "   3.140000"

%.2f => "3.14"

%10.2f => "      3.14"

%010.2f => "0000003.14"


%*d

printf("%5d",123);

== printf("%*d", 5 , 123);

또는

입력하나무시

scanf("%*d %d",&i) => 1 2 => 1만받음


print Y/N?을 scanf로 받을시 두번연속받으면

첫번째 받았을때 버퍼에 \n(엔터)가 남았을수도있다.

그래서 %*c%c로 받으면 깔끔하게 "Y'만 받아진다 

%[charters]s

scanf("%[ab]s",str); => str에 a,b가있을때만 받음,순서상관없이 문자열에 있는놈만 받음

%[^charters]s

scanf("%[^ab]s",str); => str에 a,b가 없을때만 받음

asdf\n\n\n을 받았을때 버퍼에 \n\n\n들이남는데

fflush(stdin)을하면 버퍼를 비워줌


printf(...);의 return value는 출력되는 문자열의 길이이다.

printf("asdf");는 문자열의 길이인 4를 return 함.


변수의범위

scope '{'안에서만 사용가능

전역 변수 - 어디에서든 참조 가능. Data영역

지역 변수 - 특정위치에서 참조 가능. Stack영역. 지역이 끝날시 stack 회수

Block Scope - 한 Block 내에서만 유효한 Scope. '}'까지의 범위. Stack영역

선언.

int i; 혹은 auto int i;

c++11에서는 auto의 사용법이 다르기때문에 auto는 사용하지 않는것이 좋다. 

c++11에서는 auto 키워드가 선언생략할때 쓰임

List<Map<int,int>> a = new List<Map<int,int>> ();

auto a = List<Map<int,int>> ();

Static(Block) Scope- 한 Block 내에서만 유효한 Scope. 그러나 저장은 Data영역

int main(){

static int a = 10;

} //Block이 끝나도 파기되지 않기 때문에 pointer만 갖고있으면 Block 종료후에도 사용 가능 

for( i=0~10) {

static int j = 0; //매번 새로운 선언이아니라 j에접근해서 값을 계속 증가 시킴

cout<< j++;

}

Extern Scope - 진정한 의미의 전역변수,

외부파일에서도 접근 가능.

여러사람이 동시작업 시에 주의 필요

Multi thread환경에서 충돌 가능

어떤 Block에도 속하지 않은 위치에 선언

FileA.c

int a = 10;

FileB.c

extern int a; //fileA의 변수 a 불러옴

int main(){

cout<<a; //10출력 d

}

Static(File) Scope - file내에서만 유효한 Scope. Data영역

선언방법은 static int i; //외부에서는 접근불가. c++에서 private와 같은 개념

FileA.c

int a = 11;

FileC.c

Static int a = 12;

FileB.c

extern int a; //fileA의 변수 a 불러옴, C는 private라서 접근불가 

int main(){

cout<<a; //10출력 d

}


변수가 저장되는공간

RunTime시에 프로세스(메모리에올라와있을때)의 메모리는 크게 4가지로 나눠짐

Code(Text)

프로그램의 코드와 상수가 들어간다.(ASM)

Data

전연 변수들이 들어감

Static이나 Extern Scope변수들이 들어감.

프로그램 파괴시 같이 파기됨

프로그램 실행전의 Data와 BSS영역이 합쳐져서 Runtime에는 Data영역으로 변함

int a = 0; //초기화 된 전역변수 == Data

int b; //초기화안된 Bss

int main..

또는Struct A 가 500Byte라면

A a={}; //500바이트가 잡힘. data

A b; //실행도안했는데 할당할필요없으므로 Bssi

Heap

동적할당된 변수들이 저장된다.

malloc, calloc

주로 낮은주소부터 저장된다.(Stack은 heap과 반대로 거꾸로 내려옴)

크기가 가변적.

int *a = (int *)malloc(sizeof(int));

int *b = (int *)malloc(sizeof(int));

int *c = (int *)malloc(sizeof(int));

cout<<(&a)<<a;

cout<<(&b)<<b;

cout<<(&c)<<c;

이렇게 출력해보면 &a등은 스텍의 주소를(주소감소), a는 힙의 주소를(주소증가)나타낸다.

만약 stack과 heap이 만나면 overflow발생

Stack

Block Scope 저장

구조체 - 복잡한 형을 표현할 때 사용

구조체 정의방식

1회성 구조체

struct{

~~

} A, B; //A와 B외에는 구조체 재사용 불가능

재사용가능한 일반적 정의 방식

struct tagCar{

~~

};

struct tagCar A,B; //재선언 가능

재사용이 가능한 권장 방식

typedef struct tagCar{

~~

} Car;

Car A,B; //매번 struct를 붙일 필요가 없음 

구조체변수는 '.'을 이용해 사용

구조체내의 포인터 변수는(참조중인)  '->'를 사용

pA->ID = 1; // == (*pA).ID=1;

구조체 역시 하나의 변수형

구조체 내에 구조체 포함 가능 

struct A{

Car c,d;

}

그러나 구조체 하나의 크기가 크기때문에 주로 Pointer 참조를 이용하여 사용

Car a;

Car *b = &a;

func(&a); 

func(b); //참조를 하는게 용량면에서 유리

구조체는 선언순서에 따라 크기가 달라진다.

int int double => 16 Byte

short short folot => 8 Byte

short int char int => 16 Byte //가장 용량이 큰 4 Byte를 기준으로 변수에 용량 할당

0x1111 0x22222222 0x33 0x44444444 를 넣을땐

11 11 00 00

22 22 22 22

33 00 00 00 이렇게 빈공간이 발생하면서 저장.(Padding Bit)

44 44 44 44  

빈공간을 남기는 이유는 cpu bus가 4 byte씩 읽는게 속도면에서 유리하기때문

Padding Bit는 가장 크기가 큰 변수형을 기준으로 잡힌다. 

char a; char b; int c; double d;는

ab  cccc

dddddddd 로 잡혀서 16 Byte가 잡힌다.

이상태에서 

(char*)&test.a - (char*)&test  //기준점에 동일하므로 0출력

를 하면 기준점인 &test의 주소로부터 변수들이 얼마나 떨어져있는지 나온다.

(char*)&test.d - (char*)&test  //기준점에서 8떨어져있으므로 8출력

위에서설명한 struct내의 struct선언은 용량때문에 피한다는 설명

struct{

char a 

char b

struct{ double double}u

int c

double d

}를 하면

ab


uuuuuuuu

uuuuuuuu

cccc

dddddddd 로 잡혀서 내부 struct가 커질수록 외부 struct 용량이 어마어마해짐

Bit field

비트를 쪼개서 씀

union 공용체

하나의 메모리를 공유하여 동시에 사용. 요즘은 잘 안씀

가장 큰 변수형을 기준으로 하나의 메모리가 잡혀서 공유됨


배열

포인터의 배열

int* a[4]; //a는 4짜리배열인데 int*를 리턴한다.

포인터

2차원배열을 넘길때

int sum( ########){ =>이곳에 a를(2차원배열) 넘겨주려면..아래에설명

}

int main(){

int a[2][2]= { 10 , 20 , 30, 40};

sum(a);

}

넘겨주려면 고려해야하는사항이

1.만약 #이 int ** p이면

p[0] -> (int*)형의 0x0a =>10 을 갖고

p[1][0] -> *( (int*)형의 0x30)을 갖는다.

**로함수가 받으면 이상하게 값을 인식해서 잘못됨

2.만약 #이 int *p[2]로 받는다면

p[0] = (int*) 10 =>여기서 10은 주소값 int*형

p[0][0] = *(10+4) 를리턴하므로 잘못됨. 위의 int**로 받을때와 같은결과

1,2번둘다 4byte씩밖에안넘음

3. 정답은 int (*p)[2]로 받아야함

미리알아야하는게

int *(p[2]);

p는 배열인데 -> int*에 대한 배열

[int *| int*|....]

p[0] -> a

p[1] -> a + 1 = a + sizeof(int *) ...


int (*p)[2];  

p는 포인터인데 int[2]를 가리키는 포인터

[int[2] | int[2] | ...]

p[0] -> a

p[1] -> a + 1 = a+sizeof(int[2]) ...


그래서 int (*p)[2]로받으면

p[0] => p[1]로갈때 8byte씩 jump하므로 원하는 결과값이 나온다.


등가포인터 정리

int a[4] -> int *p

[int | int | int | int] 

int* a[4] -> int**p

[int * | int * | int * | int * ]

int a[2][4] -> int (*p)[4] =>int를 4개씩받는놈의 포인터 

[int[4] | int[4] ]

int a[2][3][4] -> int (*pa)[3][4] =>첫번째만나는배열을 포인터로 바꾸면 된다

int (*b[3])[4] -> int (**pb)[4] =>첫번째만나는배열을 포인터로 바꾸면 된다

int *(c[2])(int*) -> int *(*pc)(int*) =>첫번째만나는배열을 포인터로 바꾸면 된다

int *d[3][4] -> int* (*pd)[4]

int (*(*e[5])(void))[4] -> int(*(* *pe)(void))[4]

등가포인터의 어려운점은 말로부터유추하거나 꼬아논게 어려움

char *[2] func(int (*p)[2], double d); => 이런거 구하는게 어려움 

 


함수포인터 등가(==델리게이터)

int add(int a, int b){}

int main(){

int (*padd)(int a, int b)= add; //함수포인터의 등가포인터

}

마찬가지로 첫만나는 함수를 포인터로 바꾸면된다 


int (*p)(int,int) // (int int)를 받아서 int를 리터하는 함수를가리키는 포인터

함수포인터는 매개변수를 void로써줘도된다.

int (*padd)(void)=arr;

padd(1,2); //이렇게 해도 동작한다. 리턴값만같으면된다


함수포인터는 이름 -> 매개변수 -> 리턴값

char (*(   *func()  )[] )()

???  (*(func()) ) => func는 함수의 의 포인터다

???  *(*func() ) [] => 포인터인데 어떤것의 배열의포인터를 반환한다

[?* | ?* | ?* | ?* |...]

???  (*(*func)() []) () => 포인터인데 어떤것의 배열의포인터를 반환하는 함수다

([?* | ?* | ?* | ?* |...]() )

char (*(   *func()  )[] )() => 함수의 포인터인데 캐릭터 배열의 포인터를 반환하는 함수다

([char* | char* | char* | char* |...]() )


STACK

main(){ fcn1(1,2,3); }

main푸시 3푸시 2푸시 1푸시 fcn1푸시 fcn1팝 1팝 2 팝 3팝

가변인수

printf는 형태가 다양하다.(매개변수갯수)

int getsum(int num, ...){

int sum=0;

int i;

va_list ap; //가변인자

int arg; //받은인자


va_start(ap,num); //총개수를 가지고 시작

for( i=0; i<num; i++){

arg= va_arg(ap,int); //다음인자를 가져옴

sum+=arg;

}

va_end(ap); //종료

return sum;

}

getsum(2,1,2)   , getsum(4,  3,4,5,6,);

재귀함수

재귀는 stack으로 이루어지기때문에 너무 많이쌓아서 heap영역과만나면 overflow

코드는 간결하지만 iterator(반복자.for문.while문)보다 메모리사용량이 크다.

가능한 다른방식이 가능하다면 수식으로 푸는게 좋음


전처리

#ifdef H1_H 

//만약 H가등록되있다면

#define H_H //디파인하라 

#else

#endif


A = 암것도없음

#ifdef A

암것도없으므로 이 구문은 실행되지 않음 

#define A

A = <null> 이 들어가있음


#define sum(a,b) a+b

#undef sum(a,b) //디파인 지움


#pragma pack(1)

struct a..... 하면 1byte씩 읽는다.(패딩을 없앤다)

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


mylib.h

fcn1() {}

fcn2() {}

mycode.c

#include "mylib.h"

mycode2.c

#include "mylib.h" => 이렇게되면 한프로젝트에서 두번 인클룯드되서 오류남.링크에러


그래서 mylib.h에서

#ifndef _MYLIB_H_ //디파인안되있으면

#define _MYLIB_H_ //디파인하시오

#endif //디파인되있으면 ifndef에서 false로 안들어가서 걍 인클루드안함


매크로의함정

#define SQRT(X) X*X

sqrt(2) -> 2*2 = 4 (o)

sqrt(2+1) -> 2+1*2+1 = 6(x)

매크로함수

#define add(a,b) ((a)+(b)) 를 a+ add(a,b) + c 이런식이아니라

add(a,b);만 강제로 하게하고싶으면 do while(0)으로 강제하면됨


#define Func(x,y) \ //돈은 줄연결 키워드

do{           \

(a)+(B)  \

}while(0)     \

이렇게되면 무조건 Func(x,y);로 써야함


#expr은 매크로에서 진짜 스트링으로 바로 쏴줌

#define a(expr) printf(#expr" abcd");  .... a(x/y) =>출력값 x/y abcd

그런데 ##expr은 진짜 식을 그대로 대입하기때문에 printf(x/y" abcd");라서 오류가 난다 




*/