ทำไม“ a”! =“ a” ในภาษา C?


110
void main() {
    if("a" == "a")
      printf("Yes, equal");  
    else
      printf("No, not equal");
}

ทำไมเอาท์พุทNo, not equal?


100
void main??? Ew ...
Paul R

47
คอมไพเลอร์ C แบบฝังอนุญาตให้โมฆะ main () เนื่องจากอาจไม่มีระบบปฏิบัติการใด ๆ ที่ให้รหัสส่งคืน
Jeanne Pindar

26
คำถามเช่นนี้จะถูกโหวตบ่อยขนาดนี้ได้อย่างไร? มันไม่น่าสนใจจริงๆ ... ฉันหมายความว่าสตริงคืออาร์เรย์และอาร์เรย์คือพอยน์เตอร์เป็นหมวกแบบเก่าใน C ไม่ใช่เหรอ?
Felix Dombek

64
@Felix เป็นคำถามที่เขียนอย่างรวบรัดซึ่งเป็นจุดที่ทำให้เกิดความสับสนสำหรับผู้มาใหม่ในภาษา ดังนั้นไม่ได้มีไว้สำหรับผู้เชี่ยวชาญเท่านั้น แต่สำหรับผู้เริ่มต้นเช่นกันและคำถามที่ตรงเป้าหมายเช่นนี้เหมาะสำหรับการอ้างอิงผู้เริ่มต้นในอนาคต
bdonlan

37
@ เฟลิกซ์: คุณคิดผิด อาร์เรย์ไม่ใช่ตัวชี้
John Dibling

คำตอบ:


209

สิ่งที่คุณกำลังเปรียบเทียบคือที่อยู่หน่วยความจำสองรายการสำหรับสตริงที่ต่างกันซึ่งเก็บไว้ในตำแหน่งที่ต่างกัน โดยพื้นฐานแล้วจะมีลักษณะดังนี้:

if(0x00403064 == 0x002D316A) // Two memory locations
{
    printf("Yes, equal");
}

ใช้รหัสต่อไปนี้เพื่อเปรียบเทียบค่าสตริงสองค่า:

#include <string.h>

...

if(strcmp("a", "a") == 0)
{
    // Equal
}

นอกจากนี้"a" == "a"อาจส่งคืนจริงจริงทั้งนี้ขึ้นอยู่กับคอมไพเลอร์ของคุณซึ่งอาจรวมสตริงที่เท่ากันในเวลาคอมไพล์เป็นหนึ่งเพื่อประหยัดเนื้อที่

เมื่อคุณเปรียบเทียบค่าอักขระสองค่า (ซึ่งไม่ใช่พอยน์เตอร์) จะเป็นการเปรียบเทียบตัวเลข ตัวอย่างเช่น:

'a' == 'a' // always true

12
GCC ยังมีตัวเลือก-fmerge-constantsและ-fno-merge-constantsเพื่อเปิด / ปิดการใช้งานสตริงและค่าคงที่ทศนิยมที่ผสานระหว่างหน่วยการแปลแม้ว่าใน GCC บางตัวจะมีการเปิดใช้งานการรวมค่าคงที่เสมอโดยไม่คำนึงถึงตัวเลือกนั้น
Adam Rosenfield

2
จะได้ผลถ้าคุณใช้ "a" แทน "a" อย่างแรกคือถ่านซึ่งเป็นค่าตัวเลข
GolezTrol

@GolezTrol: ใน C ตัวอักษร 'a' มีintประเภทอยู่จริง :-) นอกจากนี้พอยน์เตอร์ไม่จำเป็นต้องเป็นค่าตัวเลข
Bastien Léonard

intเป็นตัวเลขด้วยใช่ไหม แต่ฉันคิดว่าตัวอักษรเป็นไบต์ Int คือ 4 ไบต์ พอยน์เตอร์เองก็เป็นจำนวนเต็มเช่นกัน ประกอบด้วยที่อยู่ของข้อมูลจำนวนมาก (ข้อมูลที่ไม่จำเป็นต้องเป็นตัวเลข)
GolezTrol

'a' == 'A' // not true... MySQL ขอให้แตกต่างกัน
Steven

52

ฉันไปงานปาร์ตี้ช้าไปหน่อย แต่ฉันจะตอบอยู่ดี ในทางเทคนิคบิตเดียวกัน แต่จากมุมมองที่แตกต่างกันเล็กน้อย (C parlance ด้านล่าง):

ใน C นิพจน์"a"หมายถึงสตริงลิเทอรัลซึ่งเป็นอาร์เรย์ที่ไม่มีชื่อแบบคงที่const charโดยมีความยาวสองอาร์เรย์ประกอบด้วยอักขระ'a'และ'\0'- อักขระว่างที่สิ้นสุดจะส่งสัญญาณถึงจุดสิ้นสุดของสตริง

