HOWTO · C

C의 for-each 루프

다음 콘텐츠에서는 컴퓨터 언어 C가 foreach 루프를 지원하는지 여부를 조사합니다.

이 페이지의 내용

C에는 컬렉션의 모든 항목을 자동으로 방문하는 내장 for-each 루프가 없습니다. 일반 for 또는 while 루프를 사용하면 비슷한 방식으로 순회할 수 있지만, 컬렉션이 어디에서 끝나는지 코드가 알아야 합니다. 배열은 요소 개수를 기록하고, 연결 리스트는 각 노드의 next 포인터를 따라가다가 null에 도달하면 멈춥니다.

C에는 내장 foreach 루프가 있나요?

아니요. C 언어에는 Python이나 JavaScript처럼 전용 foreach 구문이 없습니다. 그래도 컬렉션과 범위가 분명하다면 for-each 방식의 루프를 쉽게 작성할 수 있습니다. 배열은 인덱스를 사용하는 루프가 흔하고, 연결 리스트는 포인터를 다음 노드로 이동시키며 순회합니다.

여러 고급 언어와 달리 C에서는 배열 포인터에 길이가 연결되어 있지 않습니다. 함수에서 배열을 처리해야 한다면 포인터와 함께 길이도 전달하세요. 그러면 종료 조건이 명확해지고 컬렉션에 속한 요소의 범위를 벗어나 읽는 일을 피할 수 있습니다.

for 루프로 배열 순회하기

같은 범위에서 선언한 배열이라면 sizeof array / sizeof array[0]으로 요소 개수를 계산할 수 있습니다. 그런 다음 인덱스를 0부터 시작해 그 개수에 도달하기 전에 반복하세요.

다음 독립 실행 프로그램은 각 요소를 순서대로 출력합니다. 여기서는 main 안에서 numbers가 여전히 배열이므로 길이 계산식이 유효합니다.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

#include <stddef.h>
#include <stdio.h>

int main(void) {
    int numbers[] = {10, 20, 30, 40, 50};
    size_t length = sizeof numbers / sizeof numbers[0];

    for (size_t i = 0; i < length; ++i) {
        printf("%d\n", numbers[i]);
    }

    return 0;
}
10
20
30
40
50

함수에 배열을 전달하면 매개변수는 첫 번째 요소를 가리키는 포인터가 되며 배열 크기는 함께 전달되지 않습니다. 따라서 그 매개변수에 sizeof를 적용하면 원래 배열의 바이트 크기가 아니라 포인터의 크기를 얻습니다. size_t length처럼 요소 개수를 별도로 전달하고 i < length인 동안 반복하세요. 호출하는 쪽에서 배열을 전달하기 전에 개수를 계산할 수 있습니다. 동적으로 할당한 배열이라면 할당 시점이나 이를 소유한 자료 구조에서 얻은 개수를 보관하세요.

while 루프를 사용하거나 연결 리스트 순회하기

조건식으로 다음 항목이 있는지를 자연스럽게 표현할 수 있다면 while 루프도 잘 작동합니다. 배열에서는 인덱스를 0으로 초기화하고, 저장한 길이보다 작은 동안 반복하며, 매 반복마다 인덱스를 하나씩 증가시키세요. for 루프와 마찬가지로 올바른 범위를 지정해야 합니다.

연결 리스트의 종료 조건은 다릅니다. 현재 노드 포인터가 null이 아닌 동안 노드를 처리한 다음 current->next로 이동합니다. 예를 들어 노드는 struct Node { int value; struct Node *next; };로 선언할 수 있습니다. current는 노드를 가리키는 포인터이므로 필드에 접근할 때 ->를 사용합니다. current가 NULL이 되면 리스트의 끝입니다. 연결 리스트의 노드는 포인터로 이어진 별개의 객체이고 배열 길이가 없으므로 배열의 sizeof 계산식을 사용하지 마세요. 리스트의 노드 수가 필요하다면 직접 순회하거나 이를 소유한 자료 구조에서 개수를 관리하세요.

반복 처리를 함수나 매크로로 감싸야 하는 경우

프로그램의 여러 부분에서 배열의 각 요소에 같은 작업을 수행한다면 함수로 묶는 것이 유용합니다. 함수는 요소 포인터, size_t 개수, 적용할 작업을 받을 수 있습니다. 콜백 매개변수는 void (*visit)(int) 형식일 수 있으며, 함수는 유효한 각 인덱스에 대해 visit(array[i])를 호출합니다. 개수를 명시적으로 전달하세요. 함수 안에서 배열 매개변수는 포인터이므로 sizeof로 원래 크기를 알아낼 수 없습니다.

콜백을 받는 함수는 값 출력이나 누적처럼 서로 다른 작업에도 사용할 수 있습니다. 콜백 형식이 요소 형식과 맞는지 확인하고 추가 문맥이 필요한지도 정하세요. C 콜백은 void * 매개변수를 통해 그 문맥을 전달할 수 있습니다.

매크로를 사용하면 루프를 간결하게 쓸 수 있지만, 매크로는 텍스트를 치환하므로 인수를 여러 번 평가할 수 있습니다. 또한 반복을 안전하게 만드는 범위와 형식을 감출 수 있습니다. 매크로를 쓴다면 범위가 배열과 일치하는지 확인하고 부수 효과가 있는 식은 전달하지 마세요. 재사용 가능한 코드에서는 길이를 명시적으로 받는 함수가 대체로 살펴보고 디버깅하고 유지 관리하기 쉽습니다.