int arr[5]
함수에 전달 되는 배열 이 있습니다 fillarr(int arr[])
.
int fillarr(int arr[])
{
for(...);
return arr;
}
- 해당 배열을 어떻게 반환합니까?
- 어떻게 사용합니까? 포인터에 어떻게 액세스 할 수 있습니까?
std::vector
.
5
함수 시그니처는 컴파일러에 의해 폐기된다.
int arr[5]
함수에 전달 되는 배열 이 있습니다 fillarr(int arr[])
.
int fillarr(int arr[])
{
for(...);
return arr;
}
std::vector
.
5
함수 시그니처는 컴파일러에 의해 폐기된다.
답변:
이 경우 배열 변수 arr
는 실제로 암시 적 변환을 통해 메모리에서 배열 블록의 시작을 가리키는 포인터로 취급 될 수도 있습니다. 사용중인이 구문은 다음과 같습니다.
int fillarr(int arr[])
일종의 구문 설탕입니다. 실제로 이것을 이것으로 바꿀 수 있으며 여전히 작동합니다.
int fillarr(int* arr)
같은 의미에서 함수에서 반환하려는 것은 실제로 배열의 첫 번째 요소에 대한 포인터입니다.
int* fillarr(int arr[])
그리고 일반적인 배열처럼 계속 사용할 수 있습니다.
int main()
{
int y[10];
int *a = fillarr(y);
cout << a[0] << endl;
}
array
그리고 &array
많은 경우에 교환 할 수있다.
a
에가 int a[10]
있다 int[10]
. 당신이 말할 수있는 것은 배열이 첫 번째 요소에 대한 포인터로 "부패"입니다. (이것은 암시적인 배열에서 포인터로의 변환입니다.) 그러면 당신의 대답은 나의 행을 따라 갈 것입니다. 배열, 배열 간 포인터 변환 및 포인터를 구별하기 위해 답변을 편집하면 동일한 핵심 정보가 있고 첫 번째이기 때문에 답변을 삭제합니다.
C ++ 함수는 C 스타일 배열을 값으로 반환 할 수 없습니다. 가장 가까운 것은 포인터를 반환하는 것입니다. 또한 인수 목록의 배열 유형은 단순히 포인터로 변환됩니다.
int *fillarr( int arr[] ) { // arr "decays" to type int *
return arr;
}
인수와 리턴에 배열 참조를 사용하여이를 개선하여 붕괴를 방지 할 수 있습니다.
int ( &fillarr( int (&arr)[5] ) )[5] { // no decay; argument must be size 5
return arr;
}
Boost 또는 C ++ 11을 사용하면 참조 기준 전달은 선택 사항이며 구문은 덜 구부러집니다.
array< int, 5 > &fillarr( array< int, 5 > &arr ) {
return arr; // "array" being boost::array or std::array
}
array
템플릿은 단순히 생성 struct
은 객체 지향 의미를 적용 아직 배열의 원래 단순성을 유지할 수 있도록 포함하는 C 스타일 배열을.
typedef int array[5]; array& foo();
그러나 다음과 같이 작성해야한다면 typedef가 필요하지 않습니다 . int (&foo())[5] { static int a[5] = {}; return a; }
질문의 예는 다음과 같습니다 int (&foo( int (&a)[5] ))[5] { return a; }
. 간단하지 않습니까?
error: function returning array is not allowed
이 아닌 구문으로 외부 parens를 제외하면 발생 하는 잘못된 인상을 받았습니다 . 운 좋게도 오늘 나는 또 다른 질문에 대한 오른쪽 왼쪽 규칙을 검토하고 당신이 그것이 가능하다고 말하는 것을 보았을 때…
C ++ 11에서는을 반환 할 수 있습니다 std::array
.
#include <array>
using namespace std;
array<int, 5> fillarr(int arr[])
{
array<int, 5> arr2;
for(int i=0; i<5; ++i) {
arr2[i]=arr[i]*2;
}
return arr2;
}
(...) you can consider the array returned arr2, totally another array (...)
$ 8.3.5 / 8 상태-
"함수는 리턴 형 타입의 포인터 나 함수를 가질 수 있지만 리턴 형 타입의 포인터 나 함수를 가지지 않아야한다.
int (&fn1(int (&arr)[5]))[5]{ // declare fn1 as returning refernce to array
return arr;
}
int *fn2(int arr[]){ // declare fn2 as returning pointer to array
return arr;
}
int main(){
int buf[5];
fn1(buf);
fn2(buf);
}
int
배열이 아닌에 대한 포인터를 반환합니다 .
답은 그 기능을 어떻게 사용할 것인지에 따라 조금씩 다를 수 있습니다. 가장 간단한 대답을 위해 배열 대신 실제로 원하는 것이 벡터라고 결정하십시오. 벡터는 평범한 포인터에 저장할 수있는 지루하고 평범한 값처럼 모든 세상을보기 때문에 좋습니다. 다른 옵션과 그 이후에 원하는 옵션을 살펴 보겠습니다.
std::vector<int> fillarr( std::vector<int> arr ) {
// do something
return arr;
}
이것은 당신이 기대하는 것을 정확하게 할 것입니다. 단점은 std::vector
모든 것이 깨끗하게 처리되도록하는 것입니다. 단점은 배열이 큰 경우 매우 많은 양의 데이터를 복사한다는 것입니다. 실제로 배열의 모든 요소를 두 번 복사합니다. 먼저 함수가 벡터를 매개 변수로 사용할 수 있도록 벡터를 복사합니다. 그런 다음 다시 복사하여 발신자에게 반환합니다. 벡터 관리를 직접 처리 할 수 있다면 훨씬 쉽게 할 수 있습니다. (발신자가 더 많은 계산을 수행하기 위해 일종의 변수에 저장 해야하는 경우 세 번째로 복사 할 수 있음)
실제로하려고하는 것은 컬렉션을 채우는 것 같습니다. 컬렉션의 새 인스턴스를 반환해야 할 특별한 이유가 없다면 그렇지 않습니다. 우리는 이렇게 할 수 있습니다
void fillarr(std::vector<int> & arr) {
// modify arr
// don't return anything
}
이 방법으로 개인 복사본이 아닌 함수에 전달 된 배열에 대한 참조를 얻을 수 있습니다. 매개 변수를 변경하면 호출자가 볼 수 있습니다. 원하는 경우 참조를 반환 할 수는 있지만 전달 한 것과 다른 것을 얻는다는 것을 의미하기 때문에 실제로는 좋은 생각이 아닙니다.
컬렉션의 새 인스턴스가 실제로 필요하지만 스택에 포함시키지 않으려면 (그리고 모든 복사본에 포함되는) 피하지 않으려면 해당 인스턴스를 처리하는 방법에 대한 일종의 계약을 만들어야합니다. 가장 쉬운 방법은 스마트 포인터를 사용하는 것입니다. 스마트 포인터는 누군가가 붙잡고있는 한 참조 된 인스턴스를 유지합니다. 범위를 벗어나면 깨끗하게 사라집니다. 이렇게 보일 것입니다.
std::auto_ptr<std::vector<int> > fillarr( const std::vector<int> & arr) {
std::auto_ptr<std::vector<int> > myArr(new std::vector<int>);
// do stuff with arr and *myArr
return myArr;
}
대부분의 *myArr
경우 일반 바닐라 벡터를 사용하는 것과 동일하게 작동합니다. 이 예는 또한 const
키워드 를 추가하여 매개 변수 목록을 수정합니다 . 이제 복사하지 않고 참조를 얻지 만 수정할 수 없으므로 호출자는 함수가 도착하기 전과 동일하다는 것을 알고 있습니다.
이 모든 것이 부풀어 있지만 관용적 인 C ++은 컬렉션 전체에서 거의 작동하지 않습니다. 더 일반적으로 해당 콜렉션에 대해 반복자를 사용합니다. 그게 이런 식으로 보일 것입니다
template <class Iterator>
Iterator fillarr(Iterator arrStart, Iterator arrEnd) {
Iterator arrIter = arrStart;
for(;arrIter <= arrEnd; arrIter++)
;// do something
return arrStart;
}
이 스타일을 보는 데 익숙하지 않으면 조금 이상하게 보입니다.
vector<int> arr;
vector<int>::iterator foo = fillarr(arr.begin(), arr.end());
foo는 이제 수정 된 시작을 가리 킵니다 arr
.
이것에 대해 정말 좋은 점은 일반 C 배열 및 다른 많은 유형의 컬렉션과 마찬가지로 벡터에서 동일하게 작동한다는 것입니다.
int arr[100];
int *foo = fillarr(arr, arr+100);
이제이 질문의 다른 곳에서 제공되는 평범한 포인터 예제와 끔찍하게 보입니다.
&
되었습니다. 다음 유형 뒤에 기호가 나타나야합니다.void fillarr(std::vector<int> & arr)
이:
int fillarr(int arr[])
실제로 다음과 동일하게 취급됩니다.
int fillarr(int *arr)
이제 실제로 배열을 반환하려면 해당 줄을 다음으로 변경할 수 있습니다
int * fillarr(int arr[]){
// do something to arr
return arr;
}
실제로 배열을 반환하지는 않습니다. 배열 주소의 시작 부분에 대한 포인터를 반환합니다.
그러나 배열을 전달할 때 포인터 만 전달한다는 것을 기억하십시오. 따라서 배열 데이터를 수정하면 실제로 포인터가 가리키는 데이터가 수정됩니다. 따라서 배열을 전달하기 전에 수정 된 결과 외부에 이미 있음을 알고 있어야합니다.
예 :
int fillarr(int arr[]){
array[0] = 10;
array[1] = 5;
}
int main(int argc, char* argv[]){
int arr[] = { 1,2,3,4,5 };
// arr[0] == 1
// arr[1] == 2 etc
int result = fillarr(arr);
// arr[0] == 10
// arr[1] == 5
return 0;
}
필러 기능에 길이를 넣는 것을 고려해 볼 수 있습니다.
int * fillarr(int arr[], int length)
그렇게하면 길이를 사용하여 배열을 길이에 관계없이 채울 수 있습니다.
실제로 올바르게 사용하십시오. 다음과 같이하십시오 :
int * fillarr(int arr[], int length){
for (int i = 0; i < length; ++i){
// arr[i] = ? // do what you want to do here
}
return arr;
}
// then where you want to use it.
int arr[5];
int *arr2;
arr2 = fillarr(arr, 5);
// at this point, arr & arr2 are basically the same, just slightly
// different types. You can cast arr to a (char*) and it'll be the same.
배열을 기본값으로 설정하기 만하면 내장 memset 함수를 사용해보십시오.
다음과 같습니다 : memset ((int *) & arr, 5, sizeof (int));
그래도 주제에 있습니다. 당신은 C ++을 사용한다고 말합니다. STL 벡터 사용을 살펴보십시오. 코드가 더 강력 할 것입니다.
많은 튜토리얼이 있습니다. 다음은 그것들을 사용하는 방법에 대한 아이디어를 제공합니다. http://www.yolinux.com/TUTORIALS/LinuxTutorialC++STL.html
std::copy
이상 사용하면 memset
더 안전하고 쉽습니다. (더 빠르지 않으면 빨리)
함수에서 배열을 반환하려면 해당 배열을 구조에 정의하십시오. 이렇게 생겼어요
struct Marks{
int list[5];
}
이제 타입 구조의 변수를 만들어 봅시다.
typedef struct Marks marks;
marks marks_list;
다음과 같은 방법으로 배열을 함수에 전달하고 값을 할당 할 수 있습니다.
void setMarks(int marks_array[]){
for(int i=0;i<sizeof(marks_array)/sizeof(int);i++)
marks_list.list[i]=marks_array[i];
}
배열을 반환 할 수도 있습니다. 배열을 반환하려면 함수의 반환 유형은 구조 유형, 즉 마크 여야합니다. 실제로는 배열을 포함하는 구조를 전달하기 때문입니다. 따라서 최종 코드는 다음과 같습니다.
marks getMarks(){
return marks_list;
}
이것은 상당히 오래된 질문이지만 많은 답변이 있기 때문에 2 센트를 넣을 것입니다. 그러나 가능한 모든 방법을 명확하고 간결한 방식으로 표시하지는 않습니다 (간결한 비트에 대해서는 확실하지 않습니다. TL; DR 😉).
OP가 코드를 더 예쁘게 보이도록 다른 함수로 전달하기 위해 호출자에게 직접 전달하는 수단으로 복사하지 않고 전달 된 배열을 반환하기를 원한다고 가정합니다.
그러나 이와 같은 배열을 사용하려면 포인터로 붕괴하여 컴파일러 가 배열 처럼 취급하도록하십시오 . 이렇게하면 배열에 5 개의 요소가있을 것으로 예상하지만 호출자는 실제로 다른 번호로 전달하는 등의 배열을 전달하면 미묘한 버그가 발생할 수 있습니다.
더 잘 처리 할 수있는 몇 가지 방법이 있습니다. A의 패스 std::vector
또는 std::array
(하지 않도록 경우 std::array
질문을 질문 할 때 2010 년의 주위에 있었다). 그런 다음 객체를 복사 / 이동하지 않고도 객체를 참조로 전달할 수 있습니다.
std::array<int, 5>& fillarr(std::array<int, 5>& arr)
{
// (before c++11)
for(auto it = arr.begin(); it != arr.end(); ++it)
{ /* do stuff */ }
// Note the following are for c++11 and higher. They will work for all
// the other examples below except for the stuff after the Edit.
// (c++11 and up)
for(auto it = std::begin(arr); it != std::end(arr); ++it)
{ /* do stuff */ }
// range for loop (c++11 and up)
for(auto& element : arr)
{ /* do stuff */ }
return arr;
}
std::vector<int>& fillarr(std::vector<int>& arr)
{
for(auto it = arr.begin(); it != arr.end(); ++it)
{ /* do stuff */ }
return arr;
}
그러나 C 배열을 사용한다고 주장하는 경우 배열의 항목 수에 대한 정보를 유지하는 템플릿을 사용하십시오.
template <size_t N>
int(&fillarr(int(&arr)[N]))[N]
{
// N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
for(int* it = arr; it != arr + N; ++it)
{ /* do stuff */ }
return arr;
}
그러나, 그것은보기 흉한보기에 매우보기 힘들다. 나는 이제 2010 년에 없었던 것을 돕기 위해 무언가를 사용하며, 함수 포인터에도 사용합니다.
template <typename T>
using type_t = T;
template <size_t N>
type_t<int(&)[N]> fillarr(type_t<int(&)[N]> arr)
{
// N is easier and cleaner than specifying sizeof(arr)/sizeof(arr[0])
for(int* it = arr; it != arr + N; ++it)
{ /* do stuff */ }
return arr;
}
이것은 타입을 예상 한 위치로 이동 시켜서 훨씬 더 읽기 쉽게 만듭니다. 물론 템플릿을 사용하는 것은 5 가지 요소 이외의 다른 것을 사용하지 않을 경우 불필요하므로 하드 코딩 할 수 있습니다.
type_t<int(&)[5]> fillarr(type_t<int(&)[5]> arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
내가 말했듯이, type_t<>
이 질문을 할 때 내 트릭은 효과가 없었습니다. 그때 당신이 기대했을 수있는 가장 좋은 것은 구조체에 유형을 사용하는 것이 었습니다.
template<typename T>
struct type
{
typedef T type;
};
typename type<int(&)[5]>::type fillarr(typename type<int(&)[5]>::type arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
어느 것이 다시보기 흉한 것처럼 보이지만 typename
컴파일러에 따라 선택 사항이었을 수도 있지만 적어도 여전히 더 읽기 쉽습니다 .
type<int(&)[5]>::type fillarr(type<int(&)[5]>::type arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
물론 내 도우미를 사용하지 않고 특정 유형을 지정할 수 있습니다.
typedef int(&array5)[5];
array5 fillarr(array5 arr)
{
// Prefer using the compiler to figure out how many elements there are
// as it reduces the number of locations where you have to change if needed.
for(int* it = arr; it != arr + sizeof(arr)/sizeof(arr[0]); ++it)
{ /* do stuff */ }
return arr;
}
당시 자유 기능 std::begin()
및 std::end()
존재하지 않았다는하지만 쉽게 구현 될 수 있었다. 이것은 배열이 C 배열에서 의미가 있기 때문에 더 안전한 방식으로 배열을 반복 할 수 있었지만 포인터는 아닙니다.
배열에 액세스하는 경우 동일한 매개 변수 유형을 사용하는 다른 함수에 배열을 전달하거나 별칭을 만들 수 있습니다 (이 범위의 원본을 이미 가지고 있으므로 의미가 없습니다). 배열 참조에 액세스하는 것은 원래 배열에 액세스하는 것과 같습니다.
void other_function(type_t<int(&)[5]> x) { /* do something else */ }
void fn()
{
int array[5];
other_function(fillarr(array));
}
또는
void fn()
{
int array[5];
auto& array2 = fillarr(array); // alias. But why bother.
int forth_entry = array[4];
int forth_entry2 = array2[4]; // same value as forth_entry
}
요약하면 배열을 반복하려는 경우 포인터가 배열로 붕괴되지 않도록하는 것이 가장 좋습니다. 컴파일러가 발에 걸리지 않도록 보호하고 코드를 읽기 어렵게 만드는 것은 나쁜 생각입니다. 그렇게 할 이유가없는 한, 가능한 한 오랫동안 타입을 유지함으로써 컴파일러가 당신을 도울 수 있도록 항상 노력하십시오.
아, 그리고 완전성을 위해 포인터로 포인터의 성능을 저하시킬 수는 있지만 배열을 보유하는 요소 수와 분리시킵니다. 이것은 C / C ++에서 많이 수행되며 일반적으로 배열의 요소 수를 전달하여 완화됩니다. 그러나 실수를하고 요소 수에 잘못된 값을 전달하면 컴파일러가 도움을 줄 수 없습니다.
// separate size value
int* fillarr(int* arr, size_t size)
{
for(int* it = arr; it != arr + size; ++it)
{ /* do stuff */ }
return arr;
}
크기를 전달하는 대신 끝 포인터를 전달하면 배열 끝을 지날 수 있습니다. 이것은 std 알고리즘에 더 가까운 것, 시작 및 끝 포인터를 취하는 것에 유용하지만 반환하는 것은 이제는 기억해야 할 것입니다.
// separate end pointer
int* fillarr(int* arr, int* end)
{
for(int* it = arr; it != end; ++it)
{ /* do stuff */ }
return arr;
}
또는이 함수가 5 개의 요소 만 취하고 함수 사용자가 어리석은 짓을하지 않기를 희망 할 수 있습니다.
// I document that this function will ONLY take 5 elements and
// return the same array of 5 elements. If you pass in anything
// else, may nazal demons exit thine nose!
int* fillarr(int* arr)
{
for(int* it = arr; it != arr + 5; ++it)
{ /* do stuff */ }
return arr;
}
반환 값은 원래 형식을 잃어 포인터로 저하됩니다. 이로 인해, 이제 어레이를 오버런하지 않도록 스스로 할 수 있습니다.
당신은 std::pair<int*, int*>
시작과 끝을 위해 그것을 사용할 수있는를 전달할 수 있습니다. 그러나 그 배열은 실제로 배열처럼 보이지 않습니다.
std::pair<int*, int*> fillarr(std::pair<int*, int*> arr)
{
for(int* it = arr.first; it != arr.second; ++it)
{ /* do stuff */ }
return arr; // if you change arr, then return the original arr value.
}
void fn()
{
int array[5];
auto array2 = fillarr(std::make_pair(&array[0], &array[5]));
// Can be done, but you have the original array in scope, so why bother.
int fourth_element = array2.first[4];
}
또는
void other_function(std::pair<int*, int*> array)
{
// Can be done, but you have the original array in scope, so why bother.
int fourth_element = array2.first[4];
}
void fn()
{
int array[5];
other_function(fillarr(std::make_pair(&array[0], &array[5])));
}
재밌지 만, 이것은 std::initializer_list
작업 방식 (c ++ 11) 과 매우 유사 하지만이 맥락에서는 작동하지 않습니다.
template<typename T, size_t N>
using ARR_REF = T (&)[N];
template <typename T, size_t N>
ARR_REF<T,N> ArraySizeHelper(ARR_REF<T,N> arr);
#define arraysize(arr) sizeof(ArraySizeHelper(arr))
출처 : https://www.tutorialspoint.com/cplusplus/cpp_return_arrays_from_functions.htm
C ++에서는 전체 배열을 함수의 인수로 반환 할 수 없습니다. 그러나 인덱스없이 배열 이름을 지정하여 배열에 대한 포인터를 반환 할 수 있습니다.
int * myFunction() { . . . }
현재 질문에 이러한 규칙을 적용하여 다음과 같이 프로그램을 작성할 수 있습니다.
# include <iostream> using namespace std; int * fillarr( ); int main () { int *p; p = fillarr(); for ( int i = 0; i < 5; i++ ) cout << "p[" << i << "] : "<< *(p + i) << endl; return 0; } int * fillarr( ) { static int arr[5]; for (int i = 0; i < 5; ++i) arr[i] = i; return arr; }
출력은 다음과 같습니다.
p[0]=0
p[1]=1
p[2]=2
p[3]=3
p[4]=4
실제로 함수 내에서 배열을 전달하면 원래 배열에 대한 포인터가 함수 매개 변수에 전달되므로 해당 함수 내 배열에 대한 변경 사항은 실제로 원래 배열에서 이루어집니다.
#include <iostream>
using namespace std;
int* func(int ar[])
{
for(int i=0;i<100;i++)
ar[i]=i;
int *ptr=ar;
return ptr;
}
int main() {
int *p;
int y[100]={0};
p=func(y);
for(int i=0;i<100;i++)
cout<<i<<" : "<<y[i]<<'\n';
}
그것을 실행하면 변경 사항을 볼 수 있습니다
y
자체는 자체의 복사본으로 전달되지만 포인터이기 때문에 배열에서 직접 작동합니다. 답을 수정하십시오.
해결해야 할 이런 종류의 문제에 대한 전체 예는 다음과 같습니다.
#include <bits/stdc++.h>
using namespace std;
int* solve(int brr[],int n)
{
sort(brr,brr+n);
return brr;
}
int main()
{
int n;
cin>>n;
int arr[n];
for(int i=0;i<n;i++)
{
cin>>arr[i];
}
int *a=solve(arr,n);
for(int i=0;i<n;i++)
{
cout<<a[i]<<endl;
}
return 0;
}