본문 바로가기
Programming/C

[따배씨] 섹션 4. 문자열과 형식 맞춘 입출력

by DONGKU 2020. 7. 21.

4.1 문자열 입출력하기

#include <stdio.h>

int main()
{
    char fruit_name[40];    // 문자열 배열

    printf("What is your favorate fruit?\n");

    scanf("%s", fruit_name);    // 문자열 배열이므로 %s

    printf("You like %s \n!", fruit_name);

    return 0;
}

4.2 sizeof 연산자

#include <stdio.h>
#include <stdlib.h>


struct MyStruct
{
    int i;
    float f;
};

int main()
{
    /* 1. sizeof basic types */

    int a = 0;
    unsigned int int_size1 = sizeof a;    // 크기를 나타낼때 음수를 쓰지는 않으므로 unsigned int 자료형 사용
    unsigned int int_size2 = sizeof(int);
    unsigned int int_size3 = sizeof(a);

    size_t int_size4 = sizeof(a);    // 다른 시스템에서는 sizeof의 자료형이 unsigned int가 아닐 수 있으므로 typedef unsigned int size_t로 이식성을 높임
    size_t float_size = sizeof(float);

    printf("Size of int type is %u bytes.\n", int_size1);        // Size of int type is 4 bytes.    // unsigned에 대응하는 형식지정자 %u
    printf("Size of int type is %zu bytes.\n", int_size4);        // Size of int type is 4 bytes.    // size_t에 대응하는 형식지정자 %zu
    printf("Size of float type is %zu bytes.\n", float_size);    // Size of float type is 4 bytes.    

    /* 2. sizeof arrays */

    int int_arr[30]; // int_arr[0] = 1024; ...
    int* int_ptr = NULL;
    int_ptr = (int*)malloc(sizeof(int) * 30); // int_ptr[0] = 1024; ...

    printf("Size of array = %zu bytes\n", sizeof(int_arr));        // Size of array = 120 bytes
    printf("Size of pointer = %zu bytes\n", sizeof(int_ptr));    // Size of pointer = 4 bytes  // 주소를 적는 메모지의 사이즈

    /* 3. sizeof character array */

    char c = 'a';
    char string[10]; // maximally 9 character + '\0' (null character)

    size_t char_size = sizeof(char);
    size_t str_size = sizeof(string);

    printf("Size of char type is %zu bytes.\n", char_size);        // Size of char type is 1 bytes.
    printf("Size of string type is %zu bytes.\n", str_size);    // Size of string type is 10 bytes.

    /* 4. sizeof structure */

    printf("%zu\n", sizeof(struct MyStruct));    //    8 -> int 4 + float 4

    return 0;
}

4.3 문자열이 메모리에 저장되는 구조

#include <stdio.h>

int main()
{
    int a = 1;
    int int_arr[10] = { 0, 1, 2, 3,4, 5, 6, 7, 8, 9 };

    printf("%i %i %i\n", int_arr[0], int_arr[1], int_arr[9]);

    printf("%i\n", int_arr[10]);    // -858993460 -> int_arr[10]은 널문자 '\0'

    char c = 'a';
    char str1[10] = "Hello";
    char str2[10] = { 'H', 'i' };

    printf("%c\n", c);
    printf("%s\n", str1);
    printf("%s\n", str2);

    printf("%hhi %hhi % hhi %hhi %hhi\n",
        str2[0], str2[1], str2[2], str2[3], str2[4]);    // 72 105  0 0 0

    //char str3[10] = "Hello, World"; // array size is not enough
    char str3[20] = "Hello, \0World";
    printf("%s\n", str3);    // Hello,  -> \0을 만나 끊김

    return 0;
}

4.4 strlen() 함수

#include <stdio.h>
#include <string.h>    // strlen and more

