본문 바로가기
KraftonJungle2기/Today I Learned

[TIL] C언어 공부하기

by SooooooooS 2023. 5. 4.
728x90

1. 코드 refactoring

1. 12891번 - DNA 비밀번호

import sys

s, p = map(int, sys.stdin.readline().split())
dna = list(sys.stdin.readline().strip())
# A, C, G, T
esssential = list(map(int, sys.stdin.readline().split()))

def remove(st) :
    global a, c, g, t
    if dna[st] == 'A' :
        a -= 1
    elif dna[st] == 'C' :
        c -= 1
    elif dna[st] == 'G' :
        g -= 1
    elif dna[st] == 'T' :
        t -= 1
    return 0

def add(e) :
    global a, c, g, t
    if dna[e] == 'A' :
        a += 1
    elif dna[e] == 'C' :
        c += 1
    elif dna[e] == 'G' :
        g += 1
    elif dna[e] == 'T' :
        t += 1
    return 0

start = 0
end = p-1
count = 0
# 현재 부분 문자열
current = dna[0:p]
# 각각의 원소 포함 개수
a = current.count("A")
c = current.count("C")
g = current.count("G")
t = current.count("T")

while end < s :
    # 만약에 모든 원소들의 조건을 충족했다면
    if a >= esssential[0] and c >= esssential[1] and g >= esssential[2] and t >= esssential[3] :
        count += 1
    remove(start)
    start += 1
    end += 1
    if end == s :
        break
    add(end)
    
print(count)
< 원래 코드 > https://soo-note.tistory.com/61

생각해보니 deque로 저장할 필요가 없었다.
문제 풀이에 중요한 것은 각각의 문자들을 몇 개 가지고 있냐이다.
그래서 현재 문자열을 저장하는 부분을 빼고 다시 작성했다.

2. 문제 풀이

1. 1890번 - 점프

import sys

N = int(sys.stdin.readline())
game = [list(map(int, sys.stdin.readline().split())) for _ in range(N)]

dp = [[0] * N for _ in range(N)]
dp[0][0] = 1
for i in range(N) :
    for j in range(N) :
        if i == N-1 and j == N-1 :
            break
        if j + game[i][j] < N : # 오른쪽
            dp[i][j + game[i][j]] += dp[i][j]
        if i + game[i][j] < N : # 아래쪽
            dp[i + game[i][j]][j] += dp[i][j]
print(dp[N-1][N-1])

2. 1520번 - 내리막 길

import sys

M, N  = map(int, sys.stdin.readline().split())
graph = [list(map(int, sys.stdin.readline().split())) for _ in range(M)]

dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]

visited = [[-1] * N for _ in range(M)]
def dfs(x, y) :
    if x == M-1 and y == N-1 :
        return 1
    # 방문하지 않았을 경우
    # 즉, 계산을 아직 하지 않았을 경우 계산하기
    if visited[x][y] == -1 : 
        visited[x][y] = 0
        for i in range(4) :
            nx, ny = x + dx[i], y + dy[i]
            if 0 <= nx < M and 0 <= ny < N and graph[nx][ny] < graph[x][y] :
                visited[x][y] += dfs(nx, ny)
    return visited[x][y]
    
print(dfs(0, 0))

3. 풀어 볼 문제 : 1379번 -  강의실2


3. C언어

1. pointer

특정한 데이터가 저장된 (시작)주소값을 보관하는 변수
int a = 10;
int *p = &a;
  • (포인터에 주소값이 저장되는 데이터형)* (포인터 이름)
    • 시작 주소부터 주어진 데이터형 크기만큼을 읽어올 수 있도록 지정해준다.
  • & 연산자 : 피연산자의 주소값을 가져온다.
  • * 연산자 : 해당 주소값에 대응되는 데이터를 가져온다.
  • 즉, *p는 a와 같다.
  • p + 3 은 p의 주소값에 3*4 = 12 가 더해진다.
    • 4가 곱해서 더해지는 이유는 int형이 4byte이기 때문이다.  

  • const : 상수 포인터 = 값이 절대로 바뀌면 안된다.
const int *p = &a;
  • 포인터에 적용하면 포인터가 가리키는 값은 변하면 안된다.
  • 그러므로 p = &b; 는 오류 발생
  • 그러나 *p = 5; 는 가능
  • 즉, p가 가리키는 값은 변경하면 안된다 = 저장된 주소값이 바뀌면 안된다.
  • p 가 가리키는 주소값이 저장하고 있는 내용은 변경할 수 있다.

  • 배열
    • 배열의 이름 = 배열의 첫번째 주소값 / 그러나 배열의 이름이 배열의 포인터는 아니다!
