SORU
19 EKİM 2009, PAZARTESİ


C işlev işaretçileri için anlayış typedefs: Örnekler, ipuçları, lütfen

Her zaman biraz diğer insanların bağımsız değişken işlev işaretçileri için typedefs olan kod. okurken şaşkın oldum Bana biraz sayısal bir algoritma C bir süre önce yazılmış anlamaya çalışırken böyle bir tanım etrafında sürdü hatırladığım kadarıyla. Şimdi acaba paylaşım ipuçları ve düşünceler üzerine yazmayı iyi typedefs için işaretçiler fonksiyonlar (var ve yok), niye onlar yararlı ve nasıl anlamak için başkalarının işini? Teşekkürler!

CEVAP
19 EKİM 2009, PAZARTESİ


C standart signal() işlevi göz önünde bulundurun:

extern void (*signal(int, void(*)(int)))(int);

Mükemmel çok bariz - bir fonksiyon iki argüman alır, bir tamsayı ve bir işaretçi işlevi alır bir tamsayı argüman olarak verir ve hiçbir şey, ve (signal()) bir işaretçi döndürür bir işlev alır bir tamsayı argüman olarak ve hiçbir şey döndürür.

Eğer yazarsan:

typedef void (*SignalHandler)(int signum);

sonra signal() olarak bildirebilirsiniz yerine:

extern  SignalHandler signal(int signum, SignalHandler handler);

Bu aynı şey demek, ama genellikle okumak için biraz daha kolay olarak kabul edilir. İşlevi int SignalHandler döndürür SignalHandler bir daha net alır.

Alışmak biraz alır. Ama yapamayacağınız tek şey, sinyal yakalama işleviSignalHandler typedef fonksiyon tanımını kullanarak yazmak.

Bir işlev işaretçisi olarak: çağırmak için tercih hala eski okul değilim

(*functionpointer)(arg1, arg2, ...);

Modern sözdizimi kullanır:

functionpointer(arg1, arg2, ...);

Bu işleri neden görebilirsiniz - sadece değişken bir fonksiyonu functionpointer adlı ziyade başlatıldı nerede aramam gerektiğini bilmek tercih ederim.


Sam yorumladı:

Önce bu açıklamayı gördüm. Ve sonra da, şimdi olduğu gibi, benim anlamadığım iki olay arasındaki bağlantı olduğunu düşünüyorum

    extern void (*signal(int, void()(int)))(int);  /*and*/

    typedef void (*SignalHandler)(int signum);
    extern SignalHandler signal(int signum, SignalHandler handler);

Ya da, sormak istediğim şey, bir ikinci versiyonu ile gelmek için kullanabileceğiniz temel kavram nedir? Bağlayan temel "" ve ilk typedef? SignalHandler nedir Burada izah edilmesi gereken şey typedef aslında burada ne işi var, ne olduğunu düşünüyorum.

Bir daha deneyelim. Bunlardan ilki düz C standart kaldırdı - ben retyped, ve parantez doğru olduğunu kontrol düzelttim - hatırlamak zor bir kurabiye değil kadar).

Öncelikle, typedef bir türü için bir ad tanıtır unutmayın. Yani, diğer SignalHandler ve türü:

argüman olarak bir tamsayı alır ve hiçbir şey döndüren bir işlev için bir işaretçi.

'Hiçbir şey döndürür' bölümü void; bir tamsayı olduğunu öne (güveniyorum) kendi kendini açıklayıcı yazılmış. Aşağıdaki gösterimde C belirtilen ve belirli bir tür geri dönüş alarak bağımsız değişkenler işaretçi işlevi için büyü nasıl sadece (ya da değil)

type (*function)(argtypes);

Sinyal işleyicisi türünü oluşturduktan sonra, değişkenler ve benzeri bildirmek için kullanabilirim. Örneğin:

static void alarm_catcher(int signum)
{
    fprintf(stderr, "%s() called (%d)\n", __func__, signum);
}

static void signal_catcher(int signum)
{
    fprintf(stderr, "%s() called (%d) - exiting\n", __func__, signum);
    exit(1);
}

static struct Handlers
{
    int              signum;
    SignalHandler    handler;
} handler[] =
{
    { SIGALRM,   alarm_catcher  },
    { SIGINT,    signal_catcher },
    { SIGQUIT,   signal_catcher },
};

int main(void)
{
    size_t num_handlers = sizeof(handler) / sizeof(handler[0]);
    size_t i;

    for (i = 0; i < num_handlers; i  )
    {
        SignalHandler old_handler = signal(handler[i].signum, SIG_IGN);
        if (old_handler != SIG_IGN)
            old_handler = signal(handler[i].signum, handler[i].handler);
        assert(old_handler == SIG_IGN);
    }

    ...continue with ordinary processing...

    return(EXIT_SUCCESS);
}

Lütfen not How to avoid using printf() in a signal handler?

Kod temiz bir şekilde derlemek için gerekli olacağını 4 standart başlıkları ihmal al dışında ne yaptık?

İlk iki işlevi tek bir tamsayı olmayan fonksiyonların ve hiçbir şey döndürür. Bunlar aslında birer exit(1); sayesinde de geri gelmez ama başka bir mesaj yazdırdıktan sonra geri dönüş yok. Unutmayın C standart izni vermez sana ne çok içinde bir sinyal işleyici; POSIX biraz daha cömert ne izin, ama resmi olarak değil yaptırım arama fprintf(). Ben de alınan sinyal sayısı çıktı. İçinde alarm_handler() fonksiyon değerini daima yanında olacağım SIGALRM olarak tek sinyal bu işleyici için, ama signal_handler() olabilir SIGINT SIGQUIT sinyal sayısı aynı işlev için kullanılır.