อย่างไรก็ตามใน C เช่นเดียวกับที่คุณไม่สามารถส่งอาร์เรย์ไปยังฟังก์ชันตามค่าหรือกำหนดค่าให้กับอาร์เรย์ได้ ( หลังจากเริ่มต้น ) - ไม่มีตัวดำเนินการที่โอเวอร์โหลด==สำหรับอาร์เรย์ดังนั้นจึงไม่สามารถเปรียบเทียบได้โดยตรง พิจารณา

int a1[] = {1, 2, 3};
int a2[] = {3, 4, 5};
a1 == a2 // is this meaningful? Yes and no; it *does* compare the arrays for
         // "identity", but not for their values. In this case the result
         // is always false, because the arrays (a1 and a2) are distinct objects

ถ้า==ไม่ใช่การเปรียบเทียบอาร์เรย์มันจะทำอย่างไร? ใน C ในบริบทเกือบทั้งหมดรวมถึงอาร์เรย์นี้จะสลายตัวเป็นพอยน์เตอร์ (ซึ่งชี้ไปที่องค์ประกอบแรกของอาร์เรย์) - และการเปรียบเทียบพอยน์เตอร์เพื่อความเท่าเทียมกันจะเป็นสิ่งที่คุณคาดหวัง อย่างมีประสิทธิภาพเมื่อทำเช่นนี้

"a" == "a"

คุณเป็นจริงการเปรียบเทียบที่อยู่ของตัวละครเป็นครั้งแรกในสองอาร์เรย์ชื่อ ตามมาตรฐาน C การเปรียบเทียบอาจให้ผลลัพธ์เป็นจริงหรือเท็จ (เช่น 1 หรือ 0) - "a"อาจหมายถึงอาร์เรย์เดียวกันหรืออาร์เรย์สองอาร์เรย์ที่ไม่เกี่ยวข้องกันโดยสิ้นเชิง ในทางเทคนิคค่าผลลัพธ์ไม่ได้ระบุไว้ซึ่งหมายความว่าการเปรียบเทียบได้รับอนุญาต (กล่าวคือไม่ใช่พฤติกรรมที่ไม่ได้กำหนดหรือข้อผิดพลาดทางไวยากรณ์) แต่ค่าใดค่าหนึ่งถูกต้องและการใช้งาน (คอมไพเลอร์ของคุณ) ไม่จำเป็นต้องจัดทำเอกสารสิ่งที่จะเกิดขึ้นจริง

เป็นคนอื่นได้ชี้ให้เห็นเพื่อเปรียบเทียบ "สตริงค" (คือสตริงสิ้นสุดลงด้วยอักขระ null ก) คุณใช้ฟังก์ชั่นอำนวยความสะดวกที่พบในไฟล์ส่วนหัวมาตรฐานstrcmp string.hฟังก์ชันมีค่าส่งคืน0สำหรับสตริงที่เท่ากัน ถือเป็นแนวทางปฏิบัติที่ดีในการเปรียบเทียบค่าตอบแทนอย่างชัดเจน0แทนการใช้ตัวดำเนินการ "! ´กล่าวคือ

strcmp(str1, str2) == 0 // instead of !strcmp(str1, str2)

47

ตามใน C99 (มาตรา 6.4.5 / 6)

ตัวอักษรสตริง

มันเป็นไม่ได้ระบุว่าอาร์เรย์เหล่านี้มีความแตกต่างกันมีให้องค์ประกอบของพวกเขามีค่าที่เหมาะสม

ดังนั้นในกรณีนี้จะไม่ระบุว่าทั้งสอง"a"มีความแตกต่างกันหรือไม่ คอมไพเลอร์ที่ปรับให้เหมาะสมสามารถเก็บข้อมูลเดี่ยว"a"ไว้ในตำแหน่งอ่านอย่างเดียวและการอ้างอิงทั้งสองสามารถอ้างถึงสิ่งนั้นได้

ตรวจสอบผลลัพธ์ใน gcc ที่นี่


19

เนื่องจากเป็น 2 ตัวแยกกันพconst char*อยน์เตอร์ไม่มีค่าจริง คุณกำลังพูดบางอย่างเช่น0x019181217 == 0x0089178216ซึ่งแน่นอนส่งคืน NO

ใช้strcmp()แทน==


7
ตัวอักษรสตริงไม่ใช่ตัวชี้ แต่เป็นอาร์เรย์ แม้ว่าพวกเขาจะสลายตัวเป็นตัวชี้ในการเปรียบเทียบ
GManNickG

@Gman จริงขออภัยที่ไม่ชัดเจนในเรื่องนี้มีแนวโน้มที่จะลืม :)
Antwan van Houdt

9

พูดง่ายๆคือ C ไม่มีตัวดำเนินการเปรียบเทียบสตริงในตัว ไม่สามารถเปรียบเทียบสตริงด้วยวิธีนี้

แทนที่จะเปรียบเทียบสตริงโดยใช้รูทีนไลบรารีมาตรฐานเช่น strcmp () หรือโดยการเขียนโค้ดเพื่อวนซ้ำอักขระแต่ละตัวในสตริง

