C language

구차니의 잡동사니 위키
(LANG:C에서 넘어옴)
이동: 둘러보기, 찾기

구차니의 날로먹는 C언어 강좌

블로그에서 정리하다 한계를 느껴서 위키에서 정리를 시작해보려고 합니다.

- 2012. 5. 10 개발에 찌들어서 정줄놓고 있는 구차니 올림


목차

C언어 예제

#include <stdio.h> 
int main(int argc, char **argv)
{
    char str[] = "Hello World\n";
    printf("%s",str);
    return 0;
}

#include <stdio.h>

 #으로 시작하는 것은 전처리기에서 처리하는 부분이며, include는 <>안의 파일을 불러온다는 의미입니다.
다시 정리하면 stdio.h 라는 파일을 이 예제 파일을 컴파일 할때 포함하라는 의미로
위의 예제에서는 printf() 함수를 사용하기 위해 stdio.h를 불러들였습니다.

int main(int argc, char **argv)

표준적인 main()함수의 모습이며 다른 모습으로는 void main() 으로도 쓰이기도 합니다.
main() 함수는 entry-point 라고 하며 작성한 프로그램의 시작점을 의미하는 특별한 함수 입니다.
또한 main() 역시 함수로서 값을 돌려주어야 하며 일반적으로 프로그램의 실행 결과가 성공인지 실패인지를 돌려줍니다.
argc와 argv는 인자로서 리눅스 명령어 ls를 예를 들면
ls -al /bin 이라는 명령어를 실행하면
ls.c 파일의 main() 함수로 "-al","/bin" 두개의 문자열을 넘겨주게 됩니다.
이러한 옵션값을 받아서 추가적인 확장을 하게 되며, 이러한 확장이 필요없을 경우에는 단순하게 void main()을 쓰면 됩니다.

char str[] = "Hello World\n";

char는 문자를 나타내는 변수로 일반적인 컴퓨터들에서 1byte의 크기를 가집니다.
str은 문자열을 담을 변수의 이름이며
[]는 char형의 배열로 str 이라는 이름으로 선언함을 알려줍니다.
= "Hello world\n"; 는 str[] 처음부터 값을 넣어주며
\n는 escape 문자라고 하며 엔터값을 의미합니다.
그리고 " "는 문자열을 나타내며 가장 마지막에는 \0 값을 보이지 않게 넣어주는 역할을 합니다.

printf("%s", str);

printf()는 print format 의 약자로 지정한 양식으로 출력하도록 하는 표준 C 라이브러리 입니다.
stdio.h를 include 해야 사용할 수 있습니다.
"%s" 는 출력할 양식을 String 형태로 지정하고,
,str 은 %s에 str 의 내용을 넣도록 한다는 의미입니다.
즉, "%s %d %c" 이런식으로 3개의 %로 시작하는 치환자가 있다면 변수 역시 3개가 들어가야 합니다.

return 0;

main() 역시 함수이기 때문에 값을 돌려주어야 하며, 일반적으로 운영체제에서 프로그램 실행시
정상적으로 실행이 종료되었는지 오류로 종료된것인지 등을 판별하기 위한 값을 넘겨주게 됩니다.
일반적으로 0을 return하면 아무런 문제없이 종료된것으로 보며 1을 return 하면 에러인것으로 간주하게 됩니다.

C언어 용어 및 개념

변수

variable / 변수
변할 수 있는 값을 저장하기 위한 그릇/공간입니다.
반대되는 개념으로 상수(constant)가 존재하며 수정불가능한 값을 저장합니다.

변수란 무엇인가

변수는 변할 수 있는 수를 저장하기 위한 공간이며, 컴퓨터가 해석할 방법을 알려주기 위한 수단입니다.
컴퓨터에 있어서 모든 정보는 0과 1로 저장되며, 0과 1을 bit(binary digit)라고 이야기하며,
이 숫자의 갯수로 표현 가능한 종류가 늘어나게 됩니다.
하지만 0과 1만으로 저장된 수많은 정보들 사이에서 이 숫자를 어떻게 해석하냐에 따라
문자나 문자열, 상수나 실수 등으로 해석을 할 수 있게 됩니다.
컴퓨터에 있어 정보는 0과 1 뿐이므로 정보의 최소단위인 bit로는 있다(1/true) 없다(0/false) 밖에 구분을 못하며
의미있는 값을 저장하기 위해서는 더 많은 비트를 묶어서 구분을 해야합니다.
만약 8개의 비트단위로 판별시에는 2의 8승, 0에서 255, 256 가지의 종류에 대한 구분이 가능하며
ASCII 의 경우 7비트 128개의 제어문자/화면에 보여지는 알파벳, 숫자, 특수문자를 하나의 글자(캐릭터)를 구분할 수 있게 됩니다.
숫자로 해석시 256은 너무 적은 수이므로 16비트/2바이트를 묶어 해석하며 2의 16승, 65536개 혹은
32비트/4바이트를 묶어 해석하여 2의 32승 4294967296 가지를 구분하여 사용할 수 있습니다.

01000001 01000010 01000011 01000100
0x4142 4344
1,094,861,636
ABCD

예를들어 0과 1로 이루어진 숫자가 32자리가 존재하는데, 이 값을 몇자리를 어떻게 끊어서 해석하냐에 따라 의미가 달라집니다.
010000001 에서 1이 처음으로 해석할 수도 있고 3번째 0 부터 해석을 시작할수도 있습니다.
CPU의 데이터 저장/처리하는 기본 단위가 8bit 이기 때문에 일반적으로 2진수 8자리씩 끊어서 해석하면 됩니다.

변수의 크기

구현되는 프로세서의 종류에 따라 다를 수 있기에,
sizeof() 연산자를 통해 실제 할당되는 변수의 크기를 확인하는 것이 좋습니다.

print("%d\n",sizeof(char));
print("%d\n",sizeof(short));
print("%d\n",sizeof(long));
print("%d\n",sizeof(int));
print("%d\n",sizeof(float));
print("%d\n",sizeof(double));

sizeof() 연산자와 printf()를 사용하여 확인하실 수 있습니다.
signed와 unsigned는 숫자의 해석 방법이므로 변수의 범위에 영향을 미치나 크기에는 영향을 미치지 않습니다.

