필기정리 -C 언어

조형찬·2023년 1월 29일
0

필기 정리

목록 보기
5/11

c언어


  • 다운 없이 c 연습할 때

online c compiler에서 연습할수 있다


  • c언어 기본 내용

void - 리턴값이 없다는 뜻
int- 정수형 이라는 뜻
#include <stdio.h> //스탠다드 인풋 아웃풋
return 0; //끝났다는 뜻
\n // 줄바꿈 표시
main() 함수는 하나만 써야 한다.
그리고 return0; 으로 끝나고 {} 안에 내용을 적어야 한다.
\t는 탭키를 의미한다
""안에 특수문자 "를 넣기 위해선 \" 해주면 된다


  • c언어 출력 타입 형식지정자 등★

%d // 숫자를 쓰겠다는 뜻 ""안에는 문자처리인데 %d를 쓰면 숫자로 보이게 한다. "", 숫자를 적어주면 됨
printf("10진수 정수형 상수입니다. %d + %d = %d",10, 20, 10+20);
//10진수 정수형 상수입니다. 10 + 20 = 30
%d , %o, %x //각각 10진수, 8진수 ,16진수 숫자를 의미함
cf)8진수의 경우 0 , 16진수의 경우 0x를 붙이고 숫자를 적어야 함.
cf)10진수는 -(마이너스) 표현이 되지만 8진수 16진수는 음수 표현이 불가능하다

%c printf("문자상수는 %c %c %c 입니다 \n",'a', 'b', 'c');
//문자상수는 a b c 입니다 ''안에 내용 적을것

%s printf("문자열 상수는 %s 입니다 \n", "10+10");
// 문자열 상수는 10+10 입니다 ""안에 내용 적을것

%f float는 실수를 의미하고 값 뒤에 f를 항상 붙인다.
//소숫점이 포함된 숫자
%lf double도 실수를 의미하고 값 뒤에 f를 붙이지 않는다.
//float과 같은 기능을 하지만 소모되는 값이 float는 4byte double은 8byte이다.


  • c언어 변수 설정★

int num =20; //변수num에 20을 부여함, 바뀔 수 있음
const int NUM=20; // 변하지 않는 값 NUM에 20을 부여함.
const를 쓸 때는 변수명을 대문자로 써야함
소숫점인 것에 int를 쓰면 오류가 남. double로 써야함.


  • gcc 관련 실행 내용

c언어로 된 test.c파일을 만들고
cmd 에서 gcc test.c 눌러서 컴파일 하고
a 엔터치면 내용 뭔지 보임 (실행파일이라 불림)
gcc -o (아무 이름 예) myc) test.c 를 해줬으면
myc를 누르면 내용이 보임(실행 파일)
이렇게 a, 혹은 myc 등 실행파일을 알 수 있는법은 dir *.exe로 검색해보면 알 수 있다.

gcc -c 파일이름.c 를 해주면 파일이름.o (오브젝트 파일이 된다)
gcc 파일이름.o -o alldone(실행파일 이름이고 임의로 생성함)
그리고 alldone 해주면 파일이 실행 됨


  • MinGW 설치 관련

MinGW-w64 - for 32 and 64 bit Windows - Browse /mingw-w64 at SourceForge.net 링크에서 x86_64-win32-seh 다운 받고 c:// programfiles에 폴더 넣기 -cmd 키기 gcc -v 치면 나와야 하는데 오류 뜰 경우
/제어판-> 시스템 보안-> 시스템-> 고급 시스템 설정 -> 고급 ->환경변수 -> 시스템 변수에서 path 누르고 편집 ->새로만들기 해서 -> 다운받은 mingw64의bin폴더 누르고 경로 복사해서 집어넣기 -> cmd에 gcc -v 해서 뭐 뜨면 된거임.


  • runcode 오류 관련

vscode에서 runcode를 할 때 오류가 종종 났는데
vscode gcc.exe error no such file or directory 이런 내용일 경우 파일명에 공백을 없애주니 괜찮았다


  • 심볼릭 상수

const pi = 3.14; 단순히 변수에다 값을 주고 그 변수가 변경될 수 없다고 선언
const double PI =3.14; 상수를 기호화하여 변수처럼 이름이 있는 상수 선언, 대문자 사용
#define 문을 사용하여 상수를 기호화하여 사용한다
#define PI 3.14

심볼릭 상수 : 상수를 기호화하여 변수처럼 사용한다.
1. const 키워드 사용 2.#define문 사용
두가지는 기능은 같지만 선언하는 위치가 다르다
const는 함수 안에 넣고 #define문은 전처리 위치에서 사용된다.


  • 자료형의 사이즈를 알고 싶을 때

sizeof() 예) sizeof(char);


  • 자릿수 표현

