paulo1205
(usa Ubuntu)
Enviado em 21/11/2021 - 15:54h
ApprenticeX escreveu:
Boa Noite a Todos,
Como apagar um vetor/array corretamente!
Entendi que vetor e array é a mesma coisa! Está certo isso?
No contexto de programação em C,
array e vetor são frequentemente usados para referir-se ao mesmo tipo de objeto, que é a disposição de vários elementos em posições adjacentes de memória, podendo ser indexados a partir de um endereço de primeiro elemento e um deslocamento para algum elemento após o primeiro. A tradução direta de
array seria “arranjo”, mas essa expressão não vingou muito no Brasil; “vetor” acabou sendo mais comum. Em Inglês, raramente vejo usarem
vector (exceto em C++, que tem o tipo
std::vector como um tipo de
array dinamicamente alocado), e
array me parece ser mais comum.
No exemplo abaixo estou tentando saber qual a melhor forma de apagar uma string, ou melhor dizendo um vetor de caracteres
char Text[] = "Viva o Linux";
// Considerei assim a melhor, é isso mesmo? Pois aqui não uso nenhuma função apenas 1 Operador!
for(int x = 0; x < sizeof(Text); x++) Text[x] = 0;
Não esqueça de considerar o laço de repetição. Na prática, é a mesma coisa que
memset() faz por dentro.
/* Qual dos memset abaixo de fato é o correto? Porque fiquei confuso com tantas formas!
1) Uso Text OU &Text ???
2) Uso sizeof Text OU sizeof(Text)
3) Uso 0x0 OU 0 OU \0
3) O que é esse 0x0 ??? Ele é melhor?
*/
Dado que o primeiro parâmetro de
memset() é do tipo “ponteiro para qualquer tipo de dados” (
void *) e que o segundo parâmetro é do tipo
int, todas as formas são sintaticamente corretas.
Falando do segundo parâmetro primeiro, o fato de você ter um protótipo de função que obriga que o valor do parâmetro seja do tipo
int já basta para que você não tenha muito como fugir de que o valor passado à função será um valor inteiro, mesmo que ele tenha de ser convertido implicitamente pelo compilador.
Note porém que todas as constantes que você usou têm o mesmo valor e o mesmo tipo, sendo apenas representações diferentes da mesma coisa:
0 é um valor inteiro com valor igual a zero, expresso numa notação correspondente a uma constante decimal;
0x0 é o mesmo valor zero em notação hexadecimal,
00 (que você não usou, mas que eu tomei a liberdade de incluir aqui) é o mesmo valor inteiro zero, mas em notação octal; e
'\0' também é um valor inteiro em C
† e também vale zero, igual a todos os outros exemplos nesta lista.
Quanto ao primeiro argumento, lembre-se que o tipo de
Text, de acordo com a declaração, é “
array com 13 elementos do tipo
char” (12 do texto, mais o terminador). Entretanto, quando o nome de um
array é usado em expressões que não envolvem a aplicação direta de
sizeof ou de
& sobre si, o valor produzido é do tipo “ponteiro para
char”, indicando o endereço do primeiro elemento.
Por outro lado, quando você tem a aplicação de
sizeof ou
& ao nome do
array, ele não decai para ponteiro, mas continua um
array. Desse modo,
sizeof Text produz um valor do tipo
size_t correspondente ao tamanho total, medido em
bytes, ocupado na memória por todos os elementos do
array — como os elementos de
Text são do tipo
char, que, por definição, ocupam um
byte cada, o tamanho do
array é igual ao número de seus elementos elementos, que é 13.
Por sua vez,
&Text produz um valor do tipo “ponteiro para
array com 13 elementos do tipo
char”, correspondente ao endereço inicial do bloco de memória onde o
array está disposto. Devido à forma como o C prescreve a disposição de dados na memória, o endereço inicial do
array é numericamente equivalente ao endereço ocupado por seu primeiro elemento. Em C, essa equivalência pode aparecer com o valor verdadeiro retornado pelas comparações
(void *)Text==(void *)&Text, ou
(intptr_t)Text==(intptr_t)&Text (
intptr_t, definido em <stdint.h>, é um tipo de valor inteiro que seja suficiente para converter um ponteiro; nos nossos PCs de 64 bits, ele provavelmente corresponde a
unsigned long long); entretanto, a comparação
Text==&Text é errônea, pois não é válido comparar diretamente ponteiros para dados de tipos diferentes, e o próprio compilador vai alarmar.
Para uso com
memeset(), no entanto, as duas formas acabam não fazendo muita diferença, já que o tipo do primeiro parâmetro é
void *, ou um “ponteiro para qualquer tipo de dados”. A implementação interna de
memset() é funcionalmente equivalente ao seguinte código (deliberadamente não-otimizado, para ser didático).
void *memset(void *s, int c, size_t size){
unsigned char *cp=s; // Em C, pode-se converter void * em qualquer outro tipo de ponteiro automaticamente (em C++, isso seria um erro).
const unsigned char ch=c; // Valor a ser atribuído a cada byte na memória.
while(size--)
*cp++=ch;
return s;
}
O que se pode dizer sobre a diferença entre
memset(Text, 0, sizeof Text) e
memset(&Text, 0, sizeof Text) é que, no primeiro caso, o programador provavelmente tem em mente a atribuição de um valor nulo a cada um dos elementos do
array, ao passo que o segundo dá a ideia de que o programador não está preocupado com cada elemento do
array, mas apenas com que todos o bloco de memória seja preenchido com
bytes nulos.
Conhecendo a implementação interna da função, sabemos bem que o resultado é o mesmo, e a diferença acaba ficando mais no campo semântico-filosófico. É acaba sendo meio irônico que
memset() esteja em <string.h>, juntamente com outras funções que trabalham com
arrays de caracteres contendo uma representação específica de
strings, em vez de, talvez, <stdlib.h>, onde estão algumas funções que lidam com dados mais genéricos, tais como
bsearch() e
qsort() (mas, por outro lado, há em <stdlib.h> coisas que eu, particularmente, acho que ficariam mais devidamente colocadas em <string.h>, tais como as funções de conversão de
strings em valores numéricos, tais como
strtol(),
strtod() e outras da mesma família).
Com relação ao uso do operador
sizeof, seu operando não precisa estar entre parênteses se for uma expressão sintaticamente válida sem parênteses. Assim sendo, quando o operando é o nome de um símbolo (i.e. nome de uma variável,
array, função ou constante de uma enumeração), uma constante ou uma expressão que produz um
lvalue sem necessitar de parênteses, a forma canônica de fazer é escrever é “
sizeof X”. Com operandos desses tipos, o uso de parênteses é redundante, e portanto deveria ser evitado, a não ser em casos em que fossem muito convenientes para melhorar a clareza (embora não me ocorra nenhum exemplo no momento), e seria melhor que isso fosse feito na forma “
sizeof (X)”, com um espaço entre o operador e o operando, destacando que os parênteses não fazem parte da sintaxe do operando em si.
Note que eu não incluí nos casos acima nomes de tipos de dados, tais como
char,
int * ou
struct tm. Ao usar
sizeof com nomes de tipos, em lugar de com dados cujo tipo será determinado pelo compilador, você é obrigado a colocar o nome do tipo entre parênteses, sim. Mas tipicamente, isso é feito com a forma “
sizeof (T)”, com um espaço entre o operador e a abertura de parênteses.
----
† Esta é provavelmente a incompatibilidade mais básica entre o C e o C++, pois nesta última constantes literais entre apóstrofos são do tipo
char, não do tipo
int, como em C.
... Então Jesus afirmou de novo: “(...) eu vim para que tenham vida, e a tenham plenamente.” (João 10:7-10)