변수의 종류

변수형(type) bit byte unsigned signed
정수형 변수
char 8 bit 1 byte 0 ~ 255 -128 ~ 127
short 16 bit 2 byte 0 ~ 65535 -32768 ~ 32767
long 32 bit 4 byte 0 ~ 4294967296 -2147483648 ~ 2147483647
int 32 bit 4 byte 0 ~ 4294967296 -2147483648 ~ 2147483647
실수형 변수
float 32 bit 4 byte 1E-5 (소수점 7자리) 0.0000001 1E+37
double 64 bit 8 byte 1E-9 (소수점 15자리) 0.000000000000001 1E+37
정수형 변수

정수형 변수는 -2 -1 0 1 2 와 같은 소수점이 없는 정수를 저장하기 위한 변수입니다.

char / 8bit / 1byte

1바이트로 1개의 문자(ASCII)를 저장할 수 있습니다. val = 'a'; 형태로 값을 저장합니다.
혹은 -128 에서 127 까지의 정수를 저장할 수 있으며 unsigned char일 경우 0 에서 255까지 저장할 수 있습니다.
배열로 사용되면 문자열을 저장할 수 있으며 str = "helo world"; 형태로 값을 초기화 할 수 있습니다.

short / 16 bit / 2 byte

2바이트로 0에서 65535 혹은 -32768 에서 32767 까지의 정수를 저장할 수 있습니다.
세계의 모든 언어를 담는 규격인 유니코드가 2바이트를 사용합니다.
표준 c에서는 유니코드를 지원하지 않습니다.

long / 32 bit / 4 byte

4바이트로 0에서 4294967296 혹은 -2147483648 에서 2147483647 까지의 정수를 저장할 수 있습니다

int / 32 bit / 4 byte

시스템마다 다르게 할당 되며, 시스템의 처리가능한 최대크기로 할당이 됩니다.
일반적으로 32bit/4바이트로 간주하며 0에서 4294967296(약 43억) 까지의 정수를 저장할 수 있습니다.
64bit 에서는 64bit / 8byte로 간주됩니다.

실수형 변수

float / 32 bit / 4 bytes

소수점을 포함하는 실수를 저장할 수 있습니다.
소수점 7자리까지의 정확도를 보장합니다.

double / 64 bit / 8 bytes

소수점을 포함하는 실수를 저장할 수 있습니다.
float형에 비해 소수점의 정확도 및 자릿수가 증가합니다.
소수점 15자리까지의 정확도를 보장합니다.

부동소수점 / IEEE 754

컴퓨터에서는 소수를 사람이 쉽게 읽을 수 없는 방법으로 표기합니다.
2진수가 기본이기에 소수점 역시 1/2 1/2^2 ...1/2^n 의 합으로 나타내며
0에서 1에 한 없이 가까운 범위의 수를 나타내게 됩니다.
이러한 근사값에 의한 소수를 저장하기에 어느정도의 오차가 존재하게 됩니다
그리고 0에서 1 사이의 값을 나타내게 되므로 정규화를 통해 1.xE^y 승의 형태로 값을 고정합니다
지수승의 값으로 소수점 자리를 나타내기에 항상 1.0000 식의 소수점으로 나타나
부동소수점(소수점 자릿수가 고정된)의 형태를 띄게 됩니다


변수의 이름

변수의 이름은 식별자로서 영어로 시작하며, 숫자와 _ 를 포함하며 대소문자를 구분합니다.
a123456789 식으로 변수의 이름을 지을수 있으며 abc 와 Abc는 다른 변수입니다.
이렇게 대소문자를 구분하는 것을 case-sensitive라고 하며 반대는 case-insensitive라고 합니다.
프로그램이 커질수록 변수의 이름에 의미를 두는것이 해석에 유리해지며
윈도우에서는 주로 헝가리안 표기법을 사용하여 변수이름에 변수의 크기를 포함시키기도 합니다
함수이름과 변수이름이 헷갈리지 않도록 변수는 주로 소문자로만 이름을 짓기도 합니다.

형 변환

type casting / 타입 캐스팅

현변환은, 변수의 크기가 다른 변수에 대입하는 방법 및 형변환 연산자를 통환 변환을 포함합니다.
만약 char형 변수를 쓰다 255까지 숫자를 넘어서는 계산이 필요하다면 short형 변수에 넣은뒤
계산을 진행하면 데이터의 손실없이 계산이 가능해집니다. 이러한 형 변환을 암시적(implicit) 변환이라 합니다.
일시적으로 형을 바꿀때는 형 변환 연산자를 이용하며 (short)val 이런식으로 명시적으로 변환이 가능합니다
단, 형변환시 범위가 큰 변수를 작은 크기의 변수로 변환하면 데이터의 손실이 생기며 치명적인 오류를 일으킬 수 있습니다.

부호가 있고 / 부호가 없고

컴퓨터에서 음수역시 양수와 동일한 방법으로 이진수인 0과 1로 표기를 하며
이러한 이유로 부호가 있는 변수와 부호가 없는 변수를 혼용할 경우 문제가 발생합니다
예를 들어 for문의 조건식에서 도달불가능한 조건으로 인해 무한반복이 될 수 있고, 프로그램이 멈춘 듯 보이게 됩니다

char idx;
for(idx = 0; idx < 256; idx++) ;

위의 예제에서 char형은 -128에서 127 까지만 증가가 가능한데, 이는 항상 256 보다 적은 값이므로 항상 참으로 인식되어
반복문을 벗어날 수 없게 되며 종료되지 않게 됩니다.

아래는 간단한 예제로, char 형의 범위를 벗어날 경우 발생할 수 있는 문제를 보여줍니다.
char 형은 -128에서 127까지의 범위를 가지며 127의 값에서 증가를 시키면 16진수로 출력시 0x7F에서 0x80으로 값이 증가합니다.
하지만 printf에 의해서는 FFFFFF80으로 보여지며 이는 printf()의 특성으로 이렇게 보여질 뿐 실제 값은 1바이트로 0x80입니다
또한 형변환을 통해 unsigned char 로 출력을 하게 되면 -128이 아닌 128로 보이게 됩니다.
이렇듯, 부호가 있고 없고에 따라 값이 예측하지 못한 방향으로 증가할 수 있으므로 주의해야 합니다.

