ทำไม Argc ถึงไม่คงที่?


104
int main( const int argc , const char[] const argv)

ในฐานะที่เป็นที่มีประสิทธิภาพ c ++ Item # 3 รัฐ "ใช้ const เมื่อใดก็ตามที่เป็นไปได้" ผมเริ่มคิด "ทำไมไม่ทำให้ 'คงที่' พารามิเตอร์เหล่านี้const"?

มีสถานการณ์ใดบ้างที่argcมีการแก้ไขค่าในโปรแกรม?


38
คุณมีอิสระที่จะประกาศเป็นargc const
Oliver Charlesworth

14
ฉันเห็นรหัสคุณภาพการผลิตมากมายที่ทำสิ่งนี้:--argc
Aniket Inge

2
ฉันคิดว่ามันเกี่ยวข้องกับ C legacy แต่ไม่รู้ว่าจะทำอย่างไร
Luis Machuca

13
ฉันสงสัยว่า "ใช้ const เมื่อใดก็ตามที่เป็นไปได้" หมายถึงสิ่งที่ส่งผ่านโดยการอ้างอิงโดยที่การแก้ไขใด ๆ ภายในฟังก์ชันจะยังคงมีอยู่เมื่อออกจากฟังก์ชันแล้ว นี้ไม่เป็นความจริงกับสิ่งที่ผ่านค่าเช่นจำนวนเต็มจึงไม่มีอะไรที่จะได้รับโดยการทำให้มันconst; แน่นอนการส่งผ่านargcเป็นconst intวิธีการที่คุณไม่สามารถใช้argcเป็นตัวนับภายในฟังก์ชันได้
Steve Melnikoff

4
@SteveMelnikoff: ฉันไม่เห็นด้วยว่าไม่มีอะไรจะได้รับจากการสร้างconstพารามิเตอร์ pass-by-value ดูเช่นstackoverflow.com/a/8714278/277304และstackoverflow.com/a/117557/277304
leonbloy

คำตอบ:


114

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

UNIX API บางตัวเช่นgetoptมีการจัดการจริงargv[]ดังนั้นจึงไม่สามารถสร้างได้constด้วยเหตุผลนั้นเช่นกัน

(นอกเหนือ: ที่น่าสนใจแม้ว่าgetoptต้นแบบจะแนะนำว่าจะไม่แก้ไขargv[]แต่อาจปรับเปลี่ยนสตริงที่ชี้ไปหน้าคนของ Linux ระบุว่าgetoptอนุญาตข้อโต้แย้งของมันและดูเหมือนว่าพวกเขารู้ว่าพวกเขากำลังซนหน้าคนที่เปิด กลุ่มไม่ได้กล่าวถึงการเปลี่ยนแปลงนี้)

วางconstบนargcและargvจะไม่ซื้อมากและมันจะเป็นโมฆะบางเก่าโรงเรียนการเขียนโปรแกรมการปฏิบัติเช่น:

// print out all the arguments:
while (--argc)
    std::cout << *++argv << std::endl;

ฉันเขียนโปรแกรมดังกล่าวด้วยภาษา C และฉันรู้ว่าฉันไม่ได้อยู่คนเดียว ผมคัดลอกตัวอย่างจากที่ไหนสักแห่ง


1
-1 Re "UNIX API บางตัวเช่น getopt จัดการกับ argv [] จริงดังนั้นจึงไม่สามารถสร้าง const ได้ด้วยเหตุผลนั้น" หนึ่งได้อย่างอิสระสามารถเพิ่มระดับบนสุดconstไปargvโดยไม่มีผลต่อสิ่งที่ฟังก์ชัน C ใด ๆ ที่สามารถทำ นอกจากนี้ยังมีการประกาศให้เป็นgetopt int getopt(int argc, char * const argv[], const char *optstring);และที่นี่constไม่ใช่ระดับบนสุด แต่ประกาศว่าตัวชี้constเป็นสัญญาว่าจะไม่แก้ไข (แม้ว่าสตริงที่พวกเขาชี้ไปอาจมีการแก้ไขได้)
ไชโยและ hth - Alf

