ต่ำสุดและสูงสุดเป็น c


299

อยู่ที่ไหนMINและMAXกำหนดไว้ใน C ถ้าทั้งหมด?

เป็นวิธีที่ดีที่สุดในการใช้สิ่งเหล่านี้โดยทั่วไปและพิมพ์อย่างปลอดภัยที่สุดเท่าที่จะทำได้? (ส่วนขยายคอมไพเลอร์ / บิวอินสำหรับคอมไพเลอร์หลักที่ต้องการ)

คำตอบ:


392

อยู่ที่ไหนMINและMAXกำหนดไว้ใน C ถ้าทั้งหมด?

พวกเขาไม่ได้

เป็นวิธีที่ดีที่สุดในการใช้สิ่งเหล่านี้โดยทั่วไปและพิมพ์ปลอดภัยที่สุดเท่าที่จะทำได้ (ส่วนขยายคอมไพเลอร์ / บิวด์สำหรับคอมไพเลอร์หลักที่ต้องการ)

เป็นฟังก์ชั่น ฉันจะไม่ใช้แมโคร#define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))โดยเฉพาะอย่างยิ่งหากคุณวางแผนที่จะปรับใช้รหัสของคุณ ไม่ว่าจะเป็นการเขียนของคุณเองใช้สิ่งที่เป็นมาตรฐานfmaxหรือfminแก้ไขแมโครโดยใช้typeof ของ GCC (คุณจะได้รับโบนัสความปลอดภัยมากเกินไป) ในการแสดงออกของคำสั่ง GCC :

 #define max(a,b) \
   ({ __typeof__ (a) _a = (a); \
       __typeof__ (b) _b = (b); \
     _a > _b ? _a : _b; })

ทุกคนพูดว่า "โอ้ฉันรู้เกี่ยวกับการประเมินสองครั้งไม่มีปัญหา" และไม่กี่เดือนตามถนนคุณจะได้รับการแก้ไขปัญหาที่โง่ที่สุดสำหรับชั่วโมงที่สิ้นสุด

หมายเหตุการใช้งาน__typeof__แทนtypeof:

ถ้าคุณเขียนไฟล์ส่วนหัวว่าการทำงานต้องเมื่อรวมอยู่ในโปรแกรม ISO C, เขียนแทน__typeof__ typeof


68
คุณจะรู้ว่ามันจะค่อนข้างมีประโยชน์ถ้า gcc มีคำเตือนตามแนว: warning: expression with side-effects multiply evaluated by macroณ จุดใช้ ...
caf

23
@caf: นั่นจะไม่ต้องการให้ตัวประมวลผลก่อนมีความรู้ที่ซับซ้อนมากขึ้นเกี่ยวกับไวยากรณ์ C หรือไม่?
Dreamlax

3
หลังจากพยายามคิดออกมามากฉันไม่คิดว่าจะมีวิธีนี้ใน VC ++ แต่สิ่งที่ดีที่สุดของคุณคือพยายามยุ่งกับdecltypeคำหลักใหม่ MSVC ++ 2010 - แต่ถึงกระนั้น Visual Studio ไม่สามารถทำงบผสมในมาโครได้ (และdecltypeคือ C ++ ต่อไป) นั่นคือ({ ... })ไวยากรณ์ของ GCC ดังนั้นฉันค่อนข้างแน่ใจว่ามันเป็นไปไม่ได้ ฉันไม่ได้ดูคอมไพเลอร์อื่น ๆ เกี่ยวกับปัญหานี้ขอโทษ Luther: S
David Titarenco

7
@dreamlax ฉันเคยเห็นกรณีที่มีคนทำMAX(someUpperBound, someRandomFunction())เพื่อ จำกัด ค่าการสุ่มที่ขอบเขตบน มันเป็นความคิดที่แย่มาก แต่ก็ไม่ได้ผลเพราะMAXเขาใช้มีปัญหาในการประเมินสองครั้งดังนั้นเขาจึงสิ้นสุดด้วยตัวเลขสุ่มที่แตกต่างจากที่ประเมินในตอนแรก
Zev Eisenberg

8
@Soumen ตัวอย่างเช่นถ้าคุณเรียกMIN(x++, y++)preprocessor (((x++) < (y++)) ? (x++) : (y++))จะสร้างรหัสต่อไปนี้ ดังนั้นxและyจะเพิ่มขึ้นสองครั้ง
อันโตนิโอ

91

มันมีให้ใน GNU libc (Linux) และ FreeBSD เวอร์ชัน sys / param.h และมีคำจำกัดความของ Dreamlax


บนเดเบียน:

$ uname -sr
Linux 2.6.11

$ cat /etc/debian_version
5.0.2

$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

$ head -n 2 /usr/include/sys/param.h | grep GNU
This file is part of the GNU C Library.

ใน FreeBSD:

$ uname -sr
FreeBSD 5.5-STABLE

$ egrep 'MIN\(|MAX\(' /usr/include/sys/param.h
#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

