Strncmp: необычная ошибка сегментации

Я действительно новичок в C, но некоторое время пишу на C++. Я пишу клиентскую программу чата. Мне нужно предложить пользователю несколько разных вариантов в начале сеанса после того, как он ввел имя пользователя. Сначала я пытался использовать функцию getchar(), но по той или иной причине любые операторы следующего шаблона не давали ожидаемых результатов:

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

Если пользователь ввел 2, он никогда не перейдет в область «сделать что-то». Поэтому я попытался вместо этого использовать fgets и strncmp. Но теперь я продолжаю получать ошибки сегментации на strncmp. Вот наиболее важная часть кода с некоторыми закомментированными разделами из моих попыток использовать getchar. По общему признанию, это немного беспорядочно, потому что я просто собирал это вместе в качестве теста. Я думал, что, возможно, выделение дополнительного пространства для строки поможет предотвратить ошибки seg, но, конечно же, этого не произошло.

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);
    }*/
}

Вы можете увидеть в оставшихся комментариях остатки попыток сделать такие вещи, как приведение 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);
    }*/
   }

}


person James Erickson    schedule 09.10.2014    source источник
comment
Выдает ли компилятор предупреждения об этом коде?   -  person alk    schedule 09.10.2014


Ответы (3)


Поведение getchar() зависит от режима вашего терминала. Большинство из них работают в «приготовленном» режиме, что означает, что 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");
}
person Aaron Digulla    schedule 09.10.2014

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)
person Joshua Taylor    schedule 09.10.2014
comment
...или &x[0]. Но да, это довольно надежно приведет к сбою! - person Norman Gray; 09.10.2014

Комментарии на будущее

Как правило, удалять строки #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);

Сейчас, когда...

Поэтому, если я добавлю эти заголовки и попытаюсь скомпилировать ваш код, я получу несколько серьезно пугающих предупреждений. Давайте рассмотрим первый класс предупреждений.

В этом классе вы передаете переменные функциям, которые ищут переменную другого типа.

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

Вы действительно должны попытаться исправить это, потому что иначе ваш код не работает. Если вы не понимаете, почему выдается то или иное предупреждение, вам следует спросить об этом.

person Bill Lynch    schedule 09.10.2014