2013-05-06 15 views
8

Jestem nowy w programowaniu, zaczynając od Objective-C, ale zdecydowałem się wrócić do podstaw, zanim przejdę dalej. Spędzam trochę czasu w C i zmagam się z pomyłką wskaźnika. Moje pytanie dotyczy tego, w jaki sposób K & R mówi, że zaimplementowano fgets (p165, wydanie drugie). Poniższy kod jest bezpośrednio z tekstu z kilkoma moimi komentarzami.Implementacja fgets (K & R)

char* fgets(char* s, int n, FILE *iop) 
{ 
    register int c; 
    register char* cs; 
    cs = s; 

    while(--n > 0 && (c = getc(iop)) != EOF) 
    { 
    // put the input char into the current pointer position, then increment it 
    // if a newline entered, break 
    if((*cs++ = c) == '\n') 
     break;   
    } 

    *cs = '\0'; 
    return (c == EOF && cs == s) ? NULL : s; 
} 

1) Przekazujemy znak * do funkcji fgets, w której lokalizacji przechowujemy dane użytkownika. Dlaczego trzeba zadeklarować lokalny char * cs - a następnie zainicjować go na s? Dlaczego nie możemy bezpośrednio manipulować/dodawać do s w instrukcji if? Widząc, że cs jest inicjowane do punktu s, nie dodaje znaków do cs dokładnie to samo?

2) Związane z powyższym ... Po powrocie funkcji, test jest sprawdzany, czy cs == s. Dlaczego jest to konieczne?

Myślę, że być może brakuje mi czegoś bardzo podstawowego - sprawdziłem SO i Google, ale nie jestem w stanie tego rozgryźć. Dzięki!

+2

"zaczynając od Objective-C, ale zdecydowałeś się wrócić do podstaw, zanim przejdziesz dalej" - doskonale, ** każdy początkujący powinien to zrobić. ** –

+1

Właśnie zaczynałem czuć się jakbym uderzał krokami rodzajów w Obj-C. Od powrotu do C czuję się znowu kompletnym klutzem, ale mimo wielokrotnego uderzania głową o błędy segmentacji i dziwne zachowanie programu, zdecydowanie warto to zrobić. – drjimmie1976

+0

Z pewnością warto. Opanowanie C jest nieuniknione dla zrozumienia Celu-C. –

Odpowiedz

7

To z powodu sprawdzenia ostatniej linii, cs == s. To porównanie sprawdza zmodyfikowany wskaźnik cs w stosunku do oryginału s, aby sprawdzić, czy czytaliśmy jakąś postać. Jeśli nie, to zwracamy NULL.

Przy użyciu cs przez cały oryginalny wskaźnik s jest zachowany. Jeśli s zostały bezpośrednio zmanipulowane (*s++ zamiast *cs++), wówczas musielibyśmy znaleźć inny sposób sprawdzenia, czy odczytano jakiekolwiek znaki.

Można również argumentować, że dobrą praktyką jest pozostawienie samych parametrów funkcji i traktowanie ich jako const. Niektórzy programiści stosują tę praktykę jako sposób na zwiększenie przejrzystości kodu.

+0

Dziękuję bardzo John. Czyni to cel jaśniejszym, ale jeszcze parę pytań, jeśli mogę ... kiedy "cs" jest przypisane "cs = s", pomyślałem, że wskaźniki wskazywały teraz dokładnie ten sam obszar pamięci; dlatego wykonanie "* cs ++" byłoby dokładnie równoważne "* s ++". Czy to założenie jest błędne? – drjimmie1976

+0

Przepraszam, że mój komentarz wygasł, i robię również komentarz komentarza. To dlatego nie zrozumiałem ostatniej linii. Myślałem, że widząc jako oba wskaźniki "dzielenie się", będą one identyczne, gdy zostanie ocenione wyrażenie c == cs. Z góry dziękuję. – drjimmie1976

+0

@ GasMan14 Wskazują ten sam adres po przypisaniu, to prawda. '* cs ++' nie modyfikuje 's', chociaż. Upewnij się, że rozumiesz pierwszeństwo: '* cs ++' jest równe '* (cs ++)' not '(* cs) ++'. 'cs ++' zwiększa 'cs', wskazując na następny adres, a następnie adres URL '*' dereferences. 's' nie jest zmieniany, ponieważ' s' i 'cs' są dwiema oddzielnymi zmiennymi. Jeśli zamiast tego instrukcja byłaby '(* cs) ++', to * zmieni * wartość '* s'. (Pamiętaj, nie "s", ale '* s'.) –