$ cat test.c 
#include <stdio.h>

void main()
{
	char a;
	a = 127;
	printf("%d 0x%08X\n",a,a);
	a++;
	printf("%d 0x%08X\n",a,a);
	printf("%d 0x%08X\n",(unsigned char)a,(unsigned char)a);
}
$ ./a.out 
127 0x0000007F
-128 0xFFFFFF80
128 0x00000080

물론 %u 를 이용하여 unsigned 타입의 변수를 출력하는 방법도 존재는 합니다.


부호가 없는 변수(unsigned)

char형을 예로 들면, 8비트/1바이트 변수로 2의 8승 = 256 가지의 표현이 가능합니다.
0에서 255 까지 256가지를 표현이 가능하지만 음수를 나타낼 수 없습니다.
부호가 없는 변수는 양수(0과 자연수)에 대해서만 사용합니다.

부호가 있는 변수(signed)

부호를 나타내기 위해서는, 변수가 음수인지 양수인지를 알려주는 값이 필요합니다
있거나(true)/없거나(false) 두가지 이므로 1비트면 충분히 표현이 가능해지며
8비트의 값중 최상위 비트(MSB - Most Significant Bit)를 부호로 사용하게 됩니다
대신 숫자에 사용가능한 비트는 8비트에서 7비트로 1비트가 줄어 2의 7승인 128가지의 양수와 음수를 표기할 수 있게 됩니다
이러한 방법을 사용하면 +0 과 -0이 존재하게 되는 문제가 발생하게 됩니다

1의 보수

2진수에서 보수는 0을 1로, 1을 0으로 뒤집은 수 입니다.
01101101을 보수로 만들면 10010010이 되며, 서로 더하면 전부 1이 되는 특성을 지닙니다
하지만, 1의 보수를 사용할 경우 양수에서 사용하던 가산기를 사용할 수 없습니다.

2의 보수

2의 보수는 1의 보수에 1을 더합니다
1의 보수에 비해 2의 보수는 양수를 위한 기존의 가산기(adder)를 사용할 수 있는 장점이 있습니다.
(하드웨어의 중복사용으로 비용절감을 할 수 있습니다)

함수

function / 함수

함수란 무엇인가

함수는 어떠한 값을 받아 어떠한 결과를 돌려주는 일종의 마법상자입니다.
물론 함수가 받을 값이 0개 일수도 있으며, 함수의 결과 역시 0개 일수 있습니다.
함수는 값을 돌려주거나, 행위를 하거나, 행위를 하면서 값을 돌려주기도 합니다

함수의 형태

함수는 돌려줄 값의 형(리턴형), 함수의 이름 그리고 함수가 넘겨받을 변수로 구성됩니다.
그리고 다른 파일에서 함수의 형태를 알기위한 함수의 프로토타입이 있습니다.

return_type function_name(type var1,type var2); // prototype
return_type function_name(type var1,type var2) // function body
{

}

함수의 이름

함수의 이름은 변수와 동일한 규칙으로 이름을 지어줄 수 있습니다.
변수를 읽거나 저장하기 위한 함수들의 경우 get/set 으로 시작해서 첫글자를 대문자로 명명하기도 합니다
getNextvar() / setCurrentvar()
또한 파일 이름을 접두로 동일 기능을 수행함을 명시적으로 나타내기도 합니다

함수의 인자

가변인자나 고정된 인자를 사용하며, 함수안에서 처리할 값을 넘겨받습니다.

함수의 반환값(리턴값)

함수가 처리한 결과나 하나의 값을 돌려주기 위한 값입니다.
돌려줄 값이 없을 경우 void형으로 주로 함수를 선언하게 됩니다.

반복문

컴퓨터가 만들어진 가장 큰 이유가 바로 반복된 연산의 수행입니다.
for 문과 while 문이 존재하며 두개의 반복문은 서로 변환이 가능합니다.
그리고 컴퓨터는 0부터 숫자를 세므로
만약 100번을 반복해야 한다면 0에서 100미만으로 비교하는 식으로 총 100번의 반복을 하도록 구성됩니다.
for(idx = 0; idx < 100 ; idx++) ;

for 문

for문은 한줄에 필요한 초기화문/조건문/증가문을 포함하는 깔끔한 문법 구조를 가진 반복문입니다.
for(;;)으로만 쓸수도 있으며 조건도 없이 무한반복문으로 사용이 가능하며 while(1)으로도 사용이 가능합니다.

for(idx = 0; idx < loop; idx++)
{

}

while 문

while문은 for문과는 달리 여러곳에 분산이 되어있습니다.
c언어에서 0은 거짓 1이상은 참으로 인식하여 while(1) 이라고 하여 무한반복을 할 수 있습니다.

idx = 0;
while(idx < loop)
{
    idx++;
}

do - while 문

do-while문은 while과 동일하지만, 1회를 실행후 조건을 비교한다는 차이가 있습니다

idx = 0;
do
{
    idx++;
}while(idx < loop);

제어문

제어문은 조건을 비교하여 원하는 조건에 따라 실행하는 방법을 다르게하는 기능을 수행합니다.

if - else 문

if-else문은 if문의 조건을 충족하거나 충족하지않을 경우에 대해서 수행을 하게 합니다.
if / if else / else 로 조건을 세분화 할 수 있으나, 이경우 논리적으로 오류가 발생하기 쉬우니 주의해야 합니다

if (a > b)
{
    printf("a 가 b보다 큼\n");
}
else
{
    printf("b 가 a 보다 크거가나 같음\n");
} 

switch - case 문

switch - case문은 정해진 숫자에 대해 실행하거나 정해지지 않은 숫자에 대한 실행을 하게 됩니다.
case 뒤의 숫자는 상수여야 하며, 변수를 지정할 수 없습니다.
case는 지정된 숫자에 대해서 처리를 하며(if-else에서 if(a==1) 과 같이 하나의 정해진 값에 반응합니다)
default는 지정되지 않은 모든 경우에 대해 처리를 합니다(if-else에서 else의 역할을 합니다)

switch(val)
{
    case 0:
    case 1:
        break;
    default:
        break;
}

goto 문

goto는 무조건 그 위치로 이동하는 명령입니다. 제어를 무시하는 특성으로 인해 되도록이면 사용하지 않는 것이 좋습니다.

