Ir para conteúdo

POWERED BY:

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Bruno Alano

[Resolvido] Erro Simples. Printf.

Recommended Posts

Pessoal, estou com um problema neste pequeno código em C.

#include <stdio.h>

show (char msg) {
printf ("%c", msg);
}

int main() {
show ("Oie Mundo");
}

 

 

Ele retorna o seguinte erro:

 

brunoalano@brunoalano:~/C$ gcc Vars.c

Vars.c: In function 'main':

Vars.c:8: warning: passing argument 1 of 'show' makes integer from pointer without a cast

Vars.c:3: note: expected 'char' but argument is of type 'char *'

 

 

Ambiente: Ubuntu 10.10 com GCC atualizado.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Além do que o nosso colega Beraldo comentou, você esqueceu de declarar o retorno da função e também mudar '%c' para '%s', pois você quer morar uma string e não um caracter apenas.

 

Segue exemplo:

void show( char *msg )
{
   printf ("%s", msg);
}

Compartilhar este post


Link para o post
Compartilhar em outros sites

Primeiro você decide entaum:

Você qr imprimir pela função uma mensagem ou um caracter?

 

é soh você analisar essa perguntinha.. q fica facil.

 

[]s

Compartilhar este post


Link para o post
Compartilhar em outros sites

Além do que o nosso colega Beraldo comentou, você esqueceu de declarar o retorno da função e também mudar '%c' para '%s', pois você quer morar uma string e não um caracter apenas.

 

Segue exemplo:

void show( char *msg )
{
   printf ("%s", msg);
}

 

A função é do tipo void. Ele não precisa explicitar retorno, e seria errado retornar algum valor.

Compartilhar este post


Link para o post
Compartilhar em outros sites

A função é do tipo void. Ele não precisa explicitar retorno, e seria errado retornar algum valor.

 

ausência de retorno indica retorno de int (pelo menos no gcc). creio que isso possa mudar de compilador para compilador

 

mas é sempre ideal explicitar o tipo do retorno

 

boas práticas de programação

Compartilhar este post


Link para o post
Compartilhar em outros sites

Eu não estou falando de gcc. Estou falando de C, e, no C, é errado retornar valores em funções de tipo void.

 

 