printf("%10.2f \n", num);
//출력은 10칸 소수 둘째자리까지, 남은공간은 공백으로 채움
rintf("%010.2e \n", num);
//출력은 10칸 소수 둘째자리까지, 남은 공간은 0으로 채움
%10d //총 자릿수 10개의 숫자, 오른쪽 정렬
%-10d //총 자릿수 10개의 숫자, 왼쪽 정렬


  • 형변환

자료형과 값이 맞지 않으면 에러가 발생한다. 형변환을 통해서 인위적으로 값을 넣을 수 있다. 예) (short)값


  • 자료스캔

int age;
scanf("%d", &age);
스캔은 먼저 age라는 변수를 선언하고, (자료형, &변수이름)을 적어줘야 한다.
이러면 사용자가 적는 값을 입력받는다.


  • bool true, false

bool isp = true; bool isf = false;
printf("%d \n",isp); printf("%d \n",isf);
//값이 1, 0 이 나오는데 그 이유는 isp가 true값이고 isf가 false값인데 읽는 자료형은 숫자기 때문에 true인 1, false인 0을 출력하는 것이다

bool에서 true나 false를 인식 못할 때 전처리에서
#include <stdbool.h>를 넣어줘야 한다.


  • ++ , -- 위치에 따른 적용 순서★

num3 = num 1 ++
인경우 num3에 num1을 할당하고 난 후 1을 증가시킴
num3 = ++num1
인경우 num3에 1을 증가시킨 num1을 할당함.
--는 1을 감소시킴
이렇게 변수 할당을 하는것 외에도 ++,--는 위치에 따라 값이 달라짐


  • getchar()

getchar() // 문자 혹은 문자열을 입력 받아서 맨 앞에 나오는 문자를 받는다.


  • 변수 초기화★
int main()
{
    int i,num,sum=0;
    printf("양수의 숫자를 입력하세요 :");
    scanf("%d",&num);
    for(i=1; i<=num; i++)
    {
        sum+=i; 
    }
    printf("sum = %d",sum);
    return 0;
}

sum에 초기화를 안해주고 돌리니까 값이 이상하게 나왔음.
변수를 선언하고, 값을 주는 것 까지가 초기화이다.
이 초기화 없이 돌리다보면 오류가 날 수 있다.

위의 예에서 sum=0;을 안주고 돌리니까 계속 오류가 났었다.
i와 num은 모두 값을 주고 시작하는데 sum은 값이 없이 바로 돌려서 이같은 문제가 발생한것이다.


  • while 문★

while(조건){실행 코드};
while문의 주의점은 while문을 빠져나올 수 있는 조건을 실행코드에 잘 넣어야 한다.
그렇지 않으면 무한히 실행된다.
예)

int i = 0;
    while (i < 5)
    {
        printf("%d \n", i);
        i++;
    }

  • do while 문

do{실행 코드}while(조건);

while문과 기본적으로 비슷하지만 실행코드를 먼저 쓴다는 점이 다르다.
이 역시 실행코드에 while문을 빠져나가는 조건을 넣어야 한다.
적어도 한번은 실행코드가 작동한다는 점이 while문과 구별된다.
예)

int i = 0;
 do{
     printf("%d \n",i);
     i++;
 }while (i<5);

  • switch 문 ★

예)

int day = 2;
switch(day){
case 1: 실행코드 break;  
case 2: 실행코드 break;
...
default: 실행코드}

// case를 하나하나 탐색하다가 맞는게 나오면 나오고, case에 해당되는 것이 없다면 default를 실행한다.

case 뒤에 나오는 것은 switch() 안에 들어가는 것과 비교해볼 것이다.
switch(기준) case 비교대상 비교대상이 문자면 ''안에 넣어서 확인을 해야 한다.
예) switch(c) ... case '+' : 이런식으로 해야 한다.


  • 숫자관련

sqrt(숫자) // 루트 숫자
ceil(숫자) //숫자 올림
floor(숫자) // 숫자 내림
pow(숫자1,숫자2) //숫자1의 숫자2 제곱
abs(숫자) // 숫자의 절대값 이때 %d를 써야함.


  • 함수 선언★

main 함수 위에는 사용자 함수를 적을 수 있다.
이 때 실행코드를 모두 적어줘도 되고, 실행코드를 main함수 밑에 적어도 된다.
실행코드를 밑에 적을 경우에도 main함수 위에 함수 선언은 해줘야 한다.


  • double 형과 float형 차이 ★

double형은 8bytes, float형은 4bytes가 필요하다.
float형은 %f를 쓰며, 값 뒤에 f를 붙여야 하지만 실질적으로 안붙여도 크게 문제는 없다.
기본적으로 실수는 double형으로 인식한다.
double형은 %lf 를 쓰며, 값 뒤에 f를 붙이지 않아도 된다.
float형은 소숫점 6~7자리까지, double형은 소숫점 15~16자리까지 인식하기 때문에
정밀한 계산등이 필요한 경우 double형을 사용하면 된다.


  • 자료형에서 unsigned의 의미