goto LABEL;
LABEL: 

위와 같은 형태로 구성을 하며 라벨은 ;이 아닌 : 으로 표기합니다

C언어 중급개념

C언어를 배우다 보면 다음부터 머리가 아파지기 시작합니다.

변수의 범위 (scope)

변수의 범위는 변수가 살아있는(유효한) 범위와
다른 파일이나 함수에서의 접근가능 여부를 의미합니다.
일반적으로 범위는 {} 로 정해지며,
범위는 프로그램 전체 > 파일 > 함수 > {} 안 의 순서로 작아집니다.

static / auto

기본적으로 변수는 auto로 선언이 되며 범위를 벗어나면 자동으로 소멸합니다
static 변수는 동일 파일 내에서만 접근이 가능하며 소멸되지 않습니다.
함수내의 static 변수는 최초 1 회에 대해서만 값이 초기화되며,
파일 내에서 선언한 auto 변수와 동일한 유효범위를 지닙니다.

함수변수 (인자)

함수의 인자로 넘겨주는 변수는 함수내에서만 유효합니다.
그렇기 때문에 포인터를 이용해 대용량의 데이터를 간접적으로 주고받습니다.
하지만 함수변수는 값이 복사 되므로 함수가 끝나고 돌려 받기 위해서는 1개의 값을 return하거나
인자를 포인터로 쓰거나, 구조체로 만들어 return하거나, 함수내에서 외부의 변수를 끌어와 쓰고 저장하는
방법등을 이용해야 합니다.

파일변수

파일에서 변수를 선언하면 프로그램이 실행되는 동안에는 다른 파일에서도 접근이 가능하며 프로그램 종료시까지
변수가 소면하지 않습니다. 만약 파일변수를 static으로 선언하면 파일내에서만 읽을 수 있게 됩니다.
파일 내에서만 유효하므로, 다른 파일에서 동일한 이름의 변수를 사용할 수 있게 됩니다.

함수내 static 변수

파일변수처럼 초기화는 1번만 되어 계속 값을 유지하게 되는 변수입니다

변수의 접근 우선순위

동일한 이름으로 변수를 선언시 식별자가 동일하여 에러가 발생하지만,
{}로 시작하는 블럭의 시작 부분에 변수를 선언시 지역변수(auto)로 선언시 에러없이 선언되며
{} 밖의 변수는 읽어오지 못하게 되고 {} 안의 변수만을 읽을 수 있게 됩니다.

함수의 범위(static 함수)

프로그램이 방대해져서 여러파일로 작성하게 되면 함수이름 충돌을 막기위해
static 키워드로 함수의 범위를 제한하게 됩니다
static 함수는 파일 내에서만 호출이 가능하며, 파일이 다를경우 호출할수 없으며
동일한 이름으로 여러 파일에 존재할수 있습니다
프로그램이 커지거나 외부 라이브러리 사용시 합수명 충돌을 방지하는데 유용하게 사용될 수 있습니다.

또한 static 함수는 파일이 다르면 호출할 수 없는 특징으로 인해 마음대로 끌어다 쓸 수 없도록
라이브러리의 내부 구조를 숨기는데 사용하기도 합니다.

배열 (Array)

배열은 동일한 변수의 나열입니다.
만약 char 형으로 100개의 변수가 필요하다면
char a,b,c,d,e; 등으로 100개의 변수를 선언해서 사용할수도 있겠지만
for문으로 간변하게 동일 이름으로 사용할수가 없는 문제로 인해 일일이 계산을 해주어야 합니다.
그러한 불편함을 줄이기 위해서 동일한 변수 이름에 번호로 식별을 하는 방법을 고안해 내었는데 이를 배열이라고 합니다.
char a[100]; 이라고 선언하면 char 형의 변수가 100개 연속으로 생성이 되며
char a[100][100]; 이라고 선언하면 char 형의 변수가 100개씩 100개가 생성이 됩니다.
항상 그러하듯 0에서 부터 증가하여 99까지 사용하게 됩니다.

문자열도 배열로서 마지막에 문자열의 끝을 알려주는 0x00/NULL/\0 이 들어있는 문자의 배열입니다

구조체/공용체 (struct / union)

구조체는 여러가지 다양한 형태의 변수들을 묶어서 하나의 변수로 사용하는 것입니다.
예를들어 연락처를 저장하는 프로그램을 작성하는데 있어,
전화번호[100] 이름[100] 이런식으로 구성을 할 수 있습니다.
하지만 이 경우에는, n번째 인덱스가 모든 변수에 따라서 같이 움직여야 하고
연락처에 새로운 항목(팩스번호2 이런식의 확장)이 추가 될경우에는 다른 부분에서도 많은 수정이 이루어져야 합니다.
하지만 구조체를 사용하면 사람[n]에 대해서 각각 접근을 할 수 있게 되므로 직관적으로 프로그램이 구성되어질수 있게 됩니다.
또한,함수에서 값을 주고 받을때 여러개의 인자나 대규모의 변수를 손쉽게 주고받을 때에도 구조체를 사용합니다.
(함수의 리턴값은 하나뿐이므로 구조체를 통해 여러개의 값을 넘길수 있게됨)

연산자 (Operator)

연산자는 계산을 하는데 사용하는 부호입니다.
덧셈, 뺄셈, 곱셈, 나눗셈과 같이 숫자에 관련된 연산자와
참/거짓을 위한 논리 연산자, 비트단위 계산을 위한 비트연산자
포인터를 위해 주소를 변환하고 주소에 위치한 값을 읽어오는 주소 연산자가 존재합니다.

산술 연산자

수학 / 산수에서 사용하는 수식을 포함하며, 기본적인 우선순위를 사용합니다.
+ - * / % ( )
+= -= *= /= %=
++ --

a = a + 1; 은
a += 1; 로 사용이 가능하지만 += 연산자는 우선순위 판별이나 가독성이 떨어져
a++; 정도의 단순한 사용만 하는 것이 좋습니다.

++a 와 a++은 같은 ++ 연산자이지만, 동일 행에서 값을 사용시 차이가 나게 됩니다.
++a는 먼저 값을 증가시킨후 사용하게 되며, a++은 사용후 값을 증가시키게 됩니다.
물론 가독성이 좋지 않은 연산자이므로 적절하게 사용하는게 좋습니다


