Tôi đã thấy từ static
được sử dụng ở những nơi khác nhau trong mã C; đây có giống như một hàm / lớp tĩnh trong C # (nơi thực hiện được chia sẻ giữa các đối tượng) không?
Tôi đã thấy từ static
được sử dụng ở những nơi khác nhau trong mã C; đây có giống như một hàm / lớp tĩnh trong C # (nơi thực hiện được chia sẻ giữa các đối tượng) không?
Câu trả lời:
(1) là chủ đề xa lạ hơn nếu bạn là người mới, vì vậy đây là một ví dụ:
#include <stdio.h>
void foo()
{
int a = 10;
static int sa = 10;
a += 5;
sa += 5;
printf("a = %d, sa = %d\n", a, sa);
}
int main()
{
int i;
for (i = 0; i < 10; ++i)
foo();
}
Bản in này:
a = 15, sa = 15
a = 15, sa = 20
a = 15, sa = 25
a = 15, sa = 30
a = 15, sa = 35
a = 15, sa = 40
a = 15, sa = 45
a = 15, sa = 50
a = 15, sa = 55
a = 15, sa = 60
Điều này hữu ích cho các trường hợp trong đó một hàm cần giữ một số trạng thái giữa các lệnh và bạn không muốn sử dụng các biến toàn cục. Tuy nhiên, hãy cẩn thận, tính năng này nên được sử dụng một cách tiết kiệm - nó làm cho mã của bạn không an toàn cho chuỗi và khó hiểu hơn.
(2) Được sử dụng rộng rãi như một tính năng "kiểm soát truy cập". Nếu bạn có tệp .c triển khai một số chức năng, nó thường chỉ hiển thị một vài chức năng "công khai" cho người dùng. Các chức năng còn lại của nó nên được thực hiện static
để người dùng không thể truy cập chúng. Đây là đóng gói, một thực hành tốt.
Trích dẫn Wikipedia :
Trong ngôn ngữ lập trình C, static được sử dụng với các biến và hàm toàn cục để đặt phạm vi của chúng thành tệp chứa. Trong các biến cục bộ, static được sử dụng để lưu trữ biến trong bộ nhớ được cấp phát tĩnh thay vì bộ nhớ được cấp phát tự động. Mặc dù ngôn ngữ không ra lệnh thực hiện một trong hai loại bộ nhớ, bộ nhớ được cấp phát tĩnh thường được dành riêng trong phân đoạn dữ liệu của chương trình vào thời gian biên dịch, trong khi bộ nhớ được cấp phát tự động thường được triển khai như một ngăn xếp cuộc gọi tạm thời.
Và để trả lời câu hỏi thứ hai của bạn, nó không giống như trong C #.
Tuy nhiên, trong C ++, static
cũng được sử dụng để xác định các thuộc tính lớp (được chia sẻ giữa tất cả các đối tượng của cùng một lớp) và các phương thức. Trong C không có lớp, vì vậy tính năng này không liên quan.
.c
và một loạt các tệp tiêu đề, nhưng ma quỷ luôn ở trong những gì không điển hình.
Có một cách sử dụng nữa không được đề cập ở đây và đó là một phần của khai báo kiểu mảng như là một đối số cho hàm:
int someFunction(char arg[static 10])
{
...
}
Trong ngữ cảnh này, điều này xác định rằng các đối số được truyền cho hàm này phải là một mảng kiểu char
có ít nhất 10 phần tử trong đó. Để biết thêm thông tin xem câu hỏi của tôi ở đây .
arg[0]
thông qua để arg[9]
có giá trị (mà cũng có nghĩa là chức năng không chấp nhận một con trỏ null). Trình biên dịch có thể sử dụng thông tin này bằng cách nào đó để tối ưu hóa và các máy phân tích tĩnh có thể sử dụng thông tin này để đảm bảo rằng hàm không bao giờ được đưa ra một con trỏ null (hoặc nếu nó có thể cho biết, một mảng có ít phần tử hơn chỉ định).
static
trong C99. Nó đã hơn một thập kỷ rưỡi tuổi, nhưng không phải tất cả các nhà văn biên dịch đều chấp nhận tất cả các tính năng của C99 - vì vậy, C99 nói chung vẫn chưa được biết đến.
int arr[n];
thì đó là một VLA (mảng có độ dài thay đổi) , được thêm vào trong C99. Đó có phải ý của bạn?
Câu trả lời ngắn ... nó phụ thuộc.
Các biến cục bộ được xác định tĩnh không làm mất giá trị giữa các lệnh gọi hàm. Nói cách khác, chúng là các biến toàn cục, nhưng nằm trong phạm vi hàm cục bộ mà chúng được định nghĩa.
Các biến toàn cục tĩnh không hiển thị bên ngoài tệp C mà chúng được xác định.
Các hàm tĩnh không hiển thị bên ngoài tệp C mà chúng được xác định trong.
private
trong C, nhưng sự tương tự của bạn là tốt: tĩnh làm cho mọi thứ "riêng tư" với một tệp nhất định. Và các tệp trong C thường ánh xạ tới các lớp trong C ++.
Ví dụ phạm vi biến đa tệp
Ở đây tôi minh họa cách tĩnh ảnh hưởng đến phạm vi định nghĩa hàm trên nhiều tệp.
AC
#include <stdio.h>
/*
Undefined behavior: already defined in main.
Binutils 2.24 gives an error and refuses to link.
/programming/27667277/why-does-borland-compile-with-multiple-definitions-of-same-object-in-different-c
*/
/*int i = 0;*/
/* Works in GCC as an extension: https://stackoverflow.com/a/3692486/895245 */
/*int i;*/
/* OK: extern. Will use the one in main. */
extern int i;
/* OK: only visible to this file. */
static int si = 0;
void a() {
i++;
si++;
puts("a()");
printf("i = %d\n", i);
printf("si = %d\n", si);
puts("");
}
C chính
#include <stdio.h>
int i = 0;
static int si = 0;
void a();
void m() {
i++;
si++;
puts("m()");
printf("i = %d\n", i);
printf("si = %d\n", si);
puts("");
}
int main() {
m();
m();
a();
a();
return 0;
}
Biên dịch và chạy:
gcc -c a.c -o a.o
gcc -c main.c -o main.o
gcc -o main main.o a.o
Đầu ra:
m()
i = 1
si = 1
m()
i = 2
si = 2
a()
i = 3
si = 1
a()
i = 4
si = 2
Diễn dịch
si
, một biến cho mỗi tệpi
Như thường lệ, phạm vi càng nhỏ thì càng tốt, vì vậy luôn luôn khai báo các biến static
nếu bạn có thể.
Trong lập trình C, các tệp thường được sử dụng để đại diện cho "các lớp" và static
các biến đại diện cho các thành viên tĩnh riêng của lớp.
Những tiêu chuẩn nói về nó
C99 N1256 dự thảo 6.7.1 "Trình xác định lớp lưu trữ" nói rằng đó static
là "trình xác định lớp lưu trữ".
6.2.2 / 3 "Liên kết của định danh" cho biết static
ngụ ý internal linkage
:
Nếu khai báo của một định danh phạm vi tệp cho một đối tượng hoặc một hàm chứa định danh lớp lưu trữ tĩnh, thì định danh có liên kết bên trong.
và 6.2.2 / 2 nói rằng internal linkage
hành xử như trong ví dụ của chúng tôi:
Trong tập hợp các đơn vị dịch thuật và thư viện cấu thành toàn bộ chương trình, mỗi khai báo của một mã định danh cụ thể có liên kết bên ngoài biểu thị cùng một đối tượng hoặc chức năng. Trong một đơn vị dịch thuật, mỗi khai báo của một định danh có liên kết bên trong biểu thị cùng một đối tượng hoặc chức năng.
trong đó "đơn vị dịch là một tệp nguồn sau khi tiền xử lý.
GCC triển khai nó như thế nào cho ELF (Linux)?
Với sự STB_LOCAL
ràng buộc.
Nếu chúng tôi biên dịch:
int i = 0;
static int si = 0;
và tháo rời bảng biểu tượng với:
readelf -s main.o
đầu ra chứa:
Num: Value Size Type Bind Vis Ndx Name
5: 0000000000000004 4 OBJECT LOCAL DEFAULT 4 si
10: 0000000000000000 4 OBJECT GLOBAL DEFAULT 4 i
vì vậy sự ràng buộc là sự khác biệt đáng kể duy nhất giữa chúng. Value
chỉ là phần bù của họ vào .bss
phần này, vì vậy chúng tôi hy vọng nó sẽ khác.
STB_LOCAL
được ghi lại trên thông số ELF tại http://www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html :
Các biểu tượng cục bộ không thể nhìn thấy bên ngoài tệp đối tượng chứa định nghĩa của chúng. Các ký hiệu cục bộ cùng tên có thể tồn tại trong nhiều tệp mà không can thiệp lẫn nhau
mà làm cho nó một sự lựa chọn hoàn hảo để đại diện static
.
Các biến không có tĩnh là STB_GLOBAL
và thông số kỹ thuật cho biết:
Khi trình soạn thảo liên kết kết hợp một số tệp đối tượng có thể định vị lại, nó không cho phép nhiều định nghĩa của các ký hiệu STB_GLOBAL có cùng tên.
phù hợp với các lỗi liên kết trên nhiều định nghĩa không tĩnh.
Nếu chúng tôi tăng cường tối ưu hóa với -O3
,si
biểu tượng sẽ bị xóa hoàn toàn khỏi bảng biểu tượng: không thể sử dụng nó từ bên ngoài. TODO tại sao giữ các biến tĩnh trên bảng ký hiệu khi không có tối ưu hóa? Chúng có thể được sử dụng cho bất cứ điều gì? Có lẽ để gỡ lỗi.
Xem thêm
static
chức năng: https://stackoverflow.com/a/30319812/895245static
với extern
, cái "ngược lại": Làm cách nào để sử dụng extern để chia sẻ các biến giữa các tệp nguồn?Không gian tên ẩn danh C ++
Trong C ++, bạn có thể muốn sử dụng các không gian tên ẩn danh thay vì tĩnh, điều này đạt được hiệu ứng tương tự, nhưng ẩn thêm các định nghĩa kiểu: Không gian tên ẩn danh / ẩn danh so với các hàm tĩnh
Nó phụ thuộc:
int foo()
{
static int x;
return ++x;
}
Hàm sẽ trả về 1, 2, 3, v.v ... --- biến không nằm trên ngăn xếp.
static int foo()
{
}
Nó có nghĩa là chức năng này chỉ có phạm vi trong tệp này. Vì vậy, ac và bc có thể có foo()
s khác nhau và foo không được tiếp xúc với các đối tượng được chia sẻ. Vì vậy, nếu bạn đã xác định foo trong ac, bạn không thể truy cập nó từ b.c
hoặc từ bất kỳ nơi nào khác.
Trong hầu hết các thư viện C, tất cả các hàm "riêng tư" đều tĩnh và hầu hết "công khai" thì không.
Mọi người cứ nói rằng 'tĩnh' trong C có hai nghĩa. Tôi cung cấp một cách khác để xem nó mang lại cho nó một ý nghĩa duy nhất:
Lý do dường như có hai ý nghĩa là, trong C, mọi mục mà 'tĩnh' có thể được áp dụng đã có một trong hai thuộc tính này , vì vậy có vẻ như như việc sử dụng cụ thể đó chỉ liên quan đến mục đích khác.
Ví dụ, hãy xem xét các biến. Các biến được khai báo bên ngoài các hàm đã có tính bền vững (trong phân đoạn dữ liệu), do đó, việc áp dụng 'tĩnh' chỉ có thể làm cho chúng không hiển thị bên ngoài phạm vi hiện tại (đơn vị biên dịch). Ngược lại, các biến được khai báo bên trong các hàm đã không nhìn thấy được bên ngoài phạm vi (hàm) hiện tại, do đó, việc áp dụng 'tĩnh' chỉ có thể khiến chúng tồn tại lâu.
Áp dụng 'tĩnh' cho các chức năng cũng giống như áp dụng nó cho các biến toàn cục - mã nhất thiết phải tồn tại (ít nhất là trong ngôn ngữ), do đó chỉ có thể thay đổi mức độ hiển thị.
LƯU Ý: Những nhận xét này chỉ áp dụng cho C. Trong C ++, áp dụng 'tĩnh' cho các phương thức lớp thực sự mang lại cho từ khóa một ý nghĩa khác. Tương tự cho phần mở rộng đối số mảng C99.
static
cung cấp liên kết nội bộ cho một định danh.
Từ Wikipedia:
Trong ngôn ngữ lập trình C, static được sử dụng với các biến và hàm toàn cục để đặt phạm vi của chúng thành tệp chứa. Trong các biến cục bộ, static được sử dụng để lưu trữ biến trong bộ nhớ được cấp phát tĩnh thay vì bộ nhớ được cấp phát tự động. Mặc dù ngôn ngữ không ra lệnh thực hiện một trong hai loại bộ nhớ, bộ nhớ được cấp phát tĩnh thường được dành riêng trong phân đoạn dữ liệu của chương trình vào thời gian biên dịch, trong khi bộ nhớ được cấp phát tự động thường được triển khai như một ngăn xếp cuộc gọi tạm thời.
static
có nghĩa là những thứ khác nhau trong bối cảnh khác nhau.
Bạn có thể khai báo một biến tĩnh trong hàm C. Biến này chỉ hiển thị trong hàm tuy nhiên nó hoạt động như một toàn cục ở chỗ nó chỉ được khởi tạo một lần và nó vẫn giữ nguyên giá trị của nó. Trong ví dụ này, mỗi khi bạn gọi foo()
nó sẽ in số ngày càng tăng. Biến tĩnh chỉ được khởi tạo một lần.
void foo ()
{
static int i = 0;
printf("%d", i); i++
}
Một cách sử dụng tĩnh khác là khi bạn triển khai một hàm hoặc biến toàn cục trong tệp .c nhưng không muốn biểu tượng của nó hiển thị bên ngoài .obj
tệp được tạo bởi tệp. ví dụ
static void foo() { ... }
Nếu bạn khai báo một biến trong hàm tĩnh, giá trị của nó sẽ không được lưu trên ngăn xếp lệnh gọi hàm và vẫn sẽ khả dụng khi bạn gọi lại hàm.
Nếu bạn khai báo một biến toàn cục tĩnh, phạm vi của nó sẽ bị giới hạn trong tệp mà bạn đã khai báo. Điều này là an toàn hơn một chút so với toàn cầu thông thường có thể được đọc và sửa đổi trong toàn bộ chương trình của bạn.
Tôi ghét phải trả lời một câu hỏi cũ, nhưng tôi không nghĩ có ai đã đề cập đến cách K & R giải thích nó trong phần A4.1 của "Ngôn ngữ lập trình C".
Nói tóm lại, từ tĩnh được sử dụng với hai nghĩa:
static
từ khóa (nhấn mạnh lớn vào nó được sử dụng trong mã làm từ khóa) được sử dụng với một khai báo, nó cung cấp cho liên kết nội bộ đối tượng đó để nó chỉ có thể được sử dụng trong đơn vị dịch thuật đó. Nhưng nếu từ khóa được sử dụng trong một hàm, nó sẽ thay đổi lớp lưu trữ của đối tượng (dù sao đối tượng sẽ chỉ hiển thị trong hàm đó). Đối lập với tĩnh là extern
từ khóa, cung cấp cho một liên kết bên ngoài đối tượng.Peter Van Der Linden đưa ra hai ý nghĩa này trong "Lập trình C chuyên gia":
register
một công cụ xác định lớp lưu trữ (C99 6.7.1 Trình xác định lớp lưu trữ). Và nó không chỉ là một gợi ý, ví dụ bạn không thể áp dụng địa chỉ của toán tử &
trên một đối tượng với lớp lưu trữ register
bất kể trình biên dịch có phân bổ một thanh ghi hay không.
Trong C, static có hai nghĩa, tùy thuộc vào phạm vi sử dụng của nó. Trong phạm vi toàn cầu, khi một đối tượng được khai báo ở cấp tệp, điều đó có nghĩa là đối tượng đó chỉ hiển thị trong tệp đó.
Ở bất kỳ phạm vi nào khác, nó khai báo một đối tượng sẽ giữ giá trị của nó giữa các thời điểm khác nhau mà phạm vi cụ thể được nhập. Ví dụ: nếu một int được hủy trong một thủ tục:
void procedure(void)
{
static int i = 0;
i++;
}
giá trị của 'i' được khởi tạo về 0 trong lần gọi đầu tiên tới thủ tục và giá trị được giữ lại mỗi lần tiếp theo thủ tục được gọi. nếu 'i' được in, nó sẽ tạo ra một chuỗi 0, 1, 2, 3, ...
Điều quan trọng cần lưu ý là các biến tĩnh trong các hàm được khởi tạo ở lần nhập đầu tiên vào hàm đó và tồn tại ngay cả sau khi cuộc gọi của chúng kết thúc; trong trường hợp các hàm đệ quy, biến tĩnh chỉ được khởi tạo một lần và vẫn tồn tại trên tất cả các cuộc gọi đệ quy và ngay cả sau khi cuộc gọi của hàm kết thúc.
Nếu biến đã được tạo bên ngoài hàm, điều đó có nghĩa là lập trình viên chỉ có thể sử dụng biến trong tệp nguồn mà biến đã được khai báo.
Nếu bạn khai báo điều này trong một mytest.c
tệp:
static int my_variable;
Sau đó, biến này chỉ có thể được nhìn thấy từ tập tin này. Các biến không thể được xuất bất cứ nơi nào khác.
Nếu bạn khai báo bên trong một hàm, giá trị của biến sẽ giữ giá trị của nó mỗi khi hàm được gọi.
Một chức năng tĩnh không thể được xuất từ bên ngoài tệp. Vì vậy, trong một *.c
tệp, bạn đang ẩn các hàm và các biến nếu bạn khai báo chúng tĩnh.
Các biến tĩnh trong C có thời gian tồn tại của chương trình.
Nếu được định nghĩa trong một hàm, chúng có phạm vi cục bộ, tức là chúng chỉ có thể được truy cập bên trong các hàm đó. Giá trị của các biến tĩnh được bảo toàn giữa các lệnh gọi hàm.
Ví dụ:
void function()
{
static int var = 1;
var++;
printf("%d", var);
}
int main()
{
function(); // Call 1
function(); // Call 2
}
Trong chương trình trên, var
được lưu trữ trong phân đoạn dữ liệu. Cuộc đời của nó là toàn bộ chương trình C.
Sau chức năng gọi 1, var
trở thành 2. Sau chức năng gọi 2, var
trở thành 3.
Giá trị của var
không bị phá hủy giữa các lệnh gọi hàm.
Nếu var
có giữa biến không tĩnh và biến cục bộ, nó sẽ được lưu trữ trong phân đoạn ngăn xếp trong chương trình C. Vì khung ngăn xếp của hàm bị hủy sau khi hàm trả về, giá trị của var
cũng bị hủy.
Các biến tĩnh khởi tạo được lưu trữ trong phân đoạn dữ liệu của chương trình C trong khi các biến chưa được khởi tạo được lưu trữ trong phân đoạn BSS.
Một thông tin khác về tĩnh: Nếu một biến là toàn cục và tĩnh, nó có thời gian tồn tại của chương trình C, nhưng nó có phạm vi tệp. Nó chỉ được nhìn thấy trong tập tin đó.
Để thử điều này:
static int x;
int main()
{
printf("Accessing in same file%d", x):
}
extern int x;
func()
{
printf("accessing in different file %d",x); // Not allowed, x has the file scope of file1.c
}
run gcc -c file1.c
gcc -c file2.c
Bây giờ hãy thử liên kết chúng bằng cách sử dụng:
gcc -o output file1.o file2.o
Nó sẽ đưa ra một lỗi liên kết vì x có phạm vi tệp của file1.c và trình liên kết sẽ không thể giải quyết tham chiếu đến biến x được sử dụng trong file2.c.
Người giới thiệu:
static int var = 1;
thay đổi giá trị trở lại mỗi lần
Biến tĩnh là một biến đặc biệt mà bạn có thể sử dụng trong một hàm và nó lưu dữ liệu giữa các cuộc gọi và nó không xóa nó giữa các cuộc gọi. Ví dụ:
void func(){
static int count; // If you don't declare its value, the value automatically initializes to zero
printf("%d, ", count);
++count;
}
void main(){
while(true){
func();
}
}
Đầu ra:
0, 1, 2, 3, 4, 5, ...
printf("%d, ", count); count++;
bằng `printf ("% d, ", đếm ++) (không quan trọng: P).
Có 2 trường hợp:
(1) Các biến cục bộ được khai báo static
: Được phân bổ trong phân đoạn dữ liệu thay vì ngăn xếp. Giá trị của nó được giữ lại khi bạn gọi lại hàm.
(2) Các biến hoặc hàm toàn cục được khai báo static
: Vô hình bên ngoài đơn vị biên dịch (tức là các ký hiệu cục bộ trong bảng ký hiệu trong khi liên kết).
Biến tĩnh có đặc tính bảo toàn giá trị của chúng ngay cả sau khi chúng nằm ngoài phạm vi của chúng! Do đó, biến tĩnh bảo toàn giá trị trước đó trong phạm vi trước của chúng và không được khởi tạo lại trong phạm vi mới.
Nhìn vào ví dụ này - Một biến int tĩnh vẫn còn trong bộ nhớ trong khi chương trình đang chạy. Một biến bình thường hoặc tự động bị hủy khi một hàm gọi nơi biến được khai báo kết thúc.
#include<stdio.h>
int fun()
{
static int count = 0;
count++;
return count;
}
int main()
{
printf("%d ", fun());
printf("%d ", fun());
return 0;
}
Điều này sẽ xuất ra: 1 2
Khi 1 ở lại trong bộ nhớ vì nó được khai báo là tĩnh
Biến tĩnh (như biến toàn cục) được khởi tạo là 0 nếu không được khởi tạo rõ ràng. Ví dụ trong chương trình dưới đây, giá trị của x được in là 0, trong khi giá trị của y là thứ gì đó rác. Xem điều này để biết thêm chi tiết.
#include <stdio.h>
int main()
{
static int x;
int y;
printf("%d \n %d", x, y);
}
Điều này sẽ xuất ra: 0 [some_garbage_value]
Đây là những vấn đề chính tôi thấy không được giải thích ở trên cho một người mới!
Trong lập trình C, static
là một từ khóa dành riêng kiểm soát cả đời cũng như khả năng hiển thị. Nếu chúng ta khai báo một biến là tĩnh bên trong một hàm thì nó sẽ chỉ hiển thị trong suốt hàm đó. Trong cách sử dụng này, thời gian tồn tại của biến tĩnh này sẽ bắt đầu khi một hàm gọi và nó sẽ hủy sau khi thực hiện hàm đó. bạn có thể xem ví dụ sau:
#include<stdio.h>
int counterFunction()
{
static int count = 0;
count++;
return count;
}
int main()
{
printf("First Counter Output = %d\n", counterFunction());
printf("Second Counter Output = %d ", counterFunction());
return 0;
}
Chương trình trên sẽ cung cấp cho chúng tôi Kết quả này:
First Counter Output = 1
Second Counter Output = 1
Bởi vì ngay khi chúng ta gọi hàm, nó sẽ khởi tạo count = 0
. Và trong khi chúng ta thực thi counterFunction
nó sẽ phá hủy biến đếm.