ใน C สตริงข้อความในเครื่องหมายคำพูดคู่จะส่งกลับตัวชี้ไปที่สตริง ตัวอย่างของคุณกำลังเปรียบเทียบตัวชี้และดูเหมือนว่าสตริงทั้งสองเวอร์ชันของคุณมีอยู่ที่ที่อยู่ที่ต่างกัน

แต่ไม่ใช่การเปรียบเทียบสายอักขระอย่างที่คุณคาดหวัง


3

พอยน์เตอร์

ตัวแรก"a"คือตัวชี้ไปยังสตริง ASCII ที่สิ้นสุดด้วย null

ตัวที่สอง"a"คือตัวชี้ไปยังสตริง ASCII อื่นที่สิ้นสุดด้วยค่าว่าง

หากคุณใช้คอมไพเลอร์ 32 บิตฉันคาดหวัง"a"=="a"-4ไว้ ฉันได้พยายามเพียงกับทีซีซี / Win32 "a"=="a"-2แม้ว่าและฉันได้รับ โอ้ดี ...


6
เหตุใดคุณจึงคาดว่าสตริงจะจัดแนวตามขอบเขต 4 ไบต์ ไม่ใช่ ints 2 คือสิ่งที่ฉันคาดหวัง (หากคอมไพเลอร์ไม่รวมเข้าด้วยกัน) เนื่องจากแต่ละสตริงมีความยาวสองไบต์รวมถึงตัวยุติที่เป็นโมฆะ
Sergei Tachenov

ตัวอย่างเช่นการจัดตำแหน่งบางระดับอาจอนุญาตให้strcmpเรียกใช้ครั้งละหลายไบต์ คอมไพเลอร์บางตัวทำบางตัวทำไม่ได้บางตัวทำเฉพาะสำหรับสตริงที่ยาวกว่าขั้นต่ำบางตัว ...
zwol

@ แซ็ค: พวกเขาจะทราบความยาวของสตริงได้อย่างไรก่อนที่จะเปรียบเทียบจริง?
Joachim Sauer

ฉันหมายถึงคอมไพเลอร์บางตัวจัดเรียงสตริงที่ยาวกว่าขั้นต่ำบางตัว
zwol

1

คุณกำลังเปรียบเทียบที่อยู่หน่วยความจำสองที่อยู่ดังนั้นผลลัพธ์จะไม่เป็นจริงเสมอไป คุณลองif('a' == 'a'){...}หรือยัง?


1

คำถามนี้เป็นแนวทางในการอธิบายที่ดีมากสำหรับผู้เริ่มต้นทุกคน ....
ขอฉันมีส่วนร่วมด้วย .....

ตามที่ทุกคนอธิบายไว้ข้างต้นว่าทำไมคุณถึงได้ผลลัพธ์เช่นนี้

ตอนนี้ถ้าคุณต้องการ prog ของคุณ หากต้องการพิมพ์ "ใช่เท่ากับ" แล้ว

ใช้อย่างใดอย่างหนึ่ง

if(strcmp("a", "a") == 0)
{

}

หรือ
ไม่ใช้ "a" เป็นสตริงใช้เป็นอักขระ ....

if('a'=='a')  
{  
printf ("yes Equal");  
}  

ในอักขระ C คือจำนวนเต็มสั้น 1 ไบต์ .......


อักขระใช้เพียง 1 ไบต์ แต่ลิเทอรัลอักขระเช่นเป็น'a'จำนวนเต็ม
Spidey

0

คอมไพเลอร์บางตัวมีตัวเลือก 'ผสานสตริง' ที่คุณสามารถใช้เพื่อบังคับให้สตริงคงที่ทั้งหมดมีแอดเดรสเดียวกัน ถ้าคุณจะใช้ว่าจะเป็น"a" == "a"true


0

หากการเปรียบเทียบระหว่างอักขระมักจะอยู่ในเครื่องหมายคำพูดเดียวเช่น

if('a' == 'a')

และ C ไม่สามารถรองรับการเปรียบเทียบสตริงเช่น "abc" == "abc"

เสร็จแล้วด้วย strcmp("abc","abc")


-5

ผู้ชายคนนี้ไม่ใช้ตัวแปร เขาใช้อาร์เรย์ข้อความชั่วคราวแทน: aและa. เหตุผลว่าทำไม

void main() 
{
    if("a" == "a")
      printf("Yes, equal");  
    else
      printf("No, not equal");
}

ไม่ได้ผลแน่นอนคือคุณไม่ได้เปรียบเทียบตัวแปร
หากคุณจะสร้างตัวแปรเช่น:

ถ่าน * text = "a";
ถ่าน * text2 = "a";

คุณสามารถเปรียบเทียบtextกับtext2และมันควรจะเป็นจริง

บางทีคุณไม่ควรลืมใช้{และ}=)

void main() {
    if("a" == "a")
    {
      printf("Yes, equal");
    }
    else
    {
      printf("No, not equal");
    }
}

1
" และควรจะเป็นจริง " - ไม่ระบุว่าจะเก็บสตริงลิเทอรัลไว้ในตำแหน่งหน่วยความจำเดียวกันหรือไม่ อ่านคำตอบอื่น ๆ
Spikatrix
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.