2
ขออภัยเอกสารที่ฉันดูไม่สอดคล้องกัน: ลายเซ็นไม่อนุญาตให้มีการเปลี่ยนแปลงดังกล่าว ดูตัวอย่างตัวอย่างอย่างไรก็ตามข้อความต่อไปนี้ระบุว่าไม่อนุญาต ดังนั้นฉันจะลบการลงคะแนน
ไชโยและ hth - Alf

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

1
@ Cheersandhth. -Alf: มันน่าสนใจ ... ฉันมองหาต้นแบบในส่วนหัวและมันก็char* const* ___argvมี แต่อินเทอร์เฟซเป็นไปตามconst char **นั้นจริงๆ ความสับสนดังกล่าว ฉันสับสนในลำดับความสำคัญของ[]และที่*เกี่ยวกับconst. ขอโทษด้วย.
Joe Z

1
FYI: GNU getopt()รู้ว่ามันไม่เป็นไปตามมาตรฐานเนื่องจากให้ความสำคัญกับPOSIXLY_CORRECTตัวแปรสภาพแวดล้อมเพื่อให้ทำงานได้อย่างถูกต้อง โดยเฉพาะอย่างยิ่ง POSIX ไม่อนุญาตอาร์กิวเมนต์และไม่มองหาตัวเลือกหลังจากอาร์กิวเมนต์ที่ไม่ใช่ตัวเลือกแรก
Jonathan Leffler

36

มาตรฐาน C (ISO / IEC 9899: 2011) กล่าวว่า:

5.1.2.2.1 การเริ่มต้นโปรแกรม

main¶1ฟังก์ชั่นที่เรียกว่าเมื่อเริ่มต้นโปรแกรมการตั้งชื่อ การใช้งานประกาศว่าไม่มีต้นแบบสำหรับฟังก์ชันนี้ จะต้องกำหนดด้วยประเภทผลตอบแทนของ int และไม่มีพารามิเตอร์:

int main(void) { /* ... */ }

หรือด้วยพารามิเตอร์สองตัว (เรียกในที่นี้ว่าargcและargvแม้ว่าอาจจะใช้ชื่อใดก็ได้เนื่องจากเป็นชื่อเฉพาะของฟังก์ชันที่ประกาศไว้):

int main(int argc, char *argv[]) { /* ... */ }

หรือเทียบเท่า; 10)หรือในลักษณะอื่น ๆ ที่กำหนดในการนำไปใช้งาน

¶2หากมีการประกาศพารามิเตอร์ของmainฟังก์ชันจะต้องเป็นไปตามข้อ จำกัด ต่อไปนี้:

  • ค่าของargcจะไม่เป็นค่าลบ
  • argv[argc] จะเป็นตัวชี้โมฆะ
  • ถ้าค่าของargcมากกว่าศูนย์สมาชิกอาร์เรย์argv[0]ผ่านการ argv[argc-1]รวมจะต้องมีตัวชี้ไปยังสตริงซึ่งได้รับค่าที่กำหนดการนำไปใช้งานโดยสภาพแวดล้อมโฮสต์ก่อนที่จะเริ่มโปรแกรม มีเจตนาที่จะให้ข้อมูลโปรแกรมที่กำหนดก่อนเริ่มต้นโปรแกรมจากที่อื่นในสภาพแวดล้อมที่โฮสต์ หากสภาพแวดล้อมโฮสต์ไม่สามารถจัดหาสตริงที่มีตัวอักษรทั้งตัวพิมพ์ใหญ่และตัวพิมพ์เล็กการใช้งานจะต้องแน่ใจว่าได้รับสตริงเป็นตัวพิมพ์เล็ก
  • ถ้าค่าของargcมากกว่าศูนย์สตริงที่ชี้โดยargv[0] แสดงถึงชื่อโปรแกรม argv[0][0]จะต้องเป็นอักขระ null หากชื่อโปรแกรมไม่พร้อมใช้งานจากสภาพแวดล้อมโฮสต์ ถ้าค่าของargcมากกว่าหนึ่งสตริงจะชี้argv[1]ผ่านargv[argc-1] แสดงถึงพารามิเตอร์ของโปรแกรม
  • พารามิเตอร์argcและargvและสตริงที่argvอาร์เรย์ชี้ไปจะต้องแก้ไขได้โดยโปรแกรมและคงค่าที่เก็บไว้ล่าสุดระหว่างการเริ่มต้นโปรแกรมและการยุติโปรแกรม

