Các biến của tôi được lưu trong C ở đâu trong bộ nhớ?


155

Bằng cách xem xét rằng bộ nhớ được chia thành bốn phân đoạn: data, heap, stack và code, trong đó các biến toàn cục, biến tĩnh, kiểu dữ liệu không đổi, biến cục bộ (được xác định và khai báo trong hàm), biến (trong hàm chính), con trỏ và không gian được phân bổ động (sử dụng malloc và calloc) có được lưu trong bộ nhớ không?

Tôi nghĩ rằng họ sẽ được phân bổ như sau:

  • Biến toàn cục -------> dữ liệu
  • Biến tĩnh -------> dữ liệu
  • Các kiểu dữ liệu không đổi -----> mã
  • Biến cục bộ (được khai báo và định nghĩa trong hàm) --------> stack
  • Các biến được khai báo và định nghĩa trong hàm chính -----> heap
  • Con trỏ (ví dụ char *arr,, int *arr) -------> heap
  • Không gian được phân bổ động (sử dụng malloc và calloc) --------> stack

Tôi chỉ đề cập đến các biến này từ quan điểm C.

Xin hãy sửa tôi nếu tôi sai vì tôi là người mới C.


4
Các loại không được lưu trữ trong bộ nhớ.

5
mainchỉ là một chức năng khác. Các biến đi vào ngăn xếp trừ khi mallocgiống như ở nơi khác.
simonc

4
con trỏ được (thường) được lưu trữ trên ngăn xếp. Bộ nhớ họ trỏ đến (thường phân bổ thông qua malloc / calloc) là (thường) trên heap.
jpm

3
không gian được phân bổ động (sử dụng malloc, calloc) --------> heap
One Man crew

3
các biến được khai báo và định nghĩa trong hàm chính -----> stack
One Man crew

Câu trả lời:


216

Bạn có một số trong những điều này đúng, nhưng bất cứ ai đã viết các câu hỏi lừa bạn ít nhất một câu hỏi:

  • biến toàn cục -------> dữ liệu (chính xác)
  • biến tĩnh -------> dữ liệu (chính xác)
  • kiểu dữ liệu không đổi -----> mã và / hoặc dữ liệu. Xem xét chuỗi ký tự cho một tình huống khi một hằng số sẽ được lưu trữ trong phân đoạn dữ liệu và các tham chiếu đến nó sẽ được nhúng trong mã
  • biến cục bộ (được khai báo và định nghĩa trong hàm) --------> stack (đúng)
  • các biến được khai báo và định nghĩa trong mainhàm -----> heap cũng stack (giáo viên đang cố lừa bạn)
  • con trỏ (ví dụ : char *arr, int *arr) -------> heap data hoặc stack, tùy thuộc vào ngữ cảnh. C cho phép bạn khai báo toàn cầu hoặc một staticcon trỏ, trong trường hợp đó, chính con trỏ sẽ kết thúc trong phân đoạn dữ liệu.
  • tự động phân bổ không gian (sử dụng malloc, calloc, realloc) --------> đống đống

Điều đáng nói là "stack" được gọi chính thức là "lớp lưu trữ tự động".


6
Cũng đáng nói rằng heap chính thức không được gọi là gì cả. Bộ nhớ được phân bổ đến từ một nơi nào đó, không có tên trong tiêu chuẩn cho "nơi nào đó".
Steve Jessop

6
Trên một số hệ thống, (cụ thể là Linux và * BSD) cũng allocacó hoạt động tương tự malloc, nhưng phân bổ ngăn xếp.
Andreas Grapentin

Biến const được khai báo bên trong một phương thức đi đâu?
Mahori