int main()
{
    char str1[100] = "Hello";    // 컴파일러가 \0 이후에 0을 채워주게 된다.
    char str2[] = "Hello";        // 글자에 맞게끔 컴파일러가 공간을 할당해줌.
    char str3[100] = "\0";        // 빈칸과 \0은 아스키 코드가 다르다. 
    char str4[100] = "\n";

    printf("%zu %zu\n", sizeof(str1), strlen(str1)); // 100 5// 간혹 %zu를 지원안해주는 경우가 있다.  // strlen는 인간에게 의미있는 것만 세준다.
   	//********************************************************
    printf("%zu %zu\n", sizeof(str2), strlen(str2)); // 6(문자열+'\0') 5
    //********************************************************
    printf("%zu %zu\n", sizeof(str3), strlen(str3)); // 100 0  '\0' 문자로인식X
    printf("%zu %zu\n", sizeof(str4), strlen(str4)); // 100 1  '\n' 문자로인식o

 /*Extra;*/
 // 동적할당에 대해서. 나중에 배울건데, 미리 예제로 넣으심.

    char* str5 = (char*)malloc(sizeof(char) * 100);
    str5[0] = 'H'; str5[1] = 'e'; str5[2] = '1'; str5[3] = '1'; str5[4] = 'o';
    str5[5] = '\0';    // 동적할당은 널문자 직접 넣어줘야함
    // 동적할당을 할경우에는 위의 예제처럼 편리하게 할 수가 없음.
    // 물론 다 처리해주는 다른 프로그래머들의 library를 가져다 쓰면 해결이 되지만
    // 기본문법은 편리하지 않다.
    printf("%zu %zu\n", sizeof(str5), strlen(str5));    // 4(포인터 변수 자체 사이즈) 5

    // >>> 4byte는 포인터 변수 자체의 사이즈 
    // str1[100]은 컴파일러가 사이즈를 100byte라고 알고있기 때문에 알려줄 수 있지만,
    // char *str5는 도대체 얼마나 메모리를 많이 가져와서 할당을 받고, 오퍼레이팅 시스템에게
    // 이 메모리 쓸게요 하면서 받아올 지를 알 수가 없기 때문에 
    // 그냥 포인터 변수 주소를 적는 메모지의사이즈 만큼만 알 수 있다.
    // 지금은 sizeof 와 기능적으로 내용들이 다르게 나온다 정도만 이해.
    // 5는 글자수.

    return 0;

}

4.5 기호적 상수와 전처리기 #define

전처리기 #define 은 사람을 위해, 프로그래머를 위해 사용 - 실수를 줄이고 사용과 보기 용이함

매크로에서 많이 사용

대문자 상수를 쓰는 것이 일반적

 

※최근에 기호적 상수 권장하는 방식 const
const float pi = 3.141592f; // const 변수 선언을 통해서 기호적 상수(pi) 선언을 할 수 있음

 

#define PI 3.141592
#define PI = 3.141592 로적으면 "= 3.141592" 통째로 사용

#include <stdio.h>
#define PI 3.141592f    // 기호적 상수 선언 (기호가 상수를 대체해서 사용) - 사용과 보기 용이함
#define AI_NAME "Jarvis"

int main() 
{
    float radius, area, circum;

    printf("I'm %s.\n", AI_NAME);
    printf("Please, Input radius\n");

    scanf("%f", &radius);

    area = PI * radius * radius; // area = pi*r*r
    circum = 2.0 * PI * radius;    // circum = 2.0 * pi * r

    // sphere area, sphere volume, ...

    printf("Area is %f\n", area);
    printf("Circumference is %f\n", circum);

    //TODO: wrong usage, strings, const

    return 0;
}

4.6 명백한 상수들 manifest constants

복붙과 사용이 유사하다

#include <stdio.h>
#include <limits.h>    // INT_MAX, ..., etc.
#include <float.h>    // FLT_MAX, ..., etc.

#define PI 3.141592    // manifest constants, symbolic constants

int main() 
{
    printf("PI is %f\n", PI);    // PI is 3.141592
    printf("Biggest int: %d\n", INT_MAX);    // Biggest int: 2147483647
    printf("One byte in this system is %d bits\n", CHAR_BIT);    // One byte in this system is 8 bits
    printf("Smallest normal float %e\n", FLT_MIN);    //    Smallest normal float 1.175494e-38
}

