Có bất kỳ hướng dẫn thường được chấp nhận về cách viết C hiện đại?


13

Tôi có nền tảng Java / Groovy mạnh mẽ và tôi đã được chỉ định vào một nhóm duy trì cơ sở mã C khá lớn cho một phần mềm quản trị.

Một số điểm đau, như xử lý blob trong cơ sở dữ liệu hoặc tạo báo cáo trong PDF và Excel đã được đưa ra ngoài dịch vụ web java.

Tuy nhiên, là một nhà phát triển Java, tôi hơi bối rối bởi một số khía cạnh của mã:

  • đó là dài dòng (đặc biệt là khi xử lý 'ngoại lệ')
  • có rất nhiều phương thức lớn (nhiều phương pháp hơn 2000 dòng)
  • không có cấu trúc dữ liệu nâng cao (tôi nhớ Danh sách, Bộ và Bản đồ rất nhiều)
  • không tách rời mối quan tâm (SQL được trộn vui vẻ xung quanh mã)

Kết quả là tôi cảm thấy việc kinh doanh bị ẩn giấu trong hàng tấn mã kỹ thuật và bộ não của tôi, được định hình bằng Định hướng đối tượng và một chút lập trình Chức năng, không hề dễ dàng.

Mặt tốt của dự án là mã thẳng về phía trước: không có khung, không có thao tác mã byte khi chạy, không có AOP. Và máy chủ có thể đồng thời trả lời cho hơn 10000 người dùng với một máy bằng cách sử dụng ít bộ nhớ hơn java cần phải nhổ "hello world".

Tôi muốn học cách viết mã C phù hợp với các nguyên tắc hiện đại thường được chấp nhận. Có bất kỳ nguyên tắc thường được chấp nhận về cách viết và cấu trúc C hiện đại không?

Một cái gì đó hơi giống với cuốn sách 'Java hiệu quả', nhưng đối với C.

Chỉnh sửa theo ánh sáng của câu trả lời và ý kiến:

  • Tôi sẽ cố gắng điều chỉnh suy nghĩ của mình thành mã C và không cố gắng phản chiếu nó thành OOP.
  • Tôi đã bắt đầu quét-đọc các hướng dẫn về kiểu mã hóa được đề xuất từ ​​nhận xét (Tiêu chuẩn mã hóa GNU và Kiểu mã hóa hạt nhân Linux).
  • Sau đó tôi sẽ cố gắng đề xuất kiểu mã này cho các đồng nghiệp của tôi. Phần khó nhất có thể là thuyết phục đồng nghiệp rằng phương pháp khổng lồ có thể được chia thành các phần nhỏ hơn và việc lặp lại cùng 4 dòng mã xử lý lỗi có thể tránh được nhờ sự trợ giúp của phương pháp.

5
Có phải ứng dụng thực sự cần hiện đại hóa, hay bạn chỉ nghĩ nó như vậy bởi vì cách nó được viết là không quen thuộc?
Blrfl


1
@Blrfl, tôi cảm thấy rằng ứng dụng đã được viết với tiêu chuẩn lỗi thời. Tôi chỉ muốn biết tiêu chuẩn hôm nay (2016) cho (hành chính) C. Nếu có. Tôi không muốn cấu trúc lại hoặc định hình lại ứng dụng hiện tại, tôi muốn có ý tưởng về cách tôi nên viết phần tiếp theo của mã.
Guillaume


3
@antlersoft: Một hàm 2.000 dòng thực hiện một danh sách dài các thứ đơn giản, từng cái một, hoàn toàn không có vấn đề gì và không cần một lời bào chữa. Vui lòng không trả lời bằng các đối số tròn như "bạn không nên viết 2.000 hàm dòng vì bạn không nên viết 2.000 hàm dòng".
gnasher729

Câu trả lời:


14

