Błędy LNK2019 i LNK1120 w funkcji swap()

1

Witam mam mały problem

#include <stdio.h>  //błąd (na dole) LNK2019 i LNK1120
#include <stdlib.h>

void swap(int, int);

int main(void)
{
	printf("%s\n", "Witaj swiecie!");
	
	int a = 21;
	int b = 17;

	swap(a, b);
	printf("main: a = %d, b = %d\n", a, b);
	
	
	return EXIT_SUCCESS;

Dwa błedy:

  1. LNK2019 nierozpoznany symbol zewnętrzny swap przywołany w funkcji main. WIERSZ 1
  2. LNK1120 liczba nierozpoznanych elementów zewnętrznych: 1 WIERSZ 1
4

Masz zadeklarowaną funkcję swap, ale nie masz jej definicji, czyli ciała z kodem.

Linker próbuje znaleźć miejsce w kodzie z ktorym może połączyć Twoje wywołanie funkcji, ale takiego nie widzi, więc dostajesz błąd o nierozponanym symbolu zewnętrznym.

1

Dziękuję za szybką odpowiedź teraz rozumiem :-)

5

Poprawiony kod, jeśli dobrze rozumiem o co ci chodziło:

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

void swap(int *, int *);

int main(void)
{
	printf("%s\n", "Witaj swiecie!");
	
	int a = 21;
	int b = 17;

	swap(&a, &b);
	printf("main: a = %d, b = %d\n", a, b);
	
	
	return EXIT_SUCCESS;
}

void swap (int *ap, int *bp)
{
	int temp;

	temp = *ap;
	*ap = *bp;
	*bp = temp;
}

Problemem był nie tylko brak funkcji swap, ale też brak nawiasu klamrowego kończącego funkcję main oraz to, że jeżeli (jak się domyślam) funkcja swap miała zamieniać wartości dwóch zmiennych, to nie dasz rady tego zrobić przekazując do funkcji tylko wartości tych zmiennych - musisz w tym celu przekazywać je przez wskaźnik jak w poprawionym kodzie.

0

Kolego EXIT_SUCCESS i EXIT_FAILURE przekazujesz tylko do exit.
Na końcu funkcji jak sukces to dajesz

return 0;

Na upartego, ale w Twoim wypadku to nie ma sensu:
exit( EXIT_SUCCESS );

exit robi pewne rzeczy w tle, W razie czego zaglądaj do manualu.

0

Funkcja exit jest równoważna ze zwróceniem wartości z main.

1

Nie jest, potem rozwinę.

0

Kiedy używać wskaźników a kiedy zwykłych zmiennych?🤔

1

Proszę bardzo



#include <stdio.h>

int main( int argc, char *argv[])
{
    
    if( argc > 1 )  {
        
        return 5;
        
    }
    
    if( main( 2, (char *[] ){ "one", "two" } ) == 5 )
      printf( "widzimy roznice ?" );
    
    return 0;
    
}


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


int main( int argc, char *argv[])
{
    
    if( argc > 1 )  {
        
        exit( 5 );
        
    }
    
    if( main( 2, (char *[] ){ "one", "two" } ) == 5 )
      printf( "widzimy roznice ?" );
    
    return 0;
    
}

Wiem, że to brudny hack ale to moja dzika karta w tym momencie aby naprostować kogoś, że takie g**no w kodzie jak exit(0); ma się nie pojawiać.

Nie wspomnę o tym, że EXIT_SUCCESS i EXIT_FAILURE są dosyć mgliście zdefiniowane, pod windą oczywiście potrafią zwracać nieintuicyjne wartości.
Więc radosne return EXIT_SUCCESS które nie zwraca zera i wysadzi Ci skrypt w shellu pod cygwinem to ostatnia rzecz na jaką masz ochotę.

No i tego ten return to statement a exit to funkcja. Jednak dla mnie trochę inne rzeczy.

1
Hesoyam napisał(a):

Kiedy używać wskaźników a kiedy zwykłych zmiennych?🤔