@Ravi Cùng một nơi với các hằng số đi (điểm # 3 ở trên).
dasblinkenlight

Tôi đang sử dụng GCC 4.8.1 và dường như không lưu trữ biến const cục bộ thành chính trong phân đoạn DATA. Dưới đây là bản đồ mã và bộ nhớ cho 3 chương trình như vậy: Mã 1: int main (void) {// char a [10] = "HELLO"; // 1 // const char a [10] = "HELLO"; // 2 trả về 0; } BẢN ĐỒ NHỚ NHỚ: TRÊN dữ liệu văn bản 7280 1688 1040 10008 2718 a.exe
Mahori

123

Đối với những khách truy cập trong tương lai có thể quan tâm đến việc biết về các phân đoạn bộ nhớ đó, tôi đang viết những điểm quan trọng về 5 phân đoạn bộ nhớ trong C:

Một số người đứng đầu:

  1. Bất cứ khi nào một chương trình C được thực thi, một số bộ nhớ được cấp phát trong RAM để thực hiện chương trình. Bộ nhớ này được sử dụng để lưu trữ mã được thực thi thường xuyên (dữ liệu nhị phân), các biến chương trình, v.v ... Các phân đoạn bộ nhớ dưới đây nói về cùng:
  2. Thông thường có ba loại biến:
    • Biến cục bộ (còn được gọi là biến tự động trong C)
    • Biến toàn cầu
    • Biến tĩnh
    • Bạn có thể có các biến tĩnh toàn cục hoặc tĩnh cục bộ, nhưng ba biến trên là các kiểu cha.

5 phân đoạn bộ nhớ trong C:

1. Phân đoạn mã

  • Đoạn mã, còn được gọi là đoạn văn bản, là vùng bộ nhớ chứa mã được thực thi thường xuyên.
  • Đoạn mã thường chỉ đọc để tránh rủi ro bị ghi đè bởi các lỗi lập trình như tràn bộ đệm, v.v.
  • Đoạn mã không chứa các biến chương trình như biến cục bộ ( còn được gọi là biến tự động trong C ), biến toàn cục, v.v.
  • Dựa trên việc triển khai C, đoạn mã cũng có thể chứa các chuỗi ký tự chỉ đọc. Ví dụ: khi bạn thực hiện printf("Hello, world")thì chuỗi "Xin chào, thế giới" sẽ được tạo trong đoạn mã / văn bản. Bạn có thể xác minh điều này bằng cách sử dụng sizelệnh trong Linux OS.
  • đọc thêm

Phân đoạn dữ liệu

Phân đoạn dữ liệu được chia thành hai phần bên dưới và thường nằm bên dưới vùng heap hoặc trong một số triển khai phía trên ngăn xếp, nhưng phân đoạn dữ liệu không bao giờ nằm ​​giữa vùng heap và stack.

2. Phân đoạn dữ liệu chưa được khởi tạo

  • Phân khúc này còn được gọi là bss .
  • Đây là phần bộ nhớ chứa:
    1. Các biến toàn cục chưa được khởi tạo (bao gồm các biến con trỏ)
    2. Các biến toàn cầu không khởi tạo .
    3. Các biến tĩnh cục bộ chưa được khởi tạo .
  • Bất kỳ biến cục bộ hoặc tĩnh cục bộ nào không được khởi tạo sẽ được lưu trữ trong phân đoạn dữ liệu chưa được khởi tạo
  • Ví dụ: biến toàn cục int globalVar;hoặc biến cục bộ tĩnh static int localStatic;sẽ được lưu trữ trong phân đoạn dữ liệu chưa được khởi tạo.
  • Nếu bạn khai báo một biến toàn cục và khởi tạo nó thành 0hoặc NULLsau đó, nó sẽ chuyển đến phân đoạn dữ liệu chưa được khởi tạo hoặc bss.
  • đọc thêm

3. Phân đoạn dữ liệu khởi tạo

  • Phân khúc này lưu trữ:
    1. Biến toàn cục khởi tạo (bao gồm cả biến con trỏ)
    2. Khởi tạo các biến toàn cục không đổi .
    3. Khởi tạo biến tĩnh cục bộ .
  • Ví dụ: biến toàn cục int globalVar = 1;hoặc biến cục bộ tĩnh static int localStatic = 1;sẽ được lưu trữ trong phân đoạn dữ liệu khởi tạo.
  • Phân đoạn này có thể được phân loại thành khu vực chỉ đọc khởi tạo và khu vực đọc ghi khởi tạo . Các biến toàn cục không đổi được khởi tạo sẽ đi vào vùng chỉ đọc được khởi tạo trong khi các biến có giá trị có thể được sửa đổi trong thời gian chạy sẽ đi trong vùng đọc ghi được khởi tạo .
  • Kích thước của phân đoạn này được xác định bởi kích thước của các giá trị trong mã nguồn của chương trình và không thay đổi khi chạy .
  • đọc thêm

4. Phân đoạn ngăn xếp

  • Phân đoạn ngăn xếp được sử dụng để lưu trữ các biến được tạo bên trong các hàm ( hàm có thể là hàm chính hoặc hàm do người dùng định nghĩa ), biến như
    1. Các biến cục bộ của hàm (bao gồm các biến con trỏ)
    2. Các đối số được truyền cho hàm
    3. Địa chỉ trả lại
  • Các biến được lưu trữ trong ngăn xếp sẽ bị xóa ngay sau khi thực hiện chức năng kết thúc.
  • đọc thêm

5. Phân đoạn đống

  • Phân khúc này là để hỗ trợ phân bổ bộ nhớ động. Nếu các lập trình viên muốn phân bổ một số bộ nhớ động sau đó trong C nó được thực hiện bằng cách sử dụng malloc, callochoặc reallocphương pháp.
  • Ví dụ, khi int* prt = malloc(sizeof(int) * 2)đó tám byte sẽ được phân bổ theo heap và địa chỉ bộ nhớ của vị trí đó sẽ được trả về và được lưu trữ trong ptrbiến. Các ptrbiến sẽ ở hai ngăn xếp hoặc phân đoạn dữ liệu tùy thuộc vào cách thức mà nó được khai báo / sử dụng.
  • đọc thêm

Không nên khởi tạo thay vì chưa khởi tạo trong 3. Phân đoạn dữ liệu được khởi tạo.
Suraj Jain

Re "được lưu trữ trong các phân đoạn dữ liệu chưa được khởi tạo" (nhiều trường hợp): Đỗ bạn có nghĩa là "được lưu trữ uninitialized trong phân đoạn dữ liệu" ?
Peter Mortensen

@PeterMortensen Ý tôi là cả hai thứ. "Bất kỳ biến cục bộ toàn cầu hoặc tĩnh nào không được khởi tạo sẽ được lưu trữ trong phân đoạn dữ liệu chưa được khởi tạo"
hagrawal

Làm thế nào chúng ta có thể có biến tĩnh toàn cầu trong C?

trong "một số người đứng đầu", tôi thấy điểm này "Bạn có thể có các biến tĩnh toàn cục hoặc tĩnh cục bộ, nhưng ba biến ở trên là các kiểu cha." trong đó u đề cập đến thuật ngữ "tĩnh toàn cầu". Quan điểm của tôi là một biến tĩnh không thể là toàn cầu. tức là, nếu bất kỳ biến nào phải là toàn cục thì nó có thể truy cập được cho đến khi hoàn tất thực hiện chương trình. Hãy giải thích và giúp đỡ nếu tôi sai.

11

Sửa câu sai của bạn

constant data types ----->  code //wrong

biến hằng cục bộ -----> stack

biến hằng toàn cầu khởi tạo -----> phân đoạn dữ liệu

biến hằng toàn cầu chưa được khởi tạo -----> bss

variables declared and defined in main function  ----->  heap //wrong

các biến được khai báo và định nghĩa trong hàm chính -----> stack

pointers(ex:char *arr,int *arr) ------->  heap //wrong

dynamically allocated space(using malloc,calloc) --------> stack //wrong

con trỏ (ví dụ: char * Array, int * Array) -------> kích thước của biến con trỏ đó sẽ nằm trong ngăn xếp.

Hãy xem xét rằng bạn đang phân bổ bộ nhớ của n byte (sử dụng mallochoặc calloc) một cách linh hoạt và sau đó tạo biến con trỏ để trỏ nó. Bây giờ các nbyte của bộ nhớ đang trong heap và biến con trỏ yêu cầu 4 byte (nếu máy 64 bit 8 byte) sẽ được xếp chồng để lưu trữ con trỏ bắt đầu của các nbyte của bộ nhớ.

Lưu ý: Biến con trỏ có thể trỏ bộ nhớ của bất kỳ phân đoạn nào.

int x = 10;
void func()
{
int a = 0;
int *p = &a: //Now its pointing the memory of stack
int *p2 = &x; //Now its pointing the memory of data segment
chat *name = "ashok" //Now its pointing the constant string literal 
                     //which is actually present in text segment.
char *name2 = malloc(10); //Now its pointing memory in heap
...
}

không gian được phân bổ động (sử dụng malloc, calloc) --------> heap


con trỏ có thể ở trong ngăn xếp hoặc đống (xem đặc biệt: con trỏ tới con trỏ)
tranh luận

@airza: Hiện đã cập nhật. Thông thường tôi chỉ cập nhật thông tin chi tiết đó :)
rashok

Trong bản đồ bộ nhớ sau đây, bạn có thể vui lòng chỉ ra đâu là stack và heap không? Tôi không chắc đây có phải là câu hỏi chính xác không vì stack và bộ nhớ có thể chỉ được áp dụng khi chạy. BẢN ĐỒ NHỚ: "dữ liệu văn bản bss dec hex tên tệp 7280 1688 1040 10008 2718 a.exe"
Mahori

7

Một kiến ​​trúc máy tính để bàn phổ biến phân chia bộ nhớ ảo của một quá trình trong một số phân đoạn :

  • Đoạn văn bản: chứa mã thực thi. Con trỏ lệnh lấy các giá trị trong phạm vi này.

  • Phân đoạn dữ liệu: chứa các biến toàn cục (tức là các đối tượng có liên kết tĩnh). Được phân chia trong dữ liệu chỉ đọc (như hằng chuỗi) và dữ liệu chưa được khởi tạo ("BSS").

  • Phân đoạn ngăn xếp: chứa bộ nhớ động cho chương trình, tức là cửa hàng miễn phí ("heap") và khung ngăn xếp cục bộ cho tất cả các luồng. Theo truyền thống, ngăn xếp C và đống C được sử dụng để phát triển thành phân khúc ngăn xếp từ hai đầu đối diện, nhưng tôi tin rằng thực tế đã bị bỏ vì nó quá không an toàn.

Chương trình AC thường đặt các đối tượng có thời lượng lưu trữ tĩnh vào phân đoạn dữ liệu, các đối tượng được phân bổ động trên kho lưu trữ miễn phí và các đối tượng tự động trên ngăn xếp cuộc gọi của luồng mà nó sống.

Trên các nền tảng khác, chẳng hạn như chế độ thực x86 cũ hoặc trên các thiết bị nhúng, mọi thứ rõ ràng có thể hoàn toàn khác nhau.


"Tôi tin rằng việc luyện tập đã bị từ bỏ vì quá không an toàn" - và khiến cho việc thực hiện các luồng không thể thực hiện được, vì sau đó bạn cần nhiều hơn một ngăn xếp cho mỗi chương trình và tất cả chúng không thể kết thúc :-)
Steve Jessop