Tôi có thể đọc từ câu hỏi của bạn, vấn đề không phải là mã cũ C mà chỉ là lập trình xấu . Hầu hết các vấn đề bạn đã đề cập như tính dài dòng, các hàm dòng 2000+ lớn hoặc không có sự phân tách mối quan tâm nào có thể áp dụng cho bất kỳ ngôn ngữ nào, như C hoặc Java.

Độ dài đã được đề cập trong bối cảnh xử lý lỗi. Bạn đã không cung cấp một ví dụ để tôi chỉ có thể nhắc rằng mã xử lý lỗi cũng là mã . Không có lý do cho các phần lặp đi lặp lại của mã soạn sẵn. Yếu tố nó ra; hoặc đến một chức năng hoặc (nếu không có giá trị để tạo một chức năng riêng biệt) thực hiện goto Error;mô hình và di chuyển xử lý lỗi và dọn dẹp tài nguyên đến một Error:phần ở dưới cùng của chức năng.

Nếu chuyển lỗi lên chuỗi cuộc gọi dường như là vấn đề, hãy tự hỏi: chức năng trên đó có thực sự cần biết một số anh chàng nhỏ ở đây có vấn đề không? Các cơ chế ngoại lệ được tích hợp trong một ngôn ngữ giúp bạn dễ dàng thực hiện điều đó, nhưng nói chung, tốt hơn là xử lý sớm các ngoại lệ (bằng bất kỳ ngôn ngữ nào) để điều kiện lỗi không làm ô nhiễm logic của mã cấp cao. Và nếu chức năng trên đó thực sự cần phải biết, có nhiều cách để mô phỏng các ngoại lệ với setjmplongjmp.

Tôi nghĩ rằng vấn đề thực sự duy nhất liên quan đến C được đề cập là thiếu container tiêu chuẩn. Mặc dù Setnói chung có thể được thay thế bằng một mảng được sắp xếp và Map(đối với hầu hết các phần) bằng một mảng các cặp hoặc một struct(nếu bạn biết khóa được đặt trước, map[key] = valuebiến thành s.key = value), nhưng thực tế là không có bộ chứa mảng động trong tiêu chuẩn thư viện. Trong C99, bạn ít nhất có thể khai báo một mảng có chiều dài thay đổi trên stack ( int array[len]) nhưng bạn cần tính toán lentrước (thường không khó) và tất nhiên bạn không thể trả về nó như bất kỳ đối tượng được phân bổ stack nào. Hầu hết các dự án kết thúc bằng việc viết lên bộ chứa mảng động của riêng họ hoặc chấp nhận một nguồn mở.

Trong một lưu ý cuối cùng, tôi muốn chỉ ra rằng tôi đã ở đó. Tôi đã từng là lập trình viên Java chuyển sang C ++ và thuần C. Tôi muốn khuyên "hãy đọc cuốn sách X để học C tốt" nhưng không có gì giống như không có Java. Cách để đi tiếp là tiếp thu tất cả những nét đặc sắc của ngôn ngữ và thư viện tiêu chuẩn; google rất nhiều, đọc nhiều và viết mã nhiều cho đến khi bạn bắt đầu nghĩ về C. Cố gắng viết những thứ bằng C như trong Java cũng khó chịu như cố gắng viết một câu bằng tiếng nước ngoài với những từ được dịch trực tiếp từ mẹ bạn lưỡi; cả bạn và người đọc sẽ co rúm lại. Tin tốt là học lập trình tốt thì chậm nhưng học ngôn ngữ khác thì nhanh. Vì vậy, nếu bạn viết mã tốt trong Java,


