2012-04-26 26 views
5

Khi tôi biên dịch và thực thi mã này liên tục trong một vài lần, nó báo cáo địa chỉ của cc là 0x0012FF5C. Nhưng khi tôi cố gắng in ra chuỗi tại địa chỉ đó bằng cách sử dụng cuộc gọi thứ hai để printf trong foo, nó in rác thay vì in ra "Hello" ?? Tại sao như vậy?? Nếu tôi biết địa chỉ nằm trong không gian địa chỉ của ứng dụng (ít nhất là cho đến khi tôi không khởi động lại máy tính của mình hoặc bắt đầu một số ứng dụng khác đòi hỏi nhiều không gian và nguyên nhân) ứng dụng của tôi được phân trang) ??Tại sao chúng ta không thể sử dụng địa chỉ trực tiếp trong mã c hoặc C++?

void foo(char *cc[]) 
{ 
    printf("%x\n",cc); 
    printf("%s\n",(char *)(0x0012FF5C)); 
} 

int main() 
{ 
    char *c[] = {"Hello","World"}; 
    foo(c); 
} 
+2

Tại sao bạn muốn thực hiện điều này vì bạn chỉ bảo hiểm rủi ro cho cược của bạn mà cùng một địa chỉ sẽ được sử dụng? Thực hiện một chút thay đổi và bạn phá vỡ mã.Có vẻ như điều ngớ ngẩn để làm hoặc thậm chí suy ngẫm. –

+0

Vui lòng không xóa câu hỏi được trả lời. Ai đó cố gắng trả lời câu hỏi của bạn. –

Trả lời

2

Vị trí bộ nhớ của quá trình của bạn có thể đã thay đổi giữa cả hai lần thực hiện. Bạn không thể làm điều đó, bạn đang cố gắng để đọc một địa chỉ không được phân bổ bởi quá trình của bạn. Hơn nữa, tôi có nhiều cảnh báo:

test.c: Trong hàm 'foo': test.c: 6: warning 'format'% x 'mong muốn gõ' unsigned int ', nhưng đối số 2 có kiểu' char * * '

test.c: 7: cảnh báo: định dạng '% s' hy vọng gõ 'char *', nhưng lập luận 2 có kiểu 'int'

test.c: 9: cảnh báo: định dạng' % x 'hy vọng loại' unsigned int ', nhưng đối số 2 có loại' char ** '

12

Vì không có gì trong tiêu chuẩn C hoặc C++ để đảm bảo điều đó. Những địa chỉ này có thể có thể dự đoán được tùy thuộc vào trình biên dịch/HĐH của bạn, nhưng không được tính vào nó.

#include <stdio.h> 

int main(void) { 
    char s[] = "okay"; 
    printf("%p", (void*)s); 
    return 0; 
} 

Tôi nhận được địa chỉ khác mỗi lần (gcc trên linux). Không sử dụng "literals địa chỉ";)

Process không gian địa chỉ trên hệ điều hành hiện đại được chọn ngẫu nhiên đối với an ninh trên từng thực hiện:

http://en.wikipedia.org/wiki/Address_space_layout_randomization

+0

Bạn nên bỏ trống * địa chỉ của bạn là tiêu chuẩn. – md5

+1

Hmmph. -Wall -pedantic nói không có gì nhưng bạn là đúng, C99 dự thảo nói cho fprintf "p Đối số sẽ là một con trỏ để void". Đã chỉnh sửa. – delicateLatticeworkFever

+0

@Kirilenko: Không có chuyển đổi ngầm từ 'T *' thành 'void *' cho bất kỳ –

3

Tôi không nghĩ rằng bạn (0x0012FF5C) đại diện cho poiter. Hãy thử (char *) (0x0012FF5C).

Bên cạnh đó, như cách khác cho bạn biết, điều này không có giá trị thực tế, vì không có chuỗi bảo đảm nào sẽ được đặt tại địa chỉ đó mọi lần chạy. Đây không phải là một số lắp ráp nhúng, nơi bạn nói đặt chuỗi này ở vị trí này và sau đó sử dụng địa chỉ trực tiếp.

2

khi tôi biết rằng địa chỉ

Vâng, bạn không thực sự biết gì về địa chỉ khi bạn cứng mã hóa nó, bạn đoán vào nó.

Có rất nhiều lý do tại sao địa chỉ của một thứ có thể thay đổi giữa các lần thực thi cùng một chương trình. Thậm chí còn có nhiều lý do khiến địa chỉ có thể thay đổi nếu bạn đang thay đổi mã (khi bạn có vẻ đang làm ở đây).

2

Như ai đó đã nói trước đó, vị trí mã của bạn trong bộ nhớ, có thể khác nhau giữa các lần thực hiện và điều tương tự cũng xảy ra với các vị trí của biến. Hãy thử chạy mã này. Nó in địa chỉ thực tế của biến bạn trỏ vào trong bộ nhớ. Bạn sẽ thấy rằng nhiều lần thực thi mang lại các địa chỉ hoàn toàn khác nhau.Hãy luôn nhớ điều đó!