unsigned는 부호를 붙이지 않겠다는 것을 의미한다.
즉, 음수를 생각하지 않겠다는 것이다.
따라서 저장가능 범위에서 음수 부분을 생각하지 않아도 되기 때문에 양수 부분에서는 더 많은 양을 저장할 수 있다.


  • char와 unsigned char의 범위

char f = 숫자; 를 주었을 때
printf("%d", f); 를 해도 숫자가 출력 된다.
단, char의 자료 저장 범위는 1byte라서 최대 범위는 -127~128까지 저장 가능하다.
따라서 범위를 넘어가는 값을 저장할 경우 오류가 발생한다.
예) char f = 128; -> -128로 출력
// 오버플로우 오류가 있다는 것을 알리기 위해 -가 붙는다.

unsigned char g =255;
// char와 달리 unsigned char는 음수 없이 0~255까지 표현 가능하다.


  • short와 unsigned short의 범위

short int h = 32767;
//2bytes(-32,768~32,767) %d사용
unsigned short int i = 65535;
//2bytes(0~65535) %d사용


  • int와 unsigned int의 범위

int j = 2131314141;
//4bytes (-2,147,483,648~2,147,483,647) %d사용
unsigned int k = 4215315135;
//4bytes (0~4,294,967,295)
이경우 자료형 %d를 붙이는 것이 아닌 %u를 붙여야 적용된다.


  • long long int 와 unsigned long long int의 범위

long long int l =9223372036854775807;
//8bytes %lld를 써줄 것
unsigned long long int m =18446744073709551615;
//8bytes %llu를 써줄 것


  • %f 자료형의 %와 f사이 의미정리 ★

예) %10.2f
// 총 10칸으로 쓰겠다.
//.2 소숫점 아래 2자리까지 쓰겠다.
예) %+10.2f
// 총 10칸으로 쓰겠다.
//.2 소숫점 아래 2자리까지 쓰겠다.
//오른쪽 정렬을 하겠다.
예) %-10.2f
// 총 10칸으로 쓰겠다.
//.2 소숫점 아래 2자리까지 쓰겠다.
//왼쪽 정렬을 하겠다.


  • 포인터 %p의 의미 ★

%p는 포인터의 값(주소)을 출력하라는 서식 지정자이다.
c언어에서 모든 변수는 변수명 이외에 메모리상의 주소를 가지고 있다.
예) 변수명 : a, 변수값 : 3, 메모리상 주소 : &a=b3fb98 이면
포인터 변수명 : p, 포인터 변수값 : b3fb98 또는 &a 이다.
즉 %p는 변수 a의 메모리상 주소를 변수 값으로 가진다고 볼 수 있다.

int age =20;
int *pAge = &age;
여기서 *의 의미는 pAge라는 변수는 일반 변수가 아니라 주소를 저장하는 변수다라는 의미로 쓰인다.

printf("age의 값 %d \n", age); // 20
printf("age의 변수의 주소 %p \n", &age); // 000000000061FE14
printf("age의 변수의 주소를 갖고 있는 변수 %p \n", pAge); // 000000000061FE14
printf("age의 변수의 주소를 갖고 있는 변수 %d \n", *pAge);// 20 (dereferencing 역참조)

주소값을 받을 때는 %p로 받는다. &age, pAge(앞에*이 안붙는것 주의)
변수값을 받을 때는 %d로 받는다 age, *pAge(앞에*이 붙는 것 주의)

추가 정리★
맨처음 주소를 의미하는 포인터를 선언할 때는 *를 꼭 써야 한다.
예) int *pAge = &age; 선언 이후 pAge와 *pAge는 다르다.
pAge는 주소값을 의미하며, *pAge는 그 주소에 있는 값을 의미한다.


  • 역참조 사용 예시 ★

int myNum[4] = {25, 50, 75, 100};
printf("%d \n", *myNum); // 25출력
printf("%d \n", *(myNum+1)); //50 출력
기본적으로 배열을 출력하려면 몇번째 인지를 적어야 하는데 역참조의 경우 붙이지 않아도 된다. 기본적으로 첫번째 값이 주소이기 때문이다.
*(myNum+1)의 경우 myNum의 두번째 주소를 의미하기 때문에 50이 출력되는 것이다.

*myNum = 13;을 해주면 myNum의 첫번째 요소가 13으로 바뀐다.
*(myNum+1) = 17; 을 해주면 myNum의 두번째 요소가 17로 바뀐다.


  • \0의 의미

