1. string 타입 배열
간단하게 string 타입 배열에 대해 얘기해볼게.
이미 얘기했듯이, 배열은 아무 타입이나 될 수 있어. 그러니까 string 타입 배열도 만들 수 있지. 만약 "키보드로 10개의 문자열을 입력받아서, 그걸 역순으로 출력하는" 프로그램을 짜야 한다면 코드는 이렇게 생겼을 거야.
string[] array = new string[10]; // 10개짜리 배열 객체 만들기
for (int i = 0; i < 10; i++) // 0부터 9까지 루프
{
array[i] = Console.ReadLine(); // 키보드에서 문자열 읽어서 배열 칸에 저장
}
for (int i = 9; i >= 0; i--) // 9부터 0까지 루프
{
Console.WriteLine(array[i]); // 배열의 해당 칸을 화면에 출력
}
코드는 거의 안 바뀌었지! 배열 만들 때 int 대신 string만 바꿔주면 돼. 나머지는 완전 똑같아!
2. 메모리에서 string 타입 배열
그리고 또 하나 유용한 사실! 그림 3개로 설명해볼게:
그림 1. string 객체가 메모리에 어떻게 저장되는지:
문자열의 텍스트는 변수에 바로 저장되는 게 아니라, 따로 메모리 블록이 할당돼. string 타입 변수에는 그 텍스트 객체의 주소(참조)가 저장되는 거야.
그림 2. 정수 배열이 메모리에 어떻게 저장되는지:
이 그림도 이미 봤을 거야.
그림 3. 문자열 배열이 메모리에 어떻게 저장되는지:
왼쪽엔 string[] 타입 배열 변수(배열 객체의 주소 저장).
가운데는 string 타입 배열 객체.
오른쪽엔 각각의 문자열 객체(텍스트 저장).
string 타입 배열 객체의 칸에는 실제 문자열(텍스트)이 아니라 그 주소(참조)가 들어가. string 타입 변수에 문자열 주소가 저장되는 거랑 똑같아.
3. C#에서 배열 빠르게 초기화하기
배열은 진짜 유용해서, C# 개발자들이 최대한 편하게 쓸 수 있게 해줬어. 그중 첫 번째가 배열 초기화 간소화, 즉 시작값을 쉽게 넣는 거야.
프로그램이 외부에서 읽어오는 데이터 말고도, 내부적으로 써야 하는 데이터가 필요할 때가 많지. 예를 들어, 각 달의 일수를 배열에 저장해야 한다면 코드는 이렇게 될 수 있어:
int[] months = new int[12];
months[0] = 31; // 1월
months[1] = 28; // 2월
months[2] = 31; // 3월
months[3] = 30; // 4월
months[4] = 31; // 5월
months[5] = 30; // 6월
months[6] = 31; // 7월
months[7] = 31; // 8월
months[8] = 30; // 9월
months[9] = 31; // 10월
months[10] = 30; // 11월
months[11] = 31; // 12월
근데 C# 개발자들 덕분에 더 짧게 쓸 수 있어:
// 1년 12달의 일수
int[] months = new int[] { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
그냥 배열 값들을 콤마로 쭉 나열하면 끝!
편하지? 근데 이게 끝이 아니야.
컴파일러가 배열 변수 타입을 보고, 배열 객체 타입도 알아서 맞춰줘. 배열 길이도 중괄호 안에 쓴 값 개수로 자동 계산해줘.
그래서 더 짧게 이렇게도 가능해:
// 1년 12달의 일수
int[] months = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
진짜 깔끔하지? 🙂
이런 걸 "배열 빠른 초기화"라고 해. 참고로 int만 되는 게 아니야...
// 1년 12달의 이름
string[] months = { "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"};
4. foreach 루프
배열의 모든 요소를 순회하는 게 워낙 자주 쓰여서, 아예 전용 루프가 있어(!). 그게 바로 foreach야. 이렇게 써:
foreach (int score in scores)
{
Console.WriteLine($"점수: {score}");
}
여기선 루프가 알아서 요소를 순회해 ― 인덱스 직접 안 써도 돼. 요소를 직접 바꿀 수는 없어 (루프 안에선 읽기만 가능), 대신 배열을 쭉 훑을 땐 진짜 편해.
컴파일러가 foreach 코드를 아래처럼 바꿔줘:
for (int i = 0; i < scores.Length; i++)
{
int score = scores[i];
Console.WriteLine($"점수: {score}");
}
그러니까 마법은 아니고, 왜 score 변수로 배열 요소를 바꿀 수 없는지 이제 이해됐지.
예제 하나 더! 모든 점수의 합을 구해보자.
int sum = 0;
foreach (int score in scores)
{
sum += score;
}
Console.WriteLine($"모든 점수의 합: {sum}");
언제 어떤 루프를 써야 할까?
- 인덱스가 필요하거나 요소를 바꿔야 하면 for 루프.
- 그냥 값만 쭉 훑으면 foreach가 더 빠르고 깔끔해.
5. 끝에서 인덱싱: ^ 연산자
요즘 C#에선 배열의 끝에서부터 요소를 쉽게 가져올 수 있어. 예전엔 마지막 요소가 필요하면 이렇게 썼지:
int lastScore = scores[scores.Length - 1];
이제는 훨씬 간단해. 인덱스 앞에 ^만 붙이면 끝에서부터 세는 거야:
int lastScore = scores[^1]; // 마지막 요소
int penultimate = scores[^2]; // 끝에서 두 번째 요소
- ^1 — 끝에서 첫 번째(마지막) 요소.
- ^2 — 끝에서 두 번째.
시각화
| 인덱스 | scores 배열 값 | 일반 인덱스 | 끝에서 인덱스 |
|---|---|---|---|
| 0 | 10 | 0 | ^5 |
| 1 | 15 | 1 | ^4 |
| 2 | 8 | 2 | ^3 |
| 3 | 22 | 3 | ^2 |
| 4 | 17 | 4 | ^1 |
score[^1] = 17, score[^2] = 22 ― 쉽고 직관적이지.
중요! 끝에서 인덱싱할 땐 1부터 시작(0이 아님).
6. 범위: .. (range) 연산자
가끔은 원본 배열에서 부분 배열이 필요할 때가 있어. C#에선 .. 범위 연산자를 써서, 원하는 "조각"을 아주 간단하게 뽑아낼 수 있어.
이런 부분 배열을 슬라이스(slice)라고도 하는데, 시작과 끝 두 개의 경계로 정해져.
중요한 점: 오른쪽 경계는 결과에 포함되지 않아!
어떻게 생겼냐면
int[] scores = { 10, 15, 8, 22, 17 };
int[] top3 = scores[0..3]; // 0~2번 요소: {10, 15, 8}
- scores[0..3] — 인덱스 0부터(포함) 인덱스 3까지(불포함).
- 결과 배열엔 0, 1, 2번 요소가 들어가.
축약형
왼쪽 경계 생략하면 처음부터 가져와:
int[] firstTwo = scores[..2]; // scores[0..2]랑 같음, 즉 {10, 15}
오른쪽 경계 생략하면 끝까지 가져와:
int[] fromThird = scores[2..]; // {8, 22, 17}
음수 인덱스
^를 써서 끝에서부터 인덱싱할 수 있어:
int[] lastTwo = scores[^2..]; // {22, 17}
여기서 ^2는 "끝에서 두 번째 요소"야.
양쪽 다 음수 인덱스 써서 "어디서부터 어디까지"도 가능:
int[] mid = scores[1..^1]; // {15, 8, 22} (2번째부터 끝에서 두 번째 전까지)
꼭 기억할 것
- 오른쪽 끝은 항상 불포함 ― a..b면 a부터 b-1까지야.
- 배열 범위를 벗어난 경계를 쓰면 IndexOutOfRangeException 예외가 나와.
- 슬라이스는 새 배열이야, 원본은 안 바뀜. 새 배열을 바꿔도 원본엔 영향 없어.
실전 예제
int[] arr = { 1, 2, 3, 4, 5, 6, 7 };
int[] arr = { 1, 2, 3, 4, 5, 6, 7 };
int[] start = arr[..3]; // {1, 2, 3}
int[] end = arr[^2..]; // {6, 7}
int[] middle = arr[2..5]; // {3, 4, 5}
int[] all = arr[..]; // {1, 2, 3, 4, 5, 6, 7} (전체 복사)
GO TO FULL VERSION