แหล่งเก็บข้อมูลต้นฉบับอยู่ที่นี่:


ฉันได้เพิ่มคำจำกัดความจากระบบที่ฉันสามารถเข้าถึงได้ในคำตอบของฉันด้านบน (ช่องความคิดเห็นไม่ยอมรับการจัดรูปแบบเท่าที่ฉันสามารถบอกได้) จะพยายามค้นหาลิงก์ไปยังแหล่ง repos FreeBSD / Linux / glibc
มิเคล

+1 ดีมาก. ทำงานสำหรับopenSUSE/Linux 3.1.0-1.2-desktop/ gcc version 4.6.2 (SUSE Linux) เกินไป :) ไม่ดีเลยมันพกพาไม่ได้
แจ็ค

ทำงานบน Cygwin ด้วย
CMCDragonkai

1
รอสักครู่. มันไม่ได้ป้องกันการประเมินซ้ำสองครั้งใช่มั้ย : 3
user1857492

76

มีstd::minและstd::maxใน C ++ แต่ AFAIK ไม่มีอะไรเทียบเท่าในไลบรารีมาตรฐาน C คุณสามารถกำหนดได้ด้วยตนเองเช่นมาโคร

#define MAX(x, y) (((x) > (y)) ? (x) : (y))
#define MIN(x, y) (((x) < (y)) ? (x) : (y))

MAX(++a, ++b)แต่นี้ทำให้เกิดปัญหาถ้าคุณเขียนสิ่งที่ต้องการ


10
ทำไมต้องใส่วงเล็บมากเกินไป ??? ฉันพบคำถามที่พวกเขาบอกว่า#define MIN(A, B) ((A < B) ? A : B)ไม่ใช่วิธีที่ยืดหยุ่นทำไม ???

78
@Makouda: วงเล็บพิเศษในมาโครช่วยหลีกเลี่ยงปัญหาผู้ให้บริการ #define MULT(x, y) x * yตัวอย่างเช่นพิจารณา จากนั้นMULT(a + b, a + b)ขยายไปa + b * a + bซึ่งแยกวิเคราะห์ตามa + (b * a) + bความสำคัญ นั่นไม่ใช่สิ่งที่โปรแกรมเมอร์ตั้งใจไว้
dan04

ที่ไม่จำเป็นเมื่อไหร่?: มีลำดับความสำคัญต่ำสุดอยู่แล้ว
Winger Sendon

@WingerSendon: ไม่ได้; เครื่องหมายจุลภาคนั้น
dan04

24

หลีกเลี่ยงส่วนขยายคอมไพเลอร์ที่ไม่ได้มาตรฐานและใช้มันเป็นมาโครประเภทที่ปลอดภัยอย่างสมบูรณ์ในมาตรฐาน C บริสุทธิ์ (ISO 9899: 2011)

สารละลาย

#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))

#define ENSURE_int(i)   _Generic((i), int:   (i))
#define ENSURE_float(f) _Generic((f), float: (f))


#define MAX(type, x, y) \
  (type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))

การใช้

MAX(int, 2, 3)

คำอธิบาย

แมโคร MAX สร้างมาโครอื่นตามtypeพารามิเตอร์ มาโครควบคุมนี้หากใช้กับประเภทที่กำหนดจะใช้เพื่อตรวจสอบว่าพารามิเตอร์ทั้งสองเป็นประเภทที่ถูกต้อง หากว่าtypeไม่รองรับจะมีข้อผิดพลาดคอมไพเลอร์

หาก x หรือ y ไม่ได้เป็นประเภทที่ถูกต้องจะมีข้อผิดพลาดคอมไพเลอร์ใน ENSURE_มาโคร สามารถเพิ่มมาโครได้เช่นนี้หากรองรับประเภทเพิ่มเติม ฉันคิดว่าจะใช้เฉพาะเลขคณิต (จำนวนเต็มลอยตัวชี้ ฯลฯ ) เท่านั้นและจะไม่ใช้ structs หรืออาร์เรย์เป็นต้น

หากทุกประเภทถูกต้องจะมีการเรียกมาโคร GENERIC_MAX วงเล็บเพิ่มเติมจำเป็นต้องมีรอบพารามิเตอร์มาโครแต่ละตัวซึ่งเป็นข้อควรระวังมาตรฐานทั่วไปเมื่อเขียนมาโคร C

จากนั้นมีปัญหาตามปกติกับการส่งเสริมการพิมพ์โดยนัยในซี?:โอเปอเรเตอร์สมดุลตัวถูกดำเนินการที่ 2 และ 3 กับแต่ละอื่น ๆ ตัวอย่างเช่นผลมาจากการจะเป็นGENERIC_MAX(my_char1, my_char2) intเพื่อป้องกันไม่ให้แมโครทำการส่งเสริมประเภทที่อาจเป็นอันตรายดังกล่าวจึงใช้ตัวเลือกประเภทสุดท้ายไปยังประเภทที่ต้องการ