\0은 문자열의 끝을 나타내는데 사용된다.
기본적으로 끝을 나타내기 위해서 모든 문자열의 끝에는 널(null)문자가 자동 삽입된다.

사용자의 입력값을 변수로 받는 경우, 엔터값까지 인식하여 다음 코드에 문제가 생기는 경우가 있다.
이럴 경우 name[strlen(name)-1] = '\0';
//name은 배열인 변수이고, strlen은 배열의 길이를 의미한다.
즉, 배열 name의 마지막에서 하나 앞에 \0을 넣어달라는 뜻이다.
다시말해 엔터 앞에서 문자열을 끝내라는 의미이다.
cf) strlen()을 사용하기 위해서는 #include <string.h>를 해준 상태여야 한다.


  • 배열의 크기선언

예)
int score[10];
float score1[10];
char name[50];
배열의 크기 선언시 음수(-)값은 쓸 수 없다. 실수값(5.5등)도 쓸 수 없다.
기본적으로 변수(name 등)도 쓸 수는 없다.
단, #define NAME 10으로 한 경우 (NAME)은 넣을 수 있다.
cf) #define을 할 때는 ';'을 붙이지 않는다.


  • 배열의 선언 ★

배열의 선언은 자료형 배열명[배열크기]; 까지 해주어야 한다.
예)char name[4]; (o) char name[]; (x)
단, 배열을 선언하면서 초기화까지 하는 경우에는 배열크기는 안적어도 된다.
예)char name[3]={1,2,3}; (o) char name[]={1,2,3}; (o)


  • 배열의 초기화

char name[4]={1,2,3}; 처럼 배열의 크기보다 적은 값만 초기화 했을 때, 초기화 하지 않은 부분의 값은 0으로 채워진다.


  • 배열의 길이

strlen();으로 해줘도 되지만
size = sizeof(score) / sizeof(score[0]);
//score라는 배열에서 (배열의 전체 크기 / 첫번째 배열의 크기) = 배열의 전체 길이가 된다.


  • 문자열의 배열

배열의 크기를 선언 할 때
char name[4]={cod}; // 이처럼 문자열의 문자수가 3개면 배열의 크기는 4로 해주어야 한다. 왜냐하면 문자열의 맨 끝에는 문자열을 끝내는 의미인 \0가 들어갈 자리가 필요하기 때문이다.


  • 한글의 배열

영어(1byte)와 달리 한글은 한 문자당 2bytes의 공간이 필요하다.
따라서 char name[4]={한글들}; (x) 똑같이 3글자 처럼보이지만 배열의 크기를 4로하면 부족하다. char name[7]={한글들};(o) 한글의 공간 차지를 생각하여 계산하면 이렇게 해야한다.


  • gets()와 puts()

scanf();와 같이 입력을 받는 코드가 gets(); 다.
scanf();의 경우 입력할 때 공백이 있는 경우 공백 앞까지만 인식한다.
gets();의 경우 공백도 포함하여 모두 인식 할 수 있다.
예) char str[100]; gets(str); 이면 배열 str에 들어갈 내용을 사용자가 입력할 수 있다.
puts();의 경우 printf();와 같이 출력의 역할을 한다.

질문내용 ★
scanf();와 달리 gets();는 저장하는 공간이 따로 있는 것이 아니다.
그래서 for문 안에서 sum+=scanf(변수명[i]); 처럼
배열에 들어가는 값을 하나씩 누적하는건 scanf();는 가능하지만
sum+=gets(변수명); 은 불가능한 식이 된다.


  • scanf_s

예) scanf_s(%s,name,sizeof(name));
// 문자형, 변수명, 범위 순으로 적어준다.


  • 배열과 scanf()함수 ★

예) char str[5];
배열의 변수명, 변수명의 주소, 배열의 첫번째 값의 주소는 모두 동일하다.
str = &str =&str[0]

scanf(); 함수는 입력받는 값을 저장할 주소가 필요하다.
따라서 &를 사용하여 받는다. 하지만 위와 같이 배열의 경우 생략이 가능하기도 하다.
예) scanf("%d",str); 도 가능하다는 뜻이다.
하지만 이는 첫번째 값에 한정된다.
따라서 for문등을 사용하여 str배열의 두번째 이상의 값이 필요한 경우는 &를 붙여줘야한다.
예) scanf("%d",&str[i]);

&없이 가능한 경우도 있다.
scanf("%d",str+i);

C언어에서 배열은 포인터 개념을 가지고 있다. 배열 이름은 배열의 첫 번째 요소의 주소를 가지고 있다. 따라서 arr + i는 i 번째 요소의 주소를 가지고 있다.

예를 들어, arr는 arr[0]의 주소를 가지고 있고, arr + 1은 arr[1]의 주소를 가지고 있다.

