มีรูปแบบเมื่อจัดการกับอาร์เรย์และฟังก์ชั่น; มันเป็นเพียงเล็กน้อยยากที่จะเห็นในตอนแรก
เมื่อจัดการกับอาร์เรย์มันมีประโยชน์ที่จะจำสิ่งต่อไปนี้: เมื่อนิพจน์อาร์เรย์ปรากฏในบริบทส่วนใหญ่ประเภทของนิพจน์จะถูกแปลงโดยนัยจาก "อาร์เรย์องค์ประกอบ 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 = ...;
}
จะไม่ทำงาน