【问题标题】:Strncmp: Unusual segmentation Faultstrncmp:异常分段错误
【发布时间】:2014-12-04 14:31:45
【问题描述】:

我真的是 C 的新手,但我写 C++ 已经有一段时间了。我正在编写一个客户端服务器聊天程序。在用户输入用户名后,我需要在会话开始时向用户提示几个不同的选项。起初我试图使用 getchar() 函数,但由于某种原因,以下模式的任何语句都不会产生预期的结果:

int x = getchar();
if (x == '2') doSomething();

如果用户输入 2,它将永远不会进入“doSomething”区域。所以我尝试改用 fgets 和 strncmp 。但是现在,我不断在 strncmp 上遇到分段错误。这是代码中最相关的部分,在我尝试使用 getchar 时有一些注释掉的部分。诚然,这有点乱,因为我只是把它放在一起作为测试。我想也许为字符串分配额外的空间有助于防止段错误,但当然没有。

for( ; ; )
{
  printf("\r\n1.List Users \r\n2.Chat \r\n3.Exit \r\n \r\n \r\n");

  char *x = malloc(5);

  fgets(x, 2, stdin);

  if (x[0] != NULL)
    {

      if (strncmp (x[0],"a",1) == 0)
        {
          printf("yay");
        }
    }


/* int x = getchar();
  if(x == 'a') // Compare input to 'q' character
    break;
  fprintf(stdout, "%d\n", x);*/

  /*x = c - '0';

  if (x == 1)
    getUsers(sockfd);

  if ( x == 2 )
    {

      pthread_create(&sndThread, NULL, do_send, (void *) sockfd);
      pthread_create(&rcvThread, NULL, do_recv, (void *) sockfd);

      pthread_join(sndThread, NULL);
      pthread_join(rcvThread, NULL);
    }

  if ( x == 3 )
    {
    close(sockfd);
    exit(0);
    }*/
}

您可以在剩余的 cmets 中看到尝试执行操作的剩余部分,例如使用减法将 char 转换为 int。这来自我在互联网上找到的东西。我还在互联网上听说 getchar 将 \n 留在了输入缓冲区中。

这是我为客户端编写的完整代码,您可以将其放在上下文中:

int main(int argc, char **argv)
{
  int sockfd, i;

  char *myName = malloc(MSGSIZE);

  char c;

struct sockaddr_in servaddr;

int status;

pthread_t sndThread;
pthread_t rcvThread;

if(argc != 2)
  {
    printf("Error: expected IP address argument");
    exit(1);
}
  if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{

  error("Socket error");
}

 memset(&servaddr, 0, sizeof(servaddr));
 servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORTNUM);

if(inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <=0)
{
  printf("inet_pton error for %s \n", argv[1]);
  exit(3);
}

if(connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
{
  error("Connect error");
}

printf("Type in a username: \r\n");

while ( fgets(myName[i],MSGSIZE,stdin ) == NULL){}


printf(">%s<\n",myName);

send_userName(myName,sockfd);

for( ; ; )
{
  printf("\r\n1.List Users \r\n2.Chat \r\n3.Exit \r\n \r\n \r\n");

  char *x = malloc(5);

  fgets(x, 2, stdin);

  if (x[0] != NULL)
    {

      if (strncmp (x[0],"a",1) == 0)
        {
          printf("yay");
        }
    }


/* int x = getchar();
  if(x == 'a') // Compare input to 'q' character
    break;
  fprintf(stdout, "%d\n", x);*/

  /*x = c - '0';

  if (x == 1)
    getUsers(sockfd);

  if ( x == 2 )
    {

      pthread_create(&sndThread, NULL, do_send, (void *) sockfd);
      pthread_create(&rcvThread, NULL, do_recv, (void *) sockfd);

      pthread_join(sndThread, NULL);
      pthread_join(rcvThread, NULL);
    }

  if ( x == 3 )
    {
    close(sockfd);
    exit(0);
    }*/
   }

}

【问题讨论】:

  • 编译器是否会在输入此代码时发出警告?

标签: c fgets getchar strncmp


【解决方案1】:

x[0] 是一个字符,但x 是一个char*strncmp 应该只将 x 作为参数,而不是 x[0]。也就是说,你不想

strncmp(x[0],"a",1)

而是

strncmp(x,"a",1)

或者,如果您真的想强调您从x 的第一个字符开始,您可以执行以下任一操作:

strncmp(x+0,"a",1)

strncmp(&x[0],"a",1)

【讨论】:

  • ...或&amp;x[0]。但是,是的,这会导致相当可靠的崩溃!
【解决方案2】:

对未来的评论

从完整源代码中删除#include 行通常没有帮助。如果我想去编译你的代码,我现在必须花几分钟时间把它们拉到一起。这是浪费时间。

这是我需要添加的额外标题:

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

#define MSGSIZE 100
#define PORTNUM 12

#define SA struct sockaddr

void error(const char *);
void send_userName(const char *, int);

那么……

因此,如果我添加这些标头,并尝试编译您的代码,我会收到一些 seriously 可怕的警告。让我们看看第一类警告。

这个类用于将变量传递给正在寻找不同类型变量的函数。

foo.c:59:19: warning: incompatible integer to pointer conversion passing 'char' to parameter of type 'char *'; take the address with & [-Wint-conversion]
    while ( fgets(myName[i],MSGSIZE,stdin ) == NULL){}
                  ^~~~~~~~~
                  &
/usr/include/stdio.h:236:30: note: passing argument to parameter here
char    *fgets(char * __restrict, int, FILE *);
                                ^
foo.c:74:18: warning: comparison between pointer and integer ('int' and 'void *')
        if (x[0] != NULL)
            ~~~~ ^  ~~~~
foo.c:77:26: warning: incompatible integer to pointer conversion passing 'char' to parameter of type 'const char *'; take the address with & [-Wint-conversion]
            if (strncmp (x[0],"a",1) == 0)
                         ^~~~
                         &
/usr/include/string.h:84:26: note: passing argument to parameter here
int      strncmp(const char *, const char *, size_t);
                             ^

第二类是你使用未初始化变量的地方:

foo.c:59:26: warning: variable 'i' is uninitialized when used here [-Wuninitialized]
    while ( fgets(myName[i],MSGSIZE,stdin ) == NULL){}
                         ^
foo.c:18:18: note: initialize the variable 'i' to silence this warning
    int sockfd, i;
                 ^
                  = 0

您真的应该尝试修复这些问题,否则您的代码将被破坏。如果您不明白为什么会触发特定警告,那么您应该询问一下。

【讨论】:

    【解决方案3】:

    getchar() 的行为取决于终端的模式。大多数都在“cooked”模式下运行,这意味着getchar() 在您输入一整行(并按下回车键)后返回。终端这样做是为了允许行编辑。要让getchar() 立即返回,您需要将其切换到“原始”模式。

    接下来,您应该启用所有编译器警告,因为它会告诉您上面的代码有什么问题:

    strncmp() 期望 char* 作为第一个参数,但您传递了 char。这意味着代码将从任意内存中读取。

    x[0] != NULL 也没有意义(将字符与空指针进行比较)。要知道fgets() 是否没有返回任何内容,请查看其返回码。

    char * success = fgets(x, 2, stdin);
    if(success == null) { ... error handling... }
    
    if (strncmp (x,"a",1) == 0) {
        printf("yay");
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-10
      • 1970-01-01
      • 2013-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多