#include <stdio.h>
int main() {
  int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  int *p;

  p = &arr[0];

  printf("arr[3] = %d , *(p + 3) = %d \n", arr[3], *(p + 3));
  return 0;
}
  • 배열의 첫번째 요소의 주소값을 가지는 포인터 p 로 배열의 원소에 접근할 수 있다.
    • *(p + 3) = arr[3] = 4
    • 위와 같이 포인터에 인덱스 만큼 더해주고 그 주소 안에 있는 값을 가져오면 똑같다.
  • 2차원 배열일 경우

  • int (*p)[2] = arr : 크기가 2인 배열을 가리키는 포인터
    • 포인터 배열 : 배열의 각 원소들이 모두 포인터인 배열 → 각 원소들이 다른 일차원 배열들을 가리킬 수 있다.
  • 이차원 배열에서 배열의 이름이 첫번째 행을 가리킨다. = a[0] → (a[0][0], a[0][1])
  • 위에서 포인터 p는 길이가 2인 intg형 배열을 가리키므로 p+1은 주소값에 8을 더해줘야 한다.

< 2차원 배열 포인터 참고 >

🔗 https://wonit.tistory.com/527


2. Struct (구조체)

각 멤버(member)의 타입이 제각각인 배열
즉, 여러 자료형의 변수들을 그룹으로 묶어서 하나의 자료형으로 선언하여 사용
#include <stdio.h>
struct Human {
  int age;    /* 나이 */
  int height; /* 키 */
  int weight; /* 몸무게 */
};  /* ; 붙이는 것 주의하세요 */

void main() {
    struct Human h;
    struct Human *hp = &h;
    
    h.age = 20;
    hp->height = 170;
    hp->weight = 50;
    
    printf("%d\n", hp->age);
    printf("%d\n", h.height);
    printf("%d\n", h.weight);
}
  • 구조체의 정의에서 변수를 초기화할 수 없다.
  • 점 연산자(.) : 구조체 변수의 데이터 항목을 지정
  • 화살표 연산자(->) : 구조체형 포인터가 가리키는 구조체 변수의 데이터 항목 지정

3. enum (열거형)

각 데이터에 수를 대응시킬 때 사용
enum { MALE, FEMALE }
  • 위와 같이 선언했을 경우 MALE = 0, FEMALE = 1로  나타낼 수 있다.
  • 원하는 숫자가 있으면 MALE=3 이라고 선언해주면 FEMALE = 4가 된다.

4. 동적 메모리 할당

컴퓨터 프로그래밍에서 실행 시간 동안 사용할 메모리 공간을 할당하는 것

1. malloc : memory allocation

    : 동적으로 메모리를 할당하는 함수로 힙영역에 메모리를 할당한다.

https://ko.wikipedia.org/wiki/동적_메모리_할당

  • 장점: 상황에 따라 원하는 크기만큼의 메모리가 할당되므로 경제적이며, 이미 할당된 메모리라도 언제든지 크기를 조절할 수 있다.
  • 단점: 더 이상 사용하지 않을 때 명시적으로 메모리를 해제해 주어야 한다.
score = (int *)malloc(n * sizeof(int));
/* int score[n]과 같이 만든 것이다. */
  • n개의 int 자료형의 크기만큼 메모리를 할당한다. 
  • <stdlib.h> 헤더 파일을 포함시켜줘야 한다.
  • malloc 함수 호출시 할당하고자 하는 메모리의 크기를 바이트 단위로 전달
  • return 할당한 메모리의 첫번째 바이트 주소
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv) {
    int student;
    int i, input;
    int *score;
    int sum = 0;

    //학생 수 입력받기
    printf("학생 수는? : ");
    scanf("%d", &student);

    //학생수만큼의 배열 생성하기
    score = (int *)malloc(student * sizeof(int));

    for (i = 0; i < student; i++) {
        printf("학생 %d 의 점수 : ", i);
        scanf("%d", &input);

        score[i] = input;
        sum += score[i];
    }
    printf("전체 학생의 평균 점수 : %d \n", sum / student);
    // 힙 영역에 할당된 메모리를 해제하는 함수
    free(score);
    return 0;
}

https://ko.wikipedia.org/wiki/동적_메모리_할당

< 동적 메모리 할당 참고 >

🔗 https://dsnight.tistory.com/51

🔗 https://ko.wikipedia.org/wiki/동적_메모리_할당


앞으로도 C언어 공부
https://modoocode.com/231
 

씹어먹는 C 언어 시작하기

 

modoocode.com

 

728x90