따라서 arr + i를 scanf 함수의 첫 번째 인자로 사용하여 i 번째 요소의 주소를 전달할 수 있다. scanf 함수는 해당 주소에 입력 값을 저장한다.


  • 2차원 배열

예) int arr1[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
가장 기본형으로, 1차원 배열안에 배열을 한번더 준것이라 생각하면 된다.
int arr1[][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
[][3]처럼 두번째 요소만 있어도 가능하다.
int arr1[3][3] = {11,12,13,14,15,16,17,18,19}; 요소 숫자만 정해져있으면 {}안에 {}로 구분 안되어 있어도 된다.
int arr1[3][3] = {0}; 0으로 초기화 하는 방법


  • 2차원 배열에 gets() 함수로 값 넣는 방법★

예) char nation[4][20]; //2차원 배열 선언 이때, 배열의 크기도 넣어줄 것

for(int i=0; i<4;i++){
        printf("국가 이름 : ");
        gets(nation[i]);
    }

여기서 nation[i] 는 nation[0] nation[1] nation[2] 가 된다.
gets() 함수를 통해 각각의 값을 채워준다는 것이다.
나는 계속 gets(nation); 만 했는데 안돼서 틀렸다.
gets(nation);는 일차원 배열에 넣어주는 것이므로 틀린 것이다.
gets(nation[i]);의 의미를 잘 머리에 넣어야 한다.


  • 2차원 배열 응용

2차원 배열로 char name[2][2]; 로 선언했다고 가정한다.
이때 예)name[1] 만 따로 가져올 수 있다.
name[1][]이런식으로 적는게 아니라 name[1] 이런식으로 적어준다면 2차원 배열 name의 1행의 값들이라는 것이다. 위의gets();함수 예시에서 처럼 nation[i]를 해줄 수 있는 이유가 이것이다.


  • scanf() 함수★

아직 머릿속에 개념이 확실히 들어온 것 같지가 않다.
예) scanf("%d",&array[1][0]); 이렇게 가정을 했을 때
1.사용자가 입력하면 그 입력값은 숫자형(%d)이다.
2.입력받은 숫자를 이차원 배열 array의 1행 0열 주소(&array[1][0])에 저장한다.


  • if문 응용

if(조건){실행코드}; 이게 기본적인 구성이다.
true는 1, false는 0을 의미한다.
따라서 조건에 (1)을 쓰면 true라서 실행코드를 실행한다.
만약 조건에 (0)을 쓰면 false로 인식한다.
if(0){실행코드1} else {실행코드2}; 일때, 실행 코드2를 실행한다.

따라서 값이1인 식을 넣을 경우 if(1로 나오는 식) {실행코드1};
//실행코드1 작동
만약 값이0인 식을 넣을 경우 if(0으로 나오는 식){실행코드1} else{실행코드 2};
//실행코드 2 작동


  • 2차원 배열 내용 채우는 방법 ★

char x[3][3]; 일때 중복 for문 안에 x[i][j]='x';를 해주면 각각 배열의 내용이 x로 채워진다.
이 때 x[i][j]; 안의 내용을 간격을 두고 출력하고 싶으면 printf("%3c",x[i][j]); 이렇게 간격을 주면 된다. 굳이 배열안의 내용 자체에 공백을 줄 필요가 없다.


  • 난수(임의의수) 발생 함수

우선 #include <stdlib.h>가 필요함
int rand(void);는 난수 발생 함수의 원형이다.
단, 이것만 실행할 경우 여러번 실행해도 같은 값이 나온다.
srand(time(NULL)); 를 적고 int rand(void); 를 적어주게 되면
함수 실행마다 다른 난수 값이 나온다.
cf)난수의 자릿수를 정하고 싶다면
printf("%d \n", rand() % 100 + 1); 1~100사이의 난수가 나오게 된다.


  • 함수 정의의 위치
#include <stdio.h>
함수 원형의 선언
int main(){
함수 호출
}
함수 정의

예)

#include <stdio.h>
// 함수의 원형
int sum(int a, int b);
int main()
{ // 함수의 호출
    int a1 = 5, a2 = 8;
    sum(a1, a2);
    return 0;
}

// 함수의 정의
int sum(int a, int b)
{
    return a + b;
}

  • 지역변수, 전역변수

지역변수는 {스코프} 안에서 선언되고 실행되고 {}가 종료될 때 소멸된다.
전역변수는 초기화하지 않아도 0으로 설정된다.


  • extern 변수

다른 시트에서 변수 초기화를 해서 그 값을 가져오는 기능을 한다.
외부에서 해당 변수를 참조하기 원하지 않을 때는 static을 붙여준다.


  • 포인터 값을 서로 변경하는 함수 만들기

예)

#include <stdio.h>

void swap(int *a, int *b);