1
Tất cả trong tất cả, đây là một câu trả lời thực sự tốt. Tôi chỉ phản đối việc xem setjmp()/ longjmp()như một công cụ hợp lệ: Nó thậm chí không cố gắng thực hiện bất kỳ việc dọn dẹp nào. Mọi phân bổ sẽ bị rò rỉ, mọi khóa bị giữ sẽ không được phát hành, mọi tệp đã mở sẽ không bị đóng và mọi sự không nhất quán về dữ liệu sẽ trở thành vĩnh viễn. IMHO, cặp chức năng này về cơ bản là hack tồi tệ nhất từng được phát minh, với lý do duy nhất là có thể thực hiện nó. Cuối cùng, thực sự chỉ có một cách hợp lệ để xử lý lỗi trong mã C: mã lỗi rõ ràng.
cmaster - phục hồi monica

@cmaster phải. Cá nhân, setjmp/longjmpcó vẻ như một con cá ra khỏi nước trong C và tôi không bao giờ sử dụng chúng. Tôi cảm thấy buộc phải bao gồm chúng chỉ vì có rất nhiều hướng dẫn / thư viện trên internet để bắt chước các ngoại lệ, vì vậy tôi nghĩ rằng có những người thực sự sử dụng nó.
Một con cú

7

Mặt tốt của dự án là mã thẳng về phía trước: không có khung, không có thao tác mã byte khi chạy, không có AOP. Và máy chủ có thể đồng thời trả lời cho hơn 10000 người dùng với một máy bằng cách sử dụng ít bộ nhớ hơn java cần phải nhổ "hello world".

Tôi khuyên bạn nên thận trọng về việc liệu điều này có xứng đáng với thời gian của bạn và tiền của công ty để dành tài nguyên cho việc "hiện đại hóa" một phần mềm hoạt động với độ phức tạp mã thấp và hoạt động tốt. Có một khả năng cao mà bạn sẽ tự giới thiệu các lỗi mới, đặc biệt vì đây dường như là một hệ thống mà bạn không quen thuộc.

Nếu bạn vẫn muốn đi theo tuyến đường đó thì tôi sẽ đề xuất như sau:

  • Tạo (hoặc tạo) sơ đồ trạng thái của phần mềm / mã
  • Đi sâu vào mã và lập danh sách các phần phức tạp hoặc quan trọng nhất của mã tương ứng
  • Tìm ai đó am hiểu về cơ sở mã đó và hỏi họ tại sao nó được xây dựng theo cách này và những gì đã được biết là gây ra vấn đề
  • Viết tài liệu từ những gì bạn đã học

Tại thời điểm này, bạn sẽ quyết định xem điều này có đáng để khám phá hay không. Nếu văn hóa của công ty bạn không thưởng cho thất bại thì hãy bật đèn xanh từ cấp cao hơn hoặc người quản lý.

  • Sắp xếp các khối xây dựng khác nhau của phần mềm và viết các bài kiểm tra đơn vị cho từng phần.
  • Lặp đi lặp lại cho đến khi bạn có thể dán các mô-đun khác nhau lại với nhau
  • Thực hiện kiểm tra thêm mô phỏng tương tác người dùng thực (kiểm tra căng thẳng, v.v.)

Tôi nghĩ đó là một bản đồ đường đi khá tốt và đưa bạn đến bất cứ nơi nào bạn cần. Không biết chi tiết cụ thể của dự án này, thật khó để giúp bạn nhiều. Xin đừng loại bỏ từ chối trách nhiệm của tôi là báo động quá mức. Hàng tấn lập trình viên xuất sắc đã đánh bại bụi bằng cách cố gắng viết lại một dự án hiện có sang ngôn ngữ yêu thích của họ hoặc sử dụng các công cụ "hiện đại". Đó là một quyết định phải được suy nghĩ cẩn thận và tôi khuyên bạn không nên lừa đảo và tự mình làm điều đó mà không có sự hỗ trợ quản lý hoặc hỗ trợ từ các đồng nghiệp của bạn.