Nie ma wskaźnika bez "zwykłych zmiennych" ;)
Chyba że wskaźnik do wskaźnika , ale finalnie i tak musi być gdzieś na końcu jakaś jakaś "zwykła zmienna"

0
Hesoyam napisał(a):

Kiedy używać wskaźników a kiedy zwykłych zmiennych?

zawsze używasz zmiennych (albo stałych), wskaźnik zaś wskazuje miejsce do nich. Wskaźnika (oczywiście to uproszczenie, ale dla pytania wystarczające) używamy tam, gdzie chcemy przekazać dostęp do zmiennej zdefiniowanej w innym miejscu. Tu masz zmienna zdefiniowaną w funkcji main, ale chcesz wartość tej zmiennej zmienić w funkcji swap, dlatego przekazujesz jej wskaźnik na tą zmienną.

1
Hesoyam napisał(a):

Kiedy używać wskaźników a kiedy zwykłych zmiennych?🤔

Można powiedzieć, że używasz "zwykłej zmiennej" jeśli interesuje Cię po prostu wartość danej zmiennej i nic więcej. Klasycznie to będzie coś w stylu:

int sum(int a, int b)
{
  return a + b;
}

Tutaj mamy przekazać do funkcji wartości i zwrócić ich sumę. Jednak jeśli przekazujemy jakiś duży obiekt, np dużego stringa/obszar pamięci i chcemy mu policzyć sumę kontrolną (a może to mieć np. 10MB) więc unikamy kopiowania danych i wtedy przekazujemy do funkcji wskaźnik:

int getCRC(char *buff, int size)
{
  .... // przelecenie sie po calej pamieci i policznie sumy kontrolnej
  return crc;
}

Bo trzeba wiedzieć, że wywołując funkcję i przekazując do niej jakiś parametr tworzona jest nowa zmienna lokalna w funkcji na podstawie źródłowej. Nic nie stoi na przeszkodzie przekazać wartość buff przez wartość, ale co jeśli będzie on miał rozmiar 1GB? Wtedy będzie trzeba całość skopiować do zmiennej lokalnej, co zajmie czas, może nie starczyć miejsca na stosie itp. Dlatego aby uniknąć zbędnego kopiowania przekazujemy adres bufora z rozmiarem i już ;) O ile w przypadku pierwszej sytuacji skopiowanie dwóch zmiennych typu int będzie szybkie i bez znaczenia, to już kopiowanie całego bufora który ma potencjalnie nie wiadomo ile GB jest bez sensu.

Z tego co wyżej napisałem wynika coś ciekawego. Funkcja działa na swoich lokalnych kopiach, więc taka funkcja:

void swap(int a, int b)
{
  int temp;
  temp = a;
  a = b;
  b = temp;
}

zamieni swoje lokalne zmienne, więc jeśli damy:

void swap(int a, int b);

int x = 123;
int y = 666;
swap(x,y);
printf (x=%d, y=%d, x, y);

wypisze, że dalej x=123, y=666
Przekazanie wskaźników do zmiennych x oraz y pozwoli na zamianę już zmiennych np z funkcji wywołującej:

void swap(int *a, int *b);

int x = 123;
int y = 666;
swap(&x,&y);
printf (x=%d, y=%d, x, y);

w tym przypadku już poprawnie będzie wypisane x=666, y=123
Zwróć uwagę na zapis swap(&x,&y); z wykorzystaniem operatora pobrania adresu zmiennej & w wywołaniu funkcji swap

Przy okazji, kod pisany z palca, bez sprawdzenia w IDE, więc mogą być literówki ;)

0
ksh napisał(a):

Proszę bardzo



#include <stdio.h>

int main( int argc, char *argv[])
{
    
    if( argc > 1 )  {
        
        return 5;
        
    }
    
    if( main( 2, (char *[] ){ "one", "two" } ) == 5 )
      printf( "widzimy roznice ?" );
    
    return 0;
    
}

Takie coś jak (char *[] ){ "one", "two" } to jest chyba C++, i o ile nie ma to bezpośrednio znaczenia w temacie różnicy pomiędzy exit a return z main, bo można przepisać przykład bez tej składni, to ogólnie w standardzie C++ mogą być jakieś inne zasady niż w ANSI C.