@SteveJessop: Vâng, tôi cũng đã nghĩ vậy. Nhưng các chủ đề đã tồn tại trong một thời gian dài - tôi không biết liệu tất cả các ngăn xếp luồng cũng phát triển ngược lại, hoặc nếu chúng lớn lên như đống ... dù sao, ngày nay mọi thứ đều đi theo cùng một hướng và có bảo vệ trang.
Kerrek SB

6

Tôi chỉ đề cập đến các biến này từ quan điểm C.

Từ quan điểm của ngôn ngữ C , tất cả những gì quan trọng là phạm vi, phạm vi, liên kết và truy cập; chính xác cách các mục được ánh xạ tới các phân đoạn bộ nhớ khác nhau tùy thuộc vào việc triển khai riêng lẻ và điều đó sẽ khác nhau. Tiêu chuẩn ngôn ngữ không nói về phân đoạn bộ nhớ ở tất cả . Hầu hết các kiến ​​trúc hiện đại hành động chủ yếu theo cùng một cách; Các biến phạm vi khối và đối số hàm sẽ được phân bổ từ ngăn xếp, phạm vi tệp và biến tĩnh sẽ được phân bổ từ một phân đoạn dữ liệu hoặc mã, bộ nhớ động sẽ được phân bổ từ một đống, một số dữ liệu không đổi sẽ được lưu trữ trong các phân đoạn chỉ đọc , Vân vân.


