Tại sao ANSI C không có không gian tên?


93

Có không gian tên dường như không có trí tuệ đối với hầu hết các ngôn ngữ. Nhưng theo như tôi có thể nói, ANSI C không hỗ trợ nó. Tại sao không? Có kế hoạch đưa nó vào một tiêu chuẩn trong tương lai không?


13
Sử dụng C ++ làm C-with-namespace!
AraK

3
Tất nhiên là tôi có thể, nhưng tôi vẫn muốn biết
Pulkit Sinha

5
2 điều. Cú pháp phân biệt không cần thiết: Tất cả các ngôn ngữ khác có không gian tên chỉ cần sử dụng '.' như dấu phân cách vì nó không mơ hồ với các cách sử dụng khác của '.'. Và, nghiêm trọng hơn, c ++ chưa bao giờ giới thiệu một chỉ thị có phạm vi sử dụng. Điều đó có nghĩa là các lập trình viên đã lạm dụng việc sử dụng các lệnh để nhập không gian tên vào phạm vi toàn cầu. Điều đó có nghĩa là ủy ban tiêu chuẩn c ++ bây giờ không thể thêm các tính năng mới vào std :: bao giờ hết vì số lượng mã sẽ bị hỏng do đó đã làm cho phân vùng dư thừa.
Chris Becke

2
@Chris Becke: Tôi thích cú pháp đặc biệt. Tôi muốn biết liệu tôi đang xem một lớp trong không gian tên hay một thành viên trong lớp.
JeremyP

6
@ChrisBecke, đã muộn một vài năm, nhưng thật thú vị khi bạn cho rằng không gian tên C ++ được triển khai kém, vì vậy chúng không nên được triển khai trong C. Sau đó, bạn lưu ý rằng các ngôn ngữ khác triển khai chúng mà không cần treo C ++. Nếu các ngôn ngữ khác có thể làm được, tại sao không giới thiệu chúng với C?
weberc2

Câu trả lời:


68

C có không gian tên. Một cho các thẻ cấu trúc và một cho các loại khác. Hãy xem xét định nghĩa sau:

struct foo
{
    int a;
};

typedef struct bar
{
    int a;
} foo;

Cái đầu tiên có tag foo, và cái sau được tạo thành type foo với typedef. Vẫn không có đụng độ tên tuổi nào xảy ra. Điều này là do các thẻ và kiểu cấu trúc (kiểu cài sẵn và kiểu định sẵn) sống trong các vùng tên riêng biệt.

Điều mà C không cho phép là tạo không gian tên mới theo ý muốn. C đã được chuẩn hóa trước khi điều này được coi là quan trọng trong một ngôn ngữ và việc thêm không gian tên cũng sẽ đe dọa khả năng tương thích ngược, vì nó yêu cầu tính năng quản lý tên để hoạt động đúng. Tôi nghĩ rằng điều này có thể là do kỹ thuật, không phải triết học.

CHỈNH SỬA: May mắn thay, JeremyP đã sửa cho tôi và đề cập đến những không gian tên mà tôi đã bỏ qua. Có không gian tên cho nhãn và cho cấu trúc / đoàn viên.


8
Thực tế có nhiều hơn hai không gian tên. Ngoài hai điều bạn đề cập, có một không gian tên cho nhãn và không gian tên cho các thành viên của mỗi struct và union.
JeremyP

@JeremyP: Rất cám ơn bạn đã chỉnh sửa. Tôi chỉ viết này ra khỏi bộ nhớ, tôi đã không kiểm tra các tiêu chuẩn :-)

2
những gì về không gian tên cho các chức năng?
themihai

8
Đây có thể được gọi là không gian tên, nhưng tôi tin rằng đây không phải là loại không gian tên mà OP đã hỏi.
avl_sweden

1
@jterm Không. Tôi không ủng hộ việc hack các tính năng C, chỉ đơn thuần nêu sự thật. Mỗi structđịnh nghĩa khai báo một không gian tên mới cho các thành viên của nó. Tôi không ủng hộ việc khai thác sự thật đó, cũng như không biết về bất kỳ phương tiện khai thác nào vì structkhông thể có các thành viên tĩnh.
JeremyP

100

Để hoàn thiện, có một số cách để đạt được "lợi ích" mà bạn có thể nhận được từ không gian tên, trong C.

Một trong những phương pháp yêu thích của tôi là sử dụng cấu trúc chứa một loạt các con trỏ phương thức là giao diện cho thư viện của bạn / v.v.

