【问题标题】:Is this a legitimate C++ code? [closed]这是合法的 C++ 代码吗? [关闭]
【发布时间】:2010-12-06 23:34:54
【问题描述】:

代码来自here

鉴于在 C++ 中您可以使用 C 库,您会说以下代码是合法的 C++ 代码吗? 如果不是,需要应用哪些更改?

此代码使用 g++ 编译并按预期运行。

更新: 感谢您的所有回答。我仍然有点困惑,因为对于这段代码是否符合 C++ 标准没有达成一致。会再问一个问题来打消我的疑惑

更新2: 致关闭此问题的版主: 刚刚注意到这个问题已经结束,我认为这很荒谬。这是一个扎实的技术问题,我收到了扎实的技术答案。如果您还没有完整阅读整个主题,这里是我们达成一致结论的直观表示:

显然 C++ 不是 C 的超集。

结束处理编码标准的问题是错误的。

客户:

    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <netdb.h>
    #include <sys/types.h>
    #include <netinet/in.h>
    #include <sys/socket.h>

    #include <arpa/inet.h>
    #define PORT "3490" // the port client will be connecting to 
    #define MAXDATASIZE 100 // max number of bytes we can get at once 

    // get sockaddr, IPv4 or IPv6:
    void *get_in_addr(struct sockaddr *sa){
        if (sa->sa_family == AF_INET) {
            return &(((struct sockaddr_in*)sa)->sin_addr);
        }

        return &(((struct sockaddr_in6*)sa)->sin6_addr);
    }

    int main(int argc, char *argv[]){
        int sockfd, numbytes;  
        char buf[MAXDATASIZE];
        struct addrinfo hints, *servinfo, *p;
        int rv;
        char s[INET6_ADDRSTRLEN];

        if (argc != 2) {
            fprintf(stderr,"usage: client hostname\n");
            exit(1);
        }

        memset(&hints, 0, sizeof hints);
        hints.ai_family = AF_UNSPEC;
        hints.ai_socktype = SOCK_STREAM;

        if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
            fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
            return 1;
        }

        // loop through all the results and connect to the first we can
        for(p = servinfo; p != NULL; p = p->ai_next) {
            if ((sockfd = socket(p->ai_family, p->ai_socktype,
                    p->ai_protocol)) == -1) {
                perror("client: socket");
                continue;
            }

            if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
                close(sockfd);
                perror("client: connect");
                continue;
            }

            break;
        }

        if (p == NULL) {
            fprintf(stderr, "client: failed to connect\n");
            return 2;
        }

        inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
                s, sizeof s);
        printf("client: connecting to %s\n", s);

        freeaddrinfo(servinfo); // all done with this structure

        if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
            perror("recv");
            exit(1);
        }

        buf[numbytes] = '\0';
        printf("client: received '%s'\n",buf);
        close(sockfd);

        return 0;
    }

服务器:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

#define PORT "3490"  // the port users will be connecting to
#define BACKLOG 10     // how many pending connections queue will hold

void sigchld_handler(int s){
    while(waitpid(-1, NULL, WNOHANG) > 0);
}

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa){
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void){
    int sockfd, new_fd;  // listen on sock_fd, new connection on new_fd
    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_storage their_addr; // connector's address information
    socklen_t sin_size;
    struct sigaction sa;
    int yes=1;
    char s[INET6_ADDRSTRLEN];
    int rv;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE; // use my IP

    if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and bind to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("server: socket");
            continue;
        }

        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
                sizeof(int)) == -1) {
            perror("setsockopt");
            exit(1);
        }

        if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("server: bind");
            continue;
        }

        break;
    }

    if (p == NULL)  {
        fprintf(stderr, "server: failed to bind\n");
        return 2;
    }

    freeaddrinfo(servinfo); // all done with this structure

    if (listen(sockfd, BACKLOG) == -1) {
        perror("listen");
        exit(1);
    }

    sa.sa_handler = sigchld_handler; // reap all dead processes
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction");
        exit(1);
    }

    printf("server: waiting for connections...\n");

    while(1) {  // main accept() loop
        sin_size = sizeof their_addr;
        new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
        if (new_fd == -1) {
            perror("accept");
            continue;
        }

        inet_ntop(their_addr.ss_family,
            get_in_addr((struct sockaddr *)&their_addr),
            s, sizeof s);
        printf("server: got connection from %s\n", s);

        if (!fork()) { // this is the child process
            close(sockfd); // child doesn't need the listener
            if (send(new_fd, "Hello, world!", 13, 0) == -1)
                perror("send");
            close(new_fd);
            exit(0);
        }
        close(new_fd);  // parent doesn't need this
    }

    return 0;
}

