Apagar corretamente um vetor de caracteres? [RESOLVIDO]

1. Apagar corretamente um vetor de caracteres? [RESOLVIDO]

Apprentice X
ApprenticeX

(usa Slackware)

Enviado em 16/11/2021 - 19:02h

Boa Noite a Todos,

Como apagar um vetor/array corretamente!
Entendi que vetor e array é a mesma coisa! Está certo isso?

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;

/* 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?

*/

memset(Text , 0x0, sizeof(Text));
memset(&Text, 0x0, sizeof(Text));

memset(Text , 0, sizeof Text);
memset(&Text, 0, sizeof Text);

memset(Text , 0, sizeof(Text));
memset(&Text, 0, sizeof(Text));

memset(Text, '\0', sizeof(Text));

strcpy(Text, "");
Text[0] = 0; // Não gosto dessa solução, pode dar problemas!



  


2. MELHOR RESPOSTA

Paulo
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)

3. Re: Apagar corretamente um vetor de caracteres?

berghetti
berghetti

(usa Debian)

Enviado em 19/11/2021 - 13:41h


char Text[] = "Viva o Linux";

/*
ok vai zerar o array porem dizer que é a melhor opção é incorreto,
pois implementações da memset em bibliotecas padrões são mais performáticas já que executam sobre 4 ou 8 bytes por vez ao invés de 1 byte por vez como esta sendo feito nesse loop,
veja exemplo da implementação da glibc, https://elixir.bootlin.com/glibc/latest/source/string/memset.c,
também o compilador quando habilitado otimizações pode remover a chamada da função memset diretamente por código mais performático.
*/
for(int x = 0; x < sizeof(Text); x++) Text[x] = 0;

/* Qual dos memset abaixo de fato é o correto? Porque fiquei confuso com tantas formas!

1) Uso Text OU &Text ???
a variavel Text é um array e arrays decaem para ponteiro quando são utilizados como parametro de funções,
logo o uso do operador & é reduntante.

2) Uso sizeof Text OU sizeof(Text)
sizeof é um operador assim como os operadores + - / * e o uso de parenteses não é necessário,
somente quando precisamos resolver alguma ambiguidade.

3) Uso 0x0 OU 0 OU \0
são apenas formas de representar o mesmo valor, respectivamente hexadecimal, decimal e octal.

3) O que é esse 0x0 ??? Ele é melhor?
respondido na anterior

*/

memset(Text , 0x0, sizeof(Text));
memset(&Text, 0x0, sizeof(Text));

memset(Text , 0, sizeof Text);
memset(&Text, 0, sizeof Text);

memset(Text , 0, sizeof(Text));
memset(&Text, 0, sizeof(Text));

memset(Text, '\0', sizeof(Text));

/* quais problemas? é apenas uma atribuição normal e valida*/
strcpy(Text, "");
Text[0] = 0; // Não gosto dessa solução, pode dar problemas!




4. Re: Apagar corretamente um vetor de caracteres? [RESOLVIDO]

Apprentice X
ApprenticeX

(usa Slackware)

Enviado em 30/11/2021 - 22:18h

Quero agradecer à berghetti e ao Paulo ambas as respostas são de grande informação e aprendizado.

berghetti foi direto ao ponto, e deixou uma informação que gostei de saber referente a função do memset, mostrando como ela é por dentro, o que ainda não sei como eu mesmo encontrar o source das funções em C. Gostaria de Saber isso!

Até tentei no mesmo site indicado buscar por exemplo como é a função strcat mas sem sucesso: https://elixir.bootlin.com/glibc/latest/A/ident/strcat
Pois ele fala de macro, assembly, mas não achei o source de fato.

Paulo como sempre traz uma informação bem completa, ampla, rica em conteúdo, trazendo um aprendizado mais profundo e detalhado sobre o assunto! O que facilita e muito o entendimento do funcionamento do processo em si!

As 2 Respostas para mim se complementaram, obrigado aos 2


5. Re: Apagar corretamente um vetor de caracteres? [RESOLVIDO]

berghetti
berghetti

(usa Debian)

Enviado em 30/11/2021 - 22:40h


Até tentei no mesmo site indicado buscar por exemplo como é a função strcat mas sem sucesso: https://elixir.bootlin.com/glibc/latest/A/ident/strcat
Pois ele fala de macro, assembly, mas não achei o source de fato.


https://elixir.bootlin.com/glibc/latest/source/string/strcat.c#L20






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts