[!attention] 이 장의 목표
- 자유형식 익히기 — 현대 Fortran의 자유형식으로 코드를 쓰고, 고정형식은 읽기용으로만 학습한다.
- 프로그램의 골격 — 프로그램 단위와 문장이 어떻게 구성되는지 파악한다.
- 식별자 명명 — 변수, 상수, 프로그램 등에 명명 규칙을 적용한다.
- implicit none 의무 — 모든 단위에 implicit none을 두어 선언하지 않은 변수를 막는다.
1장에서 첫 프로그램을 작성해 컴파일·실행하고 그 결과를 파이썬으로 그려 보았다. 그러나 프로그램의 모양 자체를 들여다보면 몇 가지 의문이 생긴다.
.f90이고 어떤 코드는 .f인데 둘은 무엇이 다른가?이 장을 마치면 위 의문들이 풀리고, 이후 모든 장에서 쓸 기본 골격을 알 수 있다.
[!important] 2.1 소스 형식: 자유형식과 고정형식
Fortran 소스 코드를 텍스트 파일에 쓰는 방식에는 두 가지 형식(form) 이 있다.
자유형식(free form) — Fortran 90에서 도입된 현대적 형식. 문장을 어느 칸(column)에서 시작하든 상관없고, 들여쓰기가 자유롭다.
고정형식(fixed form) — FORTRAN 77 시절의 형식. 천공카드 시대의 흔적으로, 글자가 놓이는 칸 위치에 문법적 의미가 있다. 새 코드에는 쓰지 않으며, 옛 코드를 읽기 위해서만 알아 둔다.
gfortran은 파일 확장자로 소스 형식을 판별한다.
| 확장자 | 소스 형식 | 용도 |
|---|---|---|
.f90 .f95 .f03 .f08 |
자유형식 | 현대 코드(본문 전체) |
.f .for .ftn |
고정형식 | 레거시 코드(읽기용) |
자유형식의 핵심 규칙은 다음과 같다.
! 로 시작하며, 그 줄 끝까지 주석으로 인식된다.; 으로 구분한다.& 를 붙인다.아래는 레거시 FORTRAN 코드를 읽고 해석하기 위한 고정 형식(fixed form) 예제이다. 현대 Fortran과 달리, 고정 형식에서는 소스 코드의 칸(column) 위치가 곧 엄격한 문법 규칙이다.
1열: C, *, !가 오면 그 줄 전체를 주석으로 처리한다.
1~5열: 문장 라벨(번호)을 적는 자리다. GOTO 문이나 포맷 문 등에서 참조할 때 사용한다.
6열: 빈칸이 아닌 문자(예: &, $ 등)가 있으면, 그 줄은 앞 줄의 내용이 뒤로 이어지는 문장임을 뜻한다(줄잇기).
7~72열: 실제 Fortran 문장을 적는 영역이다. 73열부터는 컴파일러가 완전히 무시하므로 코드가 잘리지 않도록 주의해야 한다.
이해를 돕기 위해, 아래 코드를 입력하고 실행해 보자. 파일 확장자가 현대식인 .f90이 아니라 레거시 코드의 고정 형식을 뜻하는 .f임에 주목하자.
%%writefile fixed_demo.f
C234567890 (column ruler, not part of the code)
C Fixed-form example: read-only legacy style
program fixed_demo
implicit none
integer :: total
total = 21 +
& 21
print *, "total =", total
end program fixed_demo
컴파일·실행:
!gfortran -std=f2018 -Wall fixed_demo.f -o fixed_demo
!./fixed_demo
실행 결과:
total = 42
첫 번째 줄의 C로 시작하는 문장은 주석으로 처리되어 컴파일에 영향을 주지 않는다.
실제 레거시 FORTRAN 프로그램 명령들은 정확히 7열부터 시작하도록 공백이 띄워져 있다. 변수 total에 값을 더하는 과정을 보면, 첫 줄에 total = 21 +까지 적은 뒤 다음 줄 6열 위치에 & 문자를 집어넣었다. 이로 인해 컴파일러는 그다음 행의 21을 앞 줄과 연결된 하나의 문장(total = 21 + 21)으로 매끄럽게 해석하여 최종 결과인 42를 도출한다.
과거 수치 모델의 래거시 코드를 열었을 때 이처럼 매 줄 앞부분이 비어있고 숫자가 적혀있다면, 당황하지 말고 이 '칸수 규칙'을 떠올리며 읽어내려가면 된다.
[!warning] [흔한 실수] 소스 코드 확장자
.f90현대 Fortran 코드는 반드시.f90(또는.f95/.f03/.f08)으로 저장한다. 자유형식 코드를 실수로 레거시 확장자.f로 저장하면 고정형식으로 해석되어 엉뚱한 오류가 발생한다. 아래와 같은 오류 메시지가 "statement label"을 말하면 십중팔구 소스 형식을 잘못 지정한 것이다.Error: Non-numeric character in statement label at (1)
[!important] 2.2 프로그램 단위와 문장 구조
일반적으로 프로그램은 하나 이상의 프로그램 단위(program unit) 로 이루어진다.
주 프로그램(main program): 실행이 시작되는 지점이다. 한 실행 파일에 정확히 하나가 있어야 한다.
모듈(module): 자료형, 상수, 프로시저 등을 모아 두는 단위이다.
외부/내부 프로시저: 함수와 서브루틴을 정의한다.
주 프로그램은 크게 선언부와 실행부로 나뉘며, 반드시 선언부가 먼저 와야 한다.
선언부: 모듈 사용(use), implicit none, 변수·상수 선언이 포함된다. 실제 동작은 일어나지 않는다.
실행부: 대입, 계산, 입출력 등 실제 동작을 수행한다.
주 프로그램의 문법 규격은 아래와 같다.
program program_name
! 선언부
use module_name
implicit none
[declarations]
! 실행부
[statements]
[contains
internal_procedures]
end program program_name
use 문은 반드시 implicit none보다 먼저, 선언부의 최상단에 위치해야 한다.
end만 써도 문법적으로는 문제가 없으나, 가독성을 위해 항상 end program [이름] 형태로 닫는 것을 원칙으로 한다.
contains 이후의 내부 프로시저는 별도의 챕터에서 다룬다.
이 구조는 Fortran 프로그래밍의 가장 기본적인 뼈대이다. 코드를 작성할 때 이 순서를 엄격히 준수해야 한다. 참고로, end 만 써도 되지만, 이 책은 가독성을 위해 항상 end program 이름 형태로 닫는다. contains가 포함된 내부 프로시저는 10장에서 다룬다.
아래 예제는 하루가 총 몇 초인지 계산하는 단순한 프로그램이다. 이 코드를 통해 Fortran 프로그램의 뼈대를 이루는 두 축인 '선언부'와 '실행부'의 구조를 파악할 수 있다.
%%writefile seconds_per_day.f90
program seconds_per_day
implicit none
integer :: hours, minutes, seconds, total
hours = 24
minutes = 60
seconds = 60
total = hours * minutes * seconds
print *, "seconds in a day =", total
end program seconds_per_day
컴파일· 실행:
!gfortran -O2 -std=f2008 -Wall seconds_per_day.f90 -o seconds_per_day
!./seconds_per_day
실행 결과:
seconds in a day = 86400
Fortran에서는 선언부와 실행부의 순서가 엄격하게 정해져 있어, 선언부가 모두 끝난 후에만 실행부 코드를 적을 수 있다. 예제처럼 선언부와 실행부 사이에 빈 줄을 한 칸 두어 물리적으로 분리해주면, 코드의 전체적인 구조가 한눈에 들어오는 좋은 코딩 습관을 기를 수 있다.
현대 Fortran은 한 줄에 하나의 명령 문장을 적는 것이 기본 원칙이다. 하지만 필요에 따라 한 줄에 여러 문장을 작성하거나, 반대로 하나의 문장이 너무 길어 여러 줄로 나누어 적어야 할 때가 있다. 아래 예제를 통해 세미콜론(;)과 줄잇기 문자(&)의 활용법을 알아보자.
%%writefile statement_demo.f90
program statement_demo
implicit none
integer :: a, b, c
a = 1; b = 2; c = 3 ! one line, three statements
print *, "sum =", a + b + & ! continue on the next line
c
end program statement_demo
컴파일·실행:
!gfortran -O2 -std=f2018 -Wall statement_demo.f90 -o statement_demo
!./statement_demo
실행 결과:
sum = 6
세미콜론 (;): a = 1; b = 2; c = 3과 같이 문장과 문장 사이에 세미콜론을 넣으면 한 줄에 여러 개의 독립된 명령을 연속해서 밀어 넣을 수 있다. 하지만 한 줄에 너무 많은 문장을 욱여넣으면 가독성이 떨어져 코드를 읽고 해석하기 어려워지므로 권장하지 않는다.
줄잇기 문자 (&): 문장 맨 끝에 & 기호를 붙이면, 컴파일러는 그다음 줄의 내용을 앞선 문장의 연속으로 인식한다. 예제에서는 a + b + 뒤에 &를 두어 다음 줄의 c와 연결되도록 처리했다.
[!warning] [흔한 실수] 줄 잇기 문자
&자유형식에서 한 문장을 다음 줄로 이으려면 끊는 줄의 끝에
&가 있어야 한다.&없이 식을 다음 줄로 넘기면 식이 잘려Syntax error in expression오류가 난다.또한 예제처럼
&기호 뒤에 한 칸을 띄우고! 다음 줄로...와 같이 꼬리 주석을 붙여도 줄잇기 기능은 정상적으로 유지된다. 컴파일러가 주석을 먼저 걷어낸 후&기호를 해석하기 때문이다.
[!important] 2.3 주석·식별자와 변수 이름 규칙
아래 처럼 자유형식에서 주석(comment)은 ! 부터 줄 끝까지이며, 컴파일러가 무시한다.
! This whole line is a comment.
total = a + b ! a trailing comment after code
[!알아두기] 주석의 중요성
코드에서 주석은 단순히 설명을 덧붙이는 것이 아니라, 시간이 흐른 뒤의 자신과 동료 개발자를 위한 이정표와 같다. 주석의 중요성을 간단히 요약하면 다음과 같다.
- 의도 전달: 코드가 '무엇(what)'을 하는지는 코드 자체가 말해주지만, '왜(why)' 그렇게 작성했는지는 주석만이 설명할 수 있다.
- 유지보수 비용 절감: 복잡한 알고리즘이나 특이한 예외 처리 상황을 주석으로 남겨두면, 나중에 코드를 수정할 때 분석 시간을 획기적으로 줄여준다.
- 협업의 효율화: 동료가 내 코드를 읽을 때 주석은 훌륭한 가이드가 되어, 불필요한 질문과 시행착오를 방지한다.
좋은 코드는 주석이 필요 없을 만큼 명확해야 하지만, 복잡한 비즈니스 로직이나 의사결정의 배경을 남기는 주석은 필수적이다. 소스 코드는 기계를 위한 것이지만, 주석은 사람을 위한 것이다.
식별자(identifier) 는 변수, 상수, 프로그램, 프로시저 등 프로그래밍 요소에 붙이는 고유한 이름이다. Fortran에서 식별자를 정의할 때는 다음 규칙을 준수해야 한다.
_)만 사용할 수 있다. -, +, ., 한글 등)는 사용할 수 없다. count, Count, COUNT는 모두 동일한 식별자이다.Fortran의 키워드(예: program, integer, if 등)는 언어 자체적으로는 예약어(reserved word)가 아니라서, 문법적으로는 integer :: if와 같이 키워드와 동일한 이름의 변수를 선언할 수 있다. 하지만, 이는 코드의 가독성을 크게 해치고 예기치 못한 혼란을 야기하므로, 키워드와 같은 이름은 식별자로 절대 사용하지 않는다. 이 규칙은 코드가 컴파일러에 의해 명확하게 해석되도록 돕는 기본적인 약속이다.
[!warning] [흔한 실수] 식별자에 붙임표·공백·한글 사용
wave-speed는wave빼기speed로 해석되고,n steps는 토큰 두 개로 쪼개진다. 식별자는 영문자로 시작해 영문자·숫자·밑줄만 쓰고, 단어 구분은 밑줄(wave_speed)로 한다.
Fortran은 대소문자를 엄격하게 구분하는(case-sensitive) C나 파이썬과 달리, 대소문자를 완전히 동일하게 취급하는 전통을 가지고 있다. 아래 예제를 통해 대소문자를 섞어 썼을 때 컴퓨터가 어떻게 인식하는지 결과를 살펴보자.
%%writefile case_demo.f90
program case_demo
implicit none
integer :: count
count = 10
COUNT = COUNT + 5 ! 'count' and 'COUNT' are the same name
print *, "count =", count
end program case_demo
컴파일·실행:
!gfortran -O2 -std=f2008 -Wall case_demo.f90 -o case_demo
!./case_demo
실행 결과:
count = 15
소문자로 선언한 count 변수에 $10$을 대입한 후, 다음 줄에서 대문자로 COUNT = COUNT + 5라는 연산을 수행했다. 다른 대다수의 현대 언어라면 두 단어를 완전히 다른 변수로 인식하여 에러를 출력했겠지만, Fortran 컴파일러는 count와 COUNT를 단 하나의 동일한 변수 방으로 안내한다. 그 결과 변수 안의 값이 누적되어 최종적으로 15라는 결과가 출력된다.
[!note] 일관된 대소문자 사용 대소문자를 구분하지 않는 Fortran의 특성은 편해 보일 수 있지만, 코드를 복잡하게 짤 때 대소문자를 섞어 쓰면 가독성이 극도로 떨어지며 뜻하지 않은 혼란을 야기할 수 있다. 따라서 코드를 읽고 해석하기 좋게 유지하려면 하나의 이름은 처음부터 끝까지 하나의 표기법으로 일관되게 작성하는 습관이 중요하다.
[!important] 2.4 implicit none과 명시적 선언
Fortran에는 역사적으로 암묵적 형 지정(implicit typing) 규칙이 있다. 선언하지 않은 변수를 쓰면 이름의 첫 글자로 자료형이 자동 결정된다.
integerreal이 암묵적 형 지정 규칙은 타이핑을 줄여 주지만 치명적 함정을 만드는데, 변수 이름에 오타를 내면 컴파일러가 오류를 내는 대신 새 변수를 조용히 만든다. 그 결과 프로그램은 정상적으로 실행되며 틀린 답을 낸다. 따라서, implicit none 명령으로 암묵적 형 지정 규칙을 끄는 것이 절대적으로 유리하다. 따라서 모든 프로그램·모듈·프로시저에 예외 없이 implicit none 을 둔다.
컴퓨터 프로그래밍을 배울 때 가장 먼저 작성해 보아야 할 코드는 문법적으로 아무런 결함이 없는 완벽한 샘플 프로그램이다. 아래 예제는 화씨 $100$도를 섭씨 온도로 올바르게 변환하는 프로그램이다. 수학적인 변환 공식에 따른 계산 결과는 약 37.78°C다.
%%writefile f2c.f90
! Convert Fahrenheit to Celsius
program f2c
implicit none
real :: fahrenheit, celsius
fahrenheit = 100.0
celsius = (fahrenheit - 32.0) * 5.0 / 9.0
print *, "celsius =", celsius
end program f2c
컴파일·실행:
!gfortran -O2 -std=f2018 -Wall f2c.f90 -o f2c
!./f2c
실행 결과:
celsius = 37.7777786
위 프로그램은 실수형 변수 fahrenheit와 celsius를 안전하게 선언하는 것으로 시작한다. 이어서 화씨 변수에 100.0을 대입한 뒤, 섭씨 온도를 구하는 수학 공식인 (fahrenheit - 32.0) * 5.0 / 9.0을 연산하여 결과를 출력한다.
컴퓨터의 부동소수점 정밀도 한계로 인해 뒷자리가 37.7777786과 같이 표현되지만, 우리가 원하는 정답인 약 37.78°C가 정확하게 계산되었음을 알 수 있다.
이 코드의 뼈대에서 가장 주목해야 할 핵심 문장은 선언부 맨 위에 위치한 implicit none이다. 이 한 줄이 향후 수많은 대형 데이터 처리 과정에서 발생할 수 있는 치명적인 오타와 버그들을 어떻게 사전에 차단하고 프로그램을 보호해 준다.
[!important] 오류 학습
좋은 오류 메시지는 훌륭한 선생이다. 프로그래밍을 배울 때 완벽한 코드를 보는 것만큼이나 중요한 것이 바로 일부러 실수를 만들어 컴파일러의 경고를 해석하는 훈련이다. 입문자가 가장 자주 저지르는 치명적인 실수인 '변수 이름 오타'를 통해, Fortran이 이를 어떻게 잡아내는지 살펴보자.
implicit none이 없는 위험한 코드아래 예제는 앞서 다룬 온도 변환식에서 fahrenheit를 fahreheit(n이 빠짐)로 잘못 기입하고, 선언부와 implicit none을 통째로 생략한 코드다.
%%writefile no_implicit_bug.f90
! Goal: convert Fahrenheit to Celsius (100 F -> 37.78 C)
program no_implicit_bug
fahrenheit = 100.0
celsius = (fahreheit - 32.0) * 5.0 / 9.0 ! typo: fahreheit
print *, "celsius =", celsius
end program no_implicit_bug
컴파일을 수행하면, 에러가 아닌 경고(Warning)을 아래와 같이 출력한다.
!gfortran -O2 -std=f2018 -Wall no_implicit_bug.f90 -o no_implicit_bug
no_implicit_bug.f90:5:63:
5 | celsius = (fahreheit - 32.0) * 5.0 / 9.0 ! typo: fahreheit
| ^
Warning: ‘fahreheit’ is used uninitialized [-Wuninitialized]
실행:
!./no_implicit_bug
실행 결과:
celsius = -17.7777786
경고에도 불구하고 실행하면 프로그램을 구동하지만, 결과는 우리가 예상한 약 37.78°C가 아니라 전혀 뚱딴지같은 -17.78°C가 출력된다.
과거 레거시 Fortran은 선언하지 않은 변수가 나타나면 이름의 첫 글자가 i~n이면 정수, 그 외의 알파벳이면 실수 변수로 자동 선언해 버리는 암묵적 규칙(implicit 규칙)이 있었다. 즉, 오타가 난 fahreheit를 컴퓨터가 '값이 0.0인 새로운 실수 변수'로 마음대로 만들어 버린 것이다. 결과적으로 (0.0 - 32.0) * 5.0 / 9.0이 계산되어 엉터리 값이 나왔다. 수천 줄짜리 연구용 코드에서 이런 일이 벌어지면 버그를 찾느라 몇 날 며칠을 허비하게 된다.
이 조용한 런타임 버그를 해결하는 유일한 열쇠가 바로 implicit none 문장이다. 프로그램 시작점에 이 문장을 선언하면, 컴파일러에게 "내가 명시적으로 선언하지 않은 모든 변수는 불법이니 에러를 뱉어라"라고 엄격하게 명령하게 된다.
아래와 같이 코드를 수정하여 implicit none과 올바른 변수 선언부를 추가해 보자. 여전히 계산식에는 오타를 남겨둔 상태다.
program implicit_fixed
implicit none ! implicit none 삽입
real :: fahrenheit, celsius
fahrenheit = 100.0
celsius = (fahreheit - 32.0) * 5.0 / 9.0 ! 오타 유지 print *, "celsius =", celsius
end program implicit_fixed
컴파일·실행:
!gfortran -O2 -std=f2018 -Wall implicit_fixed.f90 -o implicit_fixed
컴파일러가 프로그램을 실행하기 전에 번역(컴파일)을 거부한다. 오류 메시지를 읽어보면 정확히 몇 번째 줄(7:15)에서 문제가 발생했는지 아래와 같이 안내한다.
implicit_fixed.f90:7:15:
7 | celsius = (fahreheit - 32.0) * 5.0 / 9.0
| 1
Error: Symbol 'fahreheit' at (1) has no IMPLICIT type; did you mean 'fahrenheit'?
컴파일러는 오타가 난 단어와 가장 유사하게 생긴 진짜 변수 이름까지 유추하여 우리에게 제안을 건넨다. implicit none 한 줄 덕분에 알아채기 힘든 조용한 논리 버그가 눈에 확 띄는 요란한 컴파일 오류로 바뀌었고, 개발자는 그 즉시 오타를 수정하여 대형 사고를 막을 수 있게 된다. 이것이 현대 Fortran에서 implicit none을 단 한 줄도 빼놓지 않고 작성해야 하는 이유이다.
[!warning] [흔한 실수]
implicit none을 빠뜨림
implicit none이 없으면 오타가 새 변수가 되어 조용히 틀린 답을 만든다.implicit none의 위치는 반드시 모든 프로그램과 뒤에서 배울 모듈·프로시저의 선언부 맨 앞,use문 다음이고 변수 선언문 앞이다. 이후 모든 변수를 반드시 명시적으로 선언해야 하며, 오타가 있으면 그 변수 자리에 컴파일 오류가 난다.
프로그래밍을 하다 보면 코드를 내려쓰는 중간에 새로운 변수가 필요해져서, 실행문 한가운데에 슬그머니 선언문을 끼워 넣는 실수를 자주 하게 된다. Fortran에서 이 순서를 어기면 컴파일러가 어떻게 반응하는지 확인해 보자.
%%writefile decl_after_exec.f90
program decl_after_exec
implicit none
integer :: a
a = 10
integer :: b ! declaration after an executable statement
b = 20
print *, a + b
end program decl_after_exec
컴파일을 수행하면, 다음 오류 메시지를 출력한다.
!gfortran -O2 -std=f2018 -Wall decl_after_exec.f90 -o decl_after_exec
decl_after_exec.f90:5:66:
5 | integer :: b ! declaration after an executable statement
| 1
Error: Unexpected data declaration statement at (1)
decl_after_exec.f90:6:4:
6 | b = 20
| 1
Error: Symbol ‘b’ at (1) has no IMPLICIT type
Fortran은 모든 변수 선언(선언부)은 실제 연산을 수행하는 명령(실행부)보다 물리적으로 무조건 앞서 위치해야 한다는 규칙을 가지고 있다. 컴퓨터가 변수 공간의 크기와 개수를 완벽히 파악한 뒤에야 비로소 연산을 시작할 수 있기 때문이다.
따라서 이 문제를 해결하려면 도중에 튀어나온 integer :: b 선언문을 아래와 같이 프로그램의 선언부로 이동시켜야 한다. 기존에 있던 integer :: a와 합쳐서 integer :: a, b 처럼 한 줄에 쉼표로 나란히 선언해주면 코드가 한결 깔끔해지고 컴파일 오류도 깨끗하게 해결된다.
implicit none
integer :: a, b
a = 10
b = 20
[!note] 오류 메세지 읽기
위 사례들의 공통 교훈은 "컴퓨터의 메시지를 끝까지 읽는다"이다. gfortran은 줄 번호와
1표시로 문제 위치를 정확히 가리키고, 종종 올바른 철자나 원인까지 제안한다. 오류가 났을 때 첫 번째 메시지의 위치부터 확인하는 습관이 디버깅 시간을 크게 줄인다.
이 장에서 배운 기본 골격(program, implicit none, 선언부, 실행부)만으로, 입력값 $x$가 3차 다항식 $y = x^3 - 6x^2 + 9x + 1$을 거쳐 출력값 $y$로 변환되는 흐름을 직접 계산해 보자.
곡선이 매끄럽게 표현되도록 $x$를 $0.05$라는 아주 촘촘한 간격으로 설정하여 연산한다.
계산한 결과를 flow.csv 파일로 저장하기 위해 open, write, close 명령을 사용한다. 이 명령어들의 자세한 사용법은 6장에서, do 반복문은 5장에서 정식으로 다룬다. 여기서는 데이터를 외부 파일로 내보내는 표준적인 레시피로 이해하고 예제를 따라 해보자. 참고로, open 문 내 newunit 은 flow.csv 파일을 저장하기 위해 자동으로 지정하는 장치번호이다.
%%writefile io_flow.f90
! Map each input x to a cubic output y = x^3 - 6x^2 + 9x + 1.
! A fine grid of x values makes the curve's hump and dip visible.
program io_flow
implicit none
integer, parameter :: n = 110
integer :: i, u
real :: x, y
open(newunit=u, file="flow.csv", status="replace", action="write")
write(u, '(A)') "x,y"
do i = 0, n
x = -0.5 + real(i) * 0.05
y = x**3 - 6.0 * x**2 + 9.0 * x + 1.0
write(u, '(F8.3, ",", F10.4)') x, y
end do
close(u)
print *, "flow.csv written"
end program io_flow
x3은 $x$의 세제곱($x^3$)을 뜻하며, 6.0 * x2는 $6.0 \times x^2$을 의미한다.
Fortran에서는 거듭제곱 연산자()가 곱셈 연산자(*)보다 우선순위가 높아 먼저 계산되므로, 복잡한 괄호를 많이 쓰지 않아도 식의 수학적 표기가 직관적으로 거의 같게 읽히는 장점이 있다(연산자 우선순위의 상세한 규칙은 4장에서 다룬다).
컴파일·실행:
!gfortran -O2 -std=f2008 -Wall io_flow.f90 -o io_flow
!./io_flow
실행 결과:
flow.csv written
실행 후 !head flow.csv 명령으로 저장된 파일의 내부 값을 들여다보면 다음과 같이 $x$와 $y$의 변환 흐름이 기록되어 있음을 확인할 수 있다.
x,y
0.000, 1.0000
1.000, 5.0000 ← 봉우리(극대)
3.000, 1.0000 ← 골(극소)
5.000, 21.0000
이제 파일로 저장된 데이터를 파이썬으로 읽어와 시각화해 보자.
import csv
import matplotlib.pyplot as plt
xs, ys = [], []
with open("flow.csv") as f:
reader = csv.reader(f)
next(reader) # skip the header row "x,y"
for row in reader:
xs.append(float(row[0]))
ys.append(float(row[1]))
plt.figure(figsize=(8, 4))
plt.plot(xs, ys, color="crimson", linewidth=2,
label="y = x^3 - 6x^2 + 9x + 1")
plt.fill_between(xs, ys, 0, color="crimson", alpha=0.08)
plt.axhline(0, color="gray", linewidth=0.8)
plt.xlabel("input x")
plt.ylabel("output y")
plt.title("Fortran computes a cubic, Python visualizes")
plt.legend()
plt.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig("flow.png", dpi=120)
plt.show()
코드를 실행하면 아래와 같이 3차 함수 곡선이 화면에 나타난다.
그래프를 살펴보면 $x = 1$ 부근에서 서서히 솟아올라 봉우리($y \approx 5$)에 도달했다가, $x = 3$ 부근에서 골($y \approx 1$)을 향해 매끄럽게 내려온 뒤, 다시 가파르게 치솟는 3차 함수 특유의 형태를 명확하게 확인할 수 있다.
![[Pasted image 20260627200325.png]]
[!note] 컴파일과 빌드 컴파일은 건물의 설계도를 보고 부품(벽돌, 철근 등)을 하나하나 만드는 과정이고, 빌드는 그 부품들을 모두 조립하여 최종적으로 사람이 살 수 있는 완벽한 건물을 완성하는 전체 과정을 뜻한다. 컴파일은 빌드라는 거대한 작업에 포함되는 하나의 핵심 단계다.
1. 컴파일 (Compile)
컴파일은 프로그래머가 작성한 소스 코드를 컴퓨터 하드웨어(CPU)가 실행할 수 있는 기계어(0과 1)로 번역하는 단일 작업을 의미한다. 예를 들면,
hello.f90이라는 텍스트 파일을gfortran컴파일러를 통해 컴퓨터가 이해할 수 있는 목적 파일(object file, 보통.o또는.obj확장자)로 변환하는 순수한 번역 과정이다.2. 빌드 (Build) 빌드는 소스 코드가 최종적으로 사용자가 실행할 수 있는 완성된 프로그램(
hello.exe또는hello)으로 변환되기까지의 모든 과정을 통칭한다. 빌드는 다음 단계들이 차례대로 진행된다.
- 전처리 (Preprocessing): 코드 안의 매크로나 설정을 먼저 정리한다.
- 컴파일 (Compile): 코드를 기계어로 번역하여 부품(목적 파일)들을 만든다.
- 링크 (Link): 번역된 부품들과 외부 수학 라이브러리(BLAS, LAPACK 등)를 하나로 엮고 조립하여 최종 실행 파일을 만든다.
일상적인 대화나 간단한 예제 코딩을 할 때는 컴파일러 명령 한 줄로 최종 실행 파일까지 한 번에 나오기 때문에 "컴파일한다"와 "빌드한다"를 섞어 쓰기도 한다. 하지만 수십, 수백 개의 소스 파일과 라이브러리가 얽혀 있는 대규모 과학 계산 프로그램에서는, 각 파일을 번역하는 컴파일 작업과 이들을 총망라하여 하나의 거대한 시스템으로 묶어내는 빌드 작업을 명확히 구분하여 사용한다.
[!important] 요약
.f90. 고정형식 .f는 읽기용. gfortran은 확장자로 형식을 판별한다.program 이름 → (선언부: use, implicit none, 선언) → (실행부) → end program 이름. 선언부 먼저, 실행부 나중.;, 줄잇기는 줄 끝의 &. 주석은 !. 거듭제곱은 **.implicit none: 모든 프로그램·모듈·프로시저에 예외 없이. 오타로 인한 조용한 버그를 컴파일 오류로 바꾼다.[!important] 연습 문제
program hello … end program hello 골격에 implicit none을 넣고, 자신의 이름을 출력하는 완전한 프로그램을 작성하라.Speed, 2nd_run, time_step, delta-t, n value, temp_1.width, height에 각각 7과 5를 대입하고 넓이(width*height)를 출력하는 프로그램을 작성하라. 선언부와 실행부 사이에 빈 줄을 넣어 구조를 드러내라.print 문 하나를 &로 두 줄에 걸쳐 작성하라.다음 고정형식 코드가 출력하는 값을 (실행하지 말고) 손으로 예측하라. 그런 다음 같은 동작을 하는 자유형식 .f90 프로그램으로 다시 작성하라.
fortran
C fixed-form snippet program legacy implicit none integer :: k k = 100 - & 58 print *, k end program legacy
아래 한 줄에서 컴파일러가 실제로 처리하는 문장 부분만 그대로 옮겨 적어라.
fortran
area = base * height / 2 ! triangle area; integer division here
섭씨 온도 c = 37.0을 화씨로 바꿔(f = c * 9.0 / 5.0 + 32.0) 출력하는 프로그램을 작성하라. 9 / 5로 적으면 결과가 어떻게 달라지는지 함께 확인하고 이유를 적어라.
implicit none을 일부러 빼고 velocty처럼 오타가 든 프로그램을 만들어 컴파일·실행해 보라. -Wall 경고 메시지를 적고, implicit none을 넣었을 때 나오는 오류 메시지와 비교해 차이를 두 문장으로 설명하라.mod를 사용하라.pi를 parameter로 선언한 뒤 프로그램 안에서 pi = 3.0처럼 값을 다시 대입해 보라. 어떤 컴파일러 메시지가 나오는가? 왜 그런지 설명하라.