O zaman her öğe bir sinyal sayısı ve sinyal için kurulacak işleyicisi tanımlar nerede yapıları dizisi oluşturmak. Ben seçtik endişe sinyalleri 3; ederim genellikle endişe SIGHUP, SIGPIPE SIGTERM ve çok ilgili olup olmadıkları tanımlı (#ifdef koşullu derleme), ama bu sadece işleri zorlaştırıyor. Ayrıca muhtemelen sigaction() POSIX signal() ama bu başka bir konudur yerine kullanırdım; ile başladığımız şeyi devam edelim.

main() işlevi işleyicileri listesi üzerinde kurulacak dolaşır. Her işleyici, ilk çağrıları signal() öğrenmek ister işlemidir şu anda görmezden sinyal, ve bunu yaparken, yükler SIG_IGN olarak işleyici, hangi sağlayan sinyal göz ardı kalır. Eğer sinyal daha önce göz ardı ediliyordu değilse, o zaman signal() tekrar tercih sinyal işleyici yüklemek için bu kez çağırır. (Diğer değeri muhtemelenSIG_DFL sinyal için varsayılan sinyal işleyici.) Çünkü İlk Çağrı 'sinyali()' set işleyicisi SIG_IGN ve signal() döndürür önceki hata işleyicisi değeri old sonra if ifadesi olmalı SIG_IGN - dolayısıyla onaylama. (Eğer bir şey büyük ölçüde yanlış giderse SIG_ERR olabilir - ama sonra gelen iddia eden ateş hakkında bilgi edinmek istiyorum.)

Program daha sonra normal şeyler ve çıkışlar yapıyor.

Bir fonksiyonun adını uygun türde bir işlev işaretçisi olarak kabul edilebilir unutmayın. İşlevi-Ara parantez uygulamak için zaman - örneğin başlatıcılar gibi - işlev adı bir işlev işaretçisi olur. Bu da neden makul çağırmak için fonksiyonları ile pointertofunction(arg1, arg2) gösterim; zaman alarm_handler(1), düşünün alarm_handler işaretçi işlevi ve bu nedenle alarm_handler(1) bir çağırma bir fonksiyon üzerinden bir işlev işaretçisi.

Bu yüzden, bugüne kadar yaptığım gösterilen bir SignalHandler değişken nispeten düz ileri kullanımı, sürece sen de sağ tür değer atamak - iki sinyal işleyicisi işlevleri sağlar.

Şimdi soruya geri - nasıl signal() için iki bildirimleri birbirine bağlıyoruz.

Hadi ikinci bildirimi gözden geçirin:

 extern SignalHandler signal(int signum, SignalHandler handler);

İşlev adı ve bu gibi türünü değiştirdik:

 extern double function(int num1, double num2);

sorun bağımsız ve döner int double double bir değer alır bir işlev olarak yorumlamak gerekir (olur mu? belki bu sorunlu - ama belki de bu soruları sorduğum için dikkatli olmak ne kadar zor eğer bir sorun varsa bu bir gerekir eğer itiraf) daha iyi olur.

Şimdi,* *75, signal() işlevi SignalHandler ikinci argüman olarak alır bir yerine, ve sonuç olarak döndürür.

Bu da tedavi edilebilir mekaniği:

extern void (*signal(int signum, void(*handler)(int signum)))(int signum);

açıklamak zor. muhtemelen berbat ederim. Bu sefer parametreleri isimleri verdim ama adlarını kritik değil.

C, genel olarak, bildirim mekanizması eğer yazarsan böyle olur:

type var;

sonra var yazarken type belirli bir değeri temsil eder. Örneğin:

int     i;            // i is an int
int    *ip;           // *ip is an int, so ip is a pointer to an integer
int     abs(int val); // abs(-1) is an int, so abs is a (pointer to a)
                      // function returning an int and taking an int argument

Standardı, typedef dilbilgisi depolama bir sınıf olarak kabul edilir, daha doğrusu static 85* saklama sınıfları.

typedef void (*SignalHandler)(int signum);

türünde bir değişken gördüğünde SignalHandler (alarm_handler) olarak çağrılan anlamına gelir:

(*alarm_handler)(-1);

sonuç type void - sonuç yok. (*alarm_handler)(-1); değişken -1 alarm_handler() bir duadır.

Eğer ilan ettik Yani, eğer:

extern SignalHandler alt_signal(void);

bunun anlamı:

(*alt_signal)();

bir boşluk değerini temsil eder. Ve bu nedenle:

extern void (*alt_signal(void))(int signum);

eşdeğerdir. Şimdi, signal() sadece SignalHandler de bir int ve bağımsız olarak SignalHandler her ikisi de kabul eder: verdiğinden daha karmaşıktır

extern void (*signal(int signum, SignalHandler handler))(int signum);

extern void (*signal(int signum, void (*handler)(int signum)))(int signum);

Eğer hala karıştırıyor musun, emin değilim yardım nasıl yapılır - hala bazı seviyeleri gizemli beni, ama ben edilmelerini nasıl çalıştığını ve bu nedenle sana bir sopa ile bunun için başka bir 25 yıl falan, o olur ikinci doğa size (ve hatta belki de biraz daha hızlı eğer zeki).

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Chip Johnson

    Chip Johnson

    30 AĞUSTOS 2007
  • Grace Su

    Grace Su

    6 Ocak 2006
  • Joseph Herscher

    Joseph Hersc

    14 Mart 2007