+ 덧셈
- 뺄셈, 혹은 음수로 바꾸기
* 곱셈
/ 나눗셈
% 나머지

논리 연산자

참과 거짓을 위한 연산자로, 0과 1로 구분하는 연산자 입니다.
입력값이 1이상일 경우 1로 간주합니다.
> < >= <= == != && ||

<  보다 크다
<= 크거나 같다
>  보다 작다
>= 작거나 같다
== 같다
!= 다르다
&& 그리고
|| 또는
!  반대로
논리표

AND(A&&B) 둘다 참이면 참, 나머지 거짓
OR(A||B) 둘중에 하나라도 참이면 참, 전부 거짓이어야 거짓
NAND(!(A&&B)) 둘다 거짓이면 참, 나머지는 거짓
NOR(!(A||B)) 둘다 거짓이면 참, 나머지 거짓
XOR(A^B) 서로 값이 달라야 참, 같으면 거짓
NOT(!A) 참을 거짓으로, 거짓을 참으로


AND
A B C
0 0 0
0 1 0
1 0 0
1 1 1


OR
A B C
0 0 0
0 1 1
1 0 1
1 1 1


NAND
A B C
0 0 1
0 1 1
1 0 1
1 1 0


NOR
A B C
0 0 1
0 1 0
1 0 0
1 1 0


XOR
A B C
0 0 0
0 1 1
1 0 1
1 1 0


NOT
A B
0 1
1 0

비트 연산자

비트 연산자는 최소단위인 비트를 조작하기 위한 연산자 입니다.
곱셈과 나눗셈은 무거운 연산이므로 2의 n승을 곱하거나 나눌때 시프트 연산자를 사용하기도 합니다
산술 연산자인+ - * / 보다 우선순위가 낮으므로, 되도록이면 ()로 감싸주는 것이 좋습니다.

>> << & ^ |
>>= <<= &= ^= |=

>> 1비트 우측으로 이동, /2의 기능
<< 1비트 좌측으로 이동, *2의 기능
& 비트단위 AND 연산
| 비트단위 OR 연산
^ 비트단위 XOR 연산 (n승 연산자 아님!!)

주소 연산자

메모리의 번지를 계산하고, 메모리 번지에 있는 내용을 읽어 오는데 사용하는 연산자 입니다.
& * [] . ->

& - 변수의 번지를 계산함
* - 해당 번지의 값을 읽어옴
[] - 해당 번지의 값을 읽어옴
. - 구조체에서 하위 속성의 값을 불러옴
-> - 포인터형 구조체 변수일 경우 해당 번지의 구조체에서 하위 속성의 값을 불러옴
     *(val).strval 과 동일한 의미를 지님

포인터 (Pointer)

포인터는 특정 지점을 가르치는 역할을 하며, 여기서 특정 지점은 메모리의 번지를 의미합니다.
컴퓨터에서 모든 프로그램은 메모리에 올라가서 실행하게 되며, 메모리에 올라감으로 인해 특정 번지로 지정이 가능해집니다.
그렇게 때문에 포인터는 배열을 계산하는데에도 쓰이고(변수 메모리를 지정), 함수를 선택하는데에도 쓰입니다(코드 메모를 지정)
이러한 강력한 포인터이지만 강력한 만큼 사용에 주의를 기울이지 않는다면 프로그램 사용중 알수없는 오류로 중단되기도 합니다.

포인터 변수

포인터 변수를 증가시길 때에는, 포인터 변수형의 크기에 따라 메모리 주소를 증가시키게 됩니다.
만약 char *str; 이라는 변수는 str++ 이라고 하면 1씩 주소값이 증가하며
int *val; 이라는 변수는 4씩 주소값이 증가합니다

포인터 함수 (fuction Pointer)

포인터는 메모리 번지를 가르키기 때문에, 변수(힙 영역) 뿐만 아니라 함수(코드 영역)을 가르킬수도 있습니다.


다중 포인터

**var 식으로 표현되며, 포인터로 지정된 위치의 내용이 값이 아닌 다른 곳의 주소가 되는 경우를 다중 포인터라고 합니다.
효용성으로 인해 2중 포인터 이상은 잘 쓰이지 않습니다.

가변 인자 (variable arguments)

printf() 처럼 () 안에 여러개의 인자를 주는 방법을 가변 인자라고 합니다.
만약 가변인자가 존재하지 않았다면 printf()는 인자의 갯수만큼의 함수를 정의해야 했을 것입니다.

동적 메모리 관리

엄밀하게 C언어 자체 기능은 아니며, 표준 C언어에서 제공하는 라이브러리의 기능으로
처음부터 메모리를 크게 잡고 쓰는게 아닌 필요할때 마다 잡아서 쓰는 방법입니다.
stdlib.h 와 string.h 로 두개의 헤더를 포함시켜야 다양한 메모리 관련 함수를 사용할 수 있습니다.
대부분의 메모리 관련 함수는 바이트 단위로 크기를 지정합니다.
또한 동적 메모리는 힙(heap) 영역에서 할당되며, 메모리 부족시 할당을 실패할 수 있기에 주소의 값이 0이 아닌지 확인해야 합니다..

할당 (malloc / calloc)

malloc() 은 초기화 되지 않은 메모리를 할당하며
calloc() 은 malloc() 후 0으로 초기화를 추가로 합니다.
calloc()은 내부적으로 초기화를 하기에 추가적인 시간이 소요되며, 경우에 따라 0이 아닌 다른 값으로 초기화 해야 할 필요도 있으므로 사용 빈도가 높지는 않습니다.

calloc()
{
 malloc();
 memset();
}

재할당 (realloc)

할당된 메모리가 적어서 크기를 키울때 사용하는 함수입니다.
대부분 처음부터 넉넉하게 할당하는 경향이 있어서 사용 빈도가 높진 않은 함수입니다.

realloc(addr)
{
 temp = malloc();
 memcpy(temp, addr);
 free(addr);
}

해제 (free)

malloc() 했으나 사용하지 않으면 다른 프로그램이 사용할 수 있도록 놓아주는 함수입니다.

설정 (memset)

