2010-08-05 49 views
6

Trong chương trình C bên dưới, tôi không hiểu tại sao buf [0] = 'A' sau khi tôi gọi foo. Không phải là foo làm pass-by-giá trị?Lập trình C - Truyền qua tham chiếu

#include <stdio.h> 
#include <stdlib.h> 

    void foo(char buf[]) 
    { 
     buf[0] = 'A'; 
    } 

    int main(int argc, char *argv[]) 
    { 
     char buf[10]; 

     buf[0] = 'B'; 
     printf("before foo | buf[0] = %c\n", buf[0]); 
     foo(buf); 
     printf("after foo | buf[0] = %c\n", buf[0]); 

     system("PAUSE"); 
     return 0; 
     } 

đầu ra:

before foo | buf[0] = 'B' 
after foo | buf[0] = 'A' 

Trả lời

12
void foo(char buf[]) 

cũng giống như

void foo(char* buf) 

Khi bạn gọi nó, foo(buf), bạn vượt qua một con trỏ bởi giá trị, do đó, một bản sao của con trỏ được thực hiện.

Bản sao của con trỏ trỏ đến cùng một đối tượng như con trỏ ban đầu (hoặc, trong trường hợp này, với thành phần ban đầu của mảng).

C không được chuyển qua ngữ nghĩa tham chiếu theo nghĩa C++ đã chuyển qua ngữ nghĩa tham chiếu. Mọi thứ trong C đều được truyền theo giá trị. Con trỏ được sử dụng để vượt qua bằng ngữ nghĩa tham chiếu.

+0

Nhờ tất cả mọi người, đặc biệt là James, cho câu trả lời. Tại sao mọi người sử dụng gọi theo giá trị nếu nó không thay đổi giá trị của đầu vào? –

+0

Bởi vì bạn có thể cần một giá trị của một cái gì đó nhưng không cần phải thay đổi nó. – Javier

+2

@Kevin: Vì chức năng có thể chỉ cần giá trị. Chức năng mà không có tác dụng phụ dễ dàng hơn để lý luận (không phải là bạn sẽ nhận được rất xa bằng cách sử dụng C như là một ngôn ngữ lập trình chức năng), và đi qua bởi giá trị loại bỏ tác dụng phụ có thể. –

1

* char buf [] thực sự có nghĩa là char **, do đó bạn đang đi ngang qua con trỏ/tham khảo. Điều đó cho bạn biết rằng buf là một con trỏ, cả hai trong hàm chính() và foo().

1

Vì bạn đang chuyển một con trỏ đến buf (theo giá trị). Vì vậy, nội dung được chỉ dẫn bởi buf được thay đổi.

4

một mảng chỉ là một cách ưa thích để sử dụng con trỏ. Khi bạn vượt qua buf cho hàm, bạn đang chuyển một con trỏ theo giá trị, nhưng khi bạn dereference con trỏ, bạn vẫn tham chiếu chuỗi nó trỏ đến.

0

để chuyển giá trị đó theo giá trị, hàm sẽ cần biết kích thước của đối số. Trong trường hợp này, bạn chỉ cần đi qua một con trỏ.

1

Với con trỏ, nó khác nhau; bạn đang đi qua giá trị, nhưng những gì bạn đang vượt qua là giá trị của con trỏ, mà không giống như giá trị của mảng.

Vì vậy, giá trị của con trỏ không thay đổi, nhưng bạn đang sửa đổi những gì nó trỏ đến.

2

Array như tham số chức năng tương đương với một con trỏ, vì vậy việc kê khai

void foo(char buf[]); 

cũng giống như

void foo(char* buf); 

Đối số mảng là sau đó phân rã đến con trỏ đến phần tử đầu tiên của mình .

0

Bạn đang chuyển qua tham chiếu tại đây. Trong ví dụ này, bạn có thể giải quyết vấn đề bằng cách chuyển một char đơn lẻ vào chỉ mục của mảng mong muốn.

Nếu bạn muốn bảo toàn nội dung của mảng ban đầu, bạn có thể sao chép chuỗi đó vào bộ nhớ tạm thời trong hàm.

chỉnh sửa: Điều gì sẽ xảy ra nếu bạn gói mảng char trong cấu trúc và chuyển cấu trúc?Tôi tin rằng có thể làm việc quá, mặc dù tôi không biết loại phí trên có thể tạo ra ở cấp độ trình biên dịch.