10)ดังนั้นintสามารถแทนที่ด้วยชื่อ typedef ที่กำหนดเป็นintหรือประเภทของargvสามารถเขียนเป็น char **argvและอื่น ๆ

สังเกตสัญลักษณ์แสดงหัวข้อย่อยสุดท้าย มันบอกว่าทั้งสองอย่างargcและargvควรแก้ไขได้ ไม่จำเป็นต้องแก้ไข แต่อาจมีการแก้ไขได้


น่าสนใจ. เมื่อทราบกึ่งเกี่ยวข้อง, ฉันวิ่งเข้าไปในข้อผิดพลาดที่ก่อให้เกิดความผิดพลาดที่เปิดออกมาจะเป็นเพราะ Qt ของQApplication(argc,argv)คอนสตรัคถูกกำหนดด้วยargcโดยการอ้างอิง นั่นทำให้ฉันประหลาดใจ
HostileFork บอกว่าอย่าไว้ใจ SE

23

argcโดยปกติไม่ใช่ค่าคงที่เนื่องจากลายเซ็นของฟังก์ชันสำหรับmain()วันที่constล่วงหน้า

เนื่องจาก argc เป็นตัวแปรสแต็กการเปลี่ยนแปลงจึงไม่ส่งผลอะไรนอกจากการประมวลผลบรรทัดคำสั่งของคุณเอง

แน่นอนคุณสามารถประกาศได้ฟรีconstหากต้องการ


8

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

ดังนั้นargcคุณสามารถเพิ่มไฟล์const.

แต่สำหรับargvคุณไม่สามารถสร้างข้อมูลอักขระได้constโดยไม่ต้องเปลี่ยนลายเซ็นฟังก์ชัน ซึ่งหมายความว่าไม่ใช่หนึ่งในmainลายเซ็นของฟังก์ชันมาตรฐานและจะไม่ต้องรับรู้ว่าเป็นmainฟังก์ชัน ดังนั้นไม่ใช่ความคิดที่ดี


เหตุผลที่ดีในการไม่ใช้mainอาร์กิวเมนต์มาตรฐานในโปรแกรมที่ไม่ใช่ของเล่นคือใน Windows ไม่สามารถแสดงอาร์กิวเมนต์ของโปรแกรมจริงเช่นชื่อไฟล์ที่มีอักขระสากล นั่นเป็นเพราะใน Windows พวกเขามีการเข้ารหัสที่แข็งแกร่งมากเป็น Windows ANSI ใน Windows คุณสามารถใช้สิ่งอำนวยความสะดวกในการเข้าถึงอาร์กิวเมนต์แบบพกพาเพิ่มเติมได้ในแง่ของGetCommandLineฟังก์ชัน API


เมื่อสรุปแล้วไม่มีอะไรป้องกันไม่ให้คุณเพิ่มconstเข้าไปargcแต่const-ness ที่มีประโยชน์ที่สุดargvจะทำให้คุณมีmainฟังก์ชันที่ไม่ได้มาตรฐานซึ่งส่วนใหญ่อาจไม่รู้จักเช่นนั้น มีความสุข (ในทางที่น่าขัน) มีเหตุผลที่ดีที่จะไม่ใช้mainอาร์กิวเมนต์มาตรฐานสำหรับรหัสร้ายแรงแบบพกพา ค่อนข้างง่ายสำหรับในทางปฏิบัติพวกเขาสนับสนุนเฉพาะ ASCII แบบเก่าโดยมีตัวอักษรภาษาอังกฤษเท่านั้น


1
หากคุณกำลังพัฒนาสำหรับ Windows ด้วย cygwin หรือ libc อื่นที่รองรับ UTF-8 อย่างถูกต้อง (เท่าที่ฉันรู้ cygwin เป็นเพียงคนเดียวในปัจจุบัน แต่ฉันมีคนทำงานในตัวเลือกอื่น) mainลายเซ็นมาตรฐานจะทำงานได้ดีและคุณสามารถทำได้ รับอาร์กิวเมนต์ Unicode โดยพลการ
R .. GitHub STOP HELPING ICE

