Apakah mungkin untuk memiliki input variabel dalam fungsi C

Saya membuat fungsi sederhana untuk menghitung perbedaan antara dua input. Hanya untuk kenyamanan saja. Sesuatu untuk dirujuk dalam file header pribadi.

Saya ingin memasukkan:

dua bilangan bulat : menghasilkan satu bilangan bulat

dua ganda : menghasilkan satu ganda

Saya sudah mencoba mencari semacam deklarasi masukan global secara online tetapi tidak dapat menemukannya.

Saya lebih suka tidak memiliki dua fungsi, hanya satu fungsi sederhana.

Contoh kode header: int diff(int a, int b); perbedaan ganda(ganda a, ganda b);

Terima kasih untuk bantuannya!


person PathToLife    schedule 21.10.2014    source sumber
comment
tambahkan parameter ketiga yang menunjukkan jenis argumen & keluarannya. Kemudian minta kode memeriksa parameter ketiga itu dan bertindak sesuai dengan itu.   -  person user3629249    schedule 21.10.2014


Jawaban (3)


Tidak, ini disebut kelebihan beban dan ini bukan fitur yang dimiliki C. Anda sebaiknya membuat fungsi berbeda untuk menangani hal ini.

Anda dapat melakukannya dengan semua jenis sihir C (Anda bisa melakukan apa saja dengan cukup sihir C), namun kode yang dihasilkan mungkin sangat jelek sehingga tidak dapat dipelihara: -)

Misalnya, C11 memperkenalkan pilihan umum, dengan ekspresi utama _Generic, dan ini memungkinkan Anda memanggil fungsi berbeda berdasarkan tipe argumen masukan. Sebenarnya fungsinya lebih dari itu, tapi itulah aspek yang Anda minati, berdasarkan pertanyaan Anda.

Misalnya, Anda mendefinisikan dua fungsi sebagai berikut:

int     diffi (int    a, int    b) { return a - b; }
double  diffd (double a, double b) { return a - b; }

Biasanya, Anda harus memutuskan mana yang akan dipanggil berdasarkan jenis masukan Anda. Fitur pemilihan umum C11 memungkinkan Anda melakukan ini:

#define diff(a,b)        \
    _Generic((a),        \
        double:  diffd,  \
        int:     diffi,  \
        default: diffX   \
    )(a,b)

Dan apa yang dilakukannya pada dasarnya adalah menemukan makro diff(x,y) di kode sumber:

  • menentukan jenis ekspresi (a) tanpa mengevaluasinya;
  • menyuntikkan ke aliran sumber token yang cocok dengan jenis tersebut (atau default jika tidak ditemukan kecocokan);
  • menyuntikkan ke aliran sumber teks (a,b) di akhir.

Jadi, jika file sumber Anda berisi baris:

x = diff (1,   2);
y = diff (1.0, 2);

ini akan diterjemahkan ke dalam:

x = diffi (1  , 2);
y = diffd (1.0, 2);

memberi Anda kelebihan beban yang efektif.

Itu kasus yang cukup mudah karena hanya bergantung pada tipe argumen pertama - Anda akan melihat lubang di sana jika Anda mencoba melakukannya:

z = diff (1, 2.0);

di mana tipe argumen pertama adalah int sehingga Anda akan mendapatkan:

z = diffi (1, 2.0);

yang sebenarnya bukan hal yang ingin Anda lakukan. Di sinilah kompleksitas muncul karena Anda harus mencakup empat kemungkinan: {int/int, int/double, double/int, double/double} dan ini menjadi lebih kompleks berdasarkan jumlah argumen dan kemungkinan tipe untuk setiap argumen.

Namun, kasus lengkap Anda dapat dilakukan dengan penggunaan default yang bijaksana dan pilihan umum yang disarangkan, seperti:

#define diff(a,b)              \
    _Generic((a),              \
        double:  diffd,        \
        default: _Generic((b), \
            double:  diffd,    \
            default: diffi     \
        )                      \
    )(a,b)

dan ini dapat dibaca sebagai:

  • jika tipe a adalah double, gunakan diffd;
  • sebaliknya, jika tipe b adalah double, gunakan diffd;
  • jika tidak, gunakan diffi.
  • jangan lupa untuk memasukkan argumennya juga.

Program lengkap berikut (dikompilasi dengan clang 3.0) menunjukkan cara kerja fitur ini:

#include <stdio.h>

int diffi (int a, int b) {
    printf ("diffi %d %d", a, b);
    return a - b;
}
double diffd (double a, double b) {
    printf ("diffd %f %f", a, b);
    return a - b;
}

