Strncmp: Kesalahan segmentasi yang tidak biasa

Saya benar-benar baru mengenal C, tetapi saya sudah lama menulis C++. Saya sedang menulis program obrolan server klien. Saya perlu memberi pengguna beberapa opsi berbeda di awal sesi, setelah mereka memasukkan nama pengguna. Pada awalnya saya mencoba menggunakan fungsi getchar(), tetapi karena satu dan lain hal, pernyataan apa pun dari pola berikut tidak akan memberikan hasil yang diharapkan:

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

Jika pengguna memasukkan 2, ia tidak akan pernah masuk ke area "doSomething". Jadi saya mencoba menggunakan fgets dan strncmp sebagai gantinya. Tapi sekarang, saya terus mendapatkan kesalahan segmentasi di strncmp. Ini adalah bagian kode yang paling relevan, dengan beberapa bagian yang dikomentari dari upaya saya menggunakan getchar. Memang ini agak berantakan, karena saya hanya menjadikannya sebagai ujian. Saya pikir mungkin mengalokasikan ruang ekstra ke string akan membantu mencegah kesalahan seg tapi tentu saja tidak.

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

Anda dapat melihat di komentar sisa sisa upaya untuk melakukan hal-hal seperti memasukkan karakter ke int dengan pengurangan. Ini berasal dari hal-hal yang saya temukan di internet. Saya juga mendengar di internet bahwa getchar meninggalkan \n di buffer input.

Jadi, inilah seluruh kode saya untuk klien sehingga Anda dapat memasukkannya ke dalam konteks:

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 sumber
comment
Apakah kompiler meneriakkan peringatan saat memasukkan kode ini?   -  person alk    schedule 09.10.2014


Jawaban (3)


Perilaku getchar() bergantung pada mode terminal Anda. Sebagian besar beroperasi dalam mode "matang" yang berarti getchar() kembali setelah Anda memasukkan seluruh baris (dan menekan enter). Terminal melakukan ini untuk memungkinkan pengeditan baris. Agar getchar() segera kembali, Anda perlu mengalihkannya ke mode "mentah".

Selanjutnya, Anda harus mengaktifkan semua peringatan kompiler karena ini akan memberi tahu Anda apa yang salah dengan kode di atas:

strncmp() mengharapkan char* sebagai parameter pertama tetapi Anda melewati char. Itu berarti kode akan dibaca dari memori arbitrer.

x[0] != NULL juga tidak masuk akal (bandingkan karakter dengan penunjuk nol). Untuk mengetahui apakah fgets() tidak mengembalikan apa pun, lihat kode pengembaliannya.

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] adalah karakter, tetapi x adalah char*. strncmp sebaiknya menganggap x sebagai argumen, bukan x[0]. Artinya, Anda tidak mau

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

melainkan

strncmp(x,"a",1)

Alternatifnya, jika Anda benar-benar ingin menekankan bahwa Anda memulai dari karakter pertama x, Anda dapat melakukan salah satu dari:

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

strncmp(&x[0],"a",1)
person Joshua Taylor    schedule 09.10.2014
comment
...atau &x[0]. Tapi ya, itu bisa menyebabkan crash dengan cukup andal! - person Norman Gray; 09.10.2014

Komentar untuk masa depan

Biasanya tidak ada gunanya menghapus baris #include dari kode sumber lengkap. Jika saya ingin pergi dan mengkompilasi kode Anda, sekarang saya harus membakar beberapa menit untuk menyatukannya. Ini hanya membuang-buang waktu.

Inilah header tambahan yang perlu saya tambahkan:

#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);

Sekarang...

Jadi jika saya menambahkan header tersebut, dan mencoba mengkompilasi kode Anda, saya mendapatkan beberapa peringatan yang serius menakutkan. Mari kita lihat peringatan kelas pertama.

Kelas ini adalah tempat Anda meneruskan variabel ke fungsi yang mencari tipe variabel berbeda.

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);
                             ^

Kelas kedua adalah tempat Anda menggunakan variabel yang tidak diinisialisasi:

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

Anda harus benar-benar mencoba memperbaikinya, karena sebaliknya kode Anda rusak. Jika Anda tidak memahami mengapa peringatan tertentu diaktifkan, Anda harus menanyakannya.

person Bill Lynch    schedule 09.10.2014