paulo1205
(usa Ubuntu)
Enviado em 20/07/2019 - 15:35h
tmp escreveu:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]){
FILE *r, *w;
char buffer[999999];
int it;
float ft;
if(strcmp(argv[1],"--help")==0){
printf("xcal é uma calculadora por parâmetros\n");
printf("um exemplo de cálculo seria:\n");
printf("./xcal * 2 5\n");
printf("nesse exemplo você estaria calculado 2 x 5\n");
printf("outros parâmetros seriam:\n");
printf("+ faz soma, exemplo: ./xcal + 1 1\n");
printf("- diminui um valor, exemplo de uso: ./xcal - 2 1\n");
printf("x faz conta de vezes, exemplo de uso: ./xcal x 2 5\n");
printf("/ divide, exemplo de uso: ./xcal / 5 2\n");
}
else if(strcmp(argv[1], "+")==0){
it = (int) argv[2] + argv[3];
printf("Result: %d\n", it);
}
else{
printf("Digite ./xcal --help para obter ajuda\n");
}
return 0;
}
Qual sistema operacional e qual compilador você está usando? Quando você compila o programa acima, o compilador não lhe mostra nenhuma mensagem de diagnóstico, indicando ou erro ou código perigoso?
Aqui, usando o GCC 7.4 no Linux, se eu compilar o código acima sem nenhuma opção de diagnóstico, mesmo assim o compilador mostra duas mensagens de alerta.
gcc x.c
x.c: In function ‘main’:
x.c:21:12: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
it = (int) argv[2] + argv[3];
^
x.c:21:10: warning: assignment makes integer from pointer without a cast [-Wint-conversion]
it = (int) argv[2] + argv[3];
^
Se eu ativar as opções de código que eu sempre recomendo (“
-O2 -Wall -Werror -pedantic-errors”), aí a coisa fica ainda mais séria.
gcc -O2 -Werror -Wall -pedantic-errors x.c
x.c: In function ‘main’:
x.c:21:12: error: cast from pointer to integer of different size [-Werror=pointer-to-int-cast]
it = (int) argv[2] + argv[3];
^
x.c:21:10: error: assignment makes integer from pointer without a cast [-Wint-conversion]
it = (int) argv[2] + argv[3];
^
x.c:8:11: error: unused variable ‘ft’ [-Werror=unused-variable]
float ft;
^~
x.c:6:10: error: unused variable ‘buffer’ [-Werror=unused-variable]
char buffer[999999];
^~~~~~
x.c:5:15: error: unused variable ‘w’ [-Werror=unused-variable]
FILE *r, *w;
^
x.c:5:11: error: unused variable ‘r’ [-Werror=unused-variable]
FILE *r, *w;
^
cc1: all warnings being treated as errors
Algumas das mensagens se referem a variáveis que você declarou mas não usou (
ft,
buffer,
r e
w). Mesmo que o compilador seja esperto o bastante para evitar o eventual desperdício de espaço, é bom, para facilitar a manutenção do código do programa, nunca declarar coisas que não serão usadas.
Mas o seu problema mesmo está indicado nas outras duas mensagens.
Observe que o tipo de
argv é “
char **Ӡ. Isso significa que
argv tem o tipo “ponteiro para dado do tipo ponteiro para dado do tipo
char”‡. Se
argv tem esse tipo, então o tipo de “
argv[n]”, sendo
n um inteiro qualquer, tem o tipo “ponteiro para dado do tipo
char”.
E aí vem uma pegadinha para alguns iniciantes em C (e mesmo para — ou
especialmente para — quem já programou com outras linguagens de programação): o C não tem um tipo nativo para representar
strings de texto. Em lugar disso,
strings são consideradas como meros
arrays de caracteres contíguos, e cada
string é indicada pelo ponteiro para o primeiro dos caracteres que a compõem. Essa convenção de representação é seguida por várias funções da biblioteca padrão do C que trabalham com
strings (todas as funções de <string.h>, e outras de outras partes, tais como
printf(),
scanf(),
fopen() etc.), mas
não no nível da linguagem de programação em si††.
Desse modo, quando você faz, no código acima, “
it = (int) argv[2] + argv[3];”, eis o que você efetivamente tem:
• A primeira parcela da soma é “
(int)argv[2]”, que é entendida pelo compilador, por partes, da seguinte maneira:
• você obtém o valor de
argv[2], que é do tipo “ponteiro para dado do tipo
char” (e é, portanto, um valor que representa um endereço da memória);
• você força a conversão desse valor para um valor do tipo
int (a primeira mensagem de erro que apareceu quando eu tentei compilar seu programa se deu porque, num compilador de 64 bits, como o que eu usei, ponteiros têm 64 bits de largura, mas inteiros têm apenas 32, de modo que o dado resultante da conversão necessariamente desprezou parte das informações contidas no dado original, e tal situação normalmente indica erro lógico no programa).
• A segunda parcela da soma usa o valor de
argv[3], que é um dado do tipo “ponteiro para dado do tipo
char”.
• A soma tem, portanto, uma parcela inteira (obtida por conversão) e uma parcela que é um ponteiro. Isso indica para o compilador que você tem uma operação de aritmética de ponteiros, que funciona da seguinte maneira:
• o ponteiro é entendido como apontando para múltiplos elementos (como um vetor), e o resultado da soma corresponde ao endereço do elemento desse vetor que teria o valor inteiro como índice (em outras palavras, se
p é um ponteiro para um tipo qualquer (exceto
void) e
n é um inteiro, então
(p+n == n+p) && (p+n == &p[n]));
• o resultado da soma é, portanto, também um dado de um tipo ponteiro: o mesmo tipo que o da parcela da soma que é um ponteiro (no seu caso, como essa parcela tinha o tipo “
char *”, o ponteiro produzido pela soma também é um “
char *”).
• Finalmente, o valor da soma é atribuído à variável
it. Contudo, como
it não é do mesmo tipo que a soma, o compilador lhe avisa sobre a conversão de tipos implícita que terá (ou teria, no caso da compilação que é abortada, quando eu usei a opção “
-Werror”) de ocorrer para que a atribuição do valor a uma variável com tipo diferente possa (ou pudesse) ser feita.
A moral da história toda é que o seu programa não produziu o valor que você queria porque você não expressou o que queria corretamente. Você provavelmente gostaria de transformar os numerais em forma de
strings apontadas pelos argumentos
argv[2] e
argv[3] em dados numéricos, e então obter a soma dos valores desses dados numéricos.
Como o C não oferece suporte a
strings em nível de linguagem de programação, você terá de recorrer a uma das funções da biblioteca que permitem fazer tal conversão, ou escrever seu próprio algoritmo para fazer tal conversão. Caso opte por usar uma das funções da biblioteca, pode usar
strtol(),
sscanf() com a conversão
"%d" ou
atoi(), entre outras. Dessas,
atoi() é a mais simples, mas tem a grave desvantagem de não permitir diagnóstico de erro (por exemplo, se o usuário passar como argumento uma
string que não seja um numeral válido para a conversão), ao passo que
strtol() e
sscanf() podem facilitar tal diagnóstico.
Se você quiser usar dados em ponto flutuante (trocando
it pelo
ft que você declarou mas não usou), pode usar
atof(),
strtod() ou a conversão
"%f" de
sscanf().
---------
† Aquela notação usando colchetes vazios é uma forma obsoleta, herdada da antiga linguagem B, de declarar ponteiros (B não tinha a notação usando asterisco). Ela ainda é relativamente bem aceita em C quando, na declaração de parâmetro de funções, se quer dar o sentido que que aquele ponteiro, em particular, vai apontar para um vetor de elementos, em vez de apontar para um elemento só. Assim, “
char *argv[]” é um sinônimo absoluto de “
char **argv”.
‡ Ou, se você considerar que esse ponteiro necessariamente indica um vetor, pode entender como “vetor de elementos do tipo ponteiro para dado do tipo
char”.
†† O único suporte que a linguagem dá é transformar constantes literais na forma de texto entre aspas (e.g.
"Isto é um texto" ou
"Hello, World!\n") automaticamente num
array de caracteres compatível com essa notação.
... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)