Hoje eu estava ensinando alguns amigos a usar C struct
s. Um deles perguntou se você pudesse voltar a struct
partir de uma função, ao que eu respondi: "Não! Você iria retornar ponteiros para dinamicamente malloc
ed struct
. S em vez"
Vindo de alguém que faz principalmente C ++, eu esperava não conseguir retornar struct
s por valores. No C ++, você pode sobrecarregar os operator =
objetos e faz todo sentido ter uma função para retornar seu objeto por valor. Em C, no entanto, você não tem essa opção e, por isso, me fez pensar no que o compilador está realmente fazendo. Considere o seguinte:
struct MyObj{
double x, y;
};
struct MyObj foo(){
struct MyObj a;
a.x = 10;
a.y = 10;
return a;
}
int main () {
struct MyObj a;
a = foo(); // This DOES work
struct b = a; // This does not work
return 0;
}
Entendo por struct b = a;
que não deve funcionar - você não pode sobrecarregar operator =
seu tipo de dados. Como é que a = foo();
compila bem? Isso significa algo diferente struct b = a;
? Talvez a pergunta a fazer seja: O que exatamente faz a return
declaração em conjunto para =
assinar?
[edit]: Ok, acabei de apontar que struct b = a
é um erro de sintaxe - isso é correto e eu sou um idiota! Mas isso torna ainda mais complicado! Usar struct MyObj b = a
realmente funciona! O que estou perdendo aqui?
struct b = a;
é um erro de sintaxe. E se você tentarstruct MyObj b = a;
?struct MyObj b = a;
parece funcionar :)Respostas:
Você pode retornar uma estrutura de uma função (ou usar o
=
operador) sem problemas. É uma parte bem definida da linguagem. O único problemastruct b = a
é que você não forneceu um tipo completo.struct MyObj b = a
vai funcionar muito bem. Você também pode passar estruturas para funções - uma estrutura é exatamente igual a qualquer tipo interno para fins de passagem de parâmetros, valores de retorno e atribuição.Aqui está um programa de demonstração simples que executa todos os três - passa uma estrutura como parâmetro, retorna uma estrutura de uma função e usa estruturas nas instruções de atribuição:
O próximo exemplo é praticamente o mesmo, mas usa o
int
tipo interno para fins de demonstração. Os dois programas têm o mesmo comportamento em relação à passagem por valor para passagem de parâmetros, atribuição, etc .:fonte
Ao fazer uma chamada como
a = foo();
, o compilador pode enviar o endereço da estrutura de resultados para a pilha e transmiti-lo como um ponteiro "oculto" para afoo()
função. Efetivamente, pode se tornar algo como:No entanto, a implementação exata disso depende do compilador e / ou plataforma. Como observa Carl Norum, se a estrutura é pequena o suficiente, ela pode até ser devolvida completamente em um registro.
fonte
foo
quadro da pilha. Tem que estar em um lugar que sobreviva após o retorno defoo
.foo
. Dentro da funçãofoo
, você acabou de fazer a atribuição*r = a
final faria (efetivamente) uma cópia da variável local na variável do chamador. Eu digo "efetivamente" porque o compilador pode implementar o RVO e eliminara
completamente a variável local .c return struct
: elas sabem que no cdecleax
é retornado por valor e que as estruturas em geral não se encaixameax
. Era isso que eu estava procurando.A
struct b
linha não funciona porque é um erro de sintaxe. Se você expandi-lo para incluir o tipo, ele funcionará bemO que C está fazendo aqui é essencialmente
memcpy
da estrutura de origem até o destino. Isso é verdade para a atribuição e o retorno destruct
valores (e realmente para todos os outros valores em C)fonte
memcpy
nesse caso - pelo menos, se a estrutura for razoavelmente grande.memcpy
função para as situações de estrutura. Você pode fazer um programa de teste rápido e ver o GCC fazer isso, por exemplo. Para tipos internos que não acontecem - eles não são grandes o suficiente para acionar esse tipo de otimização.memcpy
símbolo definido, por isso, muitas vezes, encontramos erros de vinculador de "símbolo indefinido" quando o compilador decide cuspir um por conta própria.sim, é possível que possamos passar estrutura e retornar estrutura também. Você estava certo, mas na verdade não passou o tipo de dados que deveria ser assim: struct MyObj b = a.
Na verdade, também fiquei sabendo quando estava tentando encontrar uma solução melhor para retornar mais de um valor para a função sem usar ponteiro ou variável global.
Agora abaixo está o exemplo do mesmo, que calcula o desvio das notas de um aluno sobre a média.
fonte
Tanto quanto me lembro, as primeiras versões de C só permitiam retornar um valor que pudesse caber em um registrador de processador, o que significa que você só podia retornar um ponteiro para uma estrutura. A mesma restrição aplicada aos argumentos da função.
Versões mais recentes permitem passar objetos de dados maiores, como estruturas. Eu acho que esse recurso já era comum nos anos oitenta ou início dos anos noventa.
As matrizes, no entanto, ainda podem ser passadas e retornadas apenas como ponteiros.
fonte
Você pode atribuir estruturas em C.
a = b;
é uma sintaxe válida.Você simplesmente deixou de lado parte do tipo - a tag struct - na sua linha que não funciona.
fonte
Não há problema em devolver uma estrutura. Será passado por valor
Mas, e se a estrutura contiver algum membro que tenha o endereço de uma variável local
Agora, aqui e1.name contém um endereço de memória local para a função get (). Quando get () retornar, o endereço local do nome teria sido liberado. Portanto, no chamador, se tentarmos acessar esse endereço, isso poderá causar uma falha de segmentação, pois estamos tentando um endereço liberado. Isso é mau..
Onde como e1.id será perfeitamente válido, pois seu valor será copiado para e2.id
Portanto, devemos sempre tentar evitar o retorno de endereços de memória local de uma função.
Qualquer coisa alocada pode ser devolvida como e quando desejado
fonte
funciona bem com versões mais recentes de compiladores. Assim como o id, o conteúdo do nome é copiado para a variável de estrutura atribuída.
fonte
struct var e2 endereço enviado como arg para chamar a pilha e os valores são atribuídos lá. De fato, get () retorna o endereço de e2 em eax reg. Isso funciona como chamada por referência.
fonte