4.7 printf() 함수의 변환 지정자들 conversion specifiers

출력을 어떻게 할지 해석을 하는 기능에 가깝다

#include <stdio.h>
#include <limits.h>
#include <float.h>


int main()
{
    double d = 3.141592653589793238462643383279502884197169399375105820974944;

    printf("%c\n", 'A');    // A
    printf("%s", "I love you\n");    // I love you    // 출력을 하려는 문자열에 '\n'을 넣어도 줄바꿈이 됨
    printf("Even if there's a small chance, \
we owe this to everyone whos not in this room to try.\n");
    //출력상 줄바꿈이 아니고, 코딩하는데 읽기 불편해서 줄바꿈 할 경우에 '\' 사용. 없으면 컴파일 에러        
    //    Even if there's a small chance, we owe this to everyone whos not in this room to try.

    printf("\n");
    printf("%d %i %i %i\n", 1004, 1234, INT_MAX, UINT_MAX);    // 1004 1234 2147483647 '-1(overflow)'     
                                                            // %i(signed integer) 범위에 UINT_MAX(unsigned integer)를 넣었으니 overflow

    printf("%u %u %u\n", 1024, -1, UINT_MAX);                // 1024 '4294967295(overflow)' 4294967295
                                                            // %u(unsigned integer) 범위에 -1(음수)을 넣어 overflow



    printf("\n");
    printf("%f %f %lf\n", 3.141592f, d, d);                    // 3.141592 3.140000 3.140000
    // double을 쓸려면 float 에서 l을 붙여줘야 하지 않을까.
    // 맞는 말인데, 다음강에서 설명해 주신다고 함.
    // 전통적인 이유로 float을 넣으면 double로 처리된다고 함.
    // 그러므로 printf를 할떄는 l을 붙일 필요 없지만 sancf는 붙여야함.


    printf("%a %A\n", d, d);                                // 0x1.91eb851eb851fp+1 0X1.91EB851EB851FP+1
    printf("%e %E\n", d, d);                                // 3.140000e+00 3.140000E+00

    printf("\n");    
    printf("%g %g\n", 12345.6789, 12345678.9);                // 12345.7(%f, 유효숫자 6자리) 1.23457e+07(%e)    
    printf("%G %G\n", 123456.789, 1234567.89);                // 123457 1.23457E+06
    printf("%g %g\n", 0.00012345, 0.000012345);                // 0.00012345 1.2345e-05
    printf("%G %G\n", 0.00012345, 0.000012345);                // 0.00012345 1.2345E-05
    //%f %e인지 컴파일러가 정해줌. 

    printf("\n");                
    printf("%o\n", 9);        // 11
    printf("%p\n", &d);        // 010FF85C
    // pointer-of pperator
    //d변수가 가지고있는 메모리의 주소를 출력할 수 있다.

    printf("\n");
    printf("%x %X\n", 11, 11);    // b B
    // 16진수
    printf("%%\n", d);            // %

    printf("\n");
    printf("%9d\n", 12345);        //     12345
    //숫자를 쓸때 9자리를 써라. 나머지는 빈칸.
    printf("%09d\n", 12345);    // 000012345
    //위에서 앞에 빈칸을 0으로 써라
    printf("%.2f\n", 3.141592);    // 3.14
    // 소수점이하자리를 2자리만 출력해준다.
    printf("%.20f %.20lf\n", d, d);    // 3.14000000000000012434 3.14000000000000012434
    // 앞에 숫자가 붙으면 자릿수를 맞춰줄 수 있다. 

    printf("\n");
    int n_printed = printf("Counting!");    
    //printf("Counting!")가 출력한 값을 n_printed에 담음
    printf("%u\n", n_printed);    // Counting!9    // 숫자 9는 문자열의 글자개수
    //n_printed를 출력하면 어떻게 되나
    //********************************************************
    // printf를 변수에 담은 return값은
    // 선언할때의 printf("Counting!")이 출력되고 
    // 출력할 글자의 개수이다 
    //********************************************************

    return 0;
}