หลักการและเหตุผล

เราต้องการให้พารามิเตอร์ทั้งสองเป็นมาโครเป็นประเภทเดียวกัน หากหนึ่งในนั้นเป็นประเภทที่แตกต่างกันแมโครจะไม่ปลอดภัยอีกต่อไปเพราะผู้ดำเนินการที่ชอบ?:จะให้การส่งเสริมการพิมพ์โดยนัย และเนื่องจากเป็นเช่นนั้นเราจำเป็นต้องส่งผลลัพธ์สุดท้ายกลับไปยังประเภทเป้าหมายตามที่อธิบายไว้ข้างต้น

แมโครที่มีพารามิเตอร์เพียงตัวเดียวสามารถเขียนด้วยวิธีที่ง่ายกว่ามาก แต่ด้วยพารามิเตอร์ 2 พารามิเตอร์ขึ้นไปจำเป็นต้องมีพารามิเตอร์ชนิดพิเศษ เพราะสิ่งนี้เป็นไปไม่ได้เลย:

// this won't work
#define MAX(x, y)                                  \
  _Generic((x),                                    \
           int: GENERIC_MAX(x, ENSURE_int(y))      \
           float: GENERIC_MAX(x, ENSURE_float(y))  \
          )

ปัญหาคือว่าถ้าแมโคด้านบนถูกเรียกเช่นเดียวMAX(1, 2)กับสองintมันจะยังคงพยายามที่จะขยายสถานการณ์จำลองที่เป็นไปได้ทั้งหมดของ_Genericรายการการเชื่อมโยง ดังนั้นENSURE_floatแมโครจะขยายตัวเช่นกันแม้ว่าจะไม่เกี่ยวข้องintกัน และเนื่องจากแมโครนั้นมีเพียงfloatชนิดเท่านั้นโดยเจตนารหัสจะไม่รวบรวม

ในการแก้ปัญหานี้ฉันได้สร้างชื่อมาโครในระหว่างตัวประมวลผลล่วงหน้าแทนด้วยตัวดำเนินการ ## เพื่อไม่ให้แมโครขยายตัวโดยไม่ตั้งใจ

ตัวอย่าง

#include <stdio.h>

#define GENERIC_MAX(x, y) ((x) > (y) ? (x) : (y))

#define ENSURE_int(i)   _Generic((i), int:   (i))
#define ENSURE_float(f) _Generic((f), float: (f))


#define MAX(type, x, y) \
  (type)GENERIC_MAX(ENSURE_##type(x), ENSURE_##type(y))

int main (void)
{
  int    ia = 1,    ib = 2;
  float  fa = 3.0f, fb = 4.0f;
  double da = 5.0,  db = 6.0;

  printf("%d\n", MAX(int,   ia, ib)); // ok
  printf("%f\n", MAX(float, fa, fb)); // ok

//printf("%d\n", MAX(int,   ia, fa));  compiler error, one of the types is wrong
//printf("%f\n", MAX(float, fa, ib));  compiler error, one of the types is wrong
//printf("%f\n", MAX(double, fa, fb)); compiler error, the specified type is wrong
//printf("%f\n", MAX(float, da, db));  compiler error, one of the types is wrong

//printf("%d\n", MAX(unsigned int, ia, ib)); // wont get away with this either
//printf("%d\n", MAX(int32_t, ia, ib)); // wont get away with this either
  return 0;
}

ที่GENERIC_MAXแมโครเป็นความคิดที่ไม่ดีโดยวิธีการที่คุณจะต้องพยายามที่GENERIC_MAX(var++, 7)จะหาว่าทำไม :-) ปัจจุบัน (โดยเฉพาะอย่างยิ่งกับการเพิ่มประสิทธิภาพอย่างหนัก / inlining คอมไพเลอร์) แมโครควรจะสวยมากถูกผลักไสให้ไปในรูปแบบที่ง่ายเท่านั้น ฟังก์ชั่นที่คล้ายฟังก์ชั่นจะดีกว่าฟังก์ชั่นและกลุ่มของค่าที่ดีกว่าสำหรับการแจกแจง
paxdiablo

21

ฉันไม่คิดว่ามันเป็นมาโครมาตรฐาน มีฟังก์ชั่นที่ได้มาตรฐานสำหรับจุดลอยตัวอยู่แล้วfmaxและfmin(และfmaxfสำหรับลอยตัวและfmaxlสำหรับคู่ยาว)

คุณสามารถนำมาใช้เป็นมาโครตราบใดที่คุณตระหนักถึงปัญหาของผลข้างเคียง / การประเมินสองครั้ง

#define MAX(a,b) ((a) > (b) ? a : b)
#define MIN(a,b) ((a) < (b) ? a : b)

ในกรณีส่วนใหญ่คุณสามารถปล่อยให้คอมไพเลอร์เพื่อกำหนดสิ่งที่คุณกำลังพยายามทำและปรับให้เหมาะสมที่สุดเท่าที่จะทำได้ ในขณะที่สิ่งนี้ทำให้เกิดปัญหาเมื่อใช้เช่นMAX(i++, j++)ฉันสงสัยว่ามีความจำเป็นมากในการตรวจสอบค่าสูงสุดที่เพิ่มขึ้นในครั้งเดียว เพิ่มค่าก่อนจากนั้นตรวจสอบ


นี่ควรเป็นคำตอบที่ต้องการเนื่องจากมีฟังก์ชั่นขั้นต่ำและสูงสุดในไลบรารีคณิตศาสตร์: cplusplus.com/reference/cmath/fmax
imranal

@imranal คุณกำลังพูดถึงอะไรกันแน่? การดำเนินงานรหัสของไลบรารีเหล่านั้นหรือไม่ แต่รหัสนั้นไม่ถูกเปิดเผยนั่นคือพวกเขาไม่ได้วางไว้ในส่วนต่อประสานของห้องสมุดซึ่งอาจไม่ปลอดภัย
อันโตนิโอ

@ อันโตนิโอฉันคิดว่าคุณกำลังใช้คำจำกัดความที่ไม่ถูกต้องของ "exposed" และ "interface" อินเตอร์เฟสของไลบรารี ac คือตัวแปรประเภทแมโครและการประกาศฟังก์ชันภายนอกในไฟล์ส่วนหัว fmin / fmax ถูกประกาศในไฟล์ส่วนหัวดังนั้นจึงมีการกล่าวถึงการเปิดเผย ฉันไม่แน่ใจว่าสิ่งที่คุณอ้างถึงไม่ปลอดภัยแม้ว่า
rationalcoder

21

นี่คือคำตอบที่ล่าช้าเนื่องจากการพัฒนาที่ค่อนข้างล่าสุด ตั้งแต่ OP ได้รับการยอมรับคำตอบที่อาศัยใน GCC ไม่ใช่แบบพกพา (และเสียงดังกราว) นามสกุลtypeof- หรือ__typeof__สำหรับ 'สะอาด' ISO C - มีทางออกที่ดีกว่าที่มีอยู่เป็นของGCC-4.9

#define max(x,y) ( \
    { __auto_type __x = (x); __auto_type __y = (y); \
      __x > __y ? __x : __y; })

ประโยชน์ที่ชัดเจนของส่วนขยายนี้คืออาร์กิวเมนต์แมโครแต่ละตัวจะถูกขยายเพียงครั้งเดียวซึ่งแตกต่างจาก__typeof__โซลูชัน

__auto_typeเป็นรูปแบบที่ จำกัด autoของภาษา C ไม่สามารถใช้ (หรือไม่ควร?) ในรหัส C ++ แม้ว่าจะไม่มีเหตุผลที่ดีที่จะไม่ใช้ความสามารถในการอนุมานประเภทที่เหนือกว่าautoเมื่อใช้ C ++ 11

ที่กล่าวว่าฉันคิดว่าไม่มีปัญหาในการใช้ไวยากรณ์นี้เมื่อแมโครรวมอยู่ในextern "C" { ... }ขอบเขต เช่นจากส่วนหัว C AFAIK ส่วนขยายนี้ไม่พบวิธีที่เสียงดังกังวาน


ที่เกี่ยวข้องกับการแสดงความคิดเห็นเบร็ทเฮล , clangเริ่มต้นการสนับสนุน__auto_typeรอบ 2016 (ดูแพทช์ )
ลาร์ส

รุ่งโรจน์การตระหนักถึงปัญหาที่เกิดขึ้นแมโคร แต่ฉันยังคงวางตัวว่าการทำงานอาจจะดีกว่า :-)
paxdiablo

@paxdiablo - ฉันเห็นด้วยแม้ว่าคำถามจะมีc-preprocessorแท็ก ฟังก์ชั่นไม่ได้รับการรับประกันว่าจะถูกขีดเส้นใต้แม้จะมีคำหลักที่กล่าวถึงเว้นแต่ใช้สิ่งที่ต้องการ__always_inline__คุณลักษณะของ gcc
Brett Hale

11

ฉันเขียนเวอร์ชันนี้ที่ใช้งานได้กับ MSVC, GCC, C และ C ++

#if defined(__cplusplus) && !defined(__GNUC__)
#   include <algorithm>
#   define MIN std::min
#   define MAX std::max
//#   define TMIN(T, a, b) std::min<T>(a, b)
//#   define TMAX(T, a, b) std::max<T>(a, b)
#else
#       define _CHOOSE2(binoper, lexpr, lvar, rexpr, rvar) \
                ({ \
                        decltype(lexpr) lvar = (lexpr); \
                        decltype(rexpr) rvar = (rexpr); \
                        lvar binoper rvar ? lvar : rvar; \
                })
#       define _CHOOSE_VAR2(prefix, unique) prefix##unique
#       define _CHOOSE_VAR(prefix, unique) _CHOOSE_VAR2(prefix, unique)
#       define _CHOOSE(binoper, lexpr, rexpr) \
                _CHOOSE2( \
                        binoper, \
                        lexpr, _CHOOSE_VAR(_left, __COUNTER__), \
                        rexpr, _CHOOSE_VAR(_right, __COUNTER__) \
                )
#       define MIN(a, b) _CHOOSE(<, a, b)
#       define MAX(a, b) _CHOOSE(>, a, b)
#endif

ฉันอัปโหลดแล้ว แต่ตัวระบุที่ขึ้นต้นด้วยขีดล่างตามด้วยตัวอักษรตัวพิมพ์ใหญ่จะถูกสงวนไว้
dreamlax

8

หากคุณต้องการขั้นต่ำ / สูงสุดเพื่อหลีกเลี่ยงสาขาที่มีราคาแพงคุณไม่ควรใช้ผู้ประกอบการที่ประกอบไปด้วยไตรภาค ลิงค์ด้านล่างอธิบายถึงวิธีการที่มีประโยชน์สำหรับการใช้ฟังก์ชั่นขั้นต่ำ / สูงสุดโดยไม่ต้องแยกสาขา

http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax


1
หากคอมไพเลอร์ฉลาดพอที่จะหลีกเลี่ยงสาขา
Axel Gneiting

2
หากเปิดใช้งานการปรับให้เหมาะสมที่สุดคอมไพเลอร์สมัยใหม่ทั้งหมดจะทำการย้ายแบบมีเงื่อนไขแทนสาขาในกรณีส่วนใหญ่ดังนั้นจึงมีจุดเล็กน้อยในการใช้แฮ็กแบบนี้
Krzysztof Kosiński

1
ความจริงอย่างแน่นอนฉันไม่รู้ว่าสิ่งที่ฉันกำลังมองย้อนกลับไปมันก็ไม่นาน gcc และ clang หลีกเลี่ยงกิ่งที่มี -O ทั้งบน x86 และ armv7a
cib

6

@ David Titarenco จับที่นี่แต่อย่างน้อยให้ฉันทำความสะอาดเล็กน้อยเพื่อให้ดูดีและแสดงทั้งสองmin() และ max()ร่วมกันเพื่อให้การคัดลอกและวางจากที่นี่ง่ายขึ้น :)

อัปเดต 25 เม.ย. 2563: ฉันได้เพิ่มส่วนที่ 3 เพื่อแสดงว่าจะทำอย่างไรกับเทมเพลต C ++ เช่นกันเพื่อเป็นการเปรียบเทียบที่มีค่าสำหรับการเรียนรู้ทั้ง C และ C ++ หรือการเปลี่ยนจากที่หนึ่งไปยังอีกที่หนึ่ง ฉันพยายามทำให้ดีที่สุดเพื่อให้ละเอียดถี่ถ้วนและเป็นจริงและถูกต้องเพื่อให้คำตอบนี้เป็นข้อมูลอ้างอิงที่เป็นที่ยอมรับฉันสามารถกลับมาอีกครั้งและอีกครั้งและฉันหวังว่าคุณจะพบว่ามันมีประโยชน์เท่าที่ฉันทำได้

1. วิธีมาโคร C แบบเก่า:

เทคนิคนี้เป็นที่นิยมใช้กันอย่างแพร่หลายโดยผู้ที่รู้จักวิธีใช้อย่างถูกต้องวิธีการ "ทำพฤตินัย" และใช้งานได้ดีหากใช้อย่างเหมาะสม แต่เป็นรถม้า (คิดว่า: ผลข้างเคียงแบบประเมินสองครั้ง ) หากคุณ เคยผ่านการแสดงออกรวมถึงการกำหนดตัวแปรในการเปรียบเทียบ:

#define MAX(a,b) ((a) > (b) ? (a) : (b))
#define MIN(a,b) ((a) < (b) ? (a) : (b))

2. วิธี " การแสดงออกของคำสั่ง " ที่ได้รับการปรับปรุงใหม่:

เทคนิคนี้หลีกเลี่ยงผลข้างเคียงและข้อผิดพลาด "การประเมินซ้ำสองครั้ง" และถือว่าเป็นวิธีที่ดีกว่าปลอดภัยกว่าและ "ทันสมัยกว่า" GCC C ในการทำเช่นนี้ คาดว่ามันจะทำงานกับคอมไพเลอร์ gcc และ clang เนื่องจาก clang คือโดยการออกแบบเข้ากันได้กับ gcc (ดูบันทึกย่อเสียงดังที่ด้านล่างของคำตอบนี้)

แต่: ระวังเอฟเฟกต์" ตัวแปรแชโดว์ตัวแปร " เนื่องจากการแสดงออกของคำสั่งจะเห็นได้ชัดเจนและดังนั้นจึงไม่มีขอบเขตตัวแปรท้องถิ่นของตัวเอง!