3

Một điều người ta cần lưu ý về việc lưu trữ là quy tắc as-if . Trình biên dịch không bắt buộc phải đặt biến ở một vị trí cụ thể - thay vào đó, nó có thể đặt nó ở bất cứ nơi nào nó thích miễn là chương trình được biên dịch hoạt động như thể nó được chạy trong máy C trừu tượng theo quy tắc của máy C trừu tượng. Điều này áp dụng cho tất cả thời lượng lưu trữ . Ví dụ:

  • một biến không được truy cập tất cả có thể được loại bỏ hoàn toàn - nó không có lưu trữ ... ở bất cứ đâu. Ví dụ - xem làm thế nào có 42mã lắp ráp được tạo nhưng không có dấu hiệu của 404.
  • một biến có thời lượng lưu trữ tự động không có địa chỉ của nó được sử dụng không cần phải được lưu trữ trong bộ nhớ. Một ví dụ sẽ là một biến vòng lặp.
  • một biến có consthiệu quả hoặc constkhông cần thiết phải có trong bộ nhớ. Ví dụ - trình biên dịch có thể chứng minh rằng nó foocó hiệu quả constvà hướng dẫn sử dụng nó vào mã. barcó liên kết bên ngoài và trình biên dịch không thể chứng minh rằng nó sẽ không bị thay đổi bên ngoài mô-đun hiện tại, do đó nó không được nội tuyến.
  • một đối tượng được phân bổ mallockhông cần nằm trong bộ nhớ được phân bổ từ heap! Ví dụ - lưu ý cách mã không có lệnh gọi mallocvà giá trị 42 không bao giờ được lưu trong bộ nhớ, nó được giữ trong một thanh ghi!
  • do đó, một đối tượng đã được phân bổ mallocvà tham chiếu bị mất mà không giải phóng đối tượng mà free không cần phải rò rỉ bộ nhớ ...
  • đối tượng được phân bổ theo mallocnhu cầu không nằm trong vùng heap bên dưới break của chương trình ( sbrk(0)) trên Unixen ...

