Dlaczego funkcje wartości bezwzględnej w C nie akceptują danych stałych?

23

W C prototypem funkcji wartości bezwzględnej (która akceptuje liczbę zmiennoprzecinkową) jest

 float fabsf( float );

Dlaczego ten prototyp nie przyjmuje stałej wartości, takiej jak ta:

 float fabsf( float const );

fabsf nie zmieni wartości danych wejściowych, prawda?

Jeśli mam funkcję, która akceptuje dane wejściowe i wywołuje fabsf, czy jestem zmuszony unikać podawania danych wejściowych jako const?

Jaki jest właściwy sposób postępowania z poprawnością const w tej sytuacji?

użytkownik24205
źródło
26
constjest tu zbędny, jak myślisz, co się dzieje?
MM
1
@MM Oczekuję, że spowodowałoby to błąd czasu kompilacji, gdybym próbował zmienić wartość danych wejściowych wewnątrz funkcji. Czy to jest nieprawidłowe?
user24205,
16
Ponieważ parametr wewnątrz funkcji jest kopią lokalną, dodawanie constjest całkowicie bez znaczenia.
Lundin
1
fabsf nie zmieni wartości danych wejściowych, prawda? ” Jak mogłeś powiedzieć? Parametr jest przekazywany przez wartość.
David Schwartz
Poniższy kod jest C prawna: float const x = -1.0; float y = fabsf(x);więc wydaje mi się, że fabsf nie akceptują wejść const. Nie można powiedzieć „możesz przekazać mi floatwartość, ale nie możesz const float”. (I jak widzimy w odpowiedziach, C nie zapewnia sposobu na wymaganie, aby dane wejściowe do funkcji były a float const.)
David K

Odpowiedzi:

14

Edytować

Jak skomentował MM, o parametrach prototypówconst jest ignorowany. Edytowane źródło oryginalnej odpowiedzi (patrz poniżej) pokazuje to:

float correct(float const value);

float erroneous(float const value);

float changer(float value);

float correct(float value) {
  return -value;
}

float erroneous(float value) {
  value = -value;
  return value;
}

float changer(float value) {
    value = -value;
    return value;
}

Brak komunikatu o błędzie.

W każdym razie zostawię oryginał na miejscu w nadziei, że może to pomóc.


Oryginalny

constNa parametr sprawia, że ten parametr tylko do odczytu wewnątrz funkcji.

Na przykład:

float correct(float const value) {
  return -value;
}

float erroneous(float const value) {
  value = -value;
  return value;
}

float changer(float value) {
  value = -value;
  return value;
}

To źródło nie skompiluje się bez komunikatu o błędzie.

Funkcja correct()odczyta podaną wartość, zmieni jej znak i zwróci wartość zanegowaną.

Funkcja erroneous()wydaje się działać tak samo, z tym wyjątkiem, że do parametru przypisano przypisanie. Ale jako parametr jest constto niedozwolone.

Następnie funkcja changer()będzie działać tak samo jak poprzednio, ale nie spowoduje błędów.

Spójrzmy na stronę połączeń:

float f = 3.14159;
float g = correct(f); // or erroneous(f) or changer(f)

Zmienna fpodana jako argument zostanie skopiowana do parametru value. Nigdy się nie zmieni, nawet jeśli changer()zostanie wywołany.

Możesz spojrzeć na parametry jako na rodzaj zmiennych lokalnych. W rzeczywistości są one w większości obsługiwane w ten sposób w wygenerowanym kodzie maszynowym.


Dlaczego więc constczasami widzisz ? Widzisz to, jeśli wskaźnik jest zdefiniowany jako parametr.

Jeśli nie chcesz, aby wskazywana wartość była zmieniana, musisz dodać const; ale zrób to we właściwej pozycji!

void effective(int const * pointer);

void futile(int * const pointer);

void possible_but_overly_restricted(int const * const pointer);
zajęty
źródło
Pytanie dotyczy jednak prototypów, prototyp float fabsf( float const );nie ma nic wspólnego z implementacją funkcji (która nie musi się powtarzać const), w rzeczywistości constjest całkowicie ignorowany w prototypie
MM
2
Czy const może wchodzić w definicje funkcji bez wchodzenia w prototyp?
user24205,
3
@ user24205 tak, może
Daniel Jour
33

C używa parametru pass by value. Wartość parametru funkcji jest kopią podanego argumentu.

Można kopiować zarówno zmiennoprzecinkowe const, jak i non-const, a wynikiem jest zmiennoprzecinkowe non-const.

Jest podobny do przypisania:

const float f = 5.5f;
float g = f;   // OK

W rzeczywistości język określa, że ​​wartość wyrażenia nigdy nie może być const, tzn. Gdy wartość jest odczytywana ze zmiennej, nie jest to wartość, constnawet jeśli zmienna była.

MM
źródło
8

Ponieważ język C używa semantyki przekazywania według wartości, każdy przekazany do niego argument, chociaż można go zmodyfikować wewnętrznie, nie wpływa bezpośrednio na przekazywaną wartość.

Oznacza to, że z punktu widzenia rozmówcy, float fabsf( float );i float fabsf( const float );są takie same. Więc nie ma sensu tworzyć parametru const.

Gdzie to ma sens stosowania constjest jeśli parametr przekazać w to wskaźnik, na przykład:

void print_string(char *str)

Ta funkcja, pomimo tego, co sugeruje nazwa, może wyłuskać dany wskaźnik i modyfikować to, co wskazuje, tj. str[0] = 'x'Aby spowodować zmianę widoczną przez funkcję wywołującą. Jeśli tę funkcję zdefiniowano w następujący sposób:

void print_string(const char *str)

Dzwoniący ma zapewnioną, że funkcja nie może dokonywać żadnych modyfikacji tego, co strwskazuje.

dbush
źródło
„Dzwoniącemu zapewnia się, że funkcja nie może dokonywać żadnych modyfikacji ...” nie jest prawdą. Funkcja zna adres danych i dlatego może go modyfikować, np .:((char*)str)[0] = 'f' .const ... *Na liście argumentów jest więc tylko „oświadczenie woli”.
oromoiluig
5

Aby dodać perspektywę prawnika językowego:

Aby dwa typy funkcji były kompatybilne, oba określają kompatybilne typy zwrotów. Ponadto listy typów parametrów, jeśli oba są obecne, powinny zgadzać się co do liczby parametrów i sposobu użycia terminatora elipsy; odpowiednie parametry powinny mieć kompatybilne typy . [..] Przy określaniu zgodności typu i typu złożonego, [..] każdy parametr zadeklarowany za pomocą typu kwalifikowanego jest traktowany jako posiadający niekwalifikowaną wersję swojego zadeklarowanego typu .

N1570 6.7.6.3/15

Oznacza to, że te dwa są kompatybilne:

void foo(int const);
void foo(int);

Dlatego możesz napisać prototyp z lub bez const(co oznacza, że ​​nie ma większego sensu; mniej pisać / czytać) i możesz dodać constdefinicję funkcji, jeśli chcesz uniknąć przypadkowej modyfikacji parametru (skopiowane - wywołaj przez wartość!) Wewnątrz funkcji ciało.

Daniel Jour
źródło