4.8 변환 지정자의 수식어들 modifiers

['+': 부호를 표시]
[10: 10자리]
[.5: 정밀도 5자리]
[h: 짧은것 담음]
i: 정수

[flag] 여러가지를 할 수 있을 때 그 중에 어떤 방식으로 출력을 할 것이다
-: 왼쪽으로 붙여라 -flag가 있으면 있으면 왼쪽, 없으면 오른쪽으로 default, ‘width'랑 같이 써야한다.
+: -의 반대개념이 아니다. +는 양수일때도 +기호를 출력하라.
space: 부호가 없을경우. 양수일 경우 빈칸이 출력
#: 16진수등을 출력할때 0, 0x, 0X 출력하는 옵션.
0: width 지정해줬을때 남는자리를 0으로 채운다.

[width]
숫자를 넣어주면 123인데 숫자 5를 지정해주면 앞에 빈칸 2개 생김

[.precision] 정밀도 조정
'.number' 숫자 자릿수 만큼 최소 출력되는 정밀도를 보장하는 것. 숫자가 부족하면 0으로 채워버림.
‘.*’ 용법은 위와 비슷한데 예제로 설명해주심.

[legnth] 데이터 타입(메모리)의 길이.
h: 짧은것
l: 긴것
z: sizeof
ptrdiff_t: 주소의 차이
(none): float는 내부적으로 double로 바뀌기 때문에 printf는 double만 출력
L: long double 출력하려면 L 붙여줌
n같은 경우가 표준 스펙은 있는데 구동도 안됨.

#include <stdio.h>
#include <limits.h>



int main()
{

    printf("%10i\n", 1234567);            //    1234567
    // 1234567 10자리가 안되서 앞에 빈칸으로 채워짐.
    printf("%-10i\n", 1234567);            // 1234567
    // '-' 빈칸 상관없이 왼쪽으로 정렬됨.    
    printf("%+i %+i\n", 123, -123);        // +123 -123
    // '+' +인지 -인지 꼭 표기해줌.
    printf("% i \n% i\n", 123, -123);    // 123    // +인경우 부호자리 space. 
    // 'space'                            // -123
    printf("%X\n", 17);                    // 11    
    // 17을 16진수로
    printf("%#X\n", 17);                // 0X11    
    // '#' 0X로 표기
    printf("%#x\n", 17);                // 0x11
    // 소문자일경우.
    printf("%05i\n", 123);                // 00123        
    //5는 width, 0이 붙으면 남는자리 0으로 채워줌.
    //file이름 같은거 지어줄때 필요함.
    printf("%*i\n", 7, 456);            //     456
    // 자리는 하난데 item은 2개. 왜 ? 7이 *자리로 들어감.
    // 즉 width를 뒤에서 안에서 바꾸는게 아니라 뒤에서 바꾸고 싶을때 사용.
    // scanf에선 의미가 다름. 나중에 설명.
    printf("\nPrecision\n");            // Precision
    printf("%.3d\n", 1024);                // 1024	//정수는 .3d를 해도 소수점이 없으므로 '.'은 영향이 없는건가?
    // 작으면 그냥 출력되고
    printf("%.5d\n", 1024);                // 01024
    //크면 채워주려고 앞에 0넣어줌.
    printf("%.3f\n", 123456.1234567);    // 123456.123
    //위에는 d(정수)인거고 f는 소수점 이하자리. 이경우 3자리
    printf("%.3f\n", 123456.1235);        // 123456.124
    // 반올림이 된다.
    printf("%10.3f\n", 123.45678);        //    123.457
    //10은 전체 자리수(소수점 아래 포함)를 의미하고 .3f이므로 소수점 이하는 3자리만.
    printf("%010.3f\n", 123.45678);        // 000123.457
    printf("%.5s\n", "ABCDEFGHIJKLMN");    // ABCDE
    //string이랑 사용하면 5개만 끊어버림
    printf("%.s\n", "ABCDEFGHIJKLMN");    // 아무글자도 출력x
    //.뒤애 숫자입력이없으면 .0이랑 동일하게 작동. string뿐만아니라 decimal floating 둘다 동일하게 작동.

    printf("\nLength\n");
    printf("%hhd %hd %d\n", 257, 257, 257);    // 1(overflow) 257 257        // hhd(signed char) hd(short int)
    printf("%d %lld %lld\n", INT_MAX + 1, INT_MAX + 1, 2147483648LL);    // -2147483648(overflow) 2147483648 2147483648
    // LL은 x64(64비트)에서 작동시킬것.
    // // -2147483648 -9223372034707292160 28731583158550528  // x86에서 작동시 출력값
    return 0;
}

