Programing

C의 함수에 인수로 배열 전달

c10106 2022. 5. 14. 09:36
반응형

C의 함수에 인수로 배열 전달

배열이 들어 있는 함수를 인수로 썼고, 배열이란 값을 다음과 같이 전달하여 부른다.

void arraytest(int a[])
{
    // changed the array a
    a[0] = a[0] + a[1];
    a[1] = a[0] - a[1];
    a[0] = a[0] - a[1];
}

void main()
{
    int arr[] = {1, 2};
    printf("%d \t %d", arr[0], arr[1]);
    arraytest(arr);
    printf("\n After calling fun arr contains: %d\t %d", arr[0], arr[1]);
}

내가 찾은 건 내가 전화했음에도 불구하고arraytest()의 원본 복사본인 값을 전달함으로써 기능한다.int arr[]바뀌었어.

이유를 설명해 주시겠습니까?

배열을 매개 변수로 전달할 때

void arraytest(int a[])

와 정확히 같은 뜻을 가지고 있다.

void arraytest(int *a)

그래서 당신은 주 단위로 값을 수정하는 것이다.

역사적 이유로 배열은 일등 시민이 아니며 가치로 전달될 수 없다.

대신 2D(또는 다차원 이상) 어레이를 전달하려면 여기에서 다른 답변을 참조하십시오.다차원 배열을 C 및 C++의 함수에 전달하는 방법

C(및 C++)에서 기능 매개 변수로 1D 어레이 전달

1.배열에서 ptr까지 자연형 부패(조정)가 있는 C의 표준배열 사용법

@Bo Persson은 여기서 그의 위대한 대답에서 다음과 같이 정확하게 말한다.

배열을 매개 변수로 전달할 때

void arraytest(int a[])

와 정확히 같은 뜻을 가지고 있다.

void arraytest(int *a)

다음 두 가지 코드 스니펫에 명확성을 추가하기 위해 몇 가지 의견을 추가하겠다.

// param is array of ints; the arg passed automatically "adjusts" (frequently said
// informally as "decays") from `int []` (array of ints) to `int *` 
// (ptr to int)
void arraytest(int a[])

// ptr to int
void arraytest(int *a)

그러나 위의 두 가지 형태도 또한 다음과 같이 덧붙이자면:

  1. 와 정확히 같은 뜻이다

     // array of 0 ints; automatically adjusts (decays) from `int [0]`
     // (array of zero ints) to `int *` (ptr to int)
     void arraytest(int a[0])
    
  2. 그 말은 정확히 같은 뜻이야

     // array of 1 int; automatically adjusts (decays) from `int [1]`
     // (array of 1 int) to `int *` (ptr to int)
     void arraytest(int a[1])
    
  3. 그 말은 정확히 같은 뜻이야

     // array of 2 ints; automatically adjusts (decays) from `int [2]`
     // (array of 2 ints) to `int *` (ptr to int)
     void arraytest(int a[2])
    
  4. 그 말은 정확히 같은 뜻이야

     // array of 1000 ints; automatically adjusts (decays) from `int [1000]`
     // (array of 1000 ints) to `int *` (ptr to int)
     void arraytest(int a[1000])
    

위의 모든 배열 예에서, 그리고 바로 아래의 코드에 있는 호출 예에서와 같이, 입력 매개변수 유형은 조정(결정)되며, 빌드 옵션에서도 경고와 오류 없이 호출될 수 있다.-Wall -Wextra -Werror다음과 같이 설정됨(이 3가지 빌드 옵션에 대한 자세한 내용은 여기에서 my repo 참조):

int array1[2];
int * array2 = array1;

// works fine because `array1` automatically decays from an array type
// to a pointer type: `int *`
arraytest(array1);
// works fine because `array2` is already an `int *` 
arraytest(array2);

"의 가치사, "기" 값 ("기" 값)[0] [1] [2] [1000], 등) 여기서 배열 매개변수 내부에는 분명히 심미적/자기 만족을 위한 것이며, 어떤 양의 정수일 수 있다.size_t(타입) 원하시는 거 아니에요!

그러나 실제로는 함수가 수신할 것으로 예상되는 배열의 최소 크기를 지정하기 위해 코드를 작성할 때 쉽게 추적하고 확인할 수 있도록 이 값을 사용해야 한다.MISRA-C-2012 표준(여기서 £15.00에 대한 표준의 236-pg 2012 버전 PDF를 구입/다운로드)은 다음과 같이까지 언급된다(강조 추가).