#define max(a,b)             \
({                           \
    __typeof__ (a) _a = (a); \
    __typeof__ (b) _b = (b); \
    _a > _b ? _a : _b;       \
})

#define min(a,b)             \
({                           \
    __typeof__ (a) _a = (a); \
    __typeof__ (b) _b = (b); \
    _a < _b ? _a : _b;       \
})

โปรดทราบว่าในนิพจน์คำสั่ง gcc นิพจน์สุดท้ายในบล็อกโค้ดคือสิ่งที่ "คืน" จากนิพจน์ราวกับว่าถูกส่งคืนจากฟังก์ชัน เอกสารของ GCCบอกด้วยวิธีนี้:

สิ่งสุดท้ายในคำสั่งผสมควรเป็นการแสดงออกตามด้วยเครื่องหมายอัฒภาค; ค่าของนิพจน์ย่อยนี้ทำหน้าที่เป็นค่าของการสร้างทั้งหมด (ถ้าคุณใช้คำสั่งชนิดอื่นบางอย่างล่าสุดภายในวงเล็บปีกกาการก่อสร้างมีโมฆะประเภทและจึงไม่มีค่าอย่างมีประสิทธิภาพ)

3. วิธีเทมเพลต C ++:

C ++ หมายเหตุ: ถ้าใช้ C ++, แนะนำให้ใช้เทมเพลตสำหรับการสร้างประเภทนี้แทน แต่ส่วนตัวแล้วฉันไม่ชอบเทมเพลตและอาจใช้หนึ่งในการสร้างข้างต้นใน C ++ ต่อไปเนื่องจากฉันมักจะใช้และชอบสไตล์ C ใน C ++ ฝังตัวด้วยเช่นกัน

ส่วนนี้เพิ่ม 25 เมษายน 2020:

ฉันได้ทำ C ++ มาหลายเดือนแล้วและความกดดันในการเลือกเทมเพลตมากกว่ามาโครที่สามารถทำได้ในชุมชน C ++ นั้นค่อนข้างแข็งแกร่ง ด้วยเหตุนี้ฉันจึงเริ่มใช้เทมเพลตได้ดีขึ้นและต้องการวางเทมเพลตรุ่น C ++ ที่นี่เพื่อความสมบูรณ์และเพื่อทำให้คำตอบนี้เป็นที่ยอมรับและทั่วถึงมากขึ้น

นี่คือเวอร์ชันเทมเพลตฟังก์ชันพื้นฐานmax()และmin()อาจมีลักษณะเป็นใน C ++:

template <typename T>
T max(T a, T b)
{
    return a > b ? a : b;
}

template <typename T>
T min(T a, T b)
{
    return a < b ? a : b;
}

ทำอ่านเพิ่มเติมเกี่ยวกับ C ++ แม่แบบที่นี่: วิกิพีเดีย: แม่แบบ (C ++)

