ทำไมการจัดทำดัชนีในอาร์เรย์เริ่มต้นด้วยศูนย์ใน C และไม่ใช่ด้วย 1
ทำไมการจัดทำดัชนีในอาร์เรย์เริ่มต้นด้วยศูนย์ใน C และไม่ใช่ด้วย 1
คำตอบ:
ใน C ชื่อของอาเรย์เป็นตัวชี้[แต่เห็นความคิดเห็น]อ้างอิงถึงตำแหน่งหน่วยความจำและการแสดงออกarray[n]
หมายถึงn
องค์ประกอบตำแหน่งหน่วยความจำห่างจากองค์ประกอบเริ่มต้น ซึ่งหมายความว่าดัชนีถูกใช้เป็นออฟเซ็ต องค์ประกอบแรกของอาร์เรย์ที่มีอยู่ตรงที่ตั้งของหน่วยความจำที่อาร์เรย์หมาย (0 องค์ประกอบออกไป) array[0]
ดังนั้นจึงควรจะแสดงเป็น
สำหรับข้อมูลเพิ่มเติม:
http://developeronline.blogspot.com/2008/04/why-array-index-should-start-from-0.html
sizeof arr
ทำให้ขนาดของวัตถุอาร์เรย์ไม่ใช่ขนาดของตัวชี้
sizeof
ผู้ประกอบการหรือ&
ผู้ประกอบการเอกหรือเป็นตัวอักษรสตริงที่ใช้ในการเริ่มต้นอาร์เรย์การแสดงออกที่มีประเภท" อาร์เรย์ ของ type "ถูกแปลงเป็นนิพจน์ที่มีประเภท" pointer to type "ที่ชี้ไปที่องค์ประกอบเริ่มต้นของวัตถุอาร์เรย์และไม่ใช่ lvalue หากวัตถุอาร์เรย์มีคลาสหน่วยเก็บข้อมูลลงทะเบียนพฤติกรรมจะไม่ได้กำหนด "
คำถามนี้ถูกโพสต์เมื่อปีที่แล้ว แต่นี่จะไป ...
ในขณะที่บทความของ Dijkstra (อ้างอิงก่อนหน้านี้ในคำตอบที่ถูกลบตอนนี้) ทำให้รู้สึกจากมุมมองทางคณิตศาสตร์มันไม่เกี่ยวข้องเมื่อมันมาถึงการเขียนโปรแกรม
การตัดสินใจที่ทำโดยข้อกำหนดภาษา & นักออกแบบคอมไพเลอร์ขึ้นอยู่กับการตัดสินใจของนักออกแบบระบบคอมพิวเตอร์ที่จะเริ่มนับที่ 0
อ้างจากข้ออ้างเพื่อสันติภาพโดย Danny Cohen
สำหรับฐาน b ใด ๆ เป็นลำดับแรก b ^ N จำนวนเต็มที่ไม่เป็นลบแรกจะถูกแทนด้วยตัวเลขNแน่นอน(รวมถึงศูนย์นำหน้า) เฉพาะเมื่อการกำหนดหมายเลขเริ่มต้นที่ 0
สามารถทดสอบได้ค่อนข้างง่าย ใน base-2 ใช้2^3 = 8
หมายเลข 8 คือ:
111
สามารถแสดงโดยใช้ 3
บิตในขณะที่1000
จะต้องมีบิตพิเศษ (4 บิต)
ที่อยู่หน่วยความจำของคอมพิวเตอร์มี2^N
เซลล์ที่N
บิต ตอนนี้ถ้าเราเริ่มนับที่ 1 2^N
เซลล์จะต้องมีN+1
บรรทัดที่อยู่ จำเป็นต้องใช้บิตพิเศษเพื่อเข้าถึงที่อยู่ 1 ที่แน่นอน (1000
ในกรณีข้างต้น) อีกวิธีในการแก้ปัญหาคือปล่อยให้ที่อยู่สุดท้ายไม่สามารถเข้าถึงได้และใช้N
บรรทัดที่อยู่
ทั้งคู่เป็นวิธีแก้ปัญหาย่อยที่ดีที่สุดเมื่อเทียบกับจำนวนเริ่มต้นที่ 0 ซึ่งจะทำให้ที่อยู่ทั้งหมดสามารถเข้าถึงได้โดยใช้N
บรรทัดที่อยู่!
การตัดสินใจที่จะเริ่มนับรวม0
ตั้งแต่ทุกระบบดิจิตอลรวมถึงซอฟต์แวร์ที่ทำงานบนเพราะมันทำให้ง่ายสำหรับรหัสที่จะแปลสิ่งที่ระบบพื้นฐานสามารถตีความ หากไม่เป็นเช่นนั้นจะมีการดำเนินการแปลที่ไม่จำเป็นระหว่างเครื่องและโปรแกรมเมอร์สำหรับการเข้าถึงอาร์เรย์ทุกครั้ง มันทำให้การรวบรวมง่ายขึ้น
ข้อความจากกระดาษ:
a[b]
มีการใช้งานเหมือน*(a+b)
ในคอมไพเลอร์ยุคแรก แม้วันนี้คุณยังสามารถเขียนแทน2[a]
a[2]
ตอนนี้ถ้าดัชนีไม่ได้เริ่มต้นที่ 0 แล้วจะกลายเป็นa[b]
*(a+b-1)
สิ่งนี้จะต้องเพิ่ม 2 ครั้งบน CPUs แทน 0 ซึ่งหมายถึงความเร็วครึ่งหนึ่ง เห็นได้ชัดว่าไม่ต้องการ
เนื่องจาก 0 คือระยะห่างจากตัวชี้ไปยังส่วนหัวของอาร์เรย์จนถึงองค์ประกอบแรกของอาร์เรย์
พิจารณา:
int foo[5] = {1,2,3,4,5};
ในการเข้าถึง 0 เราทำได้:
foo[0]
แต่ฟูย่อยสลายไปยังตัวชี้และการเข้าถึงด้านบนมีตัวชี้แบบอะนาล็อกทางคณิตศาสตร์ในการเข้าถึง
*(foo + 0)
เลขคณิตตัวชี้วันนี้ไม่ได้ถูกใช้บ่อยนัก ย้อนกลับไปแม้ว่ามันเป็นวิธีที่สะดวกในการระบุที่อยู่และย้าย X "ints" ออกจากจุดเริ่มต้นนั้น แน่นอนถ้าคุณต้องการอยู่ในที่ที่คุณอยู่คุณก็แค่เพิ่ม 0!
เนื่องจากดัชนีที่ใช้ 0 อนุญาตให้ ...
array[index]
... ที่จะดำเนินการเป็น ...
*(array + index)
หากดัชนีเป็นแบบ 1 คอมไพเลอร์จะต้องสร้าง: *(array + index - 1)
และ "-1" นี้จะทำให้ประสิทธิภาพเสียหาย
เพราะมันทำให้คอมไพเลอร์และลิงเกอร์ง่ายขึ้น (เขียนง่ายกว่า)
"... การอ้างอิงหน่วยความจำตามที่อยู่และออฟเซ็ตถูกแสดงโดยตรงในฮาร์ดแวร์บนแทบทุกสถาปัตยกรรมคอมพิวเตอร์ดังนั้นรายละเอียดการออกแบบใน C ทำให้การรวบรวมง่ายขึ้น"
และ
"... สิ่งนี้ทำให้การติดตั้งง่ายขึ้น ... "
ดัชนีอาร์เรย์เสมอเริ่มต้นด้วย zero.Let ถือว่าอยู่ฐานคือ 2000 arr[i] = *(arr+i)
ตอนนี้ ตอนif i= 0
นี้หมายถึง*(2000+0
) เท่ากับที่อยู่ฐานหรือที่อยู่ขององค์ประกอบแรกในอาร์เรย์ ดัชนีนี้ถือว่าเป็นออฟเซ็ตดังนั้นดัชนี bydeafault เริ่มต้นจากศูนย์
ด้วยเหตุผลเดียวกันว่าเมื่อเป็นวันพุธและบางคนถามคุณว่ากี่วันถึงวันพุธคุณจะพูดว่า 0 มากกว่า 1 และเมื่อเป็นวันพุธและบางคนถามคุณว่ากี่วันจนถึงวันพฤหัสบดีคุณจะพูดว่า 1 มากกว่า 2
คำอธิบายที่หรูหราที่สุดที่ฉันได้อ่านสำหรับการกำหนดตัวเลขเป็นศูนย์คือการสังเกตว่าค่าไม่ได้ถูกเก็บไว้ในสถานที่ที่ทำเครื่องหมายไว้บนบรรทัดตัวเลข แต่จะอยู่ในช่องว่างระหว่างพวกเขา รายการแรกจะถูกเก็บไว้ระหว่างศูนย์และหนึ่งรายการถัดไประหว่างหนึ่งและสอง ฯลฯ รายการที่ N จะถูกเก็บไว้ระหว่าง N-1 และ N ช่วงของรายการอาจมีการอธิบายโดยใช้ตัวเลขที่ด้านใดด้านหนึ่ง แต่ละรายการเป็นไปตามแบบแผนที่อธิบายโดยใช้หมายเลขด้านล่าง หากมีการกำหนดช่วง (X, Y) การระบุหมายเลขบุคคลโดยใช้หมายเลขด้านล่างหมายความว่าเราสามารถระบุรายการแรกได้โดยไม่ต้องใช้เลขคณิตใด ๆ (เป็นรายการที่ X) แต่ต้องลบหนึ่งรายการจาก Y เพื่อระบุรายการสุดท้าย (Y -1) การระบุไอเท็มโดยใช้หมายเลขด้านบนจะช่วยให้การระบุรายการสุดท้ายในช่วงนั้นง่ายขึ้น (เป็นรายการ Y)
แม้ว่ามันจะไม่น่ากลัวที่จะระบุไอเท็มตามจำนวนที่อยู่เหนือพวกมัน แต่การกำหนดไอเท็มแรกในช่วง (X, Y) ว่าเป็นไอเท็มหนึ่งข้างต้น X นั้นใช้งานได้ดีกว่าการกำหนดว่าเป็นหนึ่งด้านล่าง (X + 1)
เหตุผลทางเทคนิคอาจมาจากข้อเท็จจริงที่ว่าตัวชี้ไปยังตำแหน่งหน่วยความจำของอาร์เรย์นั้นเป็นเนื้อหาขององค์ประกอบแรกของอาร์เรย์ หากคุณประกาศตัวชี้พร้อมดัชนีหนึ่งโปรแกรมโดยปกติจะเพิ่มมูลค่าของหนึ่งไปยังตัวชี้เพื่อเข้าถึงเนื้อหาที่ไม่ใช่สิ่งที่คุณต้องการแน่นอน
ลองเข้าถึงหน้าจอพิกเซลโดยใช้พิกัด X, Y บนเมทริกซ์ที่ใช้ 1 สูตรมีความซับซ้อนอย่างที่สุด ทำไมมีความซับซ้อน เนื่องจากคุณแปลง X, Y ให้กลายเป็นหนึ่งหมายเลข ทำไมคุณต้องแปลง X, Y เป็นออฟเซ็ต เพราะนั่นคือวิธีที่หน่วยความจำถูกจัดระเบียบภายในคอมพิวเตอร์เช่นเดียวกับกระแสหน่วยความจำเซลล์ (อาร์เรย์) คอมพิวเตอร์เกี่ยวข้องกับเซลล์อาร์เรย์ได้อย่างไร ใช้ offsets (การกระจัดจากเซลล์แรกเป็นรูปแบบการจัดทำดัชนี zero-based)
ดังนั้นในบางจุดของรหัสที่คุณต้องการ (หรือคอมไพเลอร์ต้องการ) เพื่อแปลงสูตร 1 ฐานเป็นสูตรที่ใช้ 0 เพราะนั่นคือวิธีที่คอมพิวเตอร์จัดการกับหน่วยความจำ
สมมติว่าเราต้องการสร้างอาร์เรย์ขนาด 5
int array [5] = [2,3,5,9,8]
ให้องค์ประกอบที่ 1 ของอาร์เรย์นั้นชี้ไปที่ตำแหน่ง 100
และให้เราพิจารณาการทำดัชนีเริ่มจาก 1 ไม่ใช่ 0.
ตอนนี้เราต้องค้นหาตำแหน่งขององค์ประกอบที่ 1 ด้วยความช่วยเหลือของดัชนี
(จำตำแหน่งขององค์ประกอบที่ 1 ได้ 100)
เนื่องจากขนาดของจำนวนเต็มคือ 4 บิต
ดังนั้น -> พิจารณาดัชนี 1 ตำแหน่งจะมี
ขนาด ของ index (1) * ขนาดของจำนวนเต็ม (4) = 4
ดังนั้นตำแหน่งจริงที่มันจะแสดงให้เราเห็นคือ
100 + 4 = 104
ซึ่งไม่เป็นความจริงเพราะตำแหน่งเริ่มต้นอยู่ที่ 100
ซึ่งควรชี้ไปที่ 100 ไม่ใช่ที่ 104
ตอนนี้
ผิด
ตอนนี้สมมติว่าเราได้ทำดัชนีจาก 0
แล้ว
ตำแหน่งขององค์ประกอบที่ 1 ควรเป็น
ขนาดของดัชนี (0) * ขนาดของจำนวนเต็ม (4) = 0
ดังนั้น ->
ตำแหน่งขององค์ประกอบที่ 1 คือ 100 + 0 = 100
และนั่นคือตำแหน่งที่แท้จริงขององค์ประกอบ
นี่คือสาเหตุที่การทำดัชนีเริ่มต้นที่ 0;
ฉันหวังว่ามันจะชัดเจนจุดของคุณ
ฉันมาจากพื้นหลังของจาวา ฉันได้นำเสนอคำตอบสำหรับคำถามนี้ในแผนภาพด้านล่างซึ่งฉันได้เขียนไว้ในกระดาษซึ่งอธิบายด้วยตนเอง
ขั้นตอนหลัก:
หมายเหตุ : บล็อกที่แสดงในภาพคือการแสดงหน่วยความจำ
ก่อนอื่นคุณต้องรู้ว่าอาเรย์นั้นถูกพิจารณาเป็นพอยน์เตอร์ภายในเพราะ "ชื่อของอาเรย์นั้นมีที่อยู่ขององค์ประกอบแรกของอาเรย์"
ex. int arr[2] = {5,4};
พิจารณาว่าอาร์เรย์เริ่มต้นที่ที่อยู่ 100 ดังนั้นองค์ประกอบแรกจะเป็นที่ที่อยู่ 100 และวินาทีจะอยู่ที่ 104 ตอนนี้ให้พิจารณาว่าถ้าดัชนีอาร์เรย์เริ่มต้นจาก 1 ดังนั้น
arr[1]:-
นี้สามารถเขียนในการแสดงออกพอยน์เตอร์เช่นนี้ -
arr[1] = *(arr + 1 * (size of single element of array));
พิจารณาขนาดของ int คือ 4bytes ตอนนี้
arr[1] = *(arr + 1 * (4) );
arr[1] = *(arr + 4);
ดังที่เราทราบชื่ออาร์เรย์มีที่อยู่ขององค์ประกอบแรกดังนั้น arr = 100 ตอนนี้
arr[1] = *(100 + 4);
arr[1] = *(104);
ซึ่งจะช่วยให้,
arr[1] = 4;
เนื่องจากการแสดงออกนี้เราไม่สามารถเข้าถึงองค์ประกอบที่อยู่ 100 ซึ่งเป็นองค์ประกอบแรกอย่างเป็นทางการ
ตอนนี้ให้พิจารณาดัชนีอาเรย์เริ่มจาก 0 ดังนั้น
arr[0]:-
สิ่งนี้จะได้รับการแก้ไขในฐานะ
arr[0] = *(arr + 0 + (size of type of array));
arr[0] = *(arr + 0 * 4);
arr[0] = *(arr + 0);
arr[0] = *(arr);
ตอนนี้เรารู้ว่าชื่ออาร์เรย์มีที่อยู่ขององค์ประกอบแรกของมันดังนั้น
arr[0] = *(100);
ซึ่งให้ผลลัพธ์ที่ถูกต้อง
arr[0] = 5;
ดังนั้นดัชนีอาเรย์จะเริ่มจาก 0 ในคเสมอ
การอ้างอิง: รายละเอียดทั้งหมดเขียนไว้ในหนังสือ "ภาษาซีโดย brian kerninghan และ dennis ritchie"
ในอาร์เรย์ดัชนีจะบอกระยะทางจากองค์ประกอบเริ่มต้น ดังนั้นองค์ประกอบแรกอยู่ที่ 0 ระยะทางจากองค์ประกอบเริ่มต้น นั่นคือสาเหตุที่อาร์เรย์เริ่มต้นจาก 0
เป็นเพราะaddress
ต้องชี้ไปทางขวาelement
ในอาเรย์ ให้เราสมมติอาร์เรย์ด้านล่าง:
let arr = [10, 20, 40, 60];
ให้เราพิจารณาจุดเริ่มต้นของการเป็นที่อยู่12
และขนาดของBEelement
4 bytes
address of arr[0] = 12 + (0 * 4) => 12
address of arr[1] = 12 + (1 * 4) => 16
address of arr[2] = 12 + (2 * 4) => 20
address of arr[3] = 12 + (3 * 4) => 24
ถ้ามันไม่ได้ zero-based
ในทางเทคนิคที่อยู่องค์ประกอบแรกของเราในarray
จะเป็นซึ่งเป็นสิ่งที่ผิดเป็นสถานที่มันเป็น16
12
ชื่ออาร์เรย์เป็นตัวชี้ค่าคงที่ที่ชี้ไปยังที่อยู่พื้นฐานเมื่อคุณใช้ arr [i] คอมไพเลอร์จะจัดการมันเป็น * (arr + i) เนื่องจากช่วง int คือ -128 ถึง 127 คอมไพเลอร์คิดว่า -128 ถึง -1 คือ ตัวเลขลบและ 0 ถึง 128 เป็นตัวเลขบวกดังนั้นดัชนีอาร์เรย์จะเริ่มต้นด้วยศูนย์เสมอ
int
จำเป็นต้องมีประเภทเพื่อรองรับช่วงอย่างน้อย 16 บิตและในระบบส่วนใหญ่ในปัจจุบันรองรับ 32 บิต ฉันคิดว่าตรรกะของคุณมีข้อบกพร่องและคำตอบของคุณไม่ได้ปรับปรุงคำตอบอื่น ๆ ที่ได้รับจากคนอื่น ๆ ฉันแนะนำให้ลบสิ่งนี้