규칙 17.5배열 유형을 갖는 것으로 선언된 매개변수에 해당하는 함수 인수는 적절한 수의 요소를 가져야 한다.

...

매개변수가 지정된 크기를 가진 배열로 선언된 경우, 각 함수 호출의 해당 인수는 적어도 배열과 같은 수의 요소를 가진 객체를 가리켜야 한다.

...

함수 매개변수에 배열 선언기를 사용하는 것은 포인터를 사용하는 것보다 함수 인터페이스를 더 명확하게 지정한다.기능에 의해 예상되는 최소 원소 수가 명시되어 있는 반면, 포인터로는 이것이 가능하지 않다.

즉, 기술적으로 C 표준이 이를 강제하지는 않지만 명시적 크기 형식을 사용할 것을 권고한다. 최소한 개발자로서 귀하에게 도움이 되고, 코드를 사용하는 다른 사람들에게, 해당 함수가 전달할 것으로 예상되는 크기 배열을 명확히 하는 데 도움이 된다.


2. C의 배열에 형식 안전 강제

(권장되지 않음(수정: 때로는 권장됨, 특히 고정 크기 다차원 배열의 경우 권장됨) 그러나 가능하다.마지막에 이것을 하는 것에 반대하는 나의 짧은 주장을 보라.또한, 나의 다차원 어레이 [ex: 2D 어레이] 버전에 대해서는, 여기서 내 대답을 참조하십시오.)

@Winger Sendon이 내 대답 아래의 코멘트에서 지적했듯이, 우리는 C가 배열 크기에 따라 배열 유형을 다르게 취급하도록 강제할 수 있다!

첫째, 바로 위의 제 예에서, 당신은 그것을 인식해야 한다.int array1[2];다음과 같은 경우:arraytest(array1);원인들array1자동으로 a로 부패하다int *하지만, 만약 당신이 대신 주소가지고 전화를 한다면, 당신은 완전히 다른 행동을 하게 될 것이다!이제, 그것은 썩지 않는다.int *! 배열의 주소를 가져갈 경우 이미 포인터 유형이 있고 포인터 유형이 다른 포인터 유형에 조정되지 않기 때문이다.배열 유형만 포인터 유형에 맞게 조정하십시오.그래서 그 대신 그 종류는&array1"int의 크기 2 배열로" 또는 "int 유형의 크기 2 배열로"를 의미하거나 "2 int의 배열로"를 의미하기도 한다.따라서 다음과 같이 어레이에 명시적 포인터를 전달하여 어레이의 유형 안전을 강제로 확인할 수 있다.

// `a` is of type `int (*)[2]`, which means "pointer to array of 2 ints"; 
// since it is already a ptr, it can NOT automatically decay further
// to any other type of ptr 
void arraytest(int (*a)[2])
{
    // my function here
}

이 구문은 읽기 어렵지만 함수 포인터와 유사하다.온라인 도구 cdel은 우리에게int (*a)[2]평균: "int의 배열 2에 대한 포인터로 a 설정"(2의 배열)ints). 이 버전을 와의 버전과 혼동하지 마십시오.OUT 괄호:int * a[2], 즉: "int에 대한 포인터 배열 2로 선언"(AKA: 다음에 대한 포인터 2개 배열)int, AKA: 배열 2int*s).

이제, 이 함수는 주소 운영자와 통화할 것을 요구한다.&) 이렇게 입력 매개 변수로 올바른 크기의 배열에 대한 포인터를 사용하십시오!:

int array1[2];

// ok, since the type of `array1` is `int (*)[2]` (ptr to array of 
// 2 ints)
arraytest(&array1); // you must use the & operator here to prevent
                    // `array1` from otherwise automatically decaying
                    // into `int *`, which is the WRONG input type here!

그러나 이는 다음과 같은 경고를 발생시킬 것이다.

int array1[2];

// WARNING! Wrong type since the type of `array1` decays to `int *`:
//      main.c:32:15: warning: passing argument 1 of ‘arraytest’ from 
//      incompatible pointer type [-Wincompatible-pointer-types]                                                            
//      main.c:22:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
arraytest(array1); // (missing & operator)

여기서 이 코드를 테스트해 보십시오.