@R พวกเขายังทำงานได้ดีบนแพลตฟอร์ม * nix ส่วนใหญ่ ปัญหาไม่ได้อยู่ที่ว่ามีแพลตฟอร์มที่ใช้งานได้หรือไม่ แต่เป็นความคิดริเริ่มที่ดีมากและเมื่อมันเกิดขึ้นฉันก็ทำแบบเดียวกันจริงจังไม่มากก็น้อย
ไชโยและ hth - Alf

4

ลายเซ็นของเป็นบางส่วนของสิ่งประดิษฐ์ทางประวัติศาสตร์จากmain Cในอดีตไม่ได้มีCconst

อย่างไรก็ตามคุณสามารถประกาศพารามิเตอร์ของคุณได้constเนื่องจากเอฟเฟกต์ของ const เป็นเวลาคอมไพล์เท่านั้น


เมื่อคุณพูดว่า "Historical C" เราพูดถึงประวัติศาสตร์ที่นี่อย่างไร?
Joe Z

8
constถูกเพิ่มใน C89 / C90; ก่อนหน้านั้นไม่ได้เป็นส่วนหนึ่งของ C.
Jonathan Leffler

@JonathanLeffler: คุณก็รู้ฉันลืมไปแล้ว ฉันเรียนภาษา C ในปี 1992 ก่อนหน้านั้นฉันเป็นแฮ็กเกอร์ภาษาปาสคาลเบสิกและแอสเซมบลี
Joe Z

2

เนื่องจากargcเป็นตัวแปรท้องถิ่น (และใน C ++ ไม่ใช่ข้อมูลอ้างอิงหรือบางสิ่งบางอย่าง) และเนื่องจากสถานที่พิเศษของmainวิธีการที่ความเข้ากันได้แบบย้อนกลับทำให้มันเป็นจำนวนมากของสิ่งที่คั่งค้างโดยไม่มีเหตุผลที่น่าสนใจที่จะบังคับให้แอปพลิเคชันทำให้เป็น const

main() {}

int main() {}

main() { return 0; }

main(int argc, char* argv[]) { return 0; }

int main(const int argc, const char** argv) { /* no return*/ }

รูปแบบเหล่านี้และรูปแบบอื่น ๆ จะรวบรวมไว้ในคอมไพเลอร์ C และ C ++ ที่หลากหลาย

ท้ายที่สุดแล้วมันไม่ใช่ว่า argc ไม่ใช่ const แต่ก็ไม่จำเป็นต้องเป็น แต่ก็เป็นได้ถ้าคุณต้องการให้เป็น

http://ideone.com/FKldHFตัวอย่าง C:

main(const int argc, const char* argv[]) { return 0; }

http://ideone.com/m1qc9cตัวอย่าง C ++

main(const int argc) {}

โปรดทราบว่าตัวแปรที่ไม่มีประเภทการส่งคืนอย่างชัดเจนจะใช้ไม่ได้ใน C99 อีกต่อไป แต่ C11 เพียงอย่างเดียวแม้ว่าคอมไพเลอร์ยังคงอนุญาตให้ใช้งานได้เนื่องจากเหตุผลของความเข้ากันได้แบบย้อนกลับ คอมไพเลอร์ C ++ ไม่ควรยอมรับตัวแปรที่ไม่มีชนิดส่งคืน
Jonathan Leffler

@JonathanLeffler ใช่ แต่ตัวอย่างสุดท้ายของ ideone ที่นั่น ( ideone.com/m1qc9c ) คือ G ++ 4.8.2 พร้อมด้วย-std=c++11. เสียงดังยังยอมรับ แต่ให้warning: only one parameter on 'main' declaration [-Wmain]และ MSVC ประท้วงเฉพาะเกี่ยวกับตัวระบุประเภทการส่งคืนที่ขาดหายไปและไม่มีการอ้างอิงถึง argc อีกครั้ง 'shenanigans' และ 'leeway' :)
kfsone

1

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

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

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

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