혼자 공부하는 C언어 (1)

Erdos·2024년 10월 1일
1

감상

목록 보기
38/42
post-thumbnail

📖 back to the basic

책 한 권을 제대로 읽어보지 않았다는 게 가끔은 기초가 너무 부족하다는 느낌을 줄 때가 있다.
몰라도 되지만, 가끔은 버벅거리거나 설명이 안되는 그런 부분을 채우기 위해 시작!

chapter2 상수와 데이터 출력


2-1 C 프로그램의 구조와 데이터 출력 방법

2-2 상수와 데이터 표현 방법

정수 상수 표현법

진법별 수 표현 방법

  • 프로그래밍 언어에서는 밑수를 표기할 수 없다
  • 그래서, 8진수는 숫자 앞에 0, 16진수는 0x를 붙여 구분한다.

10진수를 8진수나 16진수로 출력하려면

#include <stdio.h>

int main(void)
{
    printf("%o\n", 12); // 8진수
    printf("%x\n", 12); // 16진수 대문자
    printf("%X\n", 12); // 16진수 소문자
    return (0);
}

실수 상수 표현법

  • 정규화(normalization) 표기법: 소수점 앞에 0이 아닌 유효 숫자 한 자리를 사용해 지수 형태로 바꾼 것

문자와 문자열 상수 표현법

상수가 컴파일된 후의 비트 형태

정수 상수와 실수 상수의 컴파일


컴퓨터에게 10과 10.0은 다르다. 정수는 가장 빠르고 정확하게 연산될 수 있는 형태이므로, 가능하다면 정수 상수를 사용하는 게 좋다.

문자 상수의 컴파일

정수 상수가 컴파일된 후의 비트 형태

음수의 변환

1) 절댓값 변환 ex) -10 → 10
2) 2진수로 변환

00000000 00000000 00000000 00001010

3) 0과 1을 바꿈(1의 보수)

11111111 11111111 11111111 11110101

4) 1을 더함

11111111 11111111 11111111 11110110

🐱 실수 상수 오차(p77) -> 설명이 좀 아쉬운데..

  • 오차가 발생하는 이유: 소수 부분을 나타내는 비트가 정확한 값을 표현할 수 없기 때문
  • double형의 경우 15자리 유효 숫자를 사용할 수 있는데, 이 범위에서 사용하는 것이 바람직하다.

chapter3 변수와 데이터 입력


3-1 변수

“변수”를 선언할 시 메모리에 저장 공간을 확보하며 대입연산자(=)로 변수값을 초기화 하거나 저장할 수 있다. 초기화 하지 않은 변수에는 쓰레기값(garbage value)이 들어 있으므로 반드시 초기화 해주어야 한다.

정수 자료형

어떤 자료형을 사용해야 하는지 고민된다.

  1. 특별한 경우가 아니면 정수형을 표현할 때는 int를 사용한다
  2. long형은 큰 값을 저장할 때 사용한다.

실수 자료형

🐱 IEEE754 표준의 개념과 오차 발생 이유

  • float: 유효 숫자 7자리 범위 내
  • double: 유효 숫자 15자리 범위 내
  1. 정수형을 기본으로 사용하고 꼭 필요한 경우에만 실수형을 사용한다.
  2. 실수형은 유효 숫자가 많은 double형을 기본으로 사용한다.

const를 사용한 변수

  • 변수를 선언할 때 그 앞에 const를 붙이면 초기화된 값을 바꿀 수 없다.

표로 정리하는 핵심 포인트


3-2 데이터 입력

🐱 p107) scanf, gets, strcpy 등 메모리 침범 문제를 일으키는 함수를 사용할 때마다 #define선언이 소스 코드에 포함되어 있어야 한다.

Chapter4 연산자


4-1 산술 연산자, 관계 연산자, 논리 연산자

증감 연산자

