Własna wersja funkcjj strncpy() - nie wiem, czy dobrze napisałem

0

Chcę się upewnić, czy dobrze rozwiązałem. Oto zadanie:
Funkcja strncpy(s1,s2,n) kopiuje z s2 do s1 dokładnie n znaków, w razie potrzeby skracając łańcuch s2 lub dodając do niego znaki zerowe. Łańcuch wynikowy może nie kończyć się znakiem zerowym, jeśli długość s2 wynosi n lub więcej. Wartością zwracaną jest s1. Napisz własną wersję tej funkcji

A oto kod:

#include <stdio.h>
#include <string.h>
char *kopiuj(char *lancuch1, char *lancuch2, int n);
int main(void)
{
	char dane1[50], dane2[50];
	int a;
	printf("Cwiczenie 11.6 w jezyku C - autor: xpeye\n\n");
	printf("Podaj pierwszy lancuch: ");
	gets(dane1);
	printf("Podaj drugi lancuch: ");
	gets(dane2);
	printf("Podaj ilosc znakow do skopiowania z drugiego lancucha do pierwszego: ");
	scanf("%d", &a);
	printf("Oto pierwszy lancuch po skopiowaniu:\n%s", kopiuj(dane1, dane2, a));
	getchar();
	getchar();
	return 0;
}

char *kopiuj(char *lancuch1, char *lancuch2, int n)
{
	int i=0, j=0;
	if(strlen(lancuch2)>n)
	*(lancuch2+n)='\0';
	else if(strlen(lancuch2)<n)
	{
		j=strlen(lancuch2);
		do
		{
			lancuch2[j]='\0';
			j++;
		} while(j<=n);
	}
	j=0;
	do
	{
		lancuch1[i]=lancuch2[j];
		i++;
		j++;
	} while(lancuch2[j-1]!='\0');
	return lancuch1;
}

I jak?

1

Twój kod jest zbyt zagmatwany. Czy nie lepiej zrobić tak:

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

char* strncpy(char to[], char from[], unsigned count);
unsigned strlen(char str[]);

int main()
{
    char a[64];
    char b[64] = "to jest testowy ciag";

    strncpy(a, b, 2);

    printf(a);
    return 0;
}

char* strncpy(char to[], char from[], unsigned count)
{
    unsigned i;
    for(i = 0; i < strlen(from); i++)
        *(to + i) = i < count ? *(from + i) : 0;
    return to;
}

unsigned strlen(char str[])
{
    unsigned i;
    for(i = 0; str[i]; i++);
    return i;
}
0

Czyli moja jest zła, czy tylko o wygląd chodzi?

0

Ten kod jest pogmatwany, że istnieje ryzyko błędu. W moim rozwiązaniu użyłem arytmetyki wskaźników, która jest bardzo fajnym rozwiązaniem. Zgodnie z zadaniem kopiuje znaki po podaniu count a gdy i będzie równe count to dopełnia znakami zerowymi (zgodnie z długością stringa źródłowego). Można dorobić jeszcze zabezpieczenia (ten kod był pisany na prędce) przeciw przekraczaniu indeksu.

Linijka kopiowania to to samo co:
to[i] = i < count ? from[i] : 0;

0

Wiem, że Twój kod jest łatwiejszy,tylko mi chodzi oto, czy mój realizuje w 100% treść zadania?

1

Twoja funkcja narusza ciąg źródłowy - takiego czegoś nie powinno być. Podczas próby dania większej liczby znaków następuje błąd segmentacji pamięci. Funkcja jest dziurawa :P

4

Strasznie sobie życie utrudniacie:

char *strncpy(char *dst,const char *src,unsigned count)
  {
   for(char *tmp=dst;(count)&&((*(tmp++)=*(src++))!='\0');--count) {}
   return dst;
  }
0

[code]
#include <stdio.h>
#include <string.h>
char *kopiuj(char *lancuch1, const char *lancuch2, int n);
int main(void)
{
char dane1[50], dane2[50];
int a;
printf("Cwiczenie 11.6 w jezyku C - autor: xpeye\n\n");
printf("Podaj pierwszy lancuch: ");
gets(dane1);
printf("Podaj drugi lancuch: ");
gets(dane2);
printf("Podaj ilosc znakow do skopiowania z drugiego lancucha do pierwszego: ");
scanf("%d", &a);
printf("Oto pierwszy lancuch po skopiowaniu:\n%s", kopiuj(dane1, dane2, a));
getchar();
getchar();
return 0;
}

