Strncmp: ข้อผิดพลาดการแบ่งส่วนที่ผิดปกติ

ฉันยังใหม่กับ C มาก แต่ฉันเขียน C++ มาระยะหนึ่งแล้ว ฉันกำลังเขียนโปรแกรมแชทไคลเอนต์เซิฟเวอร์ ฉันต้องแจ้งให้ผู้ใช้ทราบด้วยตัวเลือกต่างๆ สองสามตัวเลือกเมื่อเริ่มต้นเซสชัน หลังจากที่ผู้ใช้ป้อนชื่อผู้ใช้แล้ว ตอนแรกฉันพยายามใช้ฟังก์ชัน getchar() แต่ด้วยเหตุผลใดก็ตาม ข้อความใด ๆ ในรูปแบบต่อไปนี้จะไม่ให้ผลลัพธ์ที่คาดหวัง:

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

หากผู้ใช้ป้อน 2 ผู้ใช้จะไม่ไปที่พื้นที่ "doSomething" ดังนั้นฉันจึงลองใช้ fgets และ strncp แทน แต่ตอนนี้ ฉันยังคงได้รับข้อผิดพลาดในการแบ่งส่วนใน 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);
    }*/
}

คุณสามารถเห็นในความคิดเห็นที่เหลือถึงความพยายามที่เหลือในการทำสิ่งต่างๆ เช่น การหล่อถ่านเพื่อ 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() จะกลับมาหลังจากที่คุณป้อนทั้งบรรทัด (และกด Enter) เทอร์มินัลทำเช่นนี้เพื่อให้สามารถแก้ไขบรรทัดได้ หากต้องการให้ 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