#define diff(a,b)              \
    _Generic((a),              \
        double:  diffd,        \
        default: _Generic((b), \
            double:  diffd,    \
            default: diffi     \
        )                      \
    )(a,b)

int main (void) {
    int i; double d;
    i = diff (1  , 2  ); printf (" --> %d\n", i);
    d = diff (1.0, 2  ); printf (" --> %f\n", d);
    d = diff (1  , 2.0); printf (" --> %f\n", d);
    d = diff (1.0, 2.0); printf (" --> %f\n", d);
    return 0;
}

Output dari program itu adalah:

diffi 1 2 --> -1
diffd 1.000000 2.000000 --> -1.000000
diffd 1.000000 2.000000 --> -1.000000
diffd 1.000000 2.000000 --> -1.000000

menunjukkan bahwa fungsi yang benar dipanggil untuk empat kemungkinan.


Faktanya, seperti rici tunjukkan dalam komentar, Anda dapat mengandalkan aturan promosi C, di mana menambahkan double dan int (dalam urutan apa pun) memberi Anda double sementara menambahkan dua variabel int memberi Anda int:

#define diff(a,b) _Generic((a+b), double:diffd, default:diffi)(a,b)
person paxdiablo    schedule 21.10.2014
comment
Bagaimana dengan fitur C11 _Generic? (lihat bagian 6.5.1.1 dari open-std.org /jtc1/sc22/wg14/www/docs/n1570.pdf) Ini tidak secantik C++, tapi tidak seburuk itu. - person rici; 21.10.2014
comment
@rici, Anda harus mencobanya dengan fungsi empat argumen, yang masing-masing dapat berupa salah satu dari tiga jenis, maka Anda akan menghargai keburukannya :-) Namun, tidak masalah untuk kasus penggunaan yang lebih kecil jadi saya akan menambahkannya sebagai pilihan. - person paxdiablo; 22.10.2014
comment
@paxdiablo: Saya sendiri menggunakan C++ :-) Tapi _Generic tentu saja cukup bagus untuk pertanyaan itu. - person rici; 22.10.2014
comment
@rici, ini dia, Anda benar, ternyata tidak seburuk yang saya kira (setidaknya untuk kasus penggunaan tertentu). - person paxdiablo; 22.10.2014
comment
@rici, jarang sekali saya terkesan dengan kelicikan seseorang - keputusan yang bagus :-) - person paxdiablo; 22.10.2014
comment
Saya telah memilih Anda, jika tidak, hasil edit Anda akan lebih berharga (+1) - person Mohit Jain; 22.10.2014
comment
_Umum. Benar-benar suatu keajaiban. Terima kasih atas jawaban lengkapnya paxdiablo dan rici. :7) - person PathToLife; 22.10.2014

Fungsi yang berlebihan tidak tersedia di C. Beberapa kemungkinan solusi

  1. Gunakan dua fungsi dengan nama berbeda misalnya: diff_i dan diff_d (Disarankan)
  2. Gunakan varargs (tidak disarankan) Ini akan membuat kode sulit dipertahankan seiring berjalannya waktu.
  3. Gunakan _Generik
person Mohit Jain    schedule 21.10.2014

Penggandaan IEEE754 memiliki presisi lebih dari 32bit, jadi cukup tulis versi ganda dan biarkan konversi otomatis menangani sisanya.

Tentu saja jika Anda menggunakan sistem di mana sizeof(int)>4 atau char memiliki lebih dari 8 bit, mungkin yang terbaik adalah menulis varian untuk setiap jenis, dan mengadopsi semacam konvensi penamaan Hongaria untuk varian tersebut. Anda kemudian dapat menulis, mungkin:

int diffi(int, int);
double diffd(double, double);
ssize_t diffz(size_t, size_t);

dll. Pada dasarnya, ini adalah secara manual pengrusakan nama, teknik yang sama yang digunakan kompiler C++ untuk menghasilkan nama simbolik yang berbeda untuk tabel ekspor file objek.

person luser droog    schedule 21.10.2014
comment
AC int memiliki rentang minimum, bukan rentang maksimum. Jadi mungkin ada implementasi di mana int membutuhkan lebih dari presisi double :-) Karena itu, bukanlah ide yang buruk jika Anda mengetahui batasannya. - person paxdiablo; 21.10.2014
comment
BENAR. Terima kasih! Itu memberi saya sesuatu untuk ditambahkan pada jawabannya. - person luser droog; 21.10.2014