3

我在以下 C 代码中遇到分段错误:

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

#define PORT  6667
#define MAXDATASIZE 1024

int bot_connect(char *hostname);

int bot_connect(char *hostname) {

  int sockfd, numbytes, s;
  char buf[MAXDATASIZE];
  struct addrinfo hints, *servinfo, *p;
  int rv;
  char m[1024];
  char *message;
  char *nick = "Goo";
  char *ident = "Goo";
  char *realname = "Goo";

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

  rv = getaddrinfo(hostname, PORT, &hints, &servinfo);

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

  for (p = servinfo; p != NULL; p = p->ai_next) {
    sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
    if (sockfd == -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;
  }

  freeaddrinfo(servinfo);

  strcat(m, "NICK ");
  strcat(m, nick);
  message = m;
  s = send(sockfd, message, strlen(message), 0);

  strcat(m, "USER ");
  strcat(m, ident);
  strcat(m, " * * :");
  strcat(m, realname);
  message = m;
  s = send(sockfd, message, strlen(message), 0);

  message = "JOIN #C&T";
  s = send(sockfd, message, strlen(message), 0);

  close(sockfd);
}

我知道你会因为试图用内存做一些你不允许做的事情而遇到分段错误,比如改变只读内存,但据我所知,这个程序不会那样做。有没有人知道分段错误来自哪里?

4

6 回答 6

10

strcat( m, "NICK" );您在初始化 m 之前正在调用。在 strcat 之前,尝试m[0] = '\0';or memset( m, 0, sizeof( m ) );,或将第一个更改strcatstrcpy

此外,在套接字上发送 NICK 线后,strcat再次调用,这会将 USER 线附加到 NICK 线。同样,您可能应该将第一个更改strcatstrcpy

于 2008-11-21T13:32:01.930 回答
5

Graeme 的 "what" 是正确的,所以这里是 "why",以防你不熟悉。在 C 中,字符串被定义为以空字符 '\0' 结尾的一系列字符。字符串具有终止字符的原因是代码可以确定字符串的逻辑结束位置。即使您声明这样的字符串:

 char m[1024];

在 C 语言中,给定 m 的代码无法确定它分配了 1024 个字节,更不用说确定这些字节中有多少是有意义的,而没有一个空字符来划分有意义的字节的结尾。

strcat 是一个适用于两个 C 字符串的函数,因此它希望它的两个参数都符合规范,即有一个空字节标记有意义的输入的结束。如果 m 未初始化,它的内容是随机的,所以空字节可以在任何地方,或者根本没有空字节。为了连接,strcat 将查看第一个参数的字符,试图找到标记字符串结尾的空字节。如果偶然地,未初始化的数组中不存在空字节,它将愉快地继续查找字符串的末尾,进入可能对应于其他变量或内部堆栈信息等的任意内存位置。

当 strcat 最终找到一个空字节时,它会愉快地从该位置开始写入第二个参数的内容,如果该位置超过 m 的末尾,它将覆盖一些其他变量或其他信息。如果它是分配给其他变量的内存地址,则问题可能是无声的并且很难找到。幸运的是,在您的情况下,它是一个不应写入的位置,因此您的程序因明显错误而崩溃。

这也引起了注意,您确实应该使用 strncat 而不是 strcat。strncat 允许您指定要创建的字符串的最大长度。我之前注意到 strcat 无法确定 m 有 1024 个字节分配给它,所以如果你尝试将 m 与使 m 长于 1023 个字符的东西连接起来,你最终会遇到相同的内存覆盖问题。strncat 采用第三个参数,该参数指定结果字符串的最大长度,并具有一个返回码,该码将指示连接是否由于达到此长度而被截断。

于 2008-11-21T13:44:53.510 回答
4

这里已经是为什么和什么的正确答案。要回答您的“分段错误在哪里......”,我推荐使用调试器。用调试信息编译你的程序,你会看到它发生在哪里(让它运行并在它崩溃时查看堆栈跟踪)。答案很可能还会回答为什么和什么(在这种情况下已经回答了)

于 2008-11-21T14:09:27.830 回答
2

调高编译器的警告级别:

rv = getaddrinfo(hostname, PORT, &hints, &servinfo);

getaddrinfo 的第二个参数是 a const char *,而不是 int。将 PORT 更改为 NULL 并修改 addrinfo 结构中的端口号connect

于 2008-11-21T13:44:44.587 回答
1

使用valgrind。Valgrind 非常擅长发现代码中的资源泄漏和其他常见的人为错误类型的不当行为。

于 2008-11-21T13:39:43.753 回答
0

m数组在使用前包含垃圾。以下将起作用:

strcpy(m, "NICK ");
strcat(m, nick);
message = m;
s = send(sockfd, message, strlen(message), 0);

strcpy(m, "USER ");
strcat(m, ident);
strcat(m, " * * :");
strcat(m, realname);
message = m;
s = send(sockfd, message, strlen(message), 0);

以下也将起作用:

sprintf(m, "NICK %s", nick);
message = m;
s = send(sockfd, message, strlen(message), 0);

sprintf(m, "USER %s * * :%s", ident, realname);
s = send(sockfd, message, strlen(message), 0);
于 2013-03-13T21:37:09.600 回答