C 컴파일러가 이 경고를 오류로 전환하도록 강제하여 항상 전화를 걸어야 함arraytest(&array1);올바른 크기 및 유형의 입력 배열만 사용(int array1[2];이 경우), 추가-Werror원하는 빌드 옵션을 선택하십시오.onlinegdb.com에서 위의 테스트 코드를 실행하는 경우 오른쪽 상단에 있는 기어 아이콘을 클릭하고 "추가 컴파일러 플래그"를 클릭하여 이 옵션을 입력하십시오. 이 자, 이려고:

main.c:34:15: warning: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Wincompatible-pointer-types]                                                            
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’    

이 빌드 오류로 전환됨:

main.c: In function ‘main’:
main.c:34:15: error: passing argument 1 of ‘arraytest’ from incompatible pointer type [-Werror=incompatible-pointer-types]
     arraytest(array1); // warning!
               ^~~~~~
main.c:24:6: note: expected ‘int (*)[2]’ but argument is of type ‘int *’
 void arraytest(int (*a)[2])
      ^~~~~~~~~
cc1: all warnings being treated as errors

다음과 같이 지정된 크기의 배열로 "유형 안전" 포인터를 만들 수도 있다는 점에 유의하십시오.

int array[2]; // variable `array` is of type `int [2]`, or "array of 2 ints"

// `array_p` is a "type safe" ptr to array of size 2 of int; ie: its type
// is `int (*)[2]`, which can also be stated: "ptr to array of 2 ints"
int (*array_p)[2] = &array;

...하지만 반드시 이것을 권장하지는 않는다(C의 이러한 "유형 안전" 배열 사용), 언어 구문 복잡성, 장황함, 그리고 난이도의 예외적으로 높은 비용으로 어디에서나 형식 안전을 강제할 때 사용되었던 많은 C++ 익살스러움을 상기시켜 주고, 내가 이전에 여러 번 혐오하고 원했던 것(예: " 생각" 참조)여기 C++"에 표시하십시오.


추가 테스트 및 실험은 아래 링크를 참조하십시오.

참조

위의 링크를 참조하십시오.또한:

  1. 내 코드 온라인 실험: https://onlinegdb.com/B1RsrBDFD

참고 항목:

  1. 위에 설명된 다차원 배열(예: 2D 배열)에 대한 나의 답변은 다음과 같은 의미가 있는 다차원 배열에서 "유형 안전" 접근방식을 사용한다.다차원 배열을 C 및 C++의 함수에 전달하는 방법

함수의 인수로 단일차원 배열을 통과시키려면 다음 세 가지 방법 중 하나로 공식 매개변수를 선언해야 하며, 각 방법이 컴파일러에게 정수 포인터를 수신할 것임을 알려주기 때문에 세 가지 선언 방법 모두 유사한 결과를 도출한다.

int func(int arr[], ...){
    .
    .
    .
}

int func(int arr[SIZE], ...){
    .
    .
    .
}

int func(int* arr, ...){
    .
    .
    .
}

따라서 원래 값을 수정하는 것이다.

고마워 !!!

함수에 인수로 다차원 배열 전달.하나의 모호한 배열을 인수로 전달하는 것은 다소 사소한 일이다.2개의 희미한 배열을 통과하는 더 흥미로운 사례에 대해 살펴봅시다.C에서 포인터를 사용하여 포인터 구문을 만들 수 없음(int **2개의 딤 배열 대신 )예를 들어보자:

void assignZeros(int(*arr)[5], const int rows) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < 5; j++) {
            *(*(arr + i) + j) = 0;
            // or equivalent assignment
            arr[i][j] = 0;
        }
    }

여기서 나는 첫 번째 인수로 5개의 정수의 배열에 대한 포인터를 취하는 함수를 지정했다.다음과 같은 5개의 열이 있는 2개의 딤 어레이를 인수로 전달할 수 있다.

int arr1[1][5]
int arr1[2][5]
...
int arr1[20][5]
...

2개의 딤 어레이를 수용하고 다음과 같이 함수 서명을 변경할 수 있는 보다 일반적인 함수를 정의하기 위한 아이디어를 얻을 수 있다.

void assignZeros(int ** arr, const int rows, const int cols) {
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            *(*(arr + i) + j) = 0;
        }
    }
}

이 코드는 컴파일되지만 첫 번째 함수에서와 같은 방법으로 값을 할당하려고 할 때 런타임 오류가 발생한다.따라서 C에서 다차원 배열은 포인터의 포인터와 같지 않다... 포인터의 포인터...int(*arr)[5]5개의 원소 배열에 대한 포인터,int(*arr)[6]이다!6개의 원소 배열에 대한 포인터고, 그것들은 다른 종류의 포인터야!