int main()
{
    int a = 10;
    int b = 20;
    printf("a의 값은 %d, b의 값은 %d ", a, b);
    swap(&a, &b);
    return 0;
}
void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
    printf("a의 값은 %d, b의 값은 %d ", *a, *b);
}

설명 : main()함수 위에 void swap() 함수를 선언한다.
main()함수 아래에 void swap()함수의 내용을 만든다.
(포인터 주소값을 서로 변경하는 함수식)
이때 void로 시작했기 때문에 return이 필요없다.
main() 함수 안에 swap()함수를 출력한다.

swap함수를 호출할 때 그냥 a,b가 아니라 &a,&b를 해야 하는 이유는 함수의 정의에서 매개변수들이 값이 아니라 주소를 나타내기 때문이다. (int *a, int*b) 등


  • 함수의 포인터

예)

int (*pt)(int x, int y);
pt = plus;

plus라는 함수가 정의되어 있을 때, int (*pt)(int x, int y); 의 의미는 "매개변수 int x, int y가 있는 함수를 포인터 pt에 저장할 것이다."라고 선언하는 것이다.
그리고 pt = plus;의 의미는 함수 plus의 주소를 함수 포인터에 저장하는 것이다.
따라서 pt(a,b)=plus(a,b);다.


  • 문자열 비교 strcmp(매개변수1, 매개변수2)

매개변수1과 2를 비교해주는 함수이다. 같으면 0 같지 않으면 그 외의 값을 출력한다.
예) 아래와 같은 방법으로 비교해줄 수 있다.

 pt = strcmp(s1, s2);
    if (pt < 0 || pt > 0)
        printf("문자열이 서로 다릅니다. \n");
    if (pt == 0)
        printf("문자열이 서로 같습니다. \n");

  • tic-tac-toe 문제에서 헷갈린 부분

함수를 호출 할 때 자료형 등은 쓰지 않는다.
예)

함수선언 void table(char board[][3]);
함수호출 table(board) //table(char board)(x)
함수정의 void table(char board[][3]){}

  • 코드를 설계할 때

문제를 내고 정답을 맞추는 문제에 대한 코드를 짤 때
설계하는 것이 중요하다.

아래는 간단한 예시이지만 어떤식으로 생각하고 구상해야 할지 참고하면 좋을 것 같다.
예)
문제를 갖고 있는 배열
선택지를 갖고 있는 배열
정답을 갖고 있는 배열
사용자의 답을 입력받는 코드
점수를 기록하는 변수
문제를 출제하는 과정을 반복하는 과정


  • scanf() clear input buffer

scanf()에서 사용자가 입력하는 엔터도 값으로 인식해서
scanf('c',&저장소); 만 작성 할경우 제대로 작동하지 못한다.

scanf("%c",&guess);
scanf("%c"); ///clear input buffer

따라서 엔터 값을 저장하는scanf("%c");(clear input buffer)를 넣어서 문제를 해결한다.


  • 2차 배열안에 문자열 여러개 넣는 방법 (오류 수정)

c언어 교재 318쪽 문제에서 1번~12번까지의 문자열이 있었다.
이를 2차 배열로서 정리를 하려고 했는데

char table[3][5] = {{"1.우유", "2.커피", "3.주스","4.탄산음료"}, {"5.칸칩", "6.거북칩", "7. 감자칩", "8.고구마칩"}, {"9.안타볼", "10.초코버섯", "11.가재깡", "12.씨리얼"}};

이렇게 정리를 하니까 오류가 자꾸 났다.
2차 배열이라고 생각하고 만들었지만 이는 사실 3차 배열이었던 것이다.
내가 생각했던 table[3][5]에서 5는 예) "1.우유"가 각각 1개로 인식하고 문자열 종단에\0까지 생각하여 5라고 적었던 것이다.
하지만 이는 완전히 잘못 이해하고 있었던 것이다.
만약 이를 3차 배열로 쓰려면

char table[3][5][20] = {{"1.우유", "2.커피", "3.주스","4.탄산음료"}, {"5.칸칩", "6.거북칩", "7. 감자칩", "8.고구마칩"}, {"9.안타볼", "10.초코버섯", "11.가재깡", "12.씨리얼"}};

이렇게 해주면 가능하다.

우선 ""안에 들어가는 문자열 하나하나가 배열의 객체들이다. {}로 묶으면 한 차원이 더 추가된다. 따라서 묶지 않고

char table[][20] = {"1.우유", "2.커피", "3.주스","4.탄산음료","5.칸칩", "6.거북칩", "7. 감자칩", "8.고구마칩","9.안타볼", "10.초코버섯", "11.가재깡", "12.씨리얼"};

이렇게 써줘야 한다. 우선 이 모양이 내가 알고 있는 2차배열의 모양이다.

단, 이경우 char table[12][20]; 과 같다.

