ทำไมข้อความที่ไม่มีผลต่อการพิจารณาทางกฎหมายใน C


13

ให้อภัยหากคำถามนี้ไร้เดียงสา พิจารณาโปรแกรมต่อไปนี้:

#include <stdio.h>

int main() {
  int i = 1;
  i = i + 2;
  5;
  i;
  printf("i: %d\n", i);
}

ในตัวอย่างข้างต้นข้อความ5;และi;ดูเหมือนฟุ่มเฟือยโดยสิ้นเชิง แต่โค้ดรวบรวมโดยไม่มีคำเตือนหรือข้อผิดพลาดโดยค่าเริ่มต้น (อย่างไรก็ตาม gcc จะโยนwarning: statement with no effect [-Wunused-value]คำเตือนเมื่อวิ่งไปด้วย-Wall) พวกเขาไม่มีผลกับส่วนที่เหลือของโปรแกรมดังนั้นทำไมพวกเขาจึงพิจารณาข้อความที่ถูกต้องตั้งแต่แรก? คอมไพเลอร์ไม่สนใจพวกเขาเหรอ? มีประโยชน์ใด ๆ ในการอนุญาตข้อความดังกล่าวหรือไม่?


5
ประโยชน์ของการห้ามข้อความดังกล่าวมีอะไรบ้าง
Mooing Duck

2
การแสดงออกใด ๆ สามารถเป็นคำสั่งโดยการวาง;หลังจากนั้น มันจะซับซ้อนภาษาเพื่อเพิ่มกฎเพิ่มเติมเกี่ยวกับเมื่อการแสดงออกไม่สามารถงบ
MM

3
คุณต้องการให้รหัสของคุณล้มเหลวในการรวบรวมเพราะคุณไม่สนใจค่าส่งคืนของprintf()หรือไม่ คำสั่ง5;โดยทั่วไปว่า "ทำสิ่งที่5ไม่ (อะไร) และไม่สนใจผล. คำสั่งของคุณprintf(...)คือ 'ทำสิ่งที่printf(...)ไม่และไม่สนใจผล (ค่าตอบแทนจากprintf())'. C ถือว่าผู้เดียว. นี้ยังช่วยให้รหัสเช่น(void) i;ที่iเป็น พารามิเตอร์ของฟังก์ชันที่คุณส่งไปvoidเพื่อทำเครื่องหมาย
Andrew Henle

1
@AndrewHenle: นั่นไม่เหมือนกันเพราะการโทรprintf()มีผลแม้ว่าคุณจะไม่สนใจค่าที่มันส่งกลับในที่สุด ในทางตรงกันข้าม5;ไม่มีผลเลย
Nate Eldredge

1
เพราะเดนนิสริตชี่และเขาไม่ได้อยู่ใกล้ ๆ เพื่อบอกเรา
user207421

คำตอบ:


10

ข้อดีอย่างหนึ่งของการอนุญาตให้ใช้คำสั่งดังกล่าวนั้นมาจากรหัสที่สร้างขึ้นโดยมาโครหรือโปรแกรมอื่น ๆ แทนที่จะเขียนโดยมนุษย์

ตัวอย่างลองจินตนาการถึงฟังก์ชั่น int do_stuff(void)ที่ควรจะคืนค่า 0 เมื่อสำเร็จหรือ -1 เมื่อล้มเหลว อาจเป็นไปได้ว่าการสนับสนุน "เนื้อหา" นั้นเป็นตัวเลือกและคุณอาจมีไฟล์ส่วนหัวที่ทำ

#if STUFF_SUPPORTED
#define do_stuff() really_do_stuff()
#else
#define do_stuff() (-1)
#endif

ทีนี้ลองนึกถึงโค้ดที่ต้องการทำสิ่งต่าง ๆ ถ้าเป็นไปได้ แต่อาจหรืออาจไม่สนใจว่ามันจะสำเร็จหรือล้มเหลว

void func1(void) {
    if (do_stuff() == -1) {
        printf("stuff did not work\n");
    }
}

void func2(void) {
    do_stuff(); // don't care if it works or not
    more_stuff();
}

