C
backstory
Karım aileden bir kedi miras aldı. † Maalesef hayvanlara çok alerjim var. Kedi asalının çok ötesindeydi ve biz alınmadan bile ötenazi yapılmalıydı, ancak duygusal değeri nedeniyle ondan kurtulmak için kendisini getiremedi. Acımı sona erdirmek için bir plan yaptım .
Uzun bir tatile gidecektik, ancak kediyi veterinerin ofisine koymak istemedi. Hastalığa yakalanma ya da kötü muamele görmekten endişe duyuyordu. Otomatik bir kedi besleyici oluşturdum, böylece evde bırakabilirdik. Mikrodenetleyicinin ürün main
bilgisini C'ye yazdım. İçindeki dosya aşağıdaki koda benziyordu.
Bununla birlikte, karım da bir programcı ve kediye olan duygularımı biliyordu, bu yüzden onu evde gözetimsiz bırakmayı kabul etmeden önce bir kod incelemesinde ısrar etti. Aşağıdakiler de dahil olmak üzere çeşitli endişeleri vardı:
main
Standartlara uygun bir imzası yoktur (barındırılan bir uygulama için)
main
bir değer döndürmez
tempTm
malloc
yerine çağrıldığından bu yana başlatılmamış kullanılırcalloc
- geri dönüş değeri
malloc
atılmamalıdır
- mikrodenetleyici süresi yanlış veya devrilebilir (Y2K veya Unix time 2038 problemlerine benzer şekilde)
elapsedTime
Değişken yeterli bir aralığı olmayabilir
Çok ikna edici oldu, ancak sonunda tezlerin çeşitli nedenlerden dolayı sorun olmadığına karar verdi (uçağımıza geç kalmamıza bile zarar vermedi). Canlı test için zaman olmadığından, kodu onayladı ve tatile gittik. Bir kaç hafta sonra döndüğünde, benim (bir sonucu olarak şimdi daha bir sürü var gerçi) kedinin sefalet bitmişti.
† Tamamen hayali senaryo, endişe yok.
kod
#include <time.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
//#include "feedcat.h"
// contains extern void FeedCat(struct tm *);
// implemented in feedcat.c
// stub included here for demonstration only
#include <stdio.h>
// passed by pointer to avoid putting large structure on stack (which is very limited)
void FeedCat(struct tm *amPm)
{
if(amPm->tm_hour >= 12)
printf("Feeding cat dinner portion\n");
else
printf("Feeding cat breakfast portion\n");
}
// fallback value calculated based on MCU clock rate and average CPI
const uintmax_t FALLBACK_COUNTER_LIMIT = UINTMAX_MAX;
int main (void (*irqVector)(void))
{
// small stack variables
// seconds since last feed
int elapsedTime = 0;
// fallback fail-safe counter
uintmax_t loopIterationsSinceFeed = 0;
// last time cat was fed
time_t lastFeedingTime;
// current time
time_t nowTime;
// large struct on the heap
// stores converted calendar time to help determine how much food to
// dispense (morning vs. evening)
struct tm * tempTm = (struct tm *)malloc(sizeof(struct tm));
// assume the cat hasn't been fed for a long time (in case, for instance,
// the feeder lost power), so make sure it's fed the first time through
lastFeedingTime = (size_t)(-1);
while(1)
{
// increment fallback counter to protect in case of time loss
// or other anomaly
loopIterationsSinceFeed++;
// get current time, write into to nowTime
time(&nowTime);
// calculate time since last feeding
elapsedTime = (int)difftime(nowTime, lastFeedingTime);
// get calendar time, write into tempTm since localtime uses an
// internal static variable
memcpy(&tempTm, localtime(&nowTime), sizeof(struct tm));
// feed the cat if 12 hours have elapsed or if our fallback
// counter reaches the limit
if( elapsedTime >= 12*60*60 ||
loopIterationsSinceFeed >= FALLBACK_COUNTER_LIMIT)
{
// dispense food
FeedCat(tempTm);
// update last feeding time
time(&lastFeedingTime);
// reset fallback counter
loopIterationsSinceFeed = 0;
}
}
}
Tanımlanmamış davranış:
UB'nin kendisini bulmasını zahmet etmek istemeyenler için:
Bu kodda kesinlikle yerel-spesifik, tanımlanmamış ve uygulama-tanımlanmış bir davranış vardır, ancak hepsinin doğru çalışması gerekir. Sorun şu kod satırlarındadır: işaretçiyi
struct tm * tempTm //...
//...
memcpy(&tempTm, localtime(&nowTime), sizeof(struct tm));
memcpy
, tempTM
işaret ettiği nesne yerine yığının üzerine koyarak yazar. Bu, diğer şeylerin yanı sıra, yazar, elapsedTime
ve loopIterationsSinceFeed
. İşte değerleri yazdırdığım bir örnek çalışma:
pre-smash : elapsedTime=1394210441 loopIterationsSinceFeed=1
post-smash : elapsedTime=65 loopIterationsSinceFeed=0
Kediyi öldürme olasılığı:
- Kısıtlı yürütme ortamı ve yapı zinciri göz önüne alındığında, tanımsız davranış her zaman gerçekleşir.
- Benzer şekilde, tanımsız davranış her zaman kedi besleyicinin amaçlandığı gibi çalışmasını önler (veya daha doğrusu amaçlandığı gibi "çalışmasına" izin verir).
- Besleyici çalışmıyorsa, kedinin ölmesi muhtemeldir. Bu, kendisi için savaşabilecek bir kedi değil ve komşunun ona bakmasını istemedim.
Kedinin 0,995 olasılıkla öldüğünü tahmin ediyorum .