[C]Возвращаем из функции массив
|
От: |
slava_phirsov |
|
Дата: | 09.04.10 06:18 | ||
Оценка: |
32 (1) |
Стыдно признаться, std::string, std::vector, QString, QList и им подобные напрочь отучили думать в категориях операций с массивами. Это было типа вступление. Теперь код:
#include <stdio.h>
#include <string.h>
typedef char Foo[10];
Foo foo(void)
{
Foo result;
strcpy(result, "123n");
return result;
}
int main(int argc, char* argv [])
{
Foo f = foo();
printf("Foo.x is: %s", f.x);
return 0;
}
Не скомпилируется с диагнозом (GCC):
error: ‘foo’ declared as function returning an array
Зато вот это скомпилируется, как ни странно:
#include <stdio.h>
#include <string.h>
typedef struct {
char x[10];
} Foo;
Foo foo(void)
{
Foo result;
strcpy(result.x, "123n");
return result;
}
int main(int argc, char* argv [])
{
Foo f = foo();
printf("Foo.x is: %s", f.x);
return 0;
}
И выдает при запуске:
Foo.x is: 123
Однако вот так:
#include <stdio.h>
#include <string.h>
typedef struct {
char x[10];
} Foo;
Foo foo(void)
{
Foo result;
strcpy(result.x, "123n");
return result;
}
int main(int argc, char* argv [])
{
printf("Foo.x is: %s", foo().x);
return 0;
}
Получаем рантайм-облом:
Segmentation fault
В чем дело, док? (с) Багз Банни
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re: [C]Возвращаем из функции массив
|
От: |
OdesitVadim |
|
Дата: | 09.04.10 06:33 | ||
Оценка: |
Здравствуйте, slava_phirsov, Вы писали:
[scip]
_>Однако вот так:
_>
_>#include <stdio.h>
_>#include <string.h>
_>typedef struct {
_> char x[10];
_>} Foo;
_>Foo foo(void)
_>{
_> Foo result;
_> strcpy(result.x, "123n");
_> return result;
_>}
_>int main(int argc, char* argv [])
_>{
_> printf("Foo.x is: %s", foo().x);
_> return 0;
_>}
_>
_>Получаем рантайм-облом:
_>
_>Segmentation fault
_>В чем дело, док? (с) Багз Банни
Классика жанра в этом случае происходит обращение к памяти, которая выделена на стеке и в данный момент уже не существует. Теоретически (да и практически) может так сложиться, что этот код отработает и даже правильно — всё зависит от того, что там будет со стеком. А в втором премере компилятор вставляет код для копирования структуры… Хотя тут я могу ошибаться.
Re: [C]Возвращаем из функции массив
|
От: |
skeptic |
|
Дата: | 09.04.10 06:42 | ||
Оценка: |
13 (2) |
Здравствуйте, slava_phirsov, Вы писали:
_>Стыдно признаться, std::string, std::vector, QString, QList и им подобные напрочь отучили думать в категориях операций с массивами. Это было типа вступление. Теперь код:
_> быдлокод поскипан
Это чего какая нибудь шутка? Если да, то как бэ не смешно, хотя это конечно субъективно.
А если нет то какие там нафиг стринги и вектора…
Берём учебник по С++ за 1-ый класс и увлечённо читаем про всякую память, временные объекты, куда чего нельзя возвращать и т.д..
Re[2]: [C]Возвращаем из функции массив
|
От: |
night beast |
|
Дата: | 09.04.10 06:43 | ||
Оценка: |
Здравствуйте, OdesitVadim, Вы писали:
OV>[scip]
_>>Однако вот так:
_>>#include <stdio.h>
_>>#include <string.h>
_>>typedef struct {
_>> char x[10];
_>>} Foo;
_>>Foo foo(void)
_>>{
_>> Foo result;
_>> strcpy(result.x, "123n");
_>> return result;
_>>}
_>>int main(int argc, char* argv [])
_>>{
_>> printf("Foo.x is: %s", foo().x);
_>> return 0;
_>>}
_>>Получаем рантайм-облом:
_>>
_>>Segmentation fault
_>>В чем дело, док? (с) Багз Банни
OV>Классика жанра в этом случае происходит обращение к памяти, которая выделена на стеке и в данный момент уже не существует. Теоретически (да и практически) может так сложиться, что этот код отработает и даже правильно — всё зависит от того, что там будет со стеком. А в втором премере компилятор вставляет код для копирования структуры… Хотя тут я могу ошибаться.
тама объект по значению возвращается, а значит должен жить до конца внешнего выражения.
по идее все должно работать нормально (гсс не падает)
какой компилятор?
Re[2]: [C]Возвращаем из функции массив
|
От: |
slava_phirsov |
|
Дата: | 09.04.10 06:52 | ||
Оценка: |
Здравствуйте, OdesitVadim, Вы писали:
OV>А в втором премере компилятор вставляет код для копирования структуры… Хотя тут я могу ошибаться.
В одном эпохальном (я без иронии!) труде по дизассемблированию приведен аналогичный пример (у самого как-то все руки не доходят поковыряться с дизасмом) — там не один, там два кода копирования структур. Вызывающая функция передает указатель на одну локальную переменную, в которую вызываемая функция копирует структуру. Потом вызывающая функция копирует из этой временной переменной в нужную локальную переменную.
Собственно, на практике принято писать так, что буфер приходит со стороны вызывающего, а вызываемый его только заполняет. Это касается и строк, и массивов, и структур. Столкнувшись с нетривиальным примером из литературы я как-то шибко задумался. Зачем два копирования? Почему нельзя провернуть такой фокус с «голым» массивом, а с массивом «обернутым» в структуру можно?
Воп’госы, воп’госы, сколько вопг’осов! Один дуг’ак задаст столько вопг’осов, что и сто мудг’ецов не ответят!
Задавай им вопросы, Ерофеев! Задавай им самые мучительные социальные вопросы!
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[3]: [C]Возвращаем из функции массив
|
От: |
slava_phirsov |
|
Дата: | 09.04.10 06:54 | ||
Оценка: |
Здравствуйте, night beast, Вы писали:
NB>по идее все должно работать нормально (гсс не падает)
NB>какой компилятор?
Запускал на codepad.org
C: gcc 4.1.2
flags: -O -fmessage-length=0 -fno-merge-constants -fstrict-aliasing -fstack-protector-all
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[2]: [C]Возвращаем из функции массив
|
От: |
slava_phirsov |
|
Дата: | 09.04.10 06:56 | ||
Оценка: |
1 (1) |
Здравствуйте, skeptic, Вы писали:
Минус исключительно за применение термина «быдлкод».
Учитесь, пожалуйста вежливому общению, даже на форумах.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[3]: [C]Возвращаем из функции массив
|
От: |
uzhas |
|
Дата: | 09.04.10 07:01 | ||
Оценка: |
Здравствуйте, night beast, Вы писали:
NB>по идее все должно работать нормально (гсс не падает)
я тоже не увидел ничего криминального в коде
первая ссылка в гугле: http://bytes.com/topic/c/answers/853772-returning-array-inside-structure-allocated-afunction
в си++ такая же проблема есть?
Re: [C]Возвращаем из функции массив
|
От: |
Bell |
|
Дата: | 09.04.10 07:30 | ||
Оценка: |
Здравствуйте, slava_phirsov, Вы писали:
…
_>Не скомпилируется с диагнозом (GCC):
error: ‘foo’ declared as function returning an array
В С/С++ явно запрещено возвращать из функции массивы по значению. Массивы сами по себе вообще не являются copy-constuctable, хотя сгенерированные по умолчаниюонию для пользовательского типа конструктор копирования и оператор присваивания прекрасно копируют члены класса — массивы . На этот счет существуют разные мнения, но что есть — то есть.
8.3.5/6
…
Functions shall not have a return type of type array or function, although they may have a return type of type pointer or reference to such things
…
_>Зато вот это скомпилируется, как ни странно:
Как я уже сказал выше — компилятор поэлементно копирует массивы — члены в неявном конструкторе копирования.
_>И выдает при запуске:
_>
_>Foo.x is: 123
Ничего удивительного .
_>Однако вот так:
_>Получаем рантайм-облом:
_>
_>Segmentation fault
_>В чем дело, док? (с) Багз Банни
В С++ все было бы в порядке. Я не являюсь знатоком чистого С, но возможно в случае С временный объект разрушается до вызова printf, хотя это очень странно
Любите книгу — источник знаний (с) М.Горький
Re[2]: [C]Возвращаем из функции массив
|
От: |
slava_phirsov |
|
Дата: | 09.04.10 07:48 | ||
Оценка: |
Здравствуйте, Bell, Вы писали:
B>сгенерированные по умолчаниюонию для пользовательского типа конструктор копирования и оператор присваивания прекрасно копируют члены класса — массивы
Собственно, речь больше о чистом C, хотя А где в стандарте говорится, что такое копирование возможно? Если член класса — указатель, указывающий на память в куче, то копирование по умолчанию однозначно не проходит, нужно брать в руки напильник и подпиливать…
B>Как я уже сказал выше — компилятор поэлементно копирует массивы — члены в неявном конструкторе копирования.
Компилировалось в GCC, как чистый C.
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[3]: [C]Возвращаем из функции массив
|
От: |
Erop |
|
Дата: | 09.04.10 09:14 | ||
Оценка: |
Здравствуйте, night beast, Вы писали:
NB>какой компилятор?
Для начала стоит узнать какой язык
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно «ради красного словца». За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: [C]Возвращаем из функции массив
|
От: |
night beast |
|
Дата: | 09.04.10 09:40 | ||
Оценка: |
Здравствуйте, Erop, Вы писали:
NB>>какой компилятор?
E>Для начала стоит узнать какой язык
дык написано: Си.
не думаю что время жизни переменных у языков разное.
Re[3]: [C]Возвращаем из функции массив
|
От: |
Lorenzo_LAMAS |
|
Дата: | 09.04.10 09:55 | ||
Оценка: |
_>Компилировалось в GCC, как чистый C.
Ну ведь не совсем же правда? Нормального компилятора С ты не использовал. гцц 4.1.2, с твоими же опциями, не воспроизводится.
Of course, the code must be complete enough to compile and link.
Re[3]: [C]Возвращаем из функции массив
|
От: |
Lorenzo_LAMAS |
|
Дата: | 09.04.10 10:13 | ||
Оценка: |
A non-lvalue expression with structure or union type, where the structure or union
contains a member with array type (including, recursively, members of all contained
structures and unions) refers to an object with automatic storage duration and temporary
lifetime.29) Its lifetime begins when the expression is evaluated and its initial value is the
value of the expression. Its lifetime ends when the evaluation of the containing full
expression or full declarator ends. Any attempt to modify an object with temporary
lifetime results in undefined behavior.….
A full expression is an expression that is not part of another expression or of a declarator.
Each of the following is a full expression: an initializer; the expression in an expression
statement; the controlling expression of a selection statement (if or switch); the
controlling expression of a while or do statement; each of the (optional) expressions of
a for statement; the (optional) expression in a return statement. There is a sequence
point between the evaluation of a full expression and the evaluation of the next full
expression to be evaluated.….
Of course, the code must be complete enough to compile and link.
Re[4]: [C]Возвращаем из функции массив
|
От: |
slava_phirsov |
|
Дата: | 09.04.10 10:17 | ||
Оценка: |
Здравствуйте, Lorenzo_LAMAS, Вы писали:
_>>Компилировалось в GCC, как чистый C.
L_L>Ну ведь не совсем же правда? Нормального компилятора С ты не использовал. гцц 4.1.2, с твоими же опциями, не воспроизводится.
Эйн
Цвей
Дрей
Не помню, что идет после «дрей» — «фиа»?
Не берусь судить, насколько там у них все адекватно — ну нету у меня сейчас под рукой тулзов, позже доберусь, скомпилирую, и даже наверное посмотрю под дизасмом, что там к чему
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[5]: [C]Возвращаем из функции массив
|
От: |
Lorenzo_LAMAS |
|
Дата: | 09.04.10 10:20 | ||
Оценка: |
_>Не берусь судить, насколько там у них все адекватно — ну нету у меня сейчас под рукой тулзов, позже доберусь, скомпилирую, и даже наверное посмотрю под дизасмом, что там к чему
да, версия гцц у меня такая же, как и у них.
дизасм — так надо посмотреть, что они генерят, а не твой компилятор.
Of course, the code must be complete enough to compile and link.
Re[3]: [C]Возвращаем из функции массив
|
От: |
Bell |
|
Дата: | 09.04.10 10:28 | ||
Оценка: |
Здравствуйте, slava_phirsov, Вы писали:
_>Здравствуйте, Bell, Вы писали:
B>>сгенерированные по умолчанию для пользовательского типа конструктор копирования и оператор присваивания прекрасно копируют члены класса — массивы
_>Собственно, речь больше о чистом C, хотя А где в стандарте говорится, что такое копирование возможно?
12.8/8
The implicitly-defined copy constructor for class X performs a memberwise copy of its subobjects. The
order of copying is the same as the order of initialization of bases and members in a user-defined constructor
(see 12.6.2). Each subobject is copied in the manner appropriate to its type:
— if the subobject is of class type, the copy constructor for the class is used;
— if the subobject is an array, each element is copied, in the manner appropriate to the element type;
…
_>Если член класса — указатель, указывающий на память в куче, то копирование по умолчанию однозначно не проходит, нужно брать в руки напильник и подпиливать…
Само собой — указатель — это не массив.
B>>Как я уже сказал выше — компилятор поэлементно копирует массивы — члены в неявном конструкторе копирования.
_>Компилировалось в GCC, как чистый C.
Да, я повторил описанную тобой проблему на codepad-е.
Любите книгу — источник знаний (с) М.Горький
Re[6]: [C]Возвращаем из функции массив
|
От: |
slava_phirsov |
|
Дата: | 09.04.10 10:35 | ||
Оценка: |
Здравствуйте, Lorenzo_LAMAS, Вы писали:
L_L>дизасм — так надо посмотреть, что они генерят, а не твой компилятор.
Если бы мог, ну а так — чем богаты, тем и….
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re[4]: [C]Возвращаем из функции массив
|
От: |
uzhas |
|
Дата: | 09.04.10 10:38 | ||
Оценка: |
_>>Собственно, речь больше о чистом C, хотя А где в стандарте говорится, что такое копирование возможно?
B>
B>12.8/8
B>The implicitly-defined copy constructor for class X performs a memberwise copy of its subobjects. The
B>order of copying is the same as the order of initialization of bases and members in a user-defined constructor
B>(see 12.6.2). Each subobject is copied in the manner appropriate to its type:
B>— if the subobject is of class type, the copy constructor for the class is used;
B>— if the subobject is an array, each element is copied, in the manner appropriate to the element type;
B>…
интересно увидеть цитату из стандарта Си, а не Си++
Re[4]: [C]Возвращаем из функции массив
|
От: |
slava_phirsov |
|
Дата: | 09.04.10 10:38 | ||
Оценка: |
Здравствуйте, Lorenzo_LAMAS, Вы писали:
L_L>Any attempt to modify an object with temporary lifetime results in undefined behavior.
К чему это ты? Хочешь сказать, printf пытается модифицировать foo().x ?
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
- Переместить
- Удалить
- Выделить ветку
Пока на собственное сообщение не было ответов, его можно удалить.
Я всегда думал, что когда вы хотите вернуть массив из функции, единственный способ сделать это — использовать такие указатели:
char * func();
Но вчера, пока я разбирался в K&R, я заметил ошибочно предполагаемый который char x()[]
также является допустимой конструкцией. Итак, я пошел дальше, чтобы проверить это и написал следующий код:
#include <stdio.h>
#include <stdlib.h>
char string1[10] = "123456789";
char x(void)[10];
int main(void) {
printf("string returned by x() is %s",x());
return EXIT_SUCCESS;
}
char x(void)[10] {
return x;
}
Компиляция с использованием GCC в Windows привела к следующим ошибкам:
..src7arrreturn.c:7:6: error: 'x' declared as function returning an array
..src7arrreturn.c: In function 'main':
..src7arrreturn.c:10:2: warning: format '%s' expects argument of type 'char *', but argument 2 has type 'int' [-Wformat]
..src7arrreturn.c: At top level:
..src7arrreturn.c:14:6: error: 'x' declared as function returning an array
..src7arrreturn.c: In function 'x':
..src7arrreturn.c:15:2: warning: return makes integer from pointer without a cast [enabled by default]
Что случилось? Я неправильно понимаю, что написано в книге? Как вы можете вернуть более одного значения (или адреса) из функции? Разве это не ограничено тем фактом, что у вас есть только один регистр ЦП ограниченного размера, который может содержать возвращаемое значение? Если вам нужно вернуть большой кусок данных, вы можете сделать это, только вернув ему адрес, верно?
Что делать с char x()[]? Такая вещь вообще используется?
РЕДАКТИРОВАТЬ: Я действительно неправильно понял материал из K&R. См. комментарий ниже.
Skip to content
У меня вопрос.
Я не могу скомпилировать свою программу.
Это очень простая программа, которая присваивает массив переменной и передает его в качестве аргумента функции. Теперь, возвращая этот массив, он возвращает массив.
Теперь я столкнулся с синтаксической ошибкой, которая выглядит следующим образом:
P2.c: 3: 4: error: ожидаемый идентификатор или ‘(’ перед ‘[’ токеном
int [] lifo (int [] arr)
Я не могу устранить ошибку для следующей программы:
#include<stdio.h>
int[] lifo(int[] arr)
{
return arr;
}
int main()
{
int arr1[100],arr2[100],arr[100];
int arr1_size,arr2_size,arr_size,i;
printf("Enter size of array1: n");
scanf("%d",&arr1_size);
printf("Enter size of array2: n");
scanf("%d",&arr2_size);
printf("Enter elements of array1: n");
for(i=0;i<arr1_size;i++)
{
scanf("%d",&arr1[i]);
}
printf("Enter elements of array2: n");
for(i=0;i<arr2_size;i++)
{
scanf("%d",&arr2[i]);
}
printf("Combining 2 arrays: n");
arr_size=arr1_size+arr2_size;
arr=lifo(arr1);
for(i=0;i<arr1_size;i++)
{
printf("%dn",arr[i]);
}
return 0;
}
Ответы
3
lifo(int[] arr)
Превратите это в:
lifo( int * arr )
Или же
lifo( int arr[] )
Обратите внимание, что даже второй вариант по-прежнему будет передавать массив указатель и нет. (Во-первых, вы не сможете определить количество элементов с помощью sizeof().) Вы по-прежнему можете использовать индексированный доступ на arr, но вы можете сделать это с помощью указателя любой.
Что касается возвращаемого значения, используйте int *. В конце концов, это то, что вы вернете — еще один указатель, а не массив.
(Ваша функция lifo, конечно, нерабочая … Я предполагаю, что это только для примера.)
В C вы всегда передаете массивы по его адресу, например по его началу. Для этого вам нужно использовать указатели. Это означает, что определение вашей функции недействительно. Должно получиться так:
int* lifo(int* arr)
{
return arr;
}
Но эта реализация не имеет смысла, потому что выход указывает на то же место, что и вход. Пожалуйста, обратитесь к книгам, в которых объясняется, как массивы обрабатываются в C.
В C массивы не могут быть возвращены из функции, как и они не могут быть переданы функции. Но давайте начнем с этого: ваше объявление массива здесь неверно:
int[] lifo(int[] arr)
В C скобки в объявлении массива указывают на после декларатора. Итак, чтобы написать функцию, принимающую и возвращающую массив, «правильный» синтаксис будет выглядеть так:
int lifo(int arr[])[]
Если вы попробуете это сделать, хороший компилятор выдаст вам такую ошибку:
error: ‘lifo’ declared as function returning an array
Просто нельзя возвращать массив.
Однако компилятор не жалуется на параметр int arr[]: это из-за специального правила для параметров функции — если они имеют тип массива, они автоматически скорректированный для соответствующего типа указателя. Итак, следующие объявления функций точно такие же:
void lifo(int arr[]);
void lifo(int *arr);
Вы не можете передавать или возвращать массивы. Но синтаксис позволяет функции выглядит как принимать массив — вместо этого он принимает указатель на первый элемент этого массива.
Другие вопросы по теме
Я пытаюсь написать функцию, которая возвращает указатель на функцию. Вот мой минимальный пример:
void (*myfn)(int)() // Doesn't work: supposed to be a function called myfn
{ // that returns a pointer to a function returning void
} // and taking an int argument.
Когда я собираю это с g++ myfn.cpp
это печатает эту ошибку:
myfn.cpp:1:19: error: ‘myfn’ declared as function returning a function
myfn.cpp:1:19: warning: extended initializer lists only available with -std=c++11 or -std=gnu++11 [enabled by default]
Означает ли это, что мне не разрешено возвращать указатель на функцию?
6
Решение
Вам разрешено возвращать указатель на функцию, и правильный синтаксис выглядит так:
void (*myfn())(int)
{
}
Полный пример:
#include <cstdio>
void retfn(int) {
printf( "retfnn" );
}
void (*callfn())(int) {
printf( "callfnn" );
return retfn;
}
int main() {
callfn()(1); // Get back retfn and call it immediately
}
Который компилируется и работает так:
$ g++ myfn.cpp && ./a.out
callfn
retfn
Если у кого-нибудь есть хорошее объяснение, почему в сообщении об ошибке g ++ говорится, что это невозможно, мне было бы интересно услышать это.
8
Другие решения
Других решений пока нет …
Greetings,
I’m sure its just a lack of knowledge aboutt he language, but truly it’s breaking my head on why it won’t work.
The following;
private
{ private declarations }
SQuestion: String;
SCorrectAnswer: String;
SIncorrectAnswers: Array[0..3] of String;
public
{ public declarations }
property Question: String read SQuestion write SQuestion;
property CorrectAnswer: String read SCorrectAnswer write SCorrectAnswer;
function GetRandomIncorrectAnswers: Array[0..3] of String;
When I compile this code, I get an error at function GetRandomIncorrectanswers:
Type identifier expected
Syntax error ';' expected but 'ARRAY' found
I truly don’t understand why these errors occur. I properly have a ‘;’ after both properties
As far as I know, the declaration of the function(whom expects no parameters)
Is it impossible to return an array?
I have the function body declared as following:
function TDmQuestion.GetRandomIncorrectAnswers: Array[0..3] of String;
begin
end;
I am very new to (free) pascal so excuse me if this question is silly.
to give a mental image of the above code;
Imagine a quiz application that randomly picks a question, then displays x amount of answers where one is the correct one.
The user picks one answer clicks a button and so on.
Thanks for reading.
« Last Edit: January 01, 2012, 01:17:54 pm by kyura »
Logged