void *ft_memcpy(void *dest, const void *src, size_t n) 
{
    unsigned char *d;
    const unsigned char *s;
    unsigned char *origin;
	
	d = dest;
	s = src;
	origin = d;

    while (n--) 
        *d++ = *s++; // 이런 게 증감연산자
    return origin;
}
  • 주의할 점
(++a) + a (++a)

하나의 수식에 같은 변수를 두 번 이상 사용할 때, 그 변수에는 증감 연산자를 사용하면 안 된다.

논리 연산자

  • &&(AND): 2개의 피연산자가 모두 참일 때만 결과가 참
  • ||(OR): 둘 중에 하나라도 참이면 참
  • !(NOT)
  • short circuit rule
    • 좌항만으로 &&와 || 연산 결과를 판별하는 기능
    • 불필요한 연산을 줄여 실행 속도를 높일 수는 있으나(int)Fnum + num 예상 외의 결과가 나올 수 있으니 주의가 필요하다.

좀 더 알아보기

  1. 연산식은 컴퓨터 내부에서 어떻게 처리될까요?
  • (1,2 로드) 연산을 하기 위해서 a와 b의 값을 CPU 저장 공간인 레지스터에 복사해야 한다.
  • (3 연산) 데이터가 레지스터에 저장되면 연산장치인 ALU에 의해 덧셈 연산이 수행되고 그 결과값은 일단 레지스터에 저장
  • (4 스토어) 대입 연산을 수행하면 메모리 공간인 sum에 복사되어 수식의 모든 과정이 완료

컴퓨터 구조에서 활용할 내용 있는지 체크 + 추가

  1. CPU의 메모리와 우리가 알고 있는 메모리(RAM)은 어떻게 다른가요?
  • CPU의 메모리 = 레지스터:
    - 연산할 데이터와 연산 후의 결과를 임시 저장
    - 보통 CPU의 클럭과 1:1 동기화 되어 있어서 RAM보다 엄청 빠르다
    • 연산 결과를 메모리로 옮겨 놓지 않으면 그 값은 사라진다.

4-2 그 외 유용한 연산자

형 변환 연산자

(자료형)피연산자
  • 형 변환 연산자는 일시적으로 피연산자의 값을 원하는 형태로 바꾼다.
  • 연산 후 메모리에 남아 있는 피연산자의 형태나 값은 변하지 않음
#include <stdio.h>

int main(void)
{
    int a = 20, b = 3;
    double res;
    
    res = ((double)a) / ((double)b); 
    // a,b를 double형으로 선언하면 편할 것 같지만, 저장 공간이 크고
    // 연산 속도가 느리며 무엇보다 오차가 발생하므로 기본적으로 
    // int형을 기본으로 사용하는 것이 좋다.
    printf(" a = %d, b = %d\n", a, b);
    printf("a / b의 결과 : %.1lf\n", res);
    margin-right:10px;" />
    a = (int)res;
    printf("(int) %.1lf의 결과 : %d\n", res, a);
    return (0);
}

자동 형 변환

  • 형 변환의 기본 규칙: 데이터 크기가 작은 값이 크기가 큰 값으로 바뀐다.
  • 주의) 예상치 못한 값의 변형이 생길 수 있으니 가능하면 피연산자의 형태를 같게 맞춰 사용하는 편이 좋다.

    아래 내용은 C언어 기초: 암시적 형변환과 명시적 형변환을 참고해서 작성했음.

1. 암시적 형변환(Implicit Type Conversion): 다른 자료형 간의 정보를 저장할 때 컴퓨터가 알아서 자료형에 맞게 내용물을 바꿔서 저장해주는 것. 대놓고 말하지 않아도 저렇게 쓰면 니가 알아서 바꿔. 데이터 손실 가능성이나 오류 없이 변환이 가능한 경우에만 이뤄짐. (자동 형 변환)

#include <stdio.h>