ksh napisał(a):

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


int main( int argc, char *argv[])
{
    
    if( argc > 1 )  {
        
        exit( 5 );
        
    }
    
    if( main( 2, (char *[] ){ "one", "two" } ) == 5 )
      printf( "widzimy roznice ?" );
    
    return 0;
    
}

Wiem, że to brudny hack ale to moja dzika karta w tym momencie aby naprostować kogoś, że takie g**no w kodzie jak exit(0); ma się nie pojawiać.

Wywoływanie main (gdy jest funkcją główną w środowisku hostowanym) może stanowić UB, a więc w normalnym kodzie nie występuje, toteż argument ten nie ma sensu.

Natomiast jeśli chodzi Ci o to, że zakańczanie całego programu gdzieś w jakiejś funkcji poza main (co umożliwia exit) jest złe pod względem stylu programowania, to się zgadzam. Poza jakimiś krytycznymi błędami, ale wtedy jest abort. Niemniej jednak nie widzę nic złego w użyciu exit zamiast return pod warunkiem że i tak ma to miejsce wewnątrz main.

ksh napisał(a):

Nie wspomnę o tym, że EXIT_SUCCESS i EXIT_FAILURE są dosyć mgliście zdefiniowane, pod windą oczywiście potrafią zwracać nieintuicyjne wartości.
Więc radosne return EXIT_SUCCESS które nie zwraca zera i wysadzi Ci skrypt w shellu pod cygwinem to ostatnia rzecz na jaką masz ochotę.

Tak to jest z C, że czasami nie wszystkiego możesz być pewien, zwłaszcza w nietypowych sytuacjach typu Cygwin. W środowisku w 100% linuxowym EXIT_SUCCESS przy normalnym kompilatorze powinno jednak zawsze wynosić 0, bo tak to działa na tym systemie, choćby właśnie jeśli chodzi o skrypty.

ksh napisał(a):

No i tego ten return to statement a exit to funkcja. Jednak dla mnie trochę inne rzeczy.

OK i może nawet ma to znaczenie praktyczne, bo np. x || exit (1); ale nie x || return 1;, ale mi chodziło o różnicę w działaniu czyli tym co się stanie gdy już w jakiś sposób dojdzie do wywołania exit / powrotu z main.

Jeśli ktoś jest ciekawy, była podobna dyskusja na StackOverflow: https://stackoverflow.com/questions/35471878/difference-between-exit-and-return-in-main-function-in-c

0

"Takie coś jak (char *[] ){ "one", "two" } to jest chyba C++, i"

Takie coś to jest C kolego a nie C++. Ja C++ nie używam.

Jedyny błąd jaki tam zrobiłem, to że std wymaga NULL na końcu argv.

"Wywoływanie main (gdy jest funkcją główną w środowisku hostowanym) może stanowić UB, a więc w normalnym kodzie nie występuje, toteż argument ten nie ma sensu."

Z jakiej paki te rewelacje? To jest nie prawda.

0

XD okazuje się że GCC to kompiluje. Ale to może C99 czy jakieś rozszerzenie, bo przed chwilą przejrzałem gramatykę i nie ma czegoś takiego w wyrażeniu.

0

Tkz czego niby nie ma? Tam nie ma nic nadzwyczajnego.

0

Nie ma takiej składni że masz inicjator tablicy wewnątrz wyrażenia.

0

"since C99". Właśnie. Pisałem że to może być C99.

0

Tylko od 25 lat w standardzie. < face palm >

0
ksh napisał(a):

Tylko od 25 lat w standardzie. < face palm >

Pretensje zgłaszaj do Microsoft.
MSVC nie wspiera w pełni wymaganych funkcji C99 i pewnie dlatego nie istnieje dla Manna5
/std (Specify Language Standard Version) | Microsoft Learn

The compiler doesn't implement several required features of C99, so it isn't possible to specify C99 conformance, either.

0
ksh napisał(a):

Tylko od 25 lat w standardzie. < face palm >

W standardzie języka C, a nie C++.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.