E só pra constar,

 

 warning: `return' with a value, in function returning void

 

Esse é o warning gerado pelo gcc quando se retorna valores de uma função de tipo void.

 

O que às vezes se defende é que funções de tipo void são má prática de programação. Isso, contudo, não torna o que eu disse falso, porque neste caso o certo seria declarar a função com algum tipo diferente de void.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Sempre levo funções do tipo void como procedimentos.. Pq "geralmente" procedimentos não retornam valores.

 

[]s

Compartilhar este post


Link para o post
Compartilhar em outros sites
A função é do tipo void. Ele não precisa explicitar retorno, e seria errado retornar algum valor.

 

Sempre é bom explicitar o tipo. Você não sabe que implementação de C é usada no compilador alheio. O C99 procura remover o tipo int implícito de declarações de funções, mas nada impede que as outras pessoas usem um compilador que não implementa essa parte do padrão. É quase igual ao fflush(stdin) aqui no fórum.

 

Segundo o C99:

 

return without expression not permitted in function that returns a value (and vice versa).

 

A return statement with an expression shall not appear in a function whose return type is void. A return statement without an expression shall only appear in a function whose return type is void.

 

 

O que acontece no GCC:

 

int soma(int a, int B){
return a+b;
}

int main() {
soma(-10, 20);
return 0;
}

 

 

.file	"if.c"
.text
.globl soma
.type	soma, @function
soma:
pushl	%ebp
movl	%esp, %ebp
movl	12(%ebp), %eax
movl	8(%ebp), %edx
leal	(%edx,%eax), %eax
popl	%ebp
ret
.size	soma, .-soma
.globl main
.type	main, @function
main:
pushl	%ebp
movl	%esp, %ebp
subl	$8, %esp
movl	$20, 4(%esp)
movl	$-10, (%esp)
call	soma
movl	$0, %eax
leave
ret
.size	main, .-main
.ident	"GCC: (SUSE Linux) 4.5.0 20100604 [gcc-4_5-branch revision 160292]"
.section	.comment.SUSE.OPTs,"MS",@progbits,1
.string	"ospwg"
.section	.note.GNU-stack,"",@progbits

 

Note que o resultado da soma das parcelas é colocado no registrador eax, que, na arquitetura x86 do meu desktop, é o registrador usado para armazenar valores de retorno das funções. Você também pode reconhecer o return 0 do main sendo colocado no eax.

 

 

O código a seguir com função void e return compila sem warnings nem erros e executa normalmente:

 

#include <stdio.h>
void nada(int a) {
int b=1;
if (a < B)
	return;

while(b < a) {
	printf("%d ", B);
	b++;
}
}

int main() {
puts("a=5...");
nada(5);
puts("");
puts("a=-10...");
nada(-10);
return 0;
}

 

 

Para o programador C/C++/Java/C# a função nada não retorna nenhum valor. Alguns até diriam que o return no if está errado. Mas a coisa é um pouco mais complexa quando você desce pro código assembly:

 

 

	.file "vf.c"
	.section 	.rodata
.LC0:
	.string "%d "
	.text
.globl nada
	.type nada, @function
nada:
	pushl %ebp
	movl	%esp, %ebp
	subl	$40, %esp
	movl	$1, -12(%ebp)
	movl	8(%ebp), %eax
	cmpl	-12(%ebp), %eax
	jge 	.L4
	jmp 	.L1
.L5:
	movl	$.LC0, %eax
	movl	-12(%ebp), %edx
	movl	%edx, 4(%esp)
	movl	%eax, (%esp)
	call	printf
	addl	$1, -12(%ebp)
.L4:
	movl	-12(%ebp), %eax
	cmpl	8(%ebp), %eax
	jl 	.L5
.L1:
	leave
	ret
	.size nada, .-nada
	.section 	.rodata
.LC1:
	.string "a=5..."
.LC2:
	.string ""
.LC3:
	.string "a=-10..."
	.text
.globl main
	.type main, @function
main:
	pushl %ebp
	movl	%esp, %ebp
	andl	$-16, %esp
	subl	$16, %esp
	movl	$.LC1, (%esp)
	call	puts
	movl	$5, (%esp)
	call	nada
	movl	$.LC2, (%esp)
	call	puts
	movl	$.LC3, (%esp)
	call	puts
	movl	$-10, (%esp)
	call	nada
	movl	$0, %eax
	leave
	ret
	.size main, .-main
	.ident "GCC: (SUSE Linux) 4.5.0 20100604 [gcc-4_5-branch revision 160292]"
	.section 	.comment.SUSE.OPTs,"MS",@progbits,1
	.string "ospwg"
	.section 	.note.GNU-stack,"",@progbits

 

 

nada:
	pushl %ebp
	movl	%esp, %ebp
	subl	$40, %esp
	movl	$1, -12(%ebp)
	movl	8(%ebp), %eax
	cmpl	-12(%ebp), %eax
	jge 	.L4
	jmp 	.L1

 

Na função NADA, o valor de b é movido para a pilha, no lugar convencionado para as variáveis (depois de você ter colocado o ebp na pilha): movl $1, -12(%ebp)

O argumento 5 passado para a função é colocado no registrador eax. A sequência cmpl e jge fazem com que o fluxo seja desviado para o while, enquanto que o jmp executa o if da função.

Se o if for executado, não temos nenhuma instrução movendo qualquer valor motivada somente pela existência do return. O que acontece é que somente as instruções leave e ret são usadas.

Se o while for executado, observa-se uma troca de valores: o valor de b é movido para o eax e a sequência cmpl/jl é executada enquanto o valor de eax (1, registrador de retorno) for menor que o valor na pilha de argumentos da função.

No final das contas, o "retorno" dessa função void a baixo nível é 5. Até lembra um pouco o comportamento das funções no Ruby (o código a seguir é código Ruby e o valor da variável total é retornado sem ter um return explícito):

 

def soma(p1,p2)
print "correta"
total=p1+p2
end

soma(1,2)
resultado = soma(1,2)
print resultado

 

 

Se você compilar o seguinte código com -Wall e -Wextra vai receber um warning sobre o return:

 

int soma(int a, int B){
return;
}

int main() {
soma(-10, 20);
return 0;
}

 

Mas a coisa compila e executa, já que warnings não são impeditivos. E o que você vê no código assembly é uma instrução ret na função soma. Se alguém tiver um Linux e vontade, instale o Sun Studio (não tem pra Windows). Como dizem que é o único compilador/coleção de ferramentas que atende o ISO C99 completamente, seria uma boa ver se ele trata o return vazio em função com tipo de retorno como erro (e cancela a compilação) ou se também interpretaram o "shall not" como um relaxamento de condições e só emitem um warning como o GCC.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Isis, não estou falando de Assembly, ou de meias-implementações de C. Vide o exemplo de gente que tenta programar em C compilando com o g++.

 

Independente do assembly gerado, o que é certo, e que é definido pelo padrão, é:

 

6.8.6.4 The return statement

 

Constraints

 

1 A return statement with an expression shall not appear in a function whose return type

is void. A return statement without an expression shall only appear in a function

whose return type is void.

 

 

Como visto, a primeira afirmação do padrão do C (TC3 Committee Draft - 2007, ISO/IEC 9899) é que funções de tipo void não devem ter returns acompanhados por expressões. O seguinte código é válido:

 

void funcao(void)
{
 return;
}

 

O seguinte código é inválido:

 

void funcao(void)
{
 return 0;
}

 

Outra coisa: cuidado com "warnings não indicam erros". Um compilador pode "fazer vistas grossas" a um erro de programação (quebra de lei de definição da linguagem). Ele talvez permita que o processo de compilação continue e gere um executável que funciona, naquele sistema, da forma como o usuário deseja. Contudo, isso não significa que a causa do warning não foi um erro. Exemplo:

 

#include <stdlib.h>
#include <stdio.h>

void main(void)
{
printf("Funciona.");
}

 

Este código gera um warning no gcc, e o programa funciona de acordo com o esperado. Contudo, o código é errado e deve ser corrigido.

 

fflush(stdin) é sempre errado. Assim como retornar valores de funções retornando void é sempre errado.

 

Se flexibilizarmos a extensão do conformismo com o padrão, não existe padrão. E sem padrão não existe a linguagem.

 

 

O motivo pelo qual compiladores às vezes apresentam esta flexibilidade não é por causa de interpretação diferenciada do padrão, e sim para permitir que códigos antigos (que, apesar de errados hoje, não eram errados antes da atualização da linguagem) ainda funcionem.

 

Contudo, o que vai contra o que está escrito (e "shall not" indica obrigatoriedade) está errado, e sempre estará.

 

 

p.s.: quero complementar. Você não deve basear o julgamento quanto à corretude de um código em C no assembly gerado. O C, a não ser quando explicitamente especificado, transcende a implementação e arquitetura. Os casos que discutimos não são casos em que a escolha quanto à implementação é deixada aberta ao implementador (exemplificamos gcc). O motivo, repito, pelo qual o compilador é tolerante quanto aos erros cometidos, é que códigos escritos antes da padronização "quebrariam" caso o compilador fosse rígido. Novos códigos, contudo, devem seguir o padrão atual de corretude.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Isis, não estou falando de Assembly, ou de meias-implementações de C.

 

Mas eu estou falando de assembly no meu post e do GCC porque o compilador foi mencionado. Ponto.

 

 

 

Independente do assembly gerado, o que é certo, e que é definido pelo padrão, é:

 

6.8.6.4 The return statement

 

Constraints

 

1 A return statement with an expression shall not appear in a function whose return type

is void. A return statement without an expression shall only appear in a function

whose return type is void.

 

 

Como visto, a primeira afirmação do padrão do C (TC3 Committee Draft - 2007, ISO/IEC 9899) é que funções de tipo void não devem ter returns acompanhados por expressões.

 

 

Agora que você escreveu isso, volte ao início do meu post e repare que eu também escrevi isso. Ou seja, não estou te contradizendo.

 

 

 

Outra coisa: cuidado com "warnings não indicam erros". Um compilador pode "fazer vistas grossas" a um erro de programação (quebra de lei de definição da linguagem). Ele talvez permita que o processo de compilação continue e gere um executável que funciona, naquele sistema, da forma como o usuário deseja. Contudo, isso não significa que a causa do warning não foi um erro.

 

Eu não falei que warnings não indicam erros. Warnings não são Errors. Se você já revisou uma tradução de algum programa sabe que é comum as pessoas confundirem os dois. Simples assim. Dê uma olhada no fórum na quantidade de gente que reclama que está dando erro na compilação e tudo o que aparece são warnings (tradução correta: aviso). Ou seja, um error (erro) impede a compilação. Um warning (aviso) não, mas é o modo do compilador te avisar que pode dar m* e a responsabilidade é inteiramente do programador. Porém, se você quer ser binário, existe uma opção do GCC que trata todos os warnings como errors e cancela a compilação.

 

 

 

fflush(stdin) é sempre errado. Assim como retornar valores de funções retornando void é sempre errado.

 

Eu não falei que fflush(stdin) é certo. Procure meus históricos de post p/ ver se eu não tenho vontade de xingar quem usa isso.

E também não falei em momento algum no post que retornar valores em funções void era certo. Releia o início do post, onde eu colo duas partes do C99, se isso não ficou claro.

 

 

 

 

Se flexibilizarmos a extensão do conformismo com o padrão, não existe padrão. E sem padrão não existe a linguagem.

 

Sem padrão existe linguagem. Até sem gramática é capaz de existir linguagem (o que não adianta muito, por questões práticas).

Em 1942 Konrad Zuse criou a Plankalkül e o principal obstáculo p/ se fazer um compilador era a inexistência de gramática.

Assembly: levando em conta as diferenças de arquiteturas (RISC/CISC), não existe padrão normativo que abranja as duas ao mesmo tempo, como acontece com o C99. De um lado você tem instruções simples e em menor número que seguem um padrão entre elas e do outro você tem trocentas instruções a mais que não têm um padrão entre si.

 

 

O motivo pelo qual compiladores às vezes apresentam esta flexibilidade não é por causa de interpretação diferenciada do padrão, e sim para permitir que códigos antigos (que, apesar de errados hoje, não eram errados antes da atualização da linguagem) ainda funcionem.

 

Interpretação errada ocorre. Já vi gente dizer que "shall not" não é "must not"/"cannot".

 

 

 

 

p.s.: quero complementar. Você não deve basear o julgamento quanto à corretude de um código em C no assembly gerado. O C, a não ser quando explicitamente especificado, transcende a implementação e arquitetura. Os casos que discutimos não são casos em que a escolha quanto à implementação é deixada aberta ao implementador (exemplificamos gcc). O motivo, repito, pelo qual o compilador é tolerante quanto aos erros cometidos, é que códigos escritos antes da padronização "quebrariam" caso o compilador fosse rígido. Novos códigos, contudo, devem seguir o padrão atual de corretude.

 

Me aponte onde eu explicitamente disse que o código em assembly justificava o código em C. Vamos lá. O que foi mencionado pelo Quit foi que no gcc funcionava de um jeito, mas como normalmente as pessoas se esquecem que existe um assembly por baixo que muitas vezes explica as coisas, eu fiz um post do tamanho do mundo e que aparentemente você não entendeu.

 

Detalhe: o void main resulta num ERROR porque o padrão diz que o tipo de retorno do main deve ser compatível com int. Procure um tópico sobre isso onde diziam até que main podia ser void pq era uma função.

Compartilhar este post


Link para o post
Compartilhar em outros sites
A função é do tipo void. Ele não precisa explicitar retorno, e seria errado retornar algum valor.

 

Sempre é bom explicitar o tipo. Você não sabe que implementação de C é usada no compilador alheio. O C99 procura remover o tipo int implícito de declarações de funções, mas nada impede que as outras pessoas usem um compilador que não implementa essa parte do padrão. É quase igual ao fflush(stdin) aqui no fórum.

 

É errado explicitar valor de retorno de funções retornando void. Não importa se o compilador x, y ou z, na arquitetura X, Y ou Z constrói assembly que na verdade retorna algo. No escopo e lógica do C, é errado explicitar valores de retorno em funções retornando void. Não importa qual versão ou compilador se usa, você deve se guiar pelo padrão mais atual. E, mais uma vez, "é errado (...)". Note que eu não afirmei que é errado explicitar retorno (um "return"). É plausível (desde que não seja adicionado valor algum).

 

Das duas, uma: (1) você concorda comigo na erroneidade de se explicitar valores de retorno em funções retornando void; (2) você não entende que, independente da implementação e arquitetura, devemos seguir o padrão atual, ao afirmar que "nada impede que as outras pessoas usem um compilador que não implementa essa parte do padrão", portanto "sempre é bom explicitar o tipo".

 

Note que, em ambos os casos, a análise do assembly gerado pelo GCC em uma máquina x86 é irrelevante.

 

 

Talvez haja desentendimento de linguagem (humana) entre "explicitar o tipo void" com "não explicitar um tipo". Se for este o caso, você defende desde o início que devemos evitar usar funções retornando void. A discussão é interessante, mas fora do escopo do tópico. Se este for o caso e quiser prosseguir, vamos fazer isso por mensagens privadas.

 

 

Mas a coisa compila e executa, já que warnings não são impeditivos. E o que você vê no código assembly é uma instrução ret na função soma. Se alguém tiver um Linux e vontade, instale o Sun Studio (não tem pra Windows). Como dizem que é o único compilador/coleção de ferramentas que atende o ISO C99 completamente, seria uma boa ver se ele trata o return vazio em função com tipo de retorno como erro (e cancela a compilação) ou se também interpretaram o "shall not" como um relaxamento de condições e só emitem um warning como o GCC.

 

"Shall not" não pode ser interpretado de outra maneira que não a proibitiva (você sabe disso, eu sei). Mais uma vez eu digo: compiladores que geram executáveis que funcionam como esperado, mesmo que haja quebra de regra do padrão, o fazem somente para que programas escritos antes da data do padrão continuem funcionando. Ainda assim, seus códigos são, hoje, errados.

 

Eu preciso eliminar uma ambiguidade em meu post: "não existe linguagem sem padrão" foi infeliz. Deveria ter dito "não existe C sem padrão" (poderia-se argumentar que não existe computação de linguagens não recursivamente enumeráveis, mas entraríamos em discussões sobre Teoria da Computação e Linguagens Formais).

 

De volta ao C... Kerningham e Ritchie comentam, no início do "The C Programming Language" (bem antes da existência dos comitês na forma como se encontram hoje, muito menos da existência do padrão ao qual nos referimos), que a padronização feita pela ANSI, na época, havia possibilitado o crescimento do C como linguagem. A compilação não é um processo trivial, e, em uma atmosfera com a diversidade de arquiteturas que temos hoje, é ilógico não dar importância a padrões. De nada adianta a organização e consenso se, na prática, eles não são aplicados. A tarefa de sincronizar o caos que é a computação prática, hoje, é importante e difícil, e é nosso dever acatar os consensos que visam uniformizar o uso das ferramentas disponíveis.

 

 

Eu não falei que fflush(stdin) é certo. Procure meus históricos de post p/ ver se eu não tenho vontade de xingar quem usa isso.

 

Eu sei que você sabe que é errado. Não disse que você achava certo. Vi, aliás, que você geralmente dá bons conselhos e procura ajudar da forma coerente os usuários do fórum. Se não fosse por isso, eu não perderia meu tempo discutindo com você.

 

 

 

Detalhe: o void main resulta num ERROR porque o padrão diz que o tipo de retorno do main deve ser compatível com int. Procure um tópico sobre isso onde diziam até que main podia ser void pq era uma função.

 

Errado. Seguindo a lógica, você não deveria afirmar isto, pois:

 

1. Você sabe que o padrão afirma que é errado explicitar valores de retorno em funções retornando void.

2. Você sabe que compiladores, contudo, geram apenas warnings se alguém quebra a regra da linguagem (o programa compila e "funciona").

 

Ou seja, uma quebra de regra de linguagem não implica em geração de errors, mesmo em compiladores atuais (novamente digo que é por causa de compatibilidade regressiva).

 

Seria, então, certo, de acordo com a lógica, que você afirmasse:

 

1. Você sabe que o padrão afirma que é errado declarar main com um tipo diferente de int ou equivalente (alcançado através de typedef(s)).

2. Faria sentido, aqui, esperar que compiladores não necessariamente gerassem um "error", mas um "warning".

 

Aliás, isso é o que de fato acontece:

 

 

#include <stdlib.h>
#include <stdio.h>

void main(void)
{
}

 

a.c: In function `main':

a.c:5: warning: return type of 'main' is not `int'

 

 

Sobre os parâmetros de compilação que fazem o GCC atender estritamente o padrão, -pedantic nos satisfaz, além de -Wall e extra.

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.