อะไรคือความแตกต่างระหว่างทั้งสองฟังก์ชันใน C?
void f1(double a[]) {
//...
}
void f2(double *a) {
//...
}
ถ้าฉันจะเรียกใช้ฟังก์ชันในอาร์เรย์ที่มีความยาวมากฟังก์ชันทั้งสองนี้จะทำงานแตกต่างกันหรือไม่พวกเขาจะใช้พื้นที่บนสแต็กมากขึ้นหรือไม่
อะไรคือความแตกต่างระหว่างทั้งสองฟังก์ชันใน C?
void f1(double a[]) {
//...
}
void f2(double *a) {
//...
}
ถ้าฉันจะเรียกใช้ฟังก์ชันในอาร์เรย์ที่มีความยาวมากฟังก์ชันทั้งสองนี้จะทำงานแตกต่างกันหรือไม่พวกเขาจะใช้พื้นที่บนสแต็กมากขึ้นหรือไม่
คำตอบ:
ประการแรกมาตรฐานบางประการ:
6.7.5.3 ผู้ประกาศฟังก์ชัน (รวมถึงต้นแบบ)
...
7 การประกาศพารามิเตอร์เป็น '' อาร์เรย์ประเภท '' จะถูกปรับเป็น '' ตัวชี้คุณสมบัติที่จะ พิมพ์ '' โดยที่ระบุคุณสมบัติ (ถ้ามี) ภายใน[
และ]
ของการมาของชนิดอาร์เรย์ หากคีย์เวิร์ดstatic
ยังปรากฏอยู่ภายใน[
และ]
ของการได้มาของประเภทอาร์เรย์ด้วยดังนั้นสำหรับการเรียกใช้ฟังก์ชันแต่ละครั้งค่าของอาร์กิวเมนต์จริงที่เกี่ยวข้องจะให้การเข้าถึงองค์ประกอบแรกของอาร์เรย์ที่มีองค์ประกอบอย่างน้อยที่สุดตามที่ระบุโดยขนาด นิพจน์.
ดังนั้นในระยะสั้น, พารามิเตอร์ของฟังก์ชันใดประกาศให้เป็นT a[]
หรือT a[N]
ได้รับการปฏิบัติราวกับว่าT *a
มันถูกประกาศ
เหตุใดพารามิเตอร์อาร์เรย์จึงได้รับการปฏิบัติราวกับว่าถูกประกาศให้เป็นพอยน์เตอร์? นี่คือเหตุผล:
6.3.2.1 Lvalues อาร์เรย์และตัวกำหนดฟังก์ชัน
...
3 ยกเว้นเมื่อเป็นตัวถูกดำเนินการของตัวsizeof
ดำเนินการหรือตัวดำเนิน&
การยูนารีหรือเป็นสตริงลิเทอรัลที่ใช้เริ่มต้นอาร์เรย์นิพจน์ที่มีประเภท '' array of type ' 'ถูกแปลงเป็นนิพจน์ด้วย type' 'pointer to type ' 'ที่ชี้ไปยังองค์ประกอบเริ่มต้นของวัตถุอาร์เรย์และไม่ใช่ lvalue หากอ็อบเจ็กต์อาร์เรย์มีการลงทะเบียนคลาสหน่วยเก็บข้อมูลพฤติกรรมจะไม่ได้กำหนด
ระบุรหัสต่อไปนี้:
int main(void)
{
int arr[10];
foo(arr);
...
}
ในการเรียกไปfoo
ที่นิพจน์อาร์เรย์arr
ไม่ได้เป็นตัวถูกดำเนินการของอย่างใดอย่างหนึ่งsizeof
หรือ&
ดังนั้นชนิดของมันจึงถูกแปลงโดยปริยายจาก "อาร์เรย์ 10 องค์ประกอบของint
" เป็น "ตัวชี้ถึงint
" ตาม 6.2.3.1/3 ดังนั้นfoo
จะได้รับค่าตัวชี้แทนที่จะเป็นค่าอาร์เรย์
เนื่องจาก 6.7.5.3/7 คุณสามารถเขียนfoo
เป็น
void foo(int a[]) // or int a[10]
{
...
}
แต่จะถูกตีความว่า
void foo(int *a)
{
...
}
ดังนั้นทั้งสองรูปแบบจึงเหมือนกัน
ประโยคสุดท้ายใน 6.7.5.3/7 ถูกนำมาใช้กับ C99 และโดยทั่วไปหมายความว่าหากคุณมีการประกาศพารามิเตอร์เช่น
void foo(int a[static 10])
{
...
}
พารามิเตอร์จริงที่เกี่ยวข้องa
ต้องเป็นอาร์เรย์ที่มีอย่างน้อย 10 องค์ประกอบ
ความแตกต่างคือไวยากรณ์ล้วนๆ ใน C เมื่อใช้สัญกรณ์อาร์เรย์สำหรับพารามิเตอร์ฟังก์ชันจะถูกแปลงเป็นการประกาศตัวชี้โดยอัตโนมัติ
ไม่ไม่มีความแตกต่างระหว่างพวกเขา เพื่อทดสอบฉันเขียนรหัส C นี้ในคอมไพเลอร์ Dev C ++ (mingw):
#include <stdio.h>
void function(int* array) {
int a =5;
}
void main() {
int array[]={2,4};
function(array);
getch();
}
เมื่อฉันแยกฟังก์ชั่นหลักใน. exe ของไฟล์ไบนารีทั้งสองเวอร์ชันที่เรียกใน IDA ฉันได้รับรหัสแอสเซมบลีที่เหมือนกันทุกประการดังนี้:
push ebp
mov ebp, esp
sub esp, 18h
and esp, 0FFFFFFF0h
mov eax, 0
add eax, 0Fh
add eax, 0Fh
shr eax, 4
shl eax, 4
mov [ebp+var_C], eax
mov eax, [ebp+var_C]
call sub_401730
call sub_4013D0
mov [ebp+var_8], 2
mov [ebp+var_4], 4
lea eax, [ebp+var_8]
mov [esp+18h+var_18], eax
call sub_401290
call _getch
leave
retn
ดังนั้นจึงไม่มีความแตกต่างระหว่างสองเวอร์ชันของการเรียกนี้อย่างน้อยคอมไพเลอร์ก็คุกคามพวกเขาเท่า ๆ กัน