#include <stdio.h> 

void foo(char *cc[]) 
{ 
    printf("%x\n",cc); 
    printf("%s\n",(0x0012FF5C)); //this line will fail, there is no guarantee to that 
    cc++; 
    printf("%x\n",cc); 
} 

int main() 
{ 
    char *c[] = {"Hello","World"}; 
    printf("c array location: %p",c); //print location 
    foo(c); 
} 
5

OK, điều đầu tiên là, không bao giờ cho rằng nhiều hành quyết sẽ trả lại địa chỉ cùng

Now. cho phép bạn nói rằng bạn may mắn và có cùng địa chỉ. Lưu ý rằng cc là một mảng con trỏ. Và bạn đang gửi địa chỉ cơ sở của mảng này. Bạn cần phải gửi giá trị của phần tử đầu tiên của mảng

Hãy thử điều này, và nếu bạn may mắn nó hoạt động,

printf("%s\n",*((char**)(0x0012FF5C))); 
+0

cũng ... giá trị của địa chỉ cơ sở và giá trị của phần tử đầu tiên sẽ giống nhau. – user1232138

+1

@ user1232138 Kiểm tra chỉnh sửa của tôi! ** Địa chỉ cơ sở và giá trị không giống nhau. Dereferencing địa chỉ Base và giá trị tại địa chỉ cơ sở giống nhau ** –

4

Nếu bạn chạy chương trình của bạn trên một máy mà không virtual memorymemory protection, bạn rất có thể sẽ thành công. Những công nghệ/tính năng này là lý do tại sao nó không hoạt động.

Mỗi quy trình có không gian địa chỉ ảo riêng, địa chỉ của chúng được dịch sang địa chỉ phần cứng theo số memory-management unit của bộ xử lý.

6

Bởi vì printf đầu tiên cung cấp cho bạn địa chỉ của một mảng char * khi printf thứ hai cố gắng in mảng char * này thành chuỗi.

+0

Yep, cc cần phải được dereferenced để có được con trỏ đến đầu của chuỗi thực tế. –

1

Điều đầu tiên, printf("%x\n",cc); không in địa chỉ cc. Tốt nhất là nó in giá trị cc, nhưng hành vi không xác định vì bạn đã chuyển đối số loại sai cho định dạng %x. Nó hy vọng unsigned int, bạn đã cung cấp một con trỏ.

Hành vi có khả năng nhất là nó sẽ in phần ít quan trọng nhất giá trị cc (do đó trên máy 32 bit, nó sẽ hoạt động, trong khi trên máy 64 bit bạn sẽ không thấy toàn bộ địa chỉ nhà). Nhưng nó có thể đi sai theo nhiều cách khác nhau.

Điều thứ hai, cc đã gõ char**, và do đó giá trị của cc không phải là một con trỏ tới ký tự đầu tiên của một chuỗi, đó là một con trỏ đến phần tử đầu tiên của mảng c từ main. Định dạng %s mong đợi một con trỏ đến ký tự đầu tiên của chuỗi. Vì vậy, ngay cả khi giá trị của cc thực sự là 0x0012FF5C, hãy chuyển giá trị đó đến printf với định dạng %s là sai.

"Rác" bạn đang thấy là một nỗ lực để in dữ liệu con trỏ từ mảng con trỏ đó, như thể nó là dữ liệu ký tự thuộc về một chuỗi. Nó không phải là.

0

Tôi đã đơn giản hóa ví dụ của bạn một chút. Nếu bạn biết địa chỉ của chuỗi, bạn có thể đọc từ địa chỉ đó.

#include <stdio.h> 

void foo(char cc[]) 
{ 
    int i; 
    printf("%p\n",cc); 
    printf("Type the address you would like to read:"); 
    scanf ("%x",&i); 
    printf("Character value at your address: %c\n",*((char *)(i))); 
} 

int main() 
{ 
    char c[] = "Hello"; 
    foo(c); 
} 

Điều này sẽ cho phép bạn đọc từ địa chỉ bạn chỉ định từ dòng lệnh. Trước tiên nó sẽ in ra địa chỉ cơ sở của mảng ký tự, vì vậy bạn biết nơi để tìm chuỗi.

Ví dụ:

$ ./a.out 
0xbfcaaf3a 
Type the address you would like to read:bfcaaf3d 
Character value at your address: l 

Đúng như dự đoán này in bức thư thứ tư của Hello.

+0

Tại sao chúng ta không thể sử dụng địa chỉ đó để in toàn bộ chuỗi? – user1232138

+0

@ user1232138: Bạn có thể. Thay đổi dòng cuối cùng của foo thành 'printf (" Giá trị ký tự tại địa chỉ của bạn:% s \ n ", (char *) (i));' – Lucas

+0

Đã thử nhưng không hoạt động ... – user1232138

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