2
Tôi nhận ra rằng câu hỏi của tôi không rõ ràng chút nào. Tôi không muốn cấu trúc lại mã. Ở tất cả. Tôi muốn duy trì cơ sở mã hiện có theo cách nó được. Tuy nhiên tôi muốn học cách viết C hiện đại cho tính năng mới. Và ở đây tôi bị lạc. Hầu hết các tài liệu tôi tìm thấy là về cách viết mã bằng C, không phải về cách viết 'hiện đại' C. Có lẽ không có thứ nào giống như 'hiện đại' C ...
Guillaume

1

Nếu bạn muốn một ngôn ngữ cấp cao hơn, có một số ngôn ngữ như C ++ hoặc Objective-C có thể được trộn với mã C khá dễ dàng.

Ngoài ra, C và C ++ tương thích hợp lý. Bạn có thể chỉ cần biên dịch toàn bộ cơ sở mã là C ++ với một vài thay đổi - bạn sẽ có biến thường xuyên có tên là "lớp" hoặc "mẫu" mà bạn cần đổi tên, nhưng thực tế đó sẽ là tất cả. (sizeof ('a') khác nhau ở C và C ++, nhưng tôi không nghĩ mình đã từng sử dụng nó).

Nếu bạn đi theo con đường đó, hãy cân nhắc rằng người bảo trì tiếp theo có thể không quá thông thạo C ++. Đừng mang đi. Tận dụng lợi thế của C ++, nhưng chỉ đến nay một lập trình viên C mới có thể dễ dàng hiểu được ý nghĩa của nó.


1
Tôi phải phản đối ở điểm này. C và C ++ là các ngôn ngữ riêng biệt và một số mã được yêu cầu bởi trình biên dịch C ++ (truyền rõ ràng giá trị trả về của malloc) được coi là thực tiễn xấu trong C. Ý nghĩa của constinlinecũng rất khác nhau giữa C và C ++, và tất nhiên C ++ không hiểu __restrict. Đừng coi các ngôn ngữ là có thể hoán đổi cho nhau, ngay cả trong tập hợp con của các nguồn biên dịch cả hai.
Angew không còn tự hào về SO

1

Về cơ bản, viết mã C tốt cũng giống như viết mã C ++ hoặc Java tốt: Bạn muốn có một lớp, hãy sử dụng a struct. Bạn muốn thừa kế, bao gồm cơ sở structlà thành viên đầu tiên không tên. Bạn muốn các hàm ảo, thêm một con trỏ vào tĩnh structcủa các con trỏ hàm. Và v.v., đó chính xác là những gì C ++ thực hiện trong chương trình, sự khác biệt duy nhất là, nó rõ ràng ở C. Vì vậy, bạn có thể thực hiện lập trình hướng đối tượng hoàn hảo trong C, nó trông hơi khác và nhiều hơn so với những gì bạn làm được sử dụng để.

Vấn đề là, lập trình tốt là về mô hình, không phải về các tính năng ngôn ngữ. Đúng, thật tuyệt nếu các tính năng ngôn ngữ của bạn cung cấp hỗ trợ tốt cho các mô hình bạn muốn sử dụng, nhưng các tính năng ngôn ngữ không phải là một yêu cầu. Một khi bạn nhận ra điều này, bạn có thể viết mã tốt bằng bất kỳ ngôn ngữ nào (ngoài một số ngôn ngữ bí truyền như brainfuck hoặc INTERCAL, nghĩa là).

Tất nhiên, vấn đề vẫn là thư viện C tiêu chuẩn không chứa bất kỳ lớp chứa tiện lợi nào mà bạn đã quen. Thật không may, điều đó có nghĩa là bạn sẽ cần phải sử dụng của riêng bạn hoặc giải quyết vấn đề thiếu này bằng cách sử dụng các mảng được phân bổ động. Nhưng tôi muốn bạn sẽ sớm phát hiện ra rằng tất cả những gì bạn thực sự cần là mảng động ( malloc()) và danh sách / cây được liên kết được triển khai thông qua các thành viên con trỏ trong các lớp của bạn.

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.