0

xin lưu ý một điều,

khai

void foo(char buf[]) 

nói, mà sẽ được sử dụng [] ký hiệu. Không phải thành phần nào của mảng bạn sẽ sử dụng.

nếu bạn muốn chỉ rằng, bạn muốn nhận được một số giá trị cụ thể, sau đó bạn nên khai báo hàm này như

void foo(char buf[X]); //where X would be a constant. 

Tất nhiên nó không phải là có thể, bởi vì nó sẽ là vô dụng (chức năng cho hoạt động tại phần tử thứ n của mảng?). Bạn không cần phải ghi lại thông tin mà phần tử của mảng bạn muốn nhận. Tất cả những gì bạn cần là đơn giản tuyên bố:

voi foo(char value); 

như vậy ...

void foo(char buf[]) 

là một tuyên bố mà nói mà ký hiệu bạn muốn sử dụng ([] - phần), và nó cũng chứa con trỏ đến Một số dữ liệu.

Hơn nữa ... những gì bạn mong chờ ... bạn gửi đến chức năng foo một tên của mảng

foo(buf); 

tương đương với & buf [0]. Vì vậy ... đây là một con trỏ.

0

Mảng trong C không được truyền theo giá trị. Chúng thậm chí không phải là các tham số chức năng hợp pháp. Thay vào đó, trình biên dịch thấy rằng bạn đang cố gắng truyền một mảng và hạ cấp nó thành con trỏ. Nó âm thầm bởi vì nó là điều xấu. Nó cũng thích đá chó con.

Sử dụng mảng trong tham số chức năng là cách tốt để báo hiệu cho người dùng API của bạn rằng điều này phải là khối bộ nhớ được chia thành khối có kích thước n-byte, nhưng không mong đợi trình biên dịch quan tâm nếu bạn đánh vần char *foochar foo[] hoặc char foo[12] trong tham số chức năng. Họ sẽ không.

2

Mảng được xử lý khác với các loại khác; bạn không thể vượt qua một mảng "bởi giá trị" trong C.

Online C99 standard (draft n1256), phần 6.3.2.1, "lvalues, mảng, và định danh chức năng", đoạn 3:

Trừ khi nó là toán hạng của toán tử sizeof hoặc toán tử & đơn nhất, hoặc là chuỗi ký tự được sử dụng để khởi tạo mảng, biểu thức có loại '' mảng kiểu '' là được chuyển đổi thành biểu thức có loại '' con trỏ thành loại '' trỏ đến yếu tố ban đầu của đối tượng mảng và không phải là một giá trị. Nếu đối tượng mảng có lớp lưu trữ đăng ký, hành vi là không xác định.

Trong cuộc gọi

foo(buf); 

biểu thức mảng buf không phải là toán hạng của sizeof hoặc &, cũng không phải là một chuỗi chữ được sử dụng để khởi tạo một mảng, vì vậy nó được ngầm chuyển đổi ("phân rã ") từ loại" mảng 10 phần tử của char "thành" con trỏ tới char "và địa chỉ của phần tử đầu tiên được chuyển tới foo. Do đó, bất kỳ điều gì bạn làm với buf trong foo() sẽ được phản ánh trong mảng buf trong main(). Do cách xác định chỉ số mảng, bạn có thể sử dụng toán tử subscript trên kiểu con trỏ để nó trông giống như bạn đang làm việc với một loại mảng, nhưng bạn thì không.

Trong bối cảnh của một tuyên bố tham số chức năng, T a[]T a[N] là đồng nghĩa với T *a, nhưng đây là chỉ trường hợp đó là sự thật.

1

mảng và con trỏ (gần như) giống nhau.

int* foo = malloc(...) 

foo[2] cũng giống như *(foo+2*sizeof(int))

giai thoại: bạn đã viết

int main(int argc, char *argv[]) 

nó cũng là hợp pháp (sẽ biên dịch và làm việc như nhau) để viết

int main(int argc, char **argv) 

và cũng

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

chúng có hiệu quả giống nhau. hơi phức tạp hơn một chút, bởi vì một mảng biết có bao nhiêu phần tử, và con trỏ thì không. nhưng chúng được sử dụng giống nhau.

Các vấn đề liên quan