4.9 printf() 함수가 인자들을 해석하는 과정

/**************************************************

printf는 부동소수점이 들어오면 double로 변환

그렇기 때문에 %f를 써도 문제 x (but scanf는 double은 무조건 %lf)

/**************************************************
n1은 float 부동소수점이므로 double로 변환
n2는 double이므로 유지
n3는 4bytes
but, 위 그림에서 %d 이므로 n1에 4bytes 쌓이고 나머지 4bytes에 또 쌓이고 n2에 4bytes 쌓이고 끝
해석을 할때는 %d를 사용했으므로 4bytes만 읽고 출력을 하려고해서 문제가 생김

#include <stdio.h>
#include <limits.h>


int main()
{
    float n1 = 3.14;    // 4byte이긴한데printf가8바이트변환.
    double n2 = 1.234;  // 8byte
    int n3 = 1024;      // 4byte

    printf("%f %f %d\n\n", n1, n2, n3);                  // 3.140000 1.234000 1024
    // Note the warnings in output window
    printf("%d %d %d\n\n", n1, n2, n3); // 4,4,4(N,N,N) // 1610612736 -927712936 1024
    // %d 4 4 4로읽으니 스택에서 밀려읽게됨. 연쇄적으로 전부 다 이상한 값이 생성.
    printf("%lld %lld %d\n\n", n1, n2, n3); // 8,8, 4(N,N,Y)    // 4614253070451212288 4608236261112822104 1024
    // 이거는타입이안맞아서그렇다. 부동소수점수를 강제로integer인것처럼
    // 해석을하고 출력을하려고드니까 이상한숫자가 나온것이다.
    // n3는 데이터 타입은 맞으므로(스택에서 밀리지 않았으므로) 제대로 출력
    printf("%f %d %d\n\n", n1, n2, n3); // 8,4,4 (Y,N,N)        // 3.140000 -927712936 1024
    // 마지막 %d는 타입도맞고 byte도 맞아보이나 앞에서 밀려읽었기 때문에 문제가생김.
    printf("%f %lld %d\n\n", n1, n2, n3); // 8,8,4 (Y,N,Y)      // 3.140000 4608236261112822104 1024
    // 가운데꺼만잘안나옴. byte는맞으나, type이안맞기때문.

    return 0;

}

4.10 scanf() 함수의 사용법

※ '*'의 의미가 printf와는 다르다

#include <stdio.h>
#include <inttypes.h> // intmax_t 사용위해
#include<Windows.h>

int main()
{
    /* multiple inputs with blank separators*/
    int i;
    float f;
    char str[30];
    scanf("%d %f %s", &i, &f, str);    // 123 123 hello hi
     Note % is absent in front of str    
    //printf에서는 double 입력받을때도 %f써도 문제 없는데, 
    //scanf에서는 %lf로 써야한다.

    printf("%d %f %s\n", i, f, str);    // 123 123.000000 hello                                    

    /* character */
    char c;
    scanf("%c", &c);        // a
    printf("%i\n", c);    // 97
    // 문자를 정수로 출력하려고하는 코드. 아스키코드를 출력하려고 하는것.
    // 빈칸도 문자로 입력 받는다. blank is 32.
    // 위에선 구분의 용도로 빈칸. 여기선 문자 입력이므로 빈칸도 문자로 인식.

    ///* Unsigned as signed*/
    unsigned i;
    // unsigned를 선언하고 
    scanf("%i", &i);        // -123
    printf("%i\n", i);    // -123
    // signed를 출력하면 어떨까
    // 분명히 unsigned인데, 받을 때 %i(signed)로 받고, 그 받은 것을
    // unsigned memory에 저장해놨다가 출력해서 signed가 정상 출력 되는 것.
     요런 트릭은 사용하지 않는 것이 좋다.

    /* Unsigned as unsigned*/
    unsigned i2;
    scanf("%u", &i2);        // -123
    printf("%u\n", i2);        // 4294967173
    //unsigned는 unsigned로 대접해 주는게 제일 깔끔.

    /*floating point numbers*/
     l for double for %f, %e, %E, %g
    double d = 0.0;
    scanf("%lf",&d);    // scanf double은 %lf로 받아야한다.
    printf("%f\n", d);    // printf는 %f

    float d = 0.0;
    scanf("%f", &d);    //float은 f로.
    printf("%f\n", d);

    /* width */
    char str[30];
    scanf("%5s", str); //5글자 까지만 입력을 받겠다.
    printf("%s\n", str);

    /* h modifier */
    char i; // 입력받는 변수의 사이즈 맞춰줘야한다.
    scanf("%hhd", &i);
    printf("%i\n", i);

    /*integer with characters*/
    // 문자를 섞어서 하면 , 문자를 만나게 될때 더이상 출력안함.
    // 숫자들만 가지고 i에 넣어줌. 
    int i;
    scanf("%i", &i);        // 123a456
    printf("%i\n", i);    // 123  (integer이므로 문자 전까지만 출력)

    /* j modifier */
    intmax_t i; // int max 포터블 타입.
    scanf("%ji", &i); //j는 이 포터블타입을 받아 들이겠다.
    printf("%ji", i);

    /* Regular characters */
     나중에 정규식이라는게 있음. 규칙에 맞춰 입력받는다.
     이 강의를 벗어남. 나중에 확장될 수 있다.
    int a, b;
    scanf("%d ,%d", &a, &b); // 여러개 입력할 때 구분법으로
    // space나 enter로 구분했는데 이번엔 ,로 구분할 것이다.
    //scanf("%d, %d", &a, &b);
    //scanf("%d,%d", &a, &b);
    //scanf("%d-%d", &a, &b);
    //scanf("%dA%d", &a, &b);
    // ,외에 알파벳, 기호로도 구분이 가능하다
    printf("%d %d\n", a, b);

    /* char receives blank*/
    int a, b;
    char c;
    scanf("%d%c%d", &a, &c, &b); // try 123 456 (blank)
    printf("%d|%c|%d", a, c, b); // | is separator

     sentences?, getchar(), fgets(), etc.
     입출력을 받는 다른함수, 파일로도, 한글자로도 등등..

    /*return value of scanf()*/
     scanf의 return값은 item을 몇개 입력 받았는가
    int a, b;
    int i = scanf("%d%d", &a, &b);    // ab
    printf("%d", i);                // 2

    /* *modifier for printf() */
    int i = 123;
    int width = 5;
    printf("Input width : ");
    /**************************************************
    scanf("%d", &width);
    printf("%*d\n", width, i);  // *에 width 대응
    /**************************************************

    /* *modifier for scanf() */
    int i;
    /**************************************************
    scanf("%*d%*d%d", &i);    // 12 23 56
    //%*d는 *때문에 무시된다. 
    printf("Your third input = %d", i);    // Your third input = 56
    /**************************************************
    //1회 입력하면 입력을 받긴 받았는데 1,2회는 무시되고
    //3회%d가 출력된다. 구분은 빈칸 or 줄바꿈. 다른 기호, 문자를 쓰면 다른걸로 구분가능.
    //surpress


    return 0;
}

댓글