Usiłuję uzyskać program, który pozwoli użytkownikowi wprowadzić słowo lub znak, zapisać je, a następnie wydrukować, dopóki użytkownik nie wpisze go ponownie, wychodząc z programu. Mój kod wygląda następująco:
#include <stdio.h>
int main()
{
char input[40];
char check[40];
int i=0;
printf("Hello!\nPlease enter a word or character:\n");
gets(input);
printf("I will now repeat this until you type it back to me.\n");
while (check != input)
{
printf("%s\n", input);
gets(check);
}
printf("Good bye!");
return 0;
}
Problem polega na tym, że ciągle otrzymuję wydruk ciągu wejściowego, nawet jeśli dane wejściowe użytkownika (sprawdź) są zgodne z oryginałem (dane wejściowe). Czy źle je porównuję?
gets( )
został usunięty ze standardu. Użyjfgets( )
zamiast tego.strcmp()
zwraca zero, gdy jego dane wejściowe są równe, wyjaśnia, jak porównać ciągi dla równości, nierówności, mniejszej, większej niż, mniejszej lub równej i większej niż lub równej. Nie wszystkie porównania ciągów służą równości. Porównywania wielkości liter są znowu różne; inne specjalne porównania (na przykład kolejność słowników) wymagają bardziej wyspecjalizowanych komparatorów, a istnieją jeszcze wyrażenia regularne dla bardziej złożonych porównań.gets()
. Został również usunięty ze standardu od C11 -> Przeczytaj, dlaczego funkcja gets jest tak niebezpieczna, że nie należy jej używać?Odpowiedzi:
Nie możesz (pożytecznie) porównywać ciągów za pomocą
!=
lub==
, musisz użyćstrcmp
:Powodem tego jest to, że
!=
i==
porówna tylko adresy podstawowe tych ciągów. Nie zawartość samych łańcuchów.źródło
while (strcmp(check, input))
jest wystarczające i jest uważane za dobrą praktykę.Ok kilka rzeczy:
gets
jest niebezpieczny i należy go zastąpićfgets(input, sizeof(input), stdin)
, aby uniknąć przepełnienia bufora.Następnie, aby porównać ciągi, musisz użyć
strcmp
, gdzie zwracana wartość 0 wskazuje, że oba ciągi są zgodne. Użycie operatorów równości (tj.!=
) Porównuje adres dwóch ciągów, w przeciwieństwie do poszczególnych znakówchar
w nich zawartych .Zwróć też uwagę, że chociaż w tym przykładzie nie spowoduje problemu,
fgets
zapisuje znak nowej linii, również'\n'
w buforach;gets()
nie. Jeśli porównasz dane wejściowe użytkownika zfgets()
dosłownym ciągiem"abc"
, który nigdy by się nie zgadzał (chyba że bufor'\n'
byłby zbyt mały, aby nie mieścił się w nim).źródło
fgets()
, łańcuch może być,"abc\n"
ponieważfgets()
zachowuje nowy wiersz. Jeśli porównasz to z"abc"
, otrzymasz „nie równy” z powodu różnicy między zakończeniem bajtu zerowego"abc"
a znakiem nowej linii w odczytanych danych. Musisz zapełnić nową linię. Niezawodny sposób na wykonanie jednej linii,buffer[strcspn(buffer, "\n")] = '\0';
który ma tę zaletę, że działa poprawnie niezależnie od tego, czy w buforze są jakieś dane, czy też dane te kończą się nową linią, czy nie. Inne sposoby zatykania nowej linii łatwo psują się.Zastosowanie
strcmp
.To jest w
string.h
bibliotece i jest bardzo popularne.strcmp
zwraca 0, jeśli łańcuchy są równe. Zobacz to, aby uzyskać lepsze wyjaśnieniestrcmp
zwrotów.Zasadniczo musisz zrobić:
lub
lub
Można sprawdzić to , tutorial
strcmp
.źródło
Nie można bezpośrednio porównywać tablic w ten sposób
Powinieneś porównać je char-by-char; w tym celu można użyć funkcji i zwrócić wartość logiczną (True: 1, False: 0). Następnie można go użyć w warunkach testowych pętli while.
Spróbuj tego:
źródło
checker
znajdzie'\0'
w jednym z ciągów, nie sprawdza innego ciągu'\0'
. Funkcja zwraca1
(„równy”), nawet jeśli jeden ciąg znaków jest tylko przedrostkiem drugiego (na przykład"foo"
i"foobar"
).||
zamiast&&
.Witamy w koncepcji wskaźnika. Wiele pokoleń początkujących programistów uznało tę koncepcję za nieuchwytną, ale jeśli chcesz wyrosnąć na kompetentnego programistę, musisz w końcu opanować tę koncepcję - a ponadto zadajesz już właściwe pytanie. Dobre.
Czy jest dla ciebie jasne, jaki jest adres? Zobacz ten schemat:
Na schemacie liczba całkowita 1 jest przechowywana w pamięci pod adresem 0x4000. Dlaczego pod adresem? Ponieważ pamięć jest duża i może przechowywać wiele liczb całkowitych, tak samo jak miasto jest duże i może pomieścić wiele rodzin. Każda liczba całkowita jest przechowywana w miejscu pamięci, ponieważ każda rodzina mieszka w domu. Każda lokalizacja pamięci jest identyfikowana przez adres , ponieważ każdy dom jest identyfikowany przez adres.
Dwa pola na schemacie reprezentują dwie różne lokalizacje pamięci. Możesz myśleć o nich jak o domach. Liczba całkowita 1 znajduje się w lokalizacji pamięci pod adresem 0x4000 (pomyśl „4000 Elm St.”). Liczba całkowita 7 znajduje się w lokalizacji pamięci pod adresem 0x4004 (pomyśl: „4004 Elm St.”).
Myślałeś, że twój program porównuje 1 z 7, ale tak nie było. Porównywał 0x4000 do 0x4004. Co się dzieje, gdy masz taką sytuację?
Dwie liczby całkowite są takie same, ale adresy się różnią. Twój program porównuje adresy.
źródło
Ilekroć próbujesz porównać ciągi, porównaj je w odniesieniu do każdego znaku. W tym celu można użyć wbudowanej funkcji łańcuchowej o nazwie strcmp (input1, input2); i powinieneś użyć pliku nagłówka o nazwie
#include<string.h>
Wypróbuj ten kod:
źródło
Zagłębmy się, aby zobaczyć, dlaczego
check != input
to nie wystarczy .W C łańcuch znaków jest standardową specyfikacją biblioteki.
input
powyżej nie jest ciągiem .input
jest tablicą 40 znaków .Zawartość
input
może stać się ciągiem .W większości przypadków, gdy tablica jest używana w wyrażeniu, jest konwertowana na adres jej pierwszego elementu.
Poniżej konwertuje
check
iinput
na ich adresy pierwszego elementu, a następnie te adresy są porównywane.Aby porównać ciągi , musimy użyć tych adresów, a następnie spojrzeć na dane, na które wskazują.
strcmp()
wykonuje robotę . §7.23.4.2Kod może nie tylko znaleźć, czy ciągi zawierają te same dane, ale który z nich jest większy / mniej, gdy się różnią.
Poniższe jest prawdziwe, gdy łańcuch różni się.
Aby uzyskać wgląd, zobacz Tworzenie własnej
strcmp()
funkcjiźródło
To bardzo proste rozwiązanie, w którym uzyskasz swoje wyniki, jak chcesz.
źródło
gets();
nie jest częścią standardu C od C11.strcmp(s1,s2)
jest UB, ponieważs2
treść nie jest początkowo określona.