하나의 값으로 덮어 씌울때 사용하는 함수입니다.
예를들어 n 개의 변수를 0(FALSE) 값으로 한번에 설정을 하는데 사용될 수 있습니다.
특이하게 mem으로 시작하는 함수임에도 불구하고 <string.h>를 포함해야 사용할 수 있습니다.
함수의 초기화 값 인자는 int 형으로 받으나(4바이트), 실제로는 1바이트(unsigned char) 형으로만 사용을 하기에,
memset의 val값은 0xFF 식으로 입력되며, 뒤의 크기는 초기화 할 배열이 int 형일 경우 n*sizeof(int)의 형태로
바이트 단위의 실제 메모리의 크기를 입력해 주어야 합니다.

복사 (memcpy)

메모리를 대량으로 복사하는 함수입니다.
아래의 형태를 가지며, size는 byte 단위로 계산을 해서 넣어주어야 합니다.

memcpy(dest,src,size);

continue / break

for/while문에서 continue를 사용하면 즉시 for/while로 이동하여 조건식을 수행하게 됩니다.
for/while문에 감싸진 switch-case문에서 continue를 사용하면 for/while로 이동하여 조건식을 수행하게 됩니다.

헤더파일 관리하기

여러곳에서 헤더파일을 포함하게 되면(특히 헤더에서 헤더를 포함할 경우)
중복선언이라며 컴파일이 안되는 문제가 발생합니다

#ifndef __FILENAME__
#define __FILENAME__
// contents of header
#endif

C언어 전처리기(pre-processor / macro processor)

전처리기는 문자상수나 여러가지 조건에 대해 소스를 일괄적으로 변경하는데 도움을 주는 기능입니다.
많이 사용하는 예약어는 아래와 같습니다

#include
#define
#if 조건식
#ifdef
#ifndef
#elif
#elif defined
#endif
##
__LINE__
__FILE__
#warning
#error
#pragma

#include

#include <INCLUDE_PATH_FILENAME>
#include "LOCAL_HEADER_FILENAME"

include는 프로그램에 필요한 변수 선언이나 문자상수 혹은 함수의 프로토타입을 끌어오는 기능을 합니다
< >는 컴파일러에 기본포함된 include 디렉토리의 헤더파일(예, stdio.h)를 불러오며
" "는 소스코드와 동일한 경로상에 존재하는 헤더파일을 불러오는데 사용됩니다

#define

#define DEFINE_NAME
#define DEFINE_NAME DEFINE_VALUE
#define DEFINE_NAME MULTI_LINE_START \
                    MULTI_LINE_END

문자상수를 정의하여 소스코드내의 문자상수를 숫자나 문자열로 치환하거나
ifdef 를 통해 선언이 되어있을시 분기를 위한 변수의 용도로 사용됩니다

#if / #elif / #endif

if는 값을 비교합니다. if의 뒤에 들어오는 값은 상수값이거나 #define으로 정의된 문자상수여야 하며
소스 코드상의 변수와 비교할수는 없습니다.

#ifdef / #elif defined / #ifndef

if는 값을 비교하는데 반해, ifdef, ifndef는 #define에 의해 정의가 되었는지 확인합니다.

##

문자열을 붙이는데 사용합니다.

__LINE__

printf 문과 함께 디버깅용도로 주로 사용하며, runtime 에러 발생 위치 출력을 위해 일반적으로 __LINE__과 함께 사용하며
해당 코드가 실행된 파일에서의 라인(행 번호)으로 치환됩니다.

__FILE__

printf 문과 함께 디버깅용도로 주로 사용하며, runtime 에러 발생 위치 출력을 위해 일반적으로 __LINE__과 함께 사용하며
해당 코드가 실행된 파일 이름으로 치환됩니다.

#warning

매크로 처리시 해당 라인을 포함하게 되면 경고를 출력합니다.
일반적으로 프리프로세서의 디버깅이나 버전관련 경고 출력을 위해 사용합니다.
error와 다르게 warning은 컴파일이 중단되지 않습니다.

#error

버전관련하여 호환되지 않는 경우에 매크로 처리를 중단하기 위해서 사용합니다.
warning은 경고만 발생하지만 error는 컴파일 과정을 중단시킵니다.

#pragma

컴파일러에 지시어를 보내기 위한 확장기능입니다.
openMP 등에서 사용하는 #pragma omp 나
컴파일러에서 바이트 단위로 구조체를 묶기위해 사용하는 확장 기능등이 있습니다.

#pragma packed // (Visual Studio)

잡다한 이야기

레지스터 변수(register)

pc에서는 잘 사용하지 않는 키워드로, CPU 내에 존재하는 메모리인 레지스터에 변수를 할당하도록 강제하는 방법이다.
컴파일러 최적화에 의해서 cpu의 레지스터를 최대한 사용하게 하지만, 할당 가능한 레지스터가 없을 경우에는 일반 변수로 RAM에 할당하게 된다.
레지스터는 CPU에 통합되어 있기 때문에 매우 빠르게 읽어 오지만,
메모리는 CPU외부에 있고 CPU에 비해 클럭이 낮고 여러 과정을 거치기 때문에 레지스터에 비하면 몇십~몇백배 느리게 불러오게 된다.
이러한 이유로 for 에서 사용하는 인덱스 등과 같이 빈번하게 사용하는 변수에 대해서 레지스터 변수를 사용하게 하면
약간의 성능에 영향을 미치게 된다.

엔디안(Endian)

엔디안은 숫자를 표기하는데 있어 큰수가 앞에 오는지 뒤에오는지를 지정합니다.
big endian은 우리가 사용하는 방식으로 자릿수가 늘어날수록 큰 값을 저장하는 방식이며
little endian은 자릿수가 줄어들수록 큰 값을 저장하는 방식입니다
모든 데이터는 바이트 단위로 구성되기 때문에 엔디안은 비트 단위에서는 변함이 없이
2^8+2^7+...+2^1+2^0 으로 왼쪽(MSB Most Significant Bit)이 가장 큰 수를 저장합니다
하지만 바이트 단위로 가게되면, 빅 엔디안에서 41200을 unsigned short 형으로 저장시
0xA0F0가 되지만 리틀 엔디안에서는 0xF0A0로 저장하게 됩니다.

인덱스와 카운터