【问题讨论】:

  • 你认为“合法”在这种情况下究竟意味着什么?
  • 你想知道它是窃取信息还是什么?
  • 它的 C 代码。 C++ 使用不同的风格。但是你经常会发现 C 代码隐藏在 C++ 应用程序中,所以这没什么大不了的。
  • @Karl Knechtel:符合 C++ 标准
  • @blindstuff:请原谅我选择表达自己的话。我的意思是:它是否适用于 C++ 标准

标签: c++ c networking gcc g++


【解决方案1】:

从您的 cmets 看来,您似乎对代码的可移植性非常感兴趣,或者“您如何确定它可以在任何 C++ 编译器上编译,而不仅仅是 g++”。

由于每个编译器都有它们支持的不同扩展集(甚至它们对标准的支持程度不同,或者它们支持哪种标准),一种方法是尝试使用不同的编译器对其进行编译。

如果您不能这样做,您可以准确指定 gcc 将使用哪组扩展和/或标准进行编译。例如:

--std=c89
--std=c90
--std=c99
--std=gnu90      (default for C compiles)
--std=gnu99

--std=c++98
--std=c++0x
--std=gnu++98    (default for C++ compiles)
--std=gnu++0x

可能还有其他,请参阅 gcc 文档了解更多详细信息:http://gcc.gnu.org/onlinedocs/gcc/Standards.html

此外,--pedantic 选项可以使 gcc 对标准合规性更加严格。

【讨论】:

  • 这个问题的原因比您想象的要简单,但感谢您的详尽回答
【解决方案2】:

我个人认为 C++ 比 C 更具声明性,后者是高度程序化的(尽管不像 prolog 那样具有声明性)。

服务器

ServerAction   action;
Server         server(action);

EventLoop      loop(server);
loop.processes();

客户

Address        addr("123.45.67");
Connection     connect(addr);

Message        message(Connection::Type::HTTP, "Plop");
connection.Send(message);