Sau đó, bạn sử dụng một phiên bản bên ngoài của cấu trúc này mà bạn khởi tạo bên trong thư viện của mình trỏ đến tất cả các hàm của bạn. Điều này cho phép bạn giữ cho tên của mình đơn giản trong thư viện của bạn mà không cần bước vào không gian tên máy khách (ngoại trừ biến extern ở phạm vi toàn cục, 1 biến so với hàng trăm phương thức ..)

Có một số bảo trì bổ sung liên quan nhưng tôi cảm thấy rằng nó là tối thiểu.

Đây là một ví dụ:

/* interface.h */

struct library {
    const int some_value;
    void (*method1)(void);
    void (*method2)(int);
    /* ... */
};

extern const struct library Library;
/* interface.h */

/* interface.c */
#include "interface.h"

void method1(void)
{
   ...
}
void method2(int arg)
{
   ...
}

const struct library Library = {
    .method1 = method1,
    .method2 = method2,
    .some_value = 36
};
/* end interface.c */

/* client code */
#include "interface.h"

int main(void)
{
    Library.method1();
    Library.method2(5);
    printf("%d\n", Library.some_value);
    return 0;
}
/* end */

Việc sử dụng. cú pháp tạo ra một liên kết mạnh mẽ qua phương thức Library_ function () Library_some_value cổ điển. Tuy nhiên, có một số hạn chế, bạn không thể sử dụng macro làm hàm.


12
... và các trình biên dịch có đủ thông minh để "bỏ qua" con trỏ hàm tại thời điểm biên dịch không library.method1()?
einpoklum

1
Việc này thật là tuyệt. Tôi có thể nói thêm một điều, tôi đang cố gắng làm cho tất cả các hàm trong .ctệp của mình ở trạng thái tĩnh theo mặc định, do đó, các hàm duy nhất được hiển thị là những hàm được hiển thị rõ ràng trong const structđịnh nghĩa trong .ctệp.
lastmjs

3
Đó là một ý tưởng tuyệt vời, nhưng làm thế nào để bạn đối phó với hằng số và enum?
nowox

1
@einpoklum - xin lỗi bạn, nhưng ít nhất kể từ phiên bản 6.3.0, gcc sẽ tính toán địa chỉ thực của function1/ method2khi biên dịch với cả -O2-flto. Trừ khi bạn biên dịch các thư viện như vậy cùng với nguồn của riêng bạn, cách tiếp cận này sẽ thêm một số chi phí vào các lệnh gọi hàm của nó.
Alex Reinking

3
@AlexReinking: Chà, điều đó thật tuyệt, nhưng chúng tôi sẽ không bao giờ có được những hàm này được nội tuyến. Và - hoại tử là rất tốt, không cần xin lỗi.
einpoklum

25

C có không gian tên. Cú pháp là namespace_name. Bạn thậm chí có thể lồng chúng vào general_specific_name. Và nếu bạn muốn có thể truy cập vào tên mà không cần viết ra tên vùng tên mọi lúc, hãy bao gồm các macro tiền xử lý có liên quan trong tệp tiêu đề, ví dụ:

#define myfunction mylib_myfunction

Điều này rõ ràng hơn rất nhiều so với việc xáo trộn tên và các hành vi tàn bạo khác mà một số ngôn ngữ nhất định cam kết cung cấp không gian tên.


24
Tôi thấy nó khác. Việc phức tạp hóa ngữ pháp, giới thiệu tên bị xáo trộn trên các ký hiệu, v.v. để đạt được điều gì đó vốn đã tầm thường đối với bộ tiền xử lý là những gì tôi sẽ gọi là một cuộc tấn công bẩn thỉu và thiết kế kém.
R .. GitHub DỪNG TRỢ GIÚP NGAY LÚC NÀY

41
Tôi không thấy làm thế nào bạn có thể thực sự hỗ trợ vị trí đó. Hỏi cộng đồng Javascript về việc tích hợp các dự án khi mọi hệ thống khác đều có một phương pháp hack cây nhà lá vườn khác nhau để triển khai không gian tên. Tôi chưa bao giờ nghe ai phàn nàn về từ khóa 'không gian tên' hoặc từ khóa 'gói' thêm quá nhiều phức tạp vào ngôn ngữ của họ. Mặt khác, cố gắng gỡ lỗi mã rải rác với macro có thể trở nên nhanh chóng!
weberc2

5
Tôi đã nghe rất nhiều người phàn nàn về việc xáo trộn tên C ++ (từ quan điểm gỡ lỗi, chuỗi công cụ, khả năng tương thích ABI, tra cứu ký hiệu động, ...) và sự phức tạp của việc không biết một tên cụ thể thực sự đang đề cập đến cái gì.
R .. GitHub DỪNG TRỢ GIÚP NGAY LÚC NÀY