1

con trỏ (ví dụ: char * Array, int * Array) -------> heap

Không, họ có thể ở trên ngăn xếp hoặc trong phân đoạn dữ liệu. Họ có thể chỉ ra bất cứ nơi nào.


Các báo cáo về mainvà các biến được phân bổ động cũng sai
simonc

Không chỉ trên ngăn xếp hoặc phân đoạn dữ liệu. Hãy nghĩ về một con trỏ trỏ đến một mảng các con trỏ. Trong trường hợp này, các con trỏ trong mảng được lưu trữ trên heap.
Sebi2020

1
  • Biến / biến tự động ---> phần ngăn xếp
  • Các biến được phân bổ động ---> phần heap
  • Biến toàn cục khởi tạo -> phần dữ liệu
  • Biến toàn cục chưa được khởi tạo -> phần dữ liệu (bss)
  • Biến tĩnh -> phần dữ liệu
  • Hằng chuỗi -> phần văn bản / phần mã
  • Chức năng -> phần văn bản / phần mã
  • Mã văn bản -> phần văn bản / phần mã
  • Thanh ghi -> Thanh ghi CPU
  • Đầu vào dòng lệnh -> phần môi trường / dòng lệnh
  • Biến môi trường -> phần môi trường / dòng lệnh

Phần môi trường / dòng lệnh là gì? Họ có tồn tại trong Linux không?
Haoyuan Ge

-1

Các ví dụ có thể chạy tối thiểu của Linux với phân tích tháo gỡ

Vì đây là một chi tiết triển khai không được quy định bởi các tiêu chuẩn, chúng ta hãy xem trình biên dịch đang làm gì trên một triển khai cụ thể.

Trong câu trả lời này, tôi sẽ liên kết đến các câu trả lời cụ thể thực hiện phân tích hoặc cung cấp phân tích trực tiếp tại đây và tóm tắt tất cả các kết quả tại đây.

Tất cả các phiên bản này đều có các phiên bản Ubuntu / GCC khác nhau và kết quả có thể khá ổn định giữa các phiên bản, nhưng nếu chúng tôi tìm thấy bất kỳ biến thể nào, hãy chỉ định các phiên bản chính xác hơn.

Biến cục bộ bên trong hàm

Có thể là nó mainhoặc bất kỳ chức năng nào khác:

