Как мне вернуть массив символов? - PullRequest
0 голосов
/ 06 мая 2020

Программа работает правильно, если моя функция void. Но мне нужно вернуть значение, и возникает проблема.

#include <iostream>
#include <cstring>

using namespace std;

char* fun(char arr[][100]);

int main() {
    char arr[5][100] = { "Saturn", "Venus","Mars", "Earth", "Neptun" };
    char *inscription = fun(arr);
    std::cout << inscription;
}

char* fun(char arr[][100]) {
    char longer[100];
    strcpy_s(longer, arr[0]);
    for (int i = 0; i < 5; ++i) {
        if (strlen(arr[i]) > strlen(longer))
            strcpy_s(longer, arr[i]);
    }
    return longer;
}

Буду благодарен за ответ.

Ответы [ 5 ]

1 голос
/ 06 мая 2020

Если вы хотите вернуть массив символов из функции, я вижу два варианта:

  • a) Создать массив символов, выделенный кучей, и вернуть на него указатель
  • б) Дайте функции массив символов для заполнения

a)

char* fun(char arr[][100]) {
    char* longer = new char[100];
    // ... do something
    return longer
}

b)

int main()
{
    //...
    char longer[100];
    fun(arr, longer);
    //...
}

void fun(char arr[][100], char* longer) {
    // ... fill longer
}

Я думаю, что лучший подход - это второй, потому что вы ничего не выделяете в куче и избавляетесь от ненужных проблем. По возможности следует избегать выделения кучи, потому что они могут вызвать проблемы, если вызывающий объект не освободит память и на это потребуется время.

0 голосов
/ 06 мая 2020

Я бы рекомендовал вернуть указатель в исходный массив вместо копирования. Вот альтернативный вариант передачи массива по ссылке, а не по значению (что дорого).

#include <iostream>
#include <cstring>

// make it a function template to accept arrays of different sizes
template<size_t N, size_t L>
char* fun(char (&arr)[N][L]) {                 // pass by reference
    char* longest = arr[0];                    // make a pointer to the first
    size_t longest_len = std::strlen(longest); // and store its length

    for(size_t i = 1; i < N; ++i) {
        // check if a longer C string is found
        if(size_t alen = strlen(arr[i]); alen > longest_len) {
            // store the new length and a pointer to the new C string
            longest_len = alen;
            longest = arr[i];
        }
    }
    return longest; // return pointer into the original array
}

int main() {
    char arr[][100] = { "Saturn", "Venus","Mars", "Earth", "Neptun" };
    char *inscription = fun(arr);
    std::cout << inscription << '\n';     // Saturn
}

Однако это было бы проще и меньше подвержено ошибкам, используя std::vector и std::string.

0 голосов
/ 06 мая 2020

Для начала вам необходимо правильно определить функцию. У него должен быть второй параметр, который будет указывать количество элементов в переданном массиве.

Он может выглядеть как минимум следующим образом

char * fun( char arr[][100] , size_t n ) 
{
    char *longer = arr[0];

    for ( size_t i = 1; i < n; ++i ) 
    {
        if ( std::strlen(longer) < std::strlen( arr[i] ) )
        {
            longer = arr[i];
        }
    }

    return longer;
}

Вот демонстрационная программа

#include <iostream>
#include <cstring>

const size_t N = 100;

const char * fun( const char arr[][N] , size_t n ) 
{
    const char *longer = arr[0];

    for ( size_t i = 1; i < n; ++i ) 
    {
        if ( std::strlen(longer) < std::strlen( arr[i] ) )
        {
            longer = arr[i];
        }
    }

    return longer;
}

int main() 
{
    char arr[][N] = 
    { 
        "Saturn", "Venus","Mars", "Earth", "Neptun" 
    };

    const size_t M = sizeof( arr ) / sizeof( *arr );

    const char *inscription = fun( arr , M );

    std::cout << inscription << '\n';

    return 0;
}

Вывод программы:

Saturn

Однако функция имеет недостаток. Например, обычно пользователь может передать в качестве второго аргумента значение 0. Разыменование переданного указателя в качестве первого аргумента в этом случае может привести к неопределенному поведению. Более того, функция strlen вызывается избыточное количество раз.

Вот более безопасная реализация функции.

#include <iostream>
#include <cstring>

const size_t N = 100;

const char ( * fun( const char arr[][N] , size_t n ) )[N] 
{
    const char ( *longer )[N] = arr;
    size_t longer_len = 0;

    if ( n != 0 )
    {
        longer_len = std::strlen( *longer );
    }

    for ( size_t i = 1; i < n; ++i ) 
    {
        size_t len = std::strlen( arr[i] );

        if ( longer_len < len )
        {
            longer = arr + i;
            longer_len = len;
        }
    }

    return longer;
}