이후에 문자열의 크기를 지정해 준다.

char table[][20] 여기서 20은 예)"1.우유"안의 문자열의 크기가 20보다 작기 때문에 20으로 넉넉하게 넣어준 것이다.

 char *table[3][4] = {{"1.우유", "2.커피", "3.주스","4.탄산음료"}, {"5.칸칩", "6.거북칩", "7. 감자칩", "8.고구마칩"}, {"9.안타볼", "10.초코버섯", "11.가재깡", "12.씨리얼"}};

단, 포인터를 사용할 경우 위와 같은 모양이 가능하다.

  • 나의 언어로 정리

문자열("내용")은 일종의 배열이라 생각하면 된다.
따라서 문자열이 들어간 배열은 한차원 높여서 써야 한다.(배열이 하나 더 있는거니까)
단, 포인터를 사용하여 저장할 경우 그대로 써도 된다.(배열의 주소를 가리키니까)


  • 배열의 복사

memcpy();를 쓰기 위해선 #include <string.h>를 넣어줘야 한다.
memcpy(복사받을 배열, 복사해줄 배열, 복사할크기);
예)

  char word[5][15] = {
        "world",
        "heroes",
        "voca",
        "source",
        "korea"};
    char save[20] = " ";
memcpy(save, word[x], sizeof(word[x]));

//word[x] 배열을 word[x]의 크기만큼 배열 save에 복사해달라는 뜻이다.


  • c언어에서 파일을 다룰 때 (FILE)
#include <stdib.h> //우선적으로 필요함.
FILE *fp; //  포인터로 선언해야함
fp = fopen("test.txt", "w"); //쓰기전용 새 파일을 생성, 변수 fp에 파일을 여는 함수를 저장.
fclose(fp); //파일 종료를 꼭 해줘야 함.

cf) fputc(c, fp); // fp파일에서 변수명이 c인거 출력
cf) while ((c = getchar()) != '\n'){} 변수명 c에 사용자 입력을 계속받는데 입력이 엔터가 아니면 계속 while문을 실행하라는 뜻이다.


  • EOF

EOF // end of file로 -1로 정의되어 있다.


  • 파일 주소 설정할 때

파일 주소 설정해줄 때 \ 두번 적어줘야 한다.
예)

FILE *file = fopen("C:\\Users\\사용자계정\\test1.txt","wb");

  • c언어 파일 정리

텍스트 방식, 이진방식이 있다,
모든 데이터는 이진수로 처리되어 저장된다.


  • 파일스트림

모든 입력과 출력에 관련된 것들을 stream이라고 하는 공통된 인터페이스 방식을 사용하여 주변기기(키보드, 모니터 등)와 연결한다.

stream  |  장치  | 하는일
---------------------------
stdio   | 키보드 | 표준입력
stdout  | 모니터 | 표준출력
stderr  | 모니터 | 표준오류출력

모니터와 키보드에 관한 입출력은 따로 열고 닫는것이 필요없지만 파일을 열고 닫기 위해서는 fopen, fclose 명령어를 사용해야 한다.


  • 파일 포인터

파일에 관한 입력과 출력을 하기 위해서 포인터(pointer)를 사용한다.
파일처리를 사용하는 포인터를 파일포인터라고 한다.

) 
FILE *fp;
fp = fopen("test.txt","w");  //fopen(파일명, 파일형식)
fclose(fp) (o) / fclose("test.txt") (x)

cf) 파일모드 : r(읽기),w(쓰기),a(기존내용에 추가)등..


  • 파일 open 오류처리
FILE *fp;
fp = fopen("test.txt","w");
if(fp ==NULL)
{
	print("오류발생");
	exit(1);
}

파일열기에 실패할 경우 NULL을 반환한다.
정상적으로 파일이 생성됐다면 오류발생 문구가 나오지 않는다.
프로그램을 종료하기 위해 exit을 사용한다.


  • 입,출력 명령어
문자일 때   |  파일쓰기  fputc    | 파일읽기 fgetc
문자열일 때 |  파일쓰기  fputs    | 파일읽기 fgets
형식지정    |  파일 쓰기 fprintf  | 파일읽기 fscanf

fprintf, fputc는 쓰는 순서가 다르다.
fprintf(fp,"Hellow, world");
fputc(c, fp);

표준 출력(문자) putchar()
표준 입력(문자) getchar()
표준 출력(문자열) puts
표준 입력(문자열) gets

형식표준출력 printf
형식표준입력 scanf

이외에 다른 명령어와 블록단위를 사용하는 것들이 있다.

while ((c = getchar()) != '\n')
    {
        printf("%c", c);
        fputc(c, fp);
    }