char *kopiuj(char *lancuch1, const char *lancuch2, int n)
{
int i=0, j=0;
while(lancuch2[j]!='\0')
{
lancuch1[i]=lancuch2[j];
i++;
j++;
if (j==n)
break;
}
while(i<=n)
{
lancuch1[i]='\0';
i++;
}
return lancuch1;
}
[/code]
Funkcji malloc() jeszcze nie ma na tym etapie
A tu są błędy?

0

Jeżeli koniecznie chcesz zrobić to na indeksach to zrób to po ludzku:

char *strncpy(char *dst,const char *src,unsigned count)
  {
   for(unsigned i=0;(i<count)&&((tmp[i]=src[i])!='\0');++i) {}
   return dst;
  }
0

Według tego co tu pisze http://pl.wikibooks.org/wiki/C/strncpy to byłoby dobre
[code]
#include <stdio.h>
char *kopiuj(char *lancuch1, const char *lancuch2, int n);
int main(void)
{
char dane1[50], dane2[50];
int a;
printf("Cwiczenie 11.6 w jezyku C - autor: xpeye\n\n");
printf("Podaj pierwszy lancuch: ");
gets(dane1);
printf("Podaj drugi lancuch: ");
gets(dane2);
printf("Podaj ilosc znakow do skopiowania z drugiego lancucha do pierwszego: ");
scanf("%d", &a);
printf("Oto pierwszy lancuch po skopiowaniu:\n%s", kopiuj(dane1, dane2, a));
getchar();
getchar();
return 0;
}

char *kopiuj(char *lancuch1, const char *lancuch2, int n)
{
int i=0;
while(lancuch2[i]!='\0' && i<n)
{
lancuch1[i]=lancuch2[i];
i++;
}
while(i<n)
{
lancuch1[i]='\0';
i++;
}
return lancuch1;
}
[/code]
?

0

Nie wiem, czy dobrze napisałem też to zadanie:
„Napisz funkcję, która pobiera łańcuch i usuwa z niego odstępy. Wypróbuj ją w prostym programie, który odczytuje wiersze za pomocą pętli i kończy działanie po wpisaniu pustej linii. Program powinien stosować funkcję dla każdego wiersza.”

dobrze napisałem
[code]
#include <stdio.h>
char *odstepy(char *lancuch);
int main(void)
{
char dane[120];
printf("Cwiczenie 11.9 w jezyku C - autor: xpeye\n\n");
do
{
printf("Podaj lancuch: ");
gets(dane);
printf("Oto lancuch: %s\n", odstepy(dane));
}while(odstepy(dane)!=NULL);
printf("\nKoniec\n");
getchar();
return 0;
}

char *odstepy(char *lancuch)
{
int i=0, j=0;
while(lancuch[j]!='\0')
{
if(lancuch[j]==' ')
j++;
lancuch[i]=lancuch[j];
i++;
j++;
}
lancuch[i]=lancuch[j];
if(lancuch[0]==0)
return NULL;
else
return lancuch;
}
[/code]
?

0

Funkcja jest dobra. Tylko jak na moje oko za dużo zmiennych korzystasz i jest za dużo instrukcji. Oto moja wersja: http://ideone.com/hxNmF8
Wykorzystuję tylko przesunięcie. Zliczam biały znak i potem gdy wskaźnik jest na bieżącej pozycji to cofam go o liczbę spacji.

0

Podana przez ciebie funkcja nie zadziała poprawnie jeżeli będzie kilka odstępów podrząd.
Najprostsza wersja:

char *skipws(char *src)
  {
   for(char *s=src,*dst=src;(*dst=*s)!='\0';++s) if((*dst!=' ')&&(*dst!='\t')) ++dst;
   return src;
  }
0

A teraz
[code]#include <stdio.h>
char *odstepy(char *lancuch);
int main(void)
{
char dane[120];
printf("Cwiczenie 11.9 w jezyku C - autor: xpeye\n\n");
do
{
printf("Podaj lancuch: ");
gets(dane);
printf("Oto lancuch: %s\n", odstepy(dane));
}while(odstepy(dane)!=NULL);
printf("\nKoniec\n");
getchar();
return 0;
}