프로그래밍 언어를 사용하는데 혼동이 되는것중에 하나가 for/while/배열의 값입니다.
컴퓨터는 0 부터 숫자를 사적하기에 1부터 세기시작하는 평범한 사람이라면 누구나 겪는 혼란입니다.
1에서 100까지 센다면 사람은 1 2 3 4 ... 99 100 으로 100개를 세지만
컴퓨터는 0 1 2 ... 98 99 로 100개를 세게 됩니다.

예를들어 for(idx = 0; idx < MAX_COUNT; idx++) 식으로 사용하며
idx는 인덱스로 0 부터 MAX_COUNT - 1 까지 증가하게 됩니다.
컴퓨터 내부적으로 0부터 세기 때문에 숫자의 낭비없이 사용하기 위한 방법으로 조건 비교시 < 를 사용하게 됩니다.

동적 / 정적

동적이라 함은 문제점도 동적으로 여기저기 예측불가능하게 나타남을 의미합니다.
malloc을 아용하는 것이 전체메모리도 적게 먹고 여러가지 장점이 있지만
메모리 관리나 할당에 대해 사용자가 써야할 신경이나
cpu의 소비시간, 그리고 디버깅시의 용이함을 고려하면 변수를 크게 잡는 것도 방법입니다.
반드시 동적이 모든면에서 월등히 좋은것은 아닙니다.
동적 메모리 할당과 관련된 함수는 malloc() / realloc() / free() 이 있으며
보조적으로 사용되는 memset() / memcpy() 가 있습니다.

들여쓰기(indent/인덴트)

코드를 꾸미는 방법으로서의 들여쓰기는 사람들 취향이기 때문에 어느것이 옳다라고 하기 힘듭니다.
탭을 몇칸으로 할지 괄호는 어디에 붙일지를 정의합니다.
K&R 스타일 키워드로 검색하면 코드스타일 관련글들이 자세히 나오니 참고하시기 바랍니다

Microsoft Visual Studio나 Eclipse IDE에서 코드 스타일을 정렬하는 기능이 존재하며
Eclipse의 경우 세부 스타일에 따라 사용자 설정이 가능하여, 일괄적으로 코드의 스타일을 변경할 수 있는 장점이 있습니다.

구차니 인덴트
탭을 사용함. 탭 크기는 4칸
변수는 같은 형끼리 선언
초기화하는 변수는 하나씩
포인터 변수도 하나씩
include define 순서로 정의
= 앞뒤로 한칸씩 띄움
;는 띄움없음
수식의 괄호는 붙여씀
조건/반복문 뒤에는 엔터 후 중괄호
조건/반복문 뒤에 한줄 짜리면 레벨을 동일하게

void 함수에 return

void도 엄밀히는 변수형으로, 정해지지 않은 변수형을 의미합니다.
void형은 일반적으로는 포인터 값을 넘겨주기위해 사용하기 때문에 4바이트 크기 변수를 주로 리턴하게 됩니다.

컴파일 과정

IDE를 통해서 간편하게 컴파일을 하게 되지만, 그 안에는 수많은 단계를 거치게 됩니다.

프리프로세서

전처리기는 #define 이나 #include와 같은 확장기능을 수행합니다.
다른 파일을 포함해서 다른 파일의 함수 프로토타입을 끌어오거나,
문자상수를 치환하는 역할을 하여 사용자가 수작업으로 바꾸는 수고를 덜어줍니다.

컴파일러

컴파일러는 구분문석/문법분석을 통해 고수준 언어를 저수준 언어(어셈블리언어 등)으로 해석을 해줍니다.
구문분석은 선언한 변수명이나 함수이름이 옳게 정의 되었는지를 확인하고
문법분석은 변수를 할당하는데 a=100; 등의 할당 순서는 맞는지 지금 호출하는 인자가
함수인지 변수인지등을 판단해서 c 언어 문법이 맞는지를 확인하고
코드를 ast라 불리는 문법 트리로 변환을 하여 어셈블리 코드로 변환을 하게 해줍니다

어셈블러

어셈블러는 어셈블리 언어를 2진(바이너리) 코드로 변환해 주는 역할을 합니다.
어셈블리까지는 메모장들의 텍스트 편집가에서 바로 열어 인간이 알아볼 수 있는 형태를 띄지만
어셈블러의 결과물은 컴퓨터가 알아먹는 모습으로 바뀌게 됩니다.
어셈블러는 cpu에 밀접한 연관이 있어 cpu에 특화된 결과물을 출력합니다.

링커

링커는 어셈블러의 결과물인 파일별로 생성된 오브젝트 파일을 묶어 하나의 실행파일(대개는 exe)로
만들어주는 역할을 합니다. 이 과정에서 누락된 함수가 존재할 경우 링커 에러로 출력하게 됩니다.

MMIO

Memory Mapped Input/Output 의 약자로 컴퓨터의 주변 장치들을 제어하는 방법중 하나 입니다.
메모리의 특정 번지를 하드웨어에 맵핑하여 하드웨어와 통신/제어를 하는 방법입니다.
이러한 주소에 접근을 함으로서 하드웨어를 제어할 수 있는 방법은 포인터를 통해
C언어가 하드웨어를 제어할수 있는 장점으로 생기는 대신, 잘못된 포인터의 조작은
하드웨어 오작동으로 OS자체를 오류나게 할 수 있는 문제가 생길수가 있게 됩니다.

자료구조

배열을 이용한 간단한 자료구조를 설명합니다
자료구조는 데이터를 어떻게 저장하고 데이터를 조직화 할것인지를 결정합니다.
공부시 요약노트를 적는데 자신만의 법칙이 있듯 프로그램에서도 프로그램의 규모에 걸맞는 자료의 저장방법이 존재하며
어떤것이 옳다고 하지못할 정도로 수많은 방법들이 있습니다.

스택

스택은 먼저 들어온게 나중에 나가고, 나중에 들어온게 먼저나가는 구조입니다.
책을 위로 쌓아올린다는 개념으로 이해하시면 간단할 것입니다.
pop() push()라는 넣고 빼는 명령어와 top() isempty() isfull() 이라는 상태를 알아내는 명령어로 구성됩니다
First In Last Out(LIFO) 라고도 하며, 일을 잠시 미뤄두고 새로운 일을 처리해야할 경우에 사용하는 데이터 구조입니다.
(함수에서 함수를 호출 할경우 이전 함수의 내용을 저장하는데 스택구조로 데이터를 쌓아둡니다.)