int main(void)
{
    int num;
    float Fnum = 123.987;
    
    num = Fnum;
    printf("%d\n", num); // num에 정수만 들어갔다!
    Fnum = num;
    printf("%f\n", Fnum);
    
    printf("%f\n", Fnum + num); // 123.000000 + 123 = 246.000000
    printf("%d\n", Fnum + num); // 오잉??
}
암시적 형변환의 특징은 서로 다른 자료형 간의 연산이 생겼을 때, 결과값을 저장할 자료형을 지정해주지 않으면 무조건 우선 순위가 더 높은 자료형으로 저장된다는 것이다. 그렇기 때문에, 컴퓨터는 int+float연산의 결과값을 float로 저장했다. 그런데, float보다 우선 순위가 낮은 int형으로 연산 결과값을 표현하려니, 데이터 손실이 일어나 다 날아가 버리고 0으로 출력!
	printf("%d\n", Fnum + num); **암시적 형변환**// 246이 나올 줄 알았는데 0이 나왔다.
  • 컴퓨터가 기준으로 삼는 자료형의 우선순위

char < int < long int < unsigned int < float < double

2. 명시적 형변환(Explicit Type Conversion): 변수의 타입을 강제적으로 프로그래머가 변환하는 방법(casting). 데이터 손실이 발생할 가능성 있음

(변환하고자 하는 타입)변수명
아래에서는 (int)Fnum + num 이 부분

#include<stdio.h>

void main()
{
	int num;
	float Fnum = 123.953;

	num = Fnum;
	printf("%d\n", num);
	Fnum = num;
	printf("%f\n", Fnum);

	printf("%f\n", Fnum + num);
	printf("%d\n", (int)Fnum + num);
}

마지막 결과값에 대해 자료형을 확실하게 알려줌. 그러니 결과값이 246

sizeof 연산자

#include <stdio.h>

int main(void)
{
    int a = 10;
    double b = 3.4;
    
    printf("int형 변수의 크기: %d\n", sizeof(a));
    printf("double형 변수의 크기: %d\n", sizeof(b));
    printf("정수형 상수의 크기: %d\n", sizeof(10));
    printf("수식의 결괏값의 크기: %d\n", sizeof(1.5 +3.4));
    printf("char 자료형의 크기: %d\n", sizeof(char));
    return 0;
}

조건 연산자


대입식도 된다.

코드를 간략히 만들어주는 효과. 매크로 함수에 사용하면 좋으나 가독성을 떨어뜨릴 가능성이 있어서 필요한 곳에만 사용해야 한다.

비트 연산자

  • 비트 연산자는 정수에만 사용 가능
  1. 비트별 논리곱 연산자(&):
  • & 연산은 두 비트가 모두 1인 경우에만 1로 계산한다.
  1. 비트별 배타적 논리합(exclusive or) 연산자(^):
  • ^ 연산은 두 비트가 서로 다른 경우에만 1로 계산한다.
  1. 비트별 논리합 연산자(|)
  • 두 비트 중에서 하나라도 참이면 1로 계산한다.
  1. 비트별 부정 연산자(~)
  • 1을 0으로 바꾸고 0을 1로 바꾼다.
  1. 비트 이동 연산자
    << 비트를 왼쪽으로 이동시키고, >> 은 오른쪽으로 이동시킨다.
  • << 비트를 왼쪽으로 n번만큼 이동
  • <<1: 2배, <<2: 4배, <<3: 8배
  • 원래 수에 2의 n제곱을 곱해주는 것이지만, 비트로 연산하기 때문에 속도가 훨씬 빠르다.
  • '>>'비트를 오른쪽으로 n번만큼 이동
  • '>>1'(2의 -1제곱 ... 위와 비슷함)
  • 원래 수에 2의 n제곱을 나눠주는 것이지만, 비트로 연산하기 때문에 속도가 훨씬 빠르다.

연산자 우선순위와 연산 방향

  • 단항 연산자 > 이항 연산자 > 삼항 연산자

  • 산술 연산자 > (비트 이동 연산자) > 관계 연산자 > 논리 연산자

profile
수학을 사랑하는 애독자📚 Stop dreaming. Start living. - 'The Secret Life of Walter Mitty'

0개의 댓글