อย่างไรก็ตามทั้งสองmax()และmin()เป็นส่วนหนึ่งของไลบรารีมาตรฐาน C ++ แล้วใน<algorithm>ส่วนหัว ( #include <algorithm>) ในไลบรารีมาตรฐาน C ++ พวกเขาถูกกำหนดแตกต่างจากที่ฉันมีไว้ด้านบนเล็กน้อย ต้นแบบเริ่มต้นสำหรับstd::max<>()และstd::min<>()ตัวอย่างเช่นใน C ++ 14 ดูที่ต้นแบบในลิงค์ cplusplus.com ด้านบนคือ:

template <class T> 
constexpr const T& max(const T& a, const T& b);

template <class T> 
constexpr const T& min(const T& a, const T& b);

หมายเหตุว่าคำหลักtypenameเป็นนามแฝงไปclass(เพื่อให้การใช้งานของพวกเขาเป็นเหมือนกันไม่ว่าคุณจะพูด<typename T>หรือ<class T>) เนื่องจากมันได้รับการยอมรับต่อมาหลังจากการประดิษฐ์ของ C ++ แม่แบบว่าประเภทแม่แบบอาจจะเป็นประเภททั่วไป ( int, floatฯลฯ ) แทนที่จะเป็นเพียง ประเภทชั้นเรียน

ที่นี่คุณจะเห็นได้ว่าทั้งสองประเภทอินพุตเช่นเดียวกับประเภทกลับเป็นconst T&ซึ่งหมายถึง "การอ้างอิงคงที่เพื่อพิมพ์T" วิธีนี้ป้อนพารามิเตอร์และค่าตอบแทนจะผ่านอ้างอิงแทนการผ่านค่า นี่เป็นเหมือนการส่งผ่านพอยน์เตอร์และมีประสิทธิภาพมากขึ้นสำหรับประเภทขนาดใหญ่เช่นคลาสอ็อบเจ็กต์ constexprส่วนหนึ่งของฟังก์ชั่นปรับเปลี่ยนฟังก์ชั่นของตัวเองและแสดงให้เห็นว่าการทำงานจะต้องมีความสามารถในการประเมินที่รวบรวมเวลา (อย่างน้อยถ้าให้constexprป้อนพารามิเตอร์) แต่ถ้ามันไม่สามารถได้รับการประเมินที่รวบรวมเวลาแล้วค่าเริ่มต้นกลับไป การประเมินเวลาทำงานเหมือนกับฟังก์ชั่นปกติอื่น ๆ

มุมมองเวลาคอมไพล์ของconstexprฟังก์ชัน C ++ ทำให้เป็นแบบ C-macro-like ซึ่งถ้าการประเมินเวลาคอมไพล์เป็นไปได้สำหรับconstexprฟังก์ชั่นมันจะทำที่รวบรวมเวลาเช่นเดียวกับการแทนที่แมโครMIN()หรือMAX()อาจเป็นไปได้ ได้รับการประเมินอย่างสมบูรณ์ที่รวบรวมเวลาใน C หรือ C ++ เกินไป สำหรับการอ้างอิงเพิ่มเติมสำหรับข้อมูลเทมเพลต C ++ นี้ดูด้านล่าง

อ้างอิง:

  1. https://gcc.gnu.org/onlinedocs/gcc/Typeof.html#Typeof
  2. https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs
  3. ต่ำสุดและสูงสุดเป็น c
  4. การอ้างอิงเท็มเพลต C ++ เพิ่มเติมได้เพิ่ม เม.ย. 2020:
    1. ***** Wikipedia: เทมเพลต (C ++) <- ข้อมูลเพิ่มเติมเกี่ยวกับเทมเพลต C ++ ที่ยอดเยี่ยม!
    2. (คำถาม & คำตอบของฉันเอง): ทำไม `constexpr` เป็นส่วนหนึ่งของต้นแบบเทมเพลต C ++ 14 สำหรับ` std :: max () `?
    3. ความแตกต่างระหว่าง "constexpr" และ "const"

เสียงดังกราวจาก Wikipedia :

[Clang] ได้รับการออกแบบมาเพื่อทำหน้าที่แทนการแทนที่สำหรับ GNU Compiler Collection (GCC) ซึ่งสนับสนุนการรวบรวมส่วนใหญ่ของแฟล็กการรวบรวมและส่วนขยายภาษาที่ไม่เป็นทางการ


ถึงผู้ลงคะแนนเสียงตั้งแต่ 24 ชั่วโมงที่ผ่านมา: ข่าวดี! ผมได้ลบของฉันมาตรา 4 พูดจาโผงผางฉันเพิ่มมาตรา 3 เมื่อวานนี้และฉันได้ใส่มันนี่แทน คุณยินดีที่จะประเมินคำตอบของฉันอีกครั้งและให้ upvote หากคุณพอใจเพราะฉันได้ใส่ข้อมูลที่ดีไว้มากมายและพยายามอย่างเต็มที่เพื่อให้เป็นคำตอบที่มั่นคงมีประโยชน์และเป็นประโยชน์ ตอนนี้กลับมามุ่งเน้น :) ขอบคุณ!
Gabriel Staples

4

มันคุ้มค่าที่จะชี้ให้เห็นว่าฉันคิดว่าถ้าคุณกำหนดminและmaxกับระดับอุดมศึกษาเช่น

#define MIN(a,b) (((a)<(b))?(a):(b))
#define MAX(a,b) (((a)>(b))?(a):(b))

จากนั้นจะได้ผลลัพธ์เดียวกันสำหรับกรณีพิเศษfmin(-0.0,0.0)และfmax(-0.0,0.0)คุณต้องสลับอาร์กิวเมนต์

fmax(a,b) = MAX(a,b)
fmin(a,b) = MIN(b,a)

ยังคงไม่ทำงานกับ NaN fmin(3.0,NaN)==fmin(NaN,3.0)==fmax(3.0,NaN)==fmax(NaN,3.0)==3.0
greggo

@greggo ฉันให้คำตอบที่ดีกว่าที่นี่stackoverflow.com/a/30915238/2542702
Z boson

4