이 상황에서 엔터키를 칠 때까지 문자가 계속 입력된다.
그러면 이것을 문자열로 봐야 한다고 생각할 수 있다.
그러나 이는 버퍼의 과정에 저장되어 있다가 한번에 출력된 것으로 생각하면 된다.


  • feof

feof는 파일의 끝에 도달하면 0이 아닌 정수값을 반환하고 그렇지 않은 경우 0을 반환한다.


  • fgets 받을 때 크기
char chr[8];

while (!feof(fp))
    {
        fgets(chr,7,fp);
        printf("%s",chr);
    }

파일 fp의 끝에 도달할 때까지 반복하라는 뜻.
배열 chr이 종단 포함해서 크기가 8이라서
fgets는 파일 fp의 배열 chr의 7자리까지 가져오라는 뜻이다.


  • 버퍼의 내용을 비우는 fflush()

fflush(stdin);을 해주면 눈에 보이지 않는 엔터\n등이 들어가는 오류를 없애줄 수 있다.
비주얼 스튜디오에서는 getchar();로 사용가능하다.


  • 구조체
struct 구조체이름 {
자료형1 변수명1
자료형2 변수명2
자료형3 변수명3
     ... }

이렇게 먼저 구조체를 만들면
struct 구조체이름 변수이름 = {내용1, 내용2, 내용3,..}
이런식으로 한번에 정의해줄 수 있다.
변수이름.자료형 ={}; 이런식으로 하나를 가져와서 초기화 해줄수 도 있다.

구조체 예시

struct point
{
    int x;
    int y;
};
int main()
{
    struct point p1; // 첫번째 점의 좌표를 입력
    struct point p2; // 두번째 점의 좌표를 입력
    int xdiff, ydiff;
    double distance;

    printf("첫번째 점의 x,y좌표를 입력하세요. : ");
    scanf("%d %d", &p1.x, &p1.y);

    printf("두번째 점의 x,y좌표를 입력하세요. : ");
    scanf("%d %d", &p2.x, &p2.y);

scanf로 좌표를 가져올 때 &p1.x (p1의 x의 주소)를 가져온다.
만약에 struct안에 배열인 변수를 가져올 경우 &기호는 붙이지 않는다.


  • 구조체 포인터
struct student
{
    int number;
    char name[20];
    double grade;
};

   struct student s = {20070001, "홍길동", 4.3};
    struct student *p;
    p = &s;
    printf("학번 =%d, 이름 = %s, 학점=%f\n", s.number,s.name,s.grade);
    printf("학번 =%d, 이름 = %s, 학점=%f\n", (*p).number,(*p).name,(*p).grade);
    printf("학번 =%d, 이름 = %s, 학점=%f\n", p->number,p->name,p->grade);

구조체의 변수 s의 주소를 p에 저장하고
s.number(*p).number로 쓸 수 있고 p->number로도 쓸 수 있다.


  • 구조체 변수 정의

구조체 안에 변수들을 정의할 때 char *name; 이런식으로 정의할 수 있다.
배열로 정의한거나 비슷하게 생각하면 될것 같다.

구조체형인 배열도 정의 할 수 있다.
예)

 struct GameInfo gameArray[2] =
        {
            {"나도게임", 2022, 50, "나도회사"},
            {"너도게임", 2022, 100, "너도회사"}};

    struct GameInfo *p;
    p = gameArray;

이 경우 포인터를 활용한다면 p->는 배열 gameArray[0]이고
(p+1)->은 배열 gmaeArray[1]이다.


  • typedef 변수명 별명 짓기
typedef struct GameInformation
{
    char *name;
    int year;
    int price;
    char *company;
    struct GameInfo *friendGame;
} GAME_INFO;

GameInformation라는 변수명이 길어서 이를 부르는 말로써,
GAME_INFO라는 변수명을 추가해준것.
쉽게말해서 GAME_INFOint 같은 역할을 해주는것이다.
예)

GAME_INFO a2;  
a2.name="한자게임";

  • 이미지 파일 fread, fwrite

fread(void *ptr,size_t size,size_t n,FILE *fp) = 함수 원형
fread(0은 파일의 끝에 도달하면 0을 반환한다)
ptr은 파일로부터 읽는 데이터를 기억시킬 버퍼에 대한 포인터
size : 읽을 데이터의 byte 수 (block의 크기)
n : size만큼 읽기 위한 반복 횟수(block의 수)
fp는 함수 fopen()을 사용하여 얻은 유효한 파일 포인터


  • scanf() 에서 특정 문자만 입력을 받을 때
    char s[80];

    printf("문자열을 입력하시오 :");
    scanf("%[abc]", s); //입력을 abc로만 받겠다

%[abc]
문자를 여러개를 받아도 abc에 해당하는 내용만 입력받겠다는 것을 의미한다.(오류처럼 보이지만 작동한다.)


profile
서버개발 공부중

0개의 댓글