6
@R .. Điều đó sẽ không xảy ra nếu tên mangling trong C ++ được chuẩn hóa. Chỉ điều này sẽ không giúp ích gì với khả năng tương thích ABI, nhưng chắc chắn sẽ khắc phục được sự cố ánh xạ tên.
Malcolm

20
Tôi thấy thật khó chịu khi những người C thực sự sẽ tranh luận điều này một cách thẳng thắn. Có rất nhiều tính năng trong C ++ với các cạnh sắc nhọn khiến người ta đau lòng. Không gian tên không phải là một trong những tính năng đó. Chúng thật tuyệt, chúng hoạt động rất tốt. Và không có gì là tầm thường với bộ tiền xử lý, đối với bản ghi. Cuối cùng, việc gỡ bỏ các tên là không đáng kể, có rất nhiều tiện ích dòng lệnh sẽ làm điều đó cho bạn.
Nir Friedman

12

Về mặt lịch sử, các trình biên dịch C không trộn tên (chúng làm được trên Windows, nhưng việc trộn lẫn cho cdeclquy ước gọi chỉ bao gồm thêm tiền tố gạch dưới).

Điều này giúp bạn dễ dàng sử dụng thư viện C từ các ngôn ngữ khác (bao gồm cả trình hợp dịch) và là một trong những lý do tại sao bạn thường thấy extern "C"trình bao bọc cho các API C ++.


2
Nhưng tại sao đó lại là một vấn đề như vậy? Ý tôi là, giả sử tất cả các tên không gian tên sẽ bắt đầu bằng _da13cd6447244ab9a30027d3d0a08903 và sau đó là tên (Đó là UUID v4 mà tôi vừa tạo)? Có một cơ hội là điều này có thể phá vỡ các tên sử dụng UUID cụ thể này, nhưng cơ hội đó về cơ bản là 0. Vì vậy, trong thực tế sẽ không có vấn đề khi xử lý only_namespace_names .
einpoklum

7

chỉ là lý do lịch sử. không ai nghĩ đến việc có một cái gì đó giống như một không gian tên vào thời điểm đó. Ngoài ra, họ đã thực sự cố gắng giữ cho ngôn ngữ đơn giản. Họ có thể có nó trong tương lai


2
Có bất kỳ phong trào nào trong ủy ban tiêu chuẩn để thêm không gian tên vào C trong tương lai không? Có thể với việc chuyển sang mô-đun C / C ++, điều này có thể làm cho nó dễ dàng hơn trong tương lai?
lanoxx

1
@lanoxx Không có ý muốn thêm không gian tên vào C vì lý do tương thích ngược.
themihai

6

Không phải là một câu trả lời, nhưng không phải là một bình luận. C không cung cấp cách xác định namespacerõ ràng. Nó có phạm vi thay đổi. Ví dụ:

int i=10;

struct ex {
  int i;
}

void foo() {
  int i=0;
}

void bar() {
  int i=5;
  foo();
  printf("my i=%d\n", i);
}

void foobar() {
  foo();
  bar();
  printf("my i=%d\n", i);
}

Bạn có thể sử dụng tên đủ điều kiện cho các biến và hàm:

mylib.h

void mylib_init();
void mylib_sayhello();

Sự khác biệt duy nhất với không gian tên là bạn không thể có usingvà không thể nhập from mylib.


Bạn cũng không thể thay thế hai dòng cuối cùng namespace mylib { void init(); void say_hello(); }cũng quan trọng (ish).
einpoklum

3

ANSI C được phát minh trước khi có không gian tên.


10
Đó là? Thông số kỹ thuật ANSI C đầu tiên là năm 1989. Tôi khá chắc chắn rằng không gian tên (ở dạng này hay dạng khác) đã có trong ngôn ngữ lập trình trước đó. Ví dụ, Ada được chuẩn hóa vào năm 1983 và có các gói là không gian tên. Đến lượt nó, về cơ bản dựa trên mô-đun Modula-2.
CHỈ LÀ Ý KIẾN CHÍNH XÁC CỦA TÔI

4
Tôi sẽ không xác định ngày phát minh ra ANSI C là khi thông số kỹ thuật của nó chính thức được thông qua; ngôn ngữ đã tồn tại từ trước và thông số kỹ thuật chỉ ghi lại những gì đã có ở đó. Mặc dù từ một số câu trả lời trên trang web này, người ta có thể nghĩ rằng thông số kỹ thuật có trước và trình biên dịch đầu tiên là suy nghĩ sau.
Crashworks