【讨论】:

    【解决方案3】:

    这确实符合 C++ 标准,从这个意义上说,它是合法 C++。

    但是,这里没有任何特定于 C++ 的内容。您可以使用 gcc 轻松编译它。

    我认为其他人在这里注意到的事实是这段代码是以过程方式编写的,而不是以面向对象方式编写的。大多数人将 C++ 与面向对象编程联系在一起,而大多数讨论道德负责任的 C++ 编程的现代书籍会说这是编写应用程序的糟糕方式。换句话说,当可以使用面向对象的范例来编写这种应用程序时,编写这样的程序就不太流行了。

    这个应用程序很难维护,而且几乎是随意编写的。一个优秀的 C 程序员会将这个问题拆分为多个函数,而不是将大部分问题转储到 main() 中。它可以作为一个示例来说明如何做客户端-服务器,但我会犹豫是否要编写这样的新代码。

    【讨论】:

    • 惯用 C++ 和惯用 C 之间的区别远不止“面向对象与过程”。事实上,许多非常有才华的 C++ 用户会强烈反对将 C++ 描述为专门的,甚至主要是“面向对象”的语言。
    【解决方案4】:

    您不是在编写 C++,而是在利用这样一个事实,即符合标准、干净的 C 代码可以毫无问题地编译为 C++。在某些地方 C++ 与 C 不同,但在这些情况下编译器会对你大喊大叫。您可以以此为基础编写一些有效的 C++;否则,没有什么特别的事情发生。

    【讨论】:

    • ... 无论如何,对于“干净”C 代码的充分手动定义。 :)
    • 没错,这是同义词。但对 C 代码进行样式检查的一种方法是尝试将其编译为 C++。
    • “干净的 C 代码将毫无问题地编译为 C++” - 不正确 - C++ 不是 C 的严格超集。您可以编写可以原样编译的 C 代码和 C++ 一样,但需要一些努力。
    • 将编译为 C++ 的 C 代码几乎肯定是不干净的;它充满了令人反感的演员表。
    【解决方案5】:

    这是有效的,但不是 C++ 首选样式。

    例如,所有标准 C 库都应包含在 &lt;cstdio&gt;,而不是 &lt;stdio.h&gt;

    另一个例子,这个代码在它可以使用一个常量整数时使用了一个#define。

    【讨论】:

    • 有效但不是首选,这意味着它是 C++ 代码。
    • @matcheek:在语法上有效。在风格上无效。如果我让你为面试问题编写一个 C++ 程序并且你回来了,我不相信你能够编写现代 C++ 代码,因此你不会得到这份工作(因为我需要你能够工作与了解正确使用该语言的其他人一起)。如果我让你写一个 C 程序,这将是一个非常有效的方法。
    【解决方案6】:

    如果我去苏格兰拜访你,用我的葡萄牙口音和(短)英语词汇说苏格兰语……你会说我说的是苏格兰语吗?

    假设每个人都理解我,我也理解每个人:-)

    【讨论】:

      【解决方案7】:

      使用 C++,您可以使用更好的类型检查功能来避免 void*#define 等问题。

      但由于它不使用专有的 C 特性,它使用 g++ 编译。

      【讨论】:

        【解决方案8】:

        “C”代码也符合 C++ 中的兼容子集。

        如果它编译它是合法的,不存在编译器错误,但在我看来你对“合法”有不同的含义。

        【讨论】:

          【解决方案9】:

          我不太明白这个问题。我想你是在问你是否应该继续使用它而不做任何改变,而不用担心它——在这种情况下,我会说是的。 C++ 是 C 的超集 - 任何有效的 C 代码都是有效的 C++ 代码,如您所见。

          按照惯例,标准标题有一种新格式 -

          #include <string.h>
          

          变成

          #include <cstring>
          

          但这并不是绝对必要的。无论如何,您不能使用 unistd 和 sys/ 标头来做到这一点。

          因此,如果您问是否可以安全地继续使用它,那就去吧,但您可能想将 errno.h 更改为 cerrno,将 stdio.h 更改为 cstdio,等等。

          【讨论】:

          • 我想我已经找到了答案。我的意思是:你怎么能确定它会在任何 C++ 编译器上编译,而不仅仅是 g++。
          • @matcheek:好吧,要学究气,总的来说,除了尝试之外,您无法检查它。没有正式的方法来检查编译器是否正确遵循标准,因此您只需吸吮它并查看
          • @Robert:“任何有效的 C 代码都是有效的 C++ 代码”——不,这不是真的。 C++ 不是 C 的严格超集。很容易想出合法的 C 代码示例,这些示例无法使用 C++ 编译器进行编译。
          • 使用 gcc 编译但不使用 g++ 编译的示例代码:int class;。另一个示例代码,不使用 C++ 保留关键字:int i=&amp;i;。另一个示例代码,不使用 C++ 保留关键字,编译时没有警告gcc -c -pedantic -ansi -W -Wallchar *p=(void*)0;
          • @matcheek, @pts: 顺便说一下,如果文件名看起来像 C++(.cpp.cc 等),gcc 也会编译 C++。它只是没有为 C++ 使用正确的链接器选项。因此,该代码是否“使用 gcc 编译”取决于它所在文件的名称。这些示例在语法上是正确的 C,但在语法上不是正确的 C++。
          猜你喜欢
          • 2017-09-17
          • 2016-03-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-22
          • 1970-01-01
          • 1970-01-01
          • 2017-09-03
          • 1970-01-01
          相关资源
          最近更新 更多