char *odstepy(char *lancuch)
{
int i=0, j=0;
while(lancuch[j]!='\0')
{
while(lancuch[j]==' ')
j++;
lancuch[i]=lancuch[j];
i++;
j++;
}
lancuch[i]=lancuch[j];
if(lancuch[0]==0)
return NULL;
else
return lancuch;
}[/code]?

0

Nie, jeżeli ciąg kończy się spacją to wyłazisz poza zakres.

0

Czyli tak
[code]
#include <stdio.h>
char *odstepy(char *lancuch);
int main(void)
{
char dane[120];
printf("Cwiczenie 11.9 w jezyku C - autor: xpeye\n\n");
do
{
printf("Podaj lancuch: ");
gets(dane);
printf("Oto lancuch: %s\n", odstepy(dane));
}while(odstepy(dane)!=NULL);
printf("\nKoniec\n");
getchar();
return 0;
}

char *odstepy(char *lancuch)
{
int i=0, j=0;
while(lancuch[j]!='\0')
{
while(lancuch[j]==' ')
{
j++;
if(lancuch[j]=='\0')
goto przerwa;
}
lancuch[i]=lancuch[j];
i++;
j++;
}
przerwa:lancuch[i]=lancuch[j];
if(lancuch[0]==0)
return NULL;
else
return lancuch;
}
[/code]?

0
xpeye napisał(a):

Czyli tak

Tak zagmatwałeś jednowierszową funkcje że stwierdzenie czy działa dobrze czy źle nie jest już oczywiste.

0

Nie zagmatwałem. Pierwsza pętla obsługuje łańcuch źródłowy do znaku zerowego. Jeśli znak nie jest znakiem zerowym i spacją, funkcja kopiuje znak z łańcucha źródłowego do docelowego, powiększa zmienne "i" i "j" i wchodzi drugi raz w pierwszą pętlę. Jeśli znak jest spacją, to "j" ulega zwiększeniu, dopóty nie wskazuje na spacje(druga pętla). Jeśli "j" po powiększeniu wskazuje na znak zerowy, wychodzę z obu pętli instrukcją goto. To proste

0

Jeżeli koniecznie chcesz zrobić to na indeksach to:

#include <stdio.h>

char *clean(char *str)
  {
   int s,d;
   for(s=d=0;(str[d]=str[s])!='\0';++s) if(str[d]!=' ') ++d; // ewentualnie if((str[d]!=' ')&&(str[d]!='\t')&&(str[d]!='\n')) to wyczyści jeszcze tabulacje i entery
   return str;
  }

int main(void)
  {
   char data[120];
   printf("Cwiczenie 11.9 w jezyku C\n\n");
   for(;;)
     {
      printf("Podaj lancuch: ");
      fgets(data,sizeof(data),stdin); // gets - niebiezpieczny
      if(!data[1]) break; // napis zawiera entera
      printf("Oto lancuch: %s\n",clean(data));
     }
   printf("\nKoniec\n");
   getchar();
   return 0;
  }
0
char *clean(char *str)
  {
   int s = 0;  //s od source       - źródło
   int d = 0;  //d od destination - przeznaczenie

     //przy każdym obiegu kopiuje znak aż do napotkania zera
   for(  ; (str[d]=str[s])!='\0' ; ++s )
   {
         //jeśli znak nie jest spacją to zapisuje do kolejnego miejsca w zmiennej
       if( str[d]!=' ' )  //if((str[d]!=' ')&&(str[d]!='\t')&&(str[d]!='\n')) - to wyczyści jeszcze tabulacje i entery
       {
           ++d;
       }
   }

   return str;
  }

Prawda, że czytelniej? :)

Nie jestem pewien, czy w visual'u '\n' zadziała na standardowej konfiguracji. Z tego co pamiętam interpretuje go jako CRLF. Ale jak mówiłem - nie jestem pewien. Poza tym nawet jeśli zinterpretuje go jako LF to tekst z notatnika windows (gdzie enter = CRLF) nie zostanie poprawnie obrobiony. Najlepiej pomijać w ten sposób:

if((str[d]!=' ')&&(str[d]!='\t')&&(str[d]!=13)&&(str[d]!=10))

Chyba, że komuś chce się sprawdzić, albo ktoś wie na pewno :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1