เมื่อSTUFF_SUPPORTEDเป็น 0 ตัวประมวลผลล่วงหน้าจะขยายการเรียกfunc2ไปยังคำสั่งที่เพิ่งอ่าน

    (-1);

และคอมไพเลอร์พาสจะเห็นเพียงคำสั่ง "ฟุ่มเฟือย" ที่ดูเหมือนจะรบกวนคุณ ยังมีอีกอย่างหนึ่งที่สามารถทำได้? หากคุณ#define do_stuff() // nothingแล้วรหัสในfunc1จะแตก (และคุณยังคงมีคำสั่งที่ว่างอยู่ในfunc2ที่เพิ่งอ่าน;ซึ่งอาจจะฟุ่มเฟือยมากขึ้น) ในทางกลับกันถ้าคุณต้องกำหนดdo_stuff()ฟังก์ชั่นที่ส่งกลับ -1 จริง ๆ คุณอาจต้องเสียค่าใช้จ่ายในการเรียกใช้ฟังก์ชัน ไม่มีเหตุผลที่ดี


รุ่นคลาสสิกมากขึ้น (หรือไม่ผมหมายถึงรุ่นทั่วไป) ของไม่มี-op ((void)0)คือ
Jonathan Leffler

assertเป็นตัวอย่างที่ดีของที่นี่คือ
Neil

3

ประโยคง่าย ๆ ใน C ถูกยกเลิกโดยเครื่องหมายอัฒภาค

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

ต้องบอกว่า "คอมไพเลอร์อัจฉริยะ" บางคนอาจทิ้ง 5; และฉัน; งบ


ฉันนึกภาพผู้แปลไม่ออกว่าจะทำอะไรกับแถลงการณ์อื่นนอกเหนือไปจากนั้น พวกเขาทำอะไรกับพวกเขาได้อีก?
Jeremy Friesner

@JeremyFriesner: คอมไพเลอร์ที่เรียบง่ายและไม่ใช้การเพิ่มประสิทธิภาพอาจสร้างโค้ดเพื่อคำนวณค่าและวางผลลัพธ์ใน register (จากจุดที่มันจะถูกละเว้น)
Nate Eldredge

มาตรฐาน C ไม่ใช่คำว่า "คำสั่งง่าย ๆ " งบแสดงออกประกอบด้วย (อุปกรณ์เสริม) การแสดงออกตามด้วยเครื่องหมายอัฒภาค ไม่ใช่ทุกนิพจน์ที่ส่งผลให้เกิดค่า การแสดงออกของประเภทvoidไม่มีค่า
Keith Thompson

2

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

งบแสดงออกประกอบด้วยการแสดงออกตามด้วยเครื่องหมายอัฒภาค พฤติกรรมของมันคือการประเมินการแสดงออกและทิ้งผล (ถ้ามี) โดยทั่วไปแล้วจุดประสงค์คือการประเมินผลของการแสดงออกมีผลข้างเคียง แต่ไม่ง่ายเสมอไปหรืออาจเป็นไปได้ที่จะตัดสินว่าการแสดงออกที่ให้นั้นมีผลข้างเคียงหรือไม่

ตัวอย่างเช่นการเรียกใช้ฟังก์ชันเป็นการแสดงออกดังนั้นการเรียกใช้ฟังก์ชันตามด้วยเครื่องหมายอัฒภาคจึงเป็นข้อความ ข้อความนี้มีผลข้างเคียงหรือไม่?

some_function();

some_functionมันเป็นไปไม่ได้ที่จะบอกไม่เห็นการดำเนินงานของ

แล้วเรื่องนี้ล่ะ

obj;

อาจไม่ใช่ - แต่ถ้าobjกำหนดไว้เป็นอย่างvolatileนั้น

