มีรูปแบบเมื่อจัดการกับอาร์เรย์และฟังก์ชั่น; มันเป็นเพียงเล็กน้อยยากที่จะเห็นในตอนแรก
เมื่อจัดการกับอาร์เรย์มันมีประโยชน์ที่จะจำสิ่งต่อไปนี้: เมื่อนิพจน์อาร์เรย์ปรากฏในบริบทส่วนใหญ่ประเภทของนิพจน์จะถูกแปลงโดยนัยจาก "อาร์เรย์องค์ประกอบ N ของ T" เป็น "ตัวชี้เป็น T" และตั้งค่าของมัน เพื่อชี้ไปที่องค์ประกอบแรกในอาร์เรย์ ข้อยกเว้นของกฎนี้คือเมื่อนิพจน์อาร์เรย์ปรากฏเป็นตัวถูกดำเนินการของตัวดำเนินการ&หรือsizeofหรือเมื่อเป็นตัวอักษรสตริงที่ใช้เป็นตัวเริ่มต้นในการประกาศ
ดังนั้นเมื่อคุณเรียกใช้ฟังก์ชันที่มีนิพจน์อาร์เรย์เป็นอาร์กิวเมนต์ฟังก์ชันจะรับตัวชี้ไม่ใช่อาร์เรย์:
int arr[10];
...
foo(arr);
...
void foo(int *arr) { ... }
นี่คือเหตุผลที่คุณไม่ใช้&โอเปอเรเตอร์เพื่อหาข้อโต้แย้งที่สอดคล้องกับ "% s" ในscanf():
char str[STRING_LENGTH];
...
scanf("%s", str);
เนื่องจากการแปลงโดยนัยscanf()ได้รับchar *ค่าที่ชี้ไปที่จุดเริ่มต้นของstrอาร์เรย์ นี่ถือเป็นจริงสำหรับฟังก์ชั่นใด ๆ ที่เรียกว่ามีการแสดงออกอาร์เรย์เป็นอาร์กิวเมนต์ (เพียงเกี่ยวกับstr*ฟังก์ชั่น*scanfและ*printfฟังก์ชั่นอื่น ๆ )
ในทางปฏิบัติคุณอาจไม่เคยเรียกใช้ฟังก์ชันที่มีนิพจน์อาร์เรย์โดยใช้&โอเปอเรเตอร์ดังเช่นใน:
int arr[N];
...
foo(&arr);
void foo(int (*p)[N]) {...}
รหัสดังกล่าวไม่ธรรมดามาก คุณต้องรู้ขนาดของอาเรย์ในการประกาศฟังก์ชั่นและฟังก์ชั่นจะทำงานร่วมกับพอยน์เตอร์ไปจนถึงอาร์เรย์ของขนาดเฉพาะ (ตัวชี้ไปยังอาเรย์ 10 องค์ประกอบของ T เป็นชนิดที่แตกต่างจากตัวชี้ไปยังอาเรย์ 11 องค์ประกอบ ของ T)
เมื่อนิพจน์อาเรย์ปรากฏเป็นตัวถูกดำเนินการกับโอเป&อเรเตอร์ชนิดของนิพจน์ที่เกิดขึ้นคือ "ตัวชี้ไปยังอาร์เรย์ขององค์ประกอบ N ของ T" หรือT (*)[N]ซึ่งแตกต่างจากอาเรย์ของพอยน์เตอร์ ( T *[N]) และตัวชี้ไปยังประเภทฐานT *)
เมื่อจัดการกับฟังก์ชั่นและพอยน์เตอร์กฎที่ต้องจำคือ: ถ้าคุณต้องการเปลี่ยนค่าของอาร์กิวเมนต์และให้มันสะท้อนในรหัสเรียกคุณต้องส่งตัวชี้ไปยังสิ่งที่คุณต้องการแก้ไข อีกครั้งอาร์เรย์โยนประแจลิงเข้าไปในงาน แต่เราจะจัดการกับกรณีปกติก่อน
จำไว้ว่า C ส่งค่าอาร์กิวเมนต์ของฟังก์ชันทั้งหมดตามค่า พารามิเตอร์ทางการได้รับสำเนาของค่าในพารามิเตอร์จริงและการเปลี่ยนแปลงใด ๆ กับพารามิเตอร์ที่เป็นทางการจะไม่ปรากฏในพารามิเตอร์จริง ตัวอย่างทั่วไปคือฟังก์ชันสลับ:
void swap(int x, int y) { int tmp = x; x = y; y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);
คุณจะได้ผลลัพธ์ต่อไปนี้:
ก่อนทำการแลกเปลี่ยน: a = 1, b = 2
หลังจากสลับ: a = 1, b = 2
พารามิเตอร์ที่เป็นทางการxและyเป็นวัตถุที่แตกต่างจากaและbเพื่อการเปลี่ยนแปลงxและyไม่ได้สะท้อนให้เห็นในและa bเนื่องจากเราต้องการแก้ไขค่าของaและbเราต้องส่งพอยน์เตอร์ไปยังฟังก์ชัน swap:
void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);
ตอนนี้ผลลัพธ์ของคุณจะเป็น
ก่อนทำการแลกเปลี่ยน: a = 1, b = 2
หลังจากสลับ: a = 2, b = 1
โปรดทราบว่าในฟังก์ชั่นการแลกเราไม่เปลี่ยนค่าของxและyแต่ค่าของสิ่งที่xและชี้ไปที่y เขียนถึง*xจะแตกต่างจากการเขียนx; เราไม่ได้อัปเดตค่าในxตัวเองเราได้รับตำแหน่งจากxและอัปเดตค่าในตำแหน่งนั้น
นี่เป็นเรื่องจริงถ้าเราต้องการแก้ไขค่าพอยน์เตอร์ ถ้าเราเขียน
int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);
จากนั้นเราจะแก้ไขค่าของพารามิเตอร์อินพุตstreamไม่ใช่สิ่งที่stream ชี้ไปที่ดังนั้นการเปลี่ยนแปลงstreamจึงไม่มีผลกับค่าของin; เพื่อให้สิ่งนี้ใช้งานได้เราจะต้องผ่านตัวชี้ไปยังตัวชี้:
int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);
อีกครั้งอาร์เรย์โยนประแจลิงลงไปในผลงาน เมื่อคุณส่งนิพจน์อาร์เรย์ไปยังฟังก์ชันสิ่งที่ฟังก์ชันรับคือตัวชี้ เนื่องจากวิธีการกำหนดตัวห้อยของอาร์เรย์คุณสามารถใช้ตัวดำเนินการตัวห้อยบนตัวชี้ในลักษณะเดียวกับที่คุณสามารถใช้ในอาร์เรย์:
int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}
โปรดทราบว่าวัตถุอาร์เรย์อาจไม่ได้รับมอบหมาย เช่นคุณไม่สามารถทำอะไรได้
int a[10], b[10];
...
a = b;
ดังนั้นคุณต้องระวังเมื่อคุณติดต่อกับพอยน์เตอร์ไปยังอาร์เรย์ สิ่งที่ต้องการ
void (int (*foo)[N])
{
...
*foo = ...;
}
จะไม่ทำงาน