C
backstory
ภรรยาของฉันสืบทอดแมวจากครอบครัว †น่าเสียดายที่ฉันแพ้สัตว์มาก แมวผ่านตัวมันไปได้ดีและควรจะได้รับการกำจัดแม้แต่ก่อนที่เราจะได้รับมัน แต่เธอไม่สามารถพาตัวเองไปกำจัดมันได้เนื่องจากคุณค่าทางอารมณ์ของมัน ผมมีแผนจะจบของฉันทุกข์
พวกเราไปพักผ่อนในช่วงวันหยุดยาว แต่เธอไม่ต้องการให้แมวไปที่สำนักงานสัตวแพทย์ เธอเป็นห่วงว่าจะทำสัญญาเจ็บป่วยหรือถูกทำร้าย ฉันสร้างเครื่องป้อนแมวอัตโนมัติเพื่อให้เราสามารถทิ้งไว้ที่บ้าน ฉันเขียนเฟิร์มแวร์ของไมโครคอนโทรลเลอร์ใน C. ไฟล์ที่มีmain
ลักษณะคล้ายกับรหัสด้านล่าง
อย่างไรก็ตามภรรยาของฉันยังเป็นโปรแกรมเมอร์และรู้จักความรู้สึกของฉันที่มีต่อแมวดังนั้นเธอจึงยืนยันในการตรวจสอบโค้ดก่อนที่จะตกลงที่จะทิ้งมันไว้ที่บ้านโดยไม่ต้องดูแล เธอมีความกังวลหลายประการรวมถึง:
main
ไม่มีลายเซ็นที่เป็นไปตามมาตรฐาน (สำหรับการใช้งานโฮสต์)
main
ไม่ส่งคืนค่า
tempTm
ถูกใช้โดยไม่กำหนดค่าเริ่มต้นเนื่องจากmalloc
ถูกเรียกใช้แทนcalloc
malloc
ไม่ควรทิ้งค่าส่งคืนของ
- เวลาของไมโครคอนโทรลเลอร์อาจไม่ถูกต้องหรือคลี่คลาย (คล้ายกับปัญหา Y2K หรือ Unix เวลา 2038)
elapsedTime
ตัวแปรอาจจะไม่ได้มีช่วงที่เพียงพอ
มันน่าเชื่อถือมาก แต่ในที่สุดเธอก็เห็นด้วยว่าสิ่งเหล่านี้ไม่ได้มีปัญหาด้วยเหตุผลหลายประการ เนื่องจากไม่มีเวลาสำหรับการทดสอบสดเธอจึงอนุมัติรหัสและเราไปเที่ยวพักผ่อน เมื่อเรากลับมาไม่กี่สัปดาห์ต่อมาฉันทุกข์ยากของแมวได้มากกว่า (แม้ว่าจะเป็นผลให้ฉันได้ในขณะนี้มีปริมาณมากขึ้น)
scenario สถานการณ์สมมติทั้งหมดโดยสิ้นเชิงไม่ต้องกังวล
รหัส
#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;
}
}
}
พฤติกรรมที่ไม่ได้กำหนด:
สำหรับผู้ที่ไม่ต้องการรบกวนการค้นหา UB ด้วยตนเอง:
มีพฤติกรรมที่เฉพาะเจาะจงในท้องถิ่นที่ไม่ได้ระบุและการใช้งานที่กำหนดไว้ในรหัสนี้ แต่ทั้งหมดนี้ควรทำงานได้อย่างถูกต้อง ปัญหาอยู่ในบรรทัดของรหัสต่อไปนี้:
struct tm * tempTm //...
//...
memcpy(&tempTm, localtime(&nowTime), sizeof(struct tm));
memcpy
เขียนทับtempTM
ตัวชี้แทนวัตถุที่ชี้ไปที่ยอดเยี่ยมกองซ้อน นี้เขียนทับนอกเหนือไปจากสิ่งอื่น ๆและelapsedTime
loopIterationsSinceFeed
นี่คือตัวอย่างการรันที่ฉันพิมพ์ค่า:
pre-smash : elapsedTime=1394210441 loopIterationsSinceFeed=1
post-smash : elapsedTime=65 loopIterationsSinceFeed=0
ความน่าจะเป็นในการฆ่าแมว:
- ด้วยสภาพแวดล้อมการทำงานที่ จำกัด และการสร้างลูกโซ่พฤติกรรมที่ไม่ได้กำหนดจะเกิดขึ้นเสมอ
- พฤติกรรมที่ไม่ได้กำหนดจะป้องกันไม่ให้ตัวป้อน cat ทำงานตามที่ตั้งใจไว้เสมอ (หรืออนุญาตให้ "ทำงาน" ตามที่ตั้งใจ)
- หากตัวป้อนไม่ทำงานก็มีโอกาสมากที่แมวจะตาย นี่ไม่ใช่แมวที่สามารถดูแลตัวเองได้และฉันก็ไม่ได้ขอให้เพื่อนบ้านมองดูมัน
ผมประเมินว่าแมวตายด้วยความน่า0.995