int main() 
{
    char arr[][N] = 
    { 
        "Saturn", "Venus","Mars", "Earth", "Neptun" 
    };

    const size_t M = sizeof( arr ) / sizeof( *arr );

    auto inscription = fun( arr , M );

    std::cout << *inscription << '\n';

    return 0;
}

Ее вывод:

Saturn

Если объявления, используемые в функции, выглядят сложными, поэтому вы можете упростить их, используя спецификатор auto. Например,

auto fun( const char arr[][N] , size_t n ) 
{
    auto longer = arr;
    size_t longer_len = 0;

    if ( n != 0 )
    {
        longer_len = std::strlen( *longer );
    }

    for ( size_t i = 1; i < n; ++i ) 
    {
        size_t len = std::strlen( arr[i] );

        if ( longer_len < len )
        {
            longer = arr + i;
            longer_len = len;
        }
    }

    return longer;
}

Также, если вы хотите, чтобы функция не зависела от элементов размера двумерного массива, вы можете объявить функцию как функцию-шаблон. Вот и вы.

#include <iostream>
#include <cstring>

template <size_t N>
auto fun( const char arr[][N] , size_t n ) 
{
    auto longer = arr;
    size_t longer_len = 0;

    if ( n != 0 )
    {
        longer_len = std::strlen( *longer );
    }

    for ( size_t i = 1; i < n; ++i ) 
    {
        size_t len = std::strlen( arr[i] );

        if ( longer_len < len )
        {
            longer = arr + i;
            longer_len = len;
        }
    }

    return longer;
}

int main() 
{
    const size_t N = 100;
    char arr[][N] = 
    { 
        "Saturn", "Venus","Mars", "Earth", "Neptun" 
    };

    const size_t M = sizeof( arr ) / sizeof( *arr );

    auto inscription = fun( arr , M );

    std::cout << *inscription << '\n';

    return 0;
}

Альтернативный подход к реализации такой функции - когда функция возвращает индекс целевого элемента вместо указателя на него.

Например

#include <iostream>
#include <cstring>

template <size_t N>
size_t fun( const char arr[][N] , size_t n ) 
{
    size_t longer = 0;
    size_t longer_len = 0;

    if ( n != 0 )
    {
        longer_len = std::strlen( arr[longer] );
    }

    for ( size_t i = 1; i < n; ++i ) 
    {
        size_t len = std::strlen( arr[i] );

        if ( longer_len < len )
        {
            longer = i;
            longer_len = len;
        }
    }

    return longer;
}

int main() 
{
    const size_t N = 100;
    char arr[][N] = 
    { 
        "Saturn", "Venus","Mars", "Earth", "Neptun" 
    };

    const size_t M = sizeof( arr ) / sizeof( *arr );

    size_t inscription = fun( arr , M );

    std::cout << arr[inscription] << '\n';

    return 0;
}

Но поскольку программа является программой на C ++, вам не нужно писать свою собственную функцию. Вы можете использовать стандартный алгоритм std::max_element, объявленный в заголовке <algorithm> с соответствующим лямбда-выражением, например

#include <iostream>
#include <cstring>
#include <iterator>
#include <algorithm>

const size_t N = 100;


int main() 
{
    char arr[][N] = 
    { 
        "Saturn", "Venus","Mars", "Earth", "Neptun" 
    };

    auto inscription = std::max_element( std::begin( arr ), std::end( arr ),
                                         []( const auto &s1, const auto &s2 )
                                         {
                                            return std::strlen( s1 ) < std::strlen( s2 );
                                         } );

    std::cout << *inscription << '\n';

    return 0;
}

. Вывод программы такой же, как показано выше

Saturn
0 голосов
/ 06 мая 2020

Проблема в том, что вы создаете локальную переменную, а затем возвращаете на нее указатель, но когда функция завершается, переменная уничтожается, поэтому указатель становится недействительным. Далее вы работаете с недопустимым указателем BOOM. Самый простой способ справиться с этим - использовать класс-функцию.

/* main.cpp */

#include <iostream>
#include <cstring>

using namespace std;

struct Func
 {
  char longer[100];

  explicit Func(char arr[][100])
   {
    strcpy(longer, arr[0]);

    for (int i = 1; i < 5; ++i) {
        if (strlen(arr[i]) > strlen(longer))
            strcpy(longer, arr[i]);
    }
   }

  const char * result() const { return longer; }
 };

int main() {
    char arr[5][100] = { "Saturn", "Venus","Mars", "Earth", "Neptun" };

    Func fun(arr);

    cout << fun.result() << endl ;

    return 0;
}

Есть еще некоторые проблемы с этим кодом, но я оставляю его здесь как есть.

0 голосов
/ 06 мая 2020

Попробуйте поправить функцию, чтобы она была такой:

char* fun(char arr[][100]) {
    char *longer = new char[100];
    strcpy_s(longer, arr[0]);
    for (int i = 0; i < 5; ++i) {
        if (strlen(arr[i]) > strlen(longer))
            strcpy_s(longer, arr[i]);
    }
    return longer;
}
...