Enviado em 23/04/2021 - 21:46h
Recentemente, seguindo um exercício propostos por uma apostila online desenvolvi a minha própria biblioteca de manipação de strings: a mystring.h. Eu não sei se ela ficou "100%", portanto eu gostaria que vocês me ajudassem tirando algumas das minhas dúvidas e sugerindo soluções mais eficientes para a minha biblioteca.
Segue o código:
Aquivo: mystring.h
Arquivo:mystring.c
Minhas dúvidas:
1. O uso de casting de char para int (ou vice-versa) é desnecessário?
Eu utilizei casting de char para int dentro de algumas funções, mas lembrei que talvez isto seja desnecessário pelo fato de um int de 2 ou 4 bytes ser totalmente capaz de armazenar um char de meros 1 byte. Mas agora fica a dúvida: e se fosse o inverso? Faria diferença o casting de int para char?
2. unsigned char
De acordo com as páginas de manuais que eu encrontrei por aí, as funções memset, memcmp, memchr e memrchr interpretam os valores passadas para *s/*s1/*s2 como unsigned char:
- https://www.cplusplus.com/reference/cstring/memset/
- https://linux.die.net/man/3/memcmp
- https://linux.die.net/man/3/memchr
- https://linux.die.net/man/3/memrchr
Dúvias em relação a este comportamento:
-> Por que em especial unsigned char e não um char assinado (signed char) ou int?
-> As função memcpy também segue esse mesmo modelo de trabalhar com unsigned char em seu funcionamento interno? (Eu não vi em nenhuma página de manual citando que essa função trabalha com unsigned char como as outras que eu mostrei)
-> A forma como eu refiz essas funções em minha biblioteca estão fieis em relação ao funcionamento interno original dessas funções?
3. memcpy()
Se tem uma coisa que eu não entendi muito bem em ralação a função memcpy foi a tal "sobreposição" (overlap) descrita em todas as página de manuais que eu li por aí.
-> Alguém poderia me explicar o que uma sobreposição com a função memcpy()? Se sim, poderia demonstrar um exemplo de código que "caí" nesse "problema" de sobreposição?
-> A minha versão da memcpy (my_memcpy) também está sujeita a essa tal de sobreposição? Se sim, como eu poderia reverter?
4. Tabulação: 4 espaçamentos vs. 8 espaçamentos
- https://www.kernel.org/doc/html/v4.10/process/coding-style.html
Eu vejo muita gente usando tabulação de 4 espaçamentos em códigos escritos em C por aí (inclusive eu). Fui tentar usar tabulação de 8 espaçamentos eu achei meio esquisito.
Eu sei que isto TALVEZ seja uma questão mesquinha e pessoal, mas eu queria mesmo assim pedir desculpas pelos 4 meros espaçamentos em todo o meu código. Eu vou tentar me acostumar com o padrão de 8 espaçamentos nos meu próximos programas/códigos.
Segue o código:
Aquivo: mystring.h
//10/04/2021 - ZXKN-60R
#ifndef MYSTRING_H
#define MYSTRING_H
#include <stdlib.h>
//Funções de exame de string
extern size_t my_strlen(const char *s);
extern int my_strcmp(const char *s1, const char *s2);
extern int my_strncmp(const char *s1, const char *s2, size_t n);
extern const char *my_strchr(const char *s, int c);
extern const char *my_strrchr(const char *s, int c);
extern size_t my_strspn(const char *s, const char *accept);
extern size_t my_strcspn(const char *s, const char *reject);
extern const char *my_strpbrk(const char *s, const char *accept);
extern const char *my_strstr(const char *haystack, const char *needle);
//funções de manipulação de strings
extern char *my_strcpy(char *dest, const char *src);
extern char *my_strncpy(char *dest, const char *src, size_t n);
extern char *my_strcat(char *dest, const char *src);
extern char *my_strncat(char *dest, const char *src, size_t n);
extern void *my_memset(void *s, int c, size_t n);
extern void *my_memcpy(void *dest, const void *src, size_t n);
extern int my_memcmp(const void *s1, const void *s2, size_t n);
extern const void *my_memchr(const void *s, int c, size_t n);
extern const void *my_memrchr(const void *s, int c, size_t n); //não oficial da string.h
#endif //MYSTRING_H
Arquivo:mystring.c
#include "mystring.h"
//Funções de exame de string
size_t my_strlen(const char *s){
size_t lenght=0;
while(s[lenght]!='\0'){
lenght++;
}
return lenght;
}
int my_strcmp(const char *s1, const char *s2){
int diff=0;
for(unsigned int i=0; i<=my_strlen(s1); i++){
if(s1[i]!=s2[i]){
diff=(int)s1[i]-(int)s2[i];
break;
}
}
return diff;
}
int my_strncmp(const char *s1, const char *s2, size_t n){
int diff=0;
for(unsigned int i=0; i<=n; i++){
if(s1[i]!=s2[i]){
diff=(int)s1[i]-(int)s2[i];
break;
}
}
return diff;
}
const char *my_strchr(const char *s, int c){
while((*s!=(char)c) && (*s!='\0')){
s++;
}
if(*s!=(char)c){
s=NULL;
}
return s;
}
const char *my_strrchr(const char *s, int c){
size_t i=0;
for(size_t j=0; j<my_strlen(s); j++){
if(s[j]==(char)c){
i=j;
}
}
if(s[i]==(char)c){
s+=i;
}else{
s=NULL;
}
return s;
}
size_t my_strspn(const char *s, const char *accept){
size_t count=0;
for(size_t i=0; i<my_strlen(s); i++){
if(s[i]==accept[i]){
count++;
}else{
break;
}
}
return count;
}
size_t my_strcspn(const char *s, const char *reject){
size_t count=0;
for(size_t i=0; i<my_strlen(s); i++){
if(s[i]!=reject[i]){
count++;
}else{
break;
}
}
return count;
}
const char *my_strpbrk(const char *s, const char *accept){
int catch=0;
for(size_t i=0; i<my_strlen(s); i++){
for(size_t j=0; j<my_strlen(accept); j++){
if(s[i]==accept[j]){
s+=i;
catch++;
break;
}
}
if(catch!=0){
break;
}
}
if(catch==0){
s=NULL;
}
return s;
}
static int strstrcmp(size_t start, const char *haystack, const char *needle){
int diff=0;
haystack+=start;
while((*haystack!='\0') && (*needle!='\0')){
if(*haystack!=*needle){
diff=(int)*haystack-(int)*needle;
break;
}else{
haystack++;
needle++;
}
}
return diff;
}
const char *my_strstr(const char *haystack, const char *needle){
int catch=0;
size_t i, len=my_strlen(haystack);
for(i=0; i<len; i++){
if((haystack[i]==needle[0]) && (haystack[i+1]==needle[1])){
if(strstrcmp(i, haystack, needle)==0){
catch=1;
break;
}
}
}
if(catch==0){
for(i=0; i<len; i++){
if(strstrcmp(i, haystack, needle)==0){
catch=1;
break;
}
}
}
if(catch==0){
haystack=NULL;
}else{
haystack+=i;
}
return haystack;
}
//funções de manipulação de strings
char *my_strcpy(char *dest, const char *src){
size_t i=0;
while(src[i]!='\0'){
dest[i]=src[i];
i++;
}
dest[i]='\0';
return dest;
}
char *my_strncpy(char *dest, const char *src, size_t n){
size_t i=0;
while(i!=n){
dest[i]=src[i];
i++;
}
return dest;
}
char *my_strcat(char *dest, const char *src){
size_t i, len=my_strlen(dest);
for(i=0; (i<my_strlen(src)) && (src[i]!='\0'); i++){
dest[i+len]=src[i];
}
dest[i+len]='\0';
return dest;
}
char *my_strncat(char *dest, const char *src, size_t n){
size_t i, len=my_strlen(dest);
for(i=0; (i<n) && (src[i]!='\0'); i++){
dest[i+len]=src[i];
}
dest[i+len]='\0';
return dest;
}
void *my_memset(void *s, int c, size_t n){
unsigned char *ps=s;
for(size_t i=0; i<n; i++){
ps[i]=c;
}
return s;
}
void *my_memcpy(void *dest, const void *src, size_t n){
unsigned char *p_dest=dest;
const unsigned char *p_src=src;
for(size_t i=0; i<n; i++){
p_dest[i]=p_src[i];
}
return dest;
}
int my_memcmp(const void *s1, const void *s2, size_t n){
int diff=0;
const unsigned char *ps1=s1, *ps2=s2;
for(size_t i=0; i<n; i++){
if(ps1[i]!=ps2[i]){
diff=(int)ps1[i]-(int)ps2[i];
break;
}
}
return diff;
}
const void *my_memchr(const void *s, int c, size_t n){
const unsigned char *ps=s, uc=(unsigned char)c;
size_t i=0;
while((*ps!=uc) && (*ps!='\0') && (i<n)){
ps++;
i++;
}
if(*ps!=uc){
ps=NULL;
}
return ps;
}
const void *my_memrchr(const void *s, int c, size_t n){
const unsigned char *ps=s;
size_t i=0;
for(size_t j=0; j<n; j++){
if(ps[j]==(unsigned char)c){
i=j;
}
}
if(ps[i]==(unsigned char)c){
ps+=i;
}else{
ps=NULL;
}
return ps;
}
Minhas dúvidas:
1. O uso de casting de char para int (ou vice-versa) é desnecessário?
Eu utilizei casting de char para int dentro de algumas funções, mas lembrei que talvez isto seja desnecessário pelo fato de um int de 2 ou 4 bytes ser totalmente capaz de armazenar um char de meros 1 byte. Mas agora fica a dúvida: e se fosse o inverso? Faria diferença o casting de int para char?
2. unsigned char
De acordo com as páginas de manuais que eu encrontrei por aí, as funções memset, memcmp, memchr e memrchr interpretam os valores passadas para *s/*s1/*s2 como unsigned char:
void *memset(void *s, int c, size_t n); //prótotipo da função de acordo com as páginas de manuais do Linux
Sets the first num bytes of the block of memory pointed by ptr to the specified value (interpreted as an unsigned char). //Descrição da memset de acordo com o site cpluscplus.com
...
int memcmp(const void *s1, const void *s2, size_t n); //prótotipo da função de acordo com as páginas de manuais do Linux
The memcmp() function compares the first n bytes (each interpreted as unsigned char) of the memory areas s1 and s2. //Descrição da memcmp de acordo com o comando man memcmp
...
void *memchr(const void *s, int c, size_t n); //prótotipo da função de acordo com as páginas de manuais do Linux
void *memrchr(const void *s, int c, size_t n); //prótotipo da função de acordo com as páginas de manuais do Linux
The memchr() function scans the initial n bytes of the memory area
pointed to by s for the first instance of c. Both c and the bytes of
the memory area pointed to by s are interpreted as unsigned char.
The memrchr() function is like the memchr() function, except that it
searches backward from the end of the n bytes pointed to by s instead
of forward from the beginning.
//Descrição das funções memchr e memrchr de acordo com o comando man memchr
- https://www.cplusplus.com/reference/cstring/memset/
- https://linux.die.net/man/3/memcmp
- https://linux.die.net/man/3/memchr
- https://linux.die.net/man/3/memrchr
Dúvias em relação a este comportamento:
-> Por que em especial unsigned char e não um char assinado (signed char) ou int?
-> As função memcpy também segue esse mesmo modelo de trabalhar com unsigned char em seu funcionamento interno? (Eu não vi em nenhuma página de manual citando que essa função trabalha com unsigned char como as outras que eu mostrei)
-> A forma como eu refiz essas funções em minha biblioteca estão fieis em relação ao funcionamento interno original dessas funções?
3. memcpy()
Se tem uma coisa que eu não entendi muito bem em ralação a função memcpy foi a tal "sobreposição" (overlap) descrita em todas as página de manuais que eu li por aí.
The memcpy() function copies n bytes from memory area src to memory
area dest. The memory areas must not overlap. Use memmove(3) if the
memory areas do overlap.
-> Alguém poderia me explicar o que uma sobreposição com a função memcpy()? Se sim, poderia demonstrar um exemplo de código que "caí" nesse "problema" de sobreposição?
-> A minha versão da memcpy (my_memcpy) também está sujeita a essa tal de sobreposição? Se sim, como eu poderia reverter?
4. Tabulação: 4 espaçamentos vs. 8 espaçamentos
Tabs are 8 characters, and thus indentations are also 8 characters. There are heretic movements that try to
make indentations 4 (or even 2!) characters deep, and that is akin to trying to define the value of PI to be 3.
- https://www.kernel.org/doc/html/v4.10/process/coding-style.html
Eu vejo muita gente usando tabulação de 4 espaçamentos em códigos escritos em C por aí (inclusive eu). Fui tentar usar tabulação de 8 espaçamentos eu achei meio esquisito.
Eu sei que isto TALVEZ seja uma questão mesquinha e pessoal, mas eu queria mesmo assim pedir desculpas pelos 4 meros espaçamentos em todo o meu código. Eu vou tentar me acostumar com o padrão de 8 espaçamentos nos meu próximos programas/códigos.