음, 어떻게 더 높은 차원에 대한 함수 인수를 정의할 수 있을까?간단해, 그냥 패턴을 따르는 거야!다음은 3차원의 배열을 취하도록 조정된 것과 동일한 기능이다.

void assignZeros2(int(*arr)[4][5], const int dim1, const int dim2, const int dim3) {
    for (int i = 0; i < dim1; i++) {
        for (int j = 0; j < dim2; j++) {
            for (int k = 0; k < dim3; k++) {
                *(*(*(arr + i) + j) + k) = 0;
                // or equivalent assignment
                arr[i][j][k] = 0;
            }
        }
    }
}

예상한 대로, 2차원의 4차원과 3차원의 5차원의 3개 조광 배열을 인수로 사용할 수 있다.이런 거라면 아무거나 괜찮아.

arr[1][4][5]
arr[2][4][5]
...
arr[10][4][5]
...

하지만 첫 번째 치수까지 모든 치수를 지정해야 해.

어레이의 첫 번째 멤버의 메모리 위치 값을 전달하고 있는 경우.

따라서 함수 내부의 배열을 수정하기 시작하면 원래 배열을 수정하는 것이다.

그 것을 기억하라.a[1]이다*(a+1).

C의 배열은 대부분의 경우 배열 자체의 첫 번째 요소에 대한 포인터로 변환된다.그리고 보다 세부적으로 함수로 전달되는 배열은 항상 포인터로 전환된다.

K&R2nd의 인용문:

배열 이름이 함수에 전달될 때 전달되는 것은 초기 요소의 위치다.호출된 함수 내에서 이 인수는 로컬 변수이므로 배열 이름 매개 변수는 포인터, 즉 주소를 포함하는 변수다.

쓰기:

void arraytest(int a[])

다음과 같은 의미를 갖는다.

void arraytest(int *a)

그래서 당신이 그것을 명시적으로 쓰지 않아도 그것은 당신이 포인터를 통과하고 있기 때문에 당신은 메인에서 값을 수정하고 있다.

더 많은 것을 위해 나는 정말로 이것을 읽는 것을 추천한다.

또한 SO에서 다른 해답을 찾을 수 있다.

배열을 사본으로 전달하지 마십시오.이는 배열의 첫 번째 요소가 메모리에 있는 주소를 가리키는 포인터일 뿐이다.

배열의 첫 번째 요소의 주소를 전달하는 경우

사용할 경우 배열이 항상 참조로 전달됨a[]또는*a:

int* printSquares(int a[], int size, int e[]) {   
    for(int i = 0; i < size; i++) {
        e[i] = i * i;
    }
    return e;
}

int* printSquares(int *a, int size, int e[]) {
    for(int i = 0; i < size; i++) {
        e[i] = i * i;
    }
    return e;
}

C에서 몇 가지 특별한 경우를 제외하고 배열 참조는 항상 배열의 첫 번째 요소에 대한 포인터를 "결정"한다.따라서 "값별" 배열을 전달할 수 없다.함수 호출의 배열이 포인터로서 함수에 전달되며, 이는 배열을 참조로 전달하는 것과 유사하다.

편집: 어레이가 첫 번째 요소에 대한 포인터까지 손상되지 않는 특별한 경우:

  1. sizeof a와 같지 않다sizeof (&a[0]).
  2. &a와 같지 않다&(&a[0])(그리고 와는 전혀 같지 않다.&a[0]).
  3. char b[] = "foo"와 같지 않다char b[] = &("foo").

배열은 붕괴 포인터라고도 할 수 있다.

일반적으로 출력 f 문장에 변수 이름을 입력하면 배열의 경우 값이 인쇄되어 첫 번째 원소의 주소로 디코딩되므로 이를 붕괴 포인터라고 부른다.

그리고 우리는 기능에만 붕괴 포인터를 전달할 수 있다.

Mr.Bo가 말한 바와 같이 형식적인 매개변수로서의 배열은 int ar[] 또는 int ar[10]는 int *arr;

그들은 4바이트의 메모리 공간을 가지고 있고 수신된 붕괴 포인터를 저장할 것이다.포인터로 산수를 하는 거야

참조URL: https://stackoverflow.com/questions/6567742/passing-an-array-as-an-argument-to-a-function-in-c

반응형