큐는 책을 나란히 세우고 옆에서 하나씩 넣고 반대편에서 하나씩 빼내는
일종의 밀어내기식 순서라고 생각하면 됩니다

데큐

링크드 리스트

동적 메모리 할당과 함께 배우는 개념으로, 포인터를 사용합니다.
기본적인 발상은 데이터에 다음 데이터의 위치를 저장하고,
연속할당하지 않더라도 데이터를 따라 다음 데이터를 연속적으로 구성한다는 것 입니다.
하지만 한방향으로만 검색이 가능하기에 양방향으로 검색이 가능하도록 이전 / 다음 노드에 대한
값을 추가로 저장하여 사용하기도 합니다.


트리

트리는 링크드 리스트의 특수한 적용예 입니다.
링크드 리스트를 통해 계층구조로 깊이를 가지게 되는 방식으로
상위를 parent 하위를 child라고 합니다.
트리에서 child의 갯수는 제한이 없으나
2개로 제한할 경우 Binary tree/B tree/이진트리 라고 합니다.
트리구조상 순차적인 목록을 출력하는 것은 어렵지만
검색에 유용하여 많이 쓰이고 있는 데이터 구조입니다.
배열에서 값을 삽입하기 위해서는 하나씩 밀어내야하나
(기존의 데이터 복사 / 하나씩 옆으로 복사 / 값 끼어넣기)
트리와 이진트리의 경우는 단순히 노드를 생성하고 연결해줌으로 이러한 부하를 줄일수 있습니다.

메모리 구조

프로그램은 메모리에 올려져 실행이되는 데이터 입니다.
실행을 위해 특정 형태로 구성이 되어있으며, 유닉스계열에서는 ELF 윈도우에서는 PE 라는 형태를 가집니다.
프로그램 역시 데이터이기 때문에 여러가지 종류의 데이터 형태를 지니며
읽기전용과 읽기쓰기용으로 나누어 집니다.

힙(heap)

힙은 malloc() / free()에 의해 동적으로 관리되는 영역입니다.
일반적으로 스택보다 큰 영역을 지닙니다.

스택 (stack)

스택은 지역변수나 전역변수등, 프로그램을 작성시 선언한 변수들이 저장되는 영역입니다.
스택은 힙과 반대방향으로 데이터를 채워갑니다(큰 번지에서 작은 번지 방향으로)

코드 (code)

코드는 프로그램에서 if문이나 for문 등의 제어구조를 의미하며,
컴파일에 의해 기계가 이해 할 수 있는 문법을 따라 생성이 됩니다.
그렇기 때문에 프로그램이 실행중에 변경되어서는 안되며 읽기전용으로 취급하게 됩니다

C언어 예약어

예약어는 c언어에서 특정 기능을 하도록 정해진 단어이며, 변수나 함수명으로 사용할 수 없습니다.

전처리기(pre-processor)

전처리기는 #으로 시작하며, 프로그램을 해석하기에 앞서서 먼저 처리된다는 의미로 "전처리기" 라고 합니다.
pre-processor(프리 프로세서) 혹은 maco-processor(매크로 프로세서) 라고 하며
#define 등에 의해 정의된 키워드를 치환해주는 등의 매크로 기능을 수행하게 됩니다.

#include
#define
#if 조건식
#ifdef
#ifndef
#elif
#elif defined
#endif
##
__LINE__
__FILE__
#warning
#error
#pragma

변수관련

  • unsigned - 양수만
  • signed - 양수 / 음수 전 범위
  • static - 해당 파일내에서만 사용 / 함수 벗어 나도 값 유지(일종의 글로벌 변수화)
  • auto - 지역 내에서만 사용
  • volatile - 컴파일러 최적화 하지 않고 매번 반드시 읽도록 강제
  • register - 변수를 CPU내 레지스터에 할당하도록 권장
  • const - 상수로 지정
  • extern - 다른 파일의 변수를 끌어 오도록 함, 단 static 변수는 다른 파일에서 접근 불가

정수형

  • char - 1바이트(8bit) 변수 대개 1개 문자(ascii 기준)로 사용 0~255 / -128 ~ 127
  • short - 2바이트(16bit)
  • long
  • int - 4바이트(32bit)
  • void - 4바이트(32bit)

실수형(소수점)

  • float
  • double 8바이트(64bit)

제어관련

  • for
  • while
  • do - while
  • if / else / else if
  • switch - case
  • continue
  • break
  • goto
  • return

연산자관련

  • + 덧셈(addition)
  • - 뺄셈(subtract) / 음수(negation)
  • * 곱셈(multiply)
  • / 나누기(divide)
  •  % 나머지(modulo)
  • ( ) 괄호
  •  ! 논리 NOT
  • * && 논리 AND
  • || 논리 OR
  • & 비트단위 AND (bitwise AND)
  • | 비트단위 OR (bitwise OR)
  • ~ 비트단위 부정 (bitwise negation)
  • ^ 비트단위 XOR (bitwise XOR)
  • . 범위연산자 (scope operator)
  • -> 포인터
  • [ ] 배열 연산자
  • == 같음
  •  != 같지않음
  • = 값 대입
  • "" 문자열 (string)
  • ' ' 문자 1개(character)
  •  :? 3항연산자
  • -= 빼고 대입
  • += 더하고 대입
  • *= 곱하고 대입
  • /= 나누고 대입
  •  %= 나머지 대입
  • << 왼쪽으로 비트 미루기
  • >> 오른쪽으로 비트 미루기

용어집

  • 상수 - constant
  • 변수 - variable
  • 구조체 - structure
  • 공용체 - union
  • 함수 - fuction
  • 식별자 - identifier
  • 연산자 - operator
  • 피연산자 - operand
  • 오브젝트 파일
  • 바이너리 파일
  • 어셈블러
  • 컴파일러
  • 링커
  • 전처리기

맺음말

먼가 허전해서 일단 맺음말 부터 만들어 놔야할 듯?

개인 도구
이름공간

변수
행위
둘러보기
도구모음