ดูเหมือนว่าWindef.h(a la #include <windows.h>) มีmaxและmin(ตัวพิมพ์เล็ก) มาโครที่ยังประสบกับความยากลำบาก "การประเมินสองครั้ง" แต่พวกเขาอยู่ที่นั่นสำหรับผู้ที่ไม่ต้องการม้วนตัวเอง :)


12
คุณยังแปลกใจเหรอ?
Matt Joiner

2

ฉันรู้ว่าคนที่แต่งตัวประหลาดพูดว่า "C" ... แต่ถ้าคุณมีโอกาสให้ใช้เทมเพลต C ++:

template<class T> T min(T a, T b) { return a < b ? a : b; }

พิมพ์ปลอดภัยและไม่มีปัญหากับ ++ ที่กล่าวถึงในความคิดเห็นอื่น


16
อาร์กิวเมนต์ควรเป็นการอ้างอิง const คุณไม่เคยรู้ว่าผู้ใช้จะผ่านอะไร
nmikhailov

6
ฟังก์ชั่นดังกล่าวได้รับการมาตรฐานแล้ว ( std :: min )
dreamlax

C ++ มีฟังก์ชั่นมาตรฐานมากมายสำหรับการใช้งานทั่วไปส่วนใหญ่อย่าประดิษฐ์ล้อเลื่อน อย่างไรก็ตามMS ยังกำหนดขั้นต่ำ / สูงสุดของตัวเองซึ่งบางครั้งก็ทำให้เกิดปัญหา
27419

0

สูงสุดของทั้งสองจำนวนเต็มaและมีb (int)(0.5((a+b)+abs(a-b)))สิ่งนี้อาจทำงานร่วมกับ(double)และfabs(a-b)สำหรับคู่ผสม (คล้ายกับลอย)


ขออภัยถ้ามันผิดฉันเป็นผู้เริ่มต้น C แต่รหัสนี้ใช้ได้กับฉัน
NRZ

2
ฉันไม่แน่ใจว่ามันทำงานได้กับจำนวนเต็มไม่ใช่ คณิตศาสตร์เลขทศนิยมมีความแม่นยำไม่เชิงเส้น
Treesrule14

หากต้องการขยายความคิดเห็นของ @ Treesrule14: วิธีนี้ใช้ไม่ได้เนื่องจากคอมพิวเตอร์ไม่ปฏิบัติกับตัวเลขในลักษณะเดียวกับนักคณิตศาสตร์ จุดลอยตัวมีปัญหาการปัดเศษดังนั้นคุณจึงไม่น่าจะได้รับคำตอบที่ถูกต้อง แม้ว่าคุณจะใช้เลขจำนวนเต็ม MAX_INT + MAX_INT ก็ให้ -2 ดังนั้นสูงสุด (MAX_INT, MAX_INT) โดยใช้สูตรของคุณก็จะออกมาเป็น -1
user9876

-3

วิธีที่ง่ายที่สุดคือการกำหนดให้เป็นฟังก์ชั่นระดับโลกใน.hไฟล์และเรียกมันเมื่อใดก็ตามที่คุณต้องการหากโปรแกรมของคุณเป็นโมดูลที่มีไฟล์จำนวนมาก ถ้าไม่double MIN(a,b){return (a<b?a:b)}เป็นวิธีที่ง่ายที่สุด


1
@technosaurus มันจะมีประโยชน์ถ้าคุณอธิบายว่าทำไมการแก้ปัญหานี้ไม่ถูกต้องไม่ใช่แค่นั้น
Tur1ng

@technosaurus คำตอบของคุณไม่มีประโยชน์จริงๆ Tur1ing ปรากฏว่าฟังก์ชั่นมีการกำหนดผิดอย่างสมบูรณ์ (ประเภทที่ขาดหายไปในอินพุท params เซมิโคลอนหายไปหลังจากคำสั่ง return) และการเปลี่ยนอินพุตอินเป็นสองเท่าเป็นวิธีที่ไม่ดีในการทำสิ่งต่าง ๆ ดังนั้นประเภทไม่ควรเป็นสองเท่า นิพจน์ define หรือ statement จะดีกว่าที่นี่ (ตัวอย่าง: ดูที่นี่ ) แต่ถ้าฟังก์ชั่นให้ลองทำฟังก์ชั่นหนึ่งเพื่อทำสิ่งนี้สำหรับประเภท int32_t หนึ่งประเภทสำหรับประเภท uint32_t และอีกหนึ่งสำหรับประเภท float หรือ double รวมเป็น 3 ฟังก์ชั่นที่แตกต่างกัน
Gabriel Staples

1
@GabrielStaples คำตอบนี้ควรถูกตั้งค่าสถานะว่าไม่ใช่คำตอบ - ไม่มีความช่วยเหลือ แม้ว่ามันจะสามารถใช้เป็นตัวอย่างของวิธีการที่จะผิดมากที่สุดในพื้นที่น้อยที่สุด แนะนำฟังก์ชั่นทั่วโลกในส่วนหัว (ไม่ใช่แบบอินไลน์คงที่หรือไม่) จะทำลายรหัสด้วย 2 + หน่วยรวบรวมไม่ได้รวบรวมการตั้งชื่อฟังก์ชั่นเช่นแมโครโดยนัย ints เช่น 1989 กลับเป็นสองเท่าโดยไม่มีเหตุผลที่ระบุ ปลดเปลื้องที่จะทำให้เกิดการเตือนที่ดีที่สุด ... และที่สำคัญที่สุดมันไม่ได้ตอบคำถาม - ไม่ทั่วไปไม่ปลอดภัยไม่พิมพ์และแน่นอนที่สุดไม่ใช่วิธีที่ดีที่สุด
technosaurus

แต่ละปัญหาเหล่านั้นสมควรได้รับการวิจารณ์เพิ่มเติมที่ไม่สามารถครอบคลุมในรายละเอียดที่เพียงพอ
technosaurus
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.