การอนุญาตให้แสดงออกใด ๆที่จะทำให้เป็นคำสั่งการแสดงออกโดยการเพิ่มเครื่องหมายอัฒภาคทำให้คำนิยามภาษาง่ายขึ้น การกำหนดให้นิพจน์มีผลข้างเคียงจะเพิ่มความซับซ้อนในการกำหนดภาษาและคอมไพเลอร์ C ถูกสร้างขึ้นบนชุดของกฎที่สอดคล้องกัน (การเรียกใช้ฟังก์ชันคือนิพจน์, การกำหนดค่าเป็นนิพจน์, นิพจน์ตามด้วยเครื่องหมายอัฒภาคคือคำสั่ง) และอนุญาตให้โปรแกรมเมอร์ทำสิ่งที่พวกเขาต้องการโดยไม่ป้องกันพวกเขาจากการทำสิ่งต่าง ๆ


2

ข้อความสั่งที่คุณแสดงรายการโดยไม่มีผลกระทบเป็นตัวอย่างของข้อความสั่งนิพจน์ซึ่งมีไวยากรณ์ให้ไว้ในส่วน 6.8.3p1 ของมาตรฐาน Cดังนี้:

 คำสั่งexpression  :
    expression opt ;

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

ดังนั้นการแสดงออกใด ๆตามด้วยเครื่องหมายอัฒภาคจึงมีคุณสมบัติเป็นคำสั่งการแสดงออก ในความเป็นจริงแต่ละบรรทัดเหล่านี้จากรหัสของคุณเป็นตัวอย่างของคำสั่ง expression:

i = i + 2;
5;
i;
printf("i: %d\n", i);

ผู้ประกอบการบางคนมีผลข้างเคียงเช่นชุดของผู้ประกอบการที่ได้รับมอบหมายและผู้ประกอบการที่เพิ่มขึ้น / ลดลงก่อน / หลังและผู้ประกอบการเรียกฟังก์ชั่น() อาจมีผลข้างเคียงขึ้นอยู่กับสิ่งที่ฟังก์ชั่นในคำถาม ไม่มีข้อกำหนดใด ๆ แต่ผู้ประกอบการรายหนึ่งต้องมีผลข้างเคียง

นี่เป็นอีกตัวอย่าง:

atoi("1");

นี่คือการเรียกใช้ฟังก์ชันและยกเลิกผลลัพธ์เช่นเดียวกับการโทรprintfในตัวอย่างของคุณ แต่การprintfเรียกฟังก์ชันที่ไม่เหมือนกันนั้นจะไม่มีผลข้างเคียง


1

บางครั้งข้อความดังกล่าวมีประโยชน์มาก:

int foo(int x, int y, int z)
{
    (void)y;   //prevents warning
    (void)z;

    return x*x;
}

หรือเมื่อคู่มืออ้างอิงบอกให้เราอ่านทะเบียนเพื่อเก็บบางสิ่ง - ตัวอย่างเช่นเพื่อล้างหรือตั้งค่าสถานะบางอย่าง (สถานการณ์ทั่วไปในโลก uC)

#define SREG   ((volatile uint32_t *)0x4000000)
#define DREG   ((volatile uint32_t *)0x4004000)

void readSREG(void)
{
    *SREG;   //we read it here
    *DREG;   // and here
}

https://godbolt.org/z/6wjh_5


เมื่อ*SREGมีความผันผวน*SREG;จะไม่มีผลในรูปแบบที่ระบุโดยมาตรฐาน C มาตรฐาน C ระบุว่ามีผลข้างเคียงที่สังเกตได้
Eric Postpischil

@EricPostpischil - ไม่มันไม่มีเอฟเฟกต์ที่สังเกตได้แต่ถ้ามีเอฟเฟกต์ ไม่มีการเปลี่ยนแปลงวัตถุที่มองเห็นได้ C
P__J__

C 2018 5.1.2.3 6 กำหนดพฤติกรรมที่สังเกตได้ของโปรแกรมเพื่อรวมว่า“ การเข้าถึงวัตถุระเหยได้รับการประเมินอย่างเคร่งครัดตามกฎของเครื่องนามธรรม” ไม่มีคำถามเกี่ยวกับการตีความหรือการหักเงิน นี่คือคำจำกัดความของพฤติกรรมที่สังเกตได้
Eric Postpischil
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.