ANSI C đã có một số khác biệt đáng kể so với trước ANSI C, nhưng không gian tên không phải là một trong số đó.
dan04

Trong khi đó, tôi sẽ viết nó vào năm 2020, sau khi không gian tên ra đời. Các tiêu chuẩn C mới nhất vẫn không có chúng. Nhiều khi C có ý nghĩa, đây là một tính năng bị thiếu rất nhiều.

3

Bởi vì những người muốn thêm khả năng này vào C đã không tập hợp và tổ chức để gây áp lực lên nhóm tác giả trình biên dịch và các cơ quan ISO.


1
Tôi nghĩ rằng chúng ta sẽ thấy không gian tên trong C chỉ khi những người này tự tổ chức và tạo (các) tiện ích mở rộng có hỗ trợ không gian tên. Sau đó, các cơ quan ISO sẽ không có lựa chọn nào khác ngoài việc đưa công bố chúng thành tiêu chuẩn (với ít nhiều thay đổi). Đó là cách javascript (có một số điểm tương đồng với C về mặt này) đã làm điều đó.
themihai

3
@themihai: "create a extension" = yêu cầu những người gcc và clang biên dịch không gian tên.
einpoklum

1

C không hỗ trợ không gian tên như C ++. Việc triển khai các không gian tên trong C ++ mang tên. Cách tiếp cận được nêu dưới đây cho phép bạn có được lợi ích của không gian tên trong C ++ trong khi có tên không bị xáo trộn. Tôi nhận ra rằng bản chất của câu hỏi là tại sao C không hỗ trợ không gian tên (và một câu trả lời tầm thường sẽ là không vì nó không được triển khai :)). Tôi chỉ nghĩ rằng nó có thể giúp ai đó biết cách tôi đã triển khai chức năng của các mẫu và không gian tên.

Tôi đã viết một hướng dẫn về cách tận dụng lợi thế của không gian tên và / hoặc mẫu bằng cách sử dụng C.

Không gian tên và mẫu trong C

Không gian tên và mẫu trong C (sử dụng Danh sách được Liên kết)

Đối với không gian tên cơ bản, người ta có thể chỉ cần đặt trước tên không gian tên như một quy ước.

namespace MY_OBJECT {
  struct HANDLE;
  HANDLE *init();
  void destroy(HANDLE * & h);

  void do_something(HANDLE *h, ... );
}

có thể được viết như

struct MY_OBJECT_HANDLE;
struct MY_OBJECT_HANDLE *my_object_init();
void my_object_destroy( MY_OBJECT_HANDLE * & h );

void my_object_do_something(MY_OBJECT_HANDLE *h, ... );

Cách tiếp cận thứ hai mà tôi cần sử dụng khái niệm không gian tên và mẫu là sử dụng nối macro và bao gồm. Ví dụ: tôi có thể tạo

template<T> T multiply<T>( T x, T y ) { return x*y }

sử dụng các tệp mẫu như sau

Multi-template.h

_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y);

Multi-template.c

_multiply_type_ _multiply_(multiply)( _multiply_type_ x, _multiply_type_ y) {
  return x*y;
}

Bây giờ chúng ta có thể định nghĩa int_multiply như sau. Trong ví dụ này, tôi sẽ tạo một tệp int_multiply.h / .c.

int_multiply.h

#ifndef _INT_MULTIPLY_H
#define _INT_MULTIPLY_H

#ifdef _multiply_
#undef _multiply_
#endif
#define _multiply_(NAME) int ## _ ## NAME 

#ifdef _multiply_type_
#undef _multiply_type_
#endif
#define _multiply_type_ int 

#include "multiply-template.h" 
#endif

int_multiply.c

#include "int_multiply.h"
#include "multiply-template.c"

Khi kết thúc tất cả những điều này, bạn sẽ có một hàm và tệp tiêu đề cho.

int int_multiply( int x, int y ) { return x * y }

Tôi đã tạo một hướng dẫn chi tiết hơn về các liên kết được cung cấp cho thấy cách nó hoạt động với các danh sách được liên kết. Hy vọng rằng điều này sẽ giúp ai đó!


3
Các liên kết của bạn giải thích cách thêm không gian tên. Tuy nhiên, câu hỏi đặt ra là tại sao không gian tên không được hỗ trợ. Vì vậy, câu trả lời này không phải là một câu trả lời và thay vào đó nên là một nhận xét.
Thomas Weller
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.