void f(void) {
    int my_local_var;
}

Như được hiển thị tại: <value được tối ưu hóa> có nghĩa là gì trong gdb?

  • -O0: cây rơm
  • -O3: đăng ký nếu chúng không tràn, chồng khác

Để biết động lực tại sao ngăn xếp tồn tại, hãy xem: Chức năng của các lệnh đẩy / pop được sử dụng trên các thanh ghi trong cụm x86 là gì?

Biến toàn cục và staticbiến hàm

/* BSS */
int my_global_implicit;
int my_global_implicit_explicit_0 = 0;

/* DATA */
int my_global_implicit_explicit_1 = 1;

void f(void) {
    /* BSS */
    static int my_static_local_var_implicit;
    static int my_static_local_var_explicit_0 = 0;

    /* DATA */
    static int my_static_local_var_explicit_1 = 1;
}

char *char c[]

Như được hiển thị tại: Các biến tĩnh được lưu trữ trong C và C ++ ở đâu?

void f(void) {
    /* RODATA / TEXT */
    char *a = "abc";

    /* Stack. */
    char b[] = "abc";
    char c[] = {'a', 'b', 'c', '\0'};
}

TODO sẽ chuỗi ký tự rất lớn cũng sẽ được đưa vào ngăn xếp? Hay là .data? Hay việc biên dịch thất bại?

Hàm đối số

void f(int i, int j);

Phải thông qua quy ước gọi có liên quan, ví dụ: https://en.wikipedia.org/wiki/X86_calling_conventions cho X86, trong đó chỉ định các thanh ghi cụ thể hoặc vị trí ngăn xếp cho từng biến.

Sau đó, như được hiển thị tại <value được tối ưu hóa> có nghĩa là gì trong gdb? , -O0sau đó nhét mọi thứ vào ngăn xếp, trong khi -O3cố gắng sử dụng các thanh ghi càng nhiều càng tốt.

Tuy nhiên, nếu chức năng được nội tuyến, chúng được xử lý giống như người dân địa phương thông thường.

const

Tôi tin rằng nó không có gì khác biệt vì bạn có thể đánh máy nó đi.

Ngược lại, nếu trình biên dịch có thể xác định rằng một số dữ liệu không bao giờ được ghi vào, về lý thuyết nó có thể đặt nó .rodatangay cả khi không phải là const.

Phân tích TODO.

Con trỏ

Chúng là các biến (chứa địa chỉ, là số), giống như tất cả các phần còn lại :-)

trung tâm thương mại

Câu hỏi không có nhiều ý nghĩa đối với malloc, vì malloclà một hàm và trong:

int *i = malloc(sizeof(int));

*i là một biến chứa địa chỉ, vì vậy nó thuộc trường hợp trên.

Về cách malloc hoạt động bên trong, khi bạn gọi nó là nhân Linux đánh dấu một số địa chỉ nhất định là có thể ghi trên cấu trúc dữ liệu bên trong của nó và khi chúng bị chương trình chạm vào ban đầu, một lỗi xảy ra và kernel cho phép các bảng trang, cho phép truy cập xảy ra mà không có segfaul: Làm thế nào để phân trang x86 hoạt động?

Tuy nhiên, xin lưu ý rằng về cơ bản, đây chính xác là những gì tòa nhà execchọc trời thực hiện khi bạn cố chạy một tệp thực thi: nó đánh dấu các trang mà nó muốn tải và viết chương trình ở đó, xem thêm: Làm thế nào để kernel có được tệp nhị phân thực thi chạy bên dưới linux? Ngoại trừ execcó một số hạn chế bổ sung về nơi tải đến (ví dụ: mã không thể di chuyển được ).

Các syscall chính xác sử dụng cho mallocmmapvào năm 2020 triển khai hiện đại, và trong quá khứ brkđã được sử dụng: Liệu malloc () sử dụng brk () hoặc mmap ()?

Thư viện động

Về cơ bản có được mmaped vào bộ nhớ: /unix/226524/what-system-call-is- used-to-load-lologists-in-linux / 462710 # 462710

biến envinroment và main'sargv

Trên ngăn xếp ban đầu: /unix/75939/where-is-the-envir-opes-actual-stored TODO tại sao không ở .data?

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.