簡體   English   中英

gcc 和 clang 給出不同的結果

[英]gcc and clang are giving different results

#include <stdio.h> 

int main(int argc, char *argv[]){

    int a;
    int *b = &a;
    a = 10;

    printf("%d %d\n", a, *b);

    int p = 20;
    int *q;
    *q = p;

    printf("%d %d\n", p, *q);

    int *t = NULL;

    return 0;
}

使用 gcc 編譯的上述程序在執行時會出現分段錯誤。 但是當用 clang 編譯時,它執行時不會出現分段錯誤。 任何人都可以給出原因嗎? gcc 版本是 9.3.0,clang 版本是 10.0.0。 操作系統是 ubuntu 20.04

問題:


問題不是源於編譯器,而是在代碼本身,特別是*q = p ,當您取消引用指針時,即使用* ,您正在訪問存儲在指針中的值,該值是它指向的地址,在在這種情況下,代碼無效,因為沒有分配給q內存,它不指向任何地方(至少我們不希望它指向任何地方)。 你不能在它指向的內存中存儲任何東西,因為它不存在或者是由一些可能存儲在q垃圾值給出的隨機內存位置。

行為解釋:


鑒於上述情況,並且知道存儲在q中的值可以是任何值,您可以並且應該在不同的編譯器、同一編譯器的不同版本,甚至同一台機器上的同一編譯器中但在同一編譯器的不同執行中預期不同的結果。程序,在某些時候它甚至可能以萬分之一的機會指向您想要的位置,然后程序會給您預期的結果,因此您的程序行為是 undefined

修復它:


如前所述, q需要指向某個有效的內存位置,然后才能在該內存位置存儲值。 您可以通過分配內存並將其分配給q或使q指向現有的有效內存地址來實現,例如

int p;
int *q;
q = &p; // now q points to p, i.e. its value is the address of p

現在你可以這樣做:

*q = 10; // stores 10 in the memory address pointed by(stored in) q, the address of p
         // p is now 10

或者

int value = 20;
*q = value; // stores a copy of value in the address of the variable pointed by q, again p
            // p is now 20

額外的:


請注意,如果您使用額外的警告標志,例如-Wall-Wextra等,編譯器可能會警告您有關錯誤構造的信息,例如您擁有的構造。

我不是編譯器方面的專家,但您肯定會在這里觸發一些未定義的行為:

int *q;
*q=p;
printf("%d %d\n",p,*q);

您在初始化之前取消引用指針q 這種段錯誤(或者更確切地說,不是段錯誤)的原因可能很少。 q可以指向任何內存位置,例如,它可以在 Clang 的情況下從堆棧中彈出后保存b舊值,從而寫入不受限制的內存。

不過,不確定您對這段代碼的初衷是什么。

原因是如果您使用尚未初始化的變量,任何事情都可能發生。 如果您在啟用警告的情況下編譯此程序,您應該會收到類似的警告

t.c:10:5: warning: ‘q’ is used uninitialized in this function [-Wuninitialized]
  *q = p;
  ~~~^~~

在初始化之前,變量可以具有恰好位於分配變量的內存位置的任何值。 這就是運行時行為不可預測的原因。 下圖說明了在分配p之前的情況:

在此處輸入圖片說明

由於我們不知道q指向何處,因此我們無法取消引用(跟隨)指針。

任何人都可以給出原因嗎?

原因來自於 C 語言本身以及編譯器是如何構建的。

首先,C 語言 - 您的代碼調用未定義的行為。 首先,因為使用未初始化的變量是未定義的行為,但顯然是因為您在“無效”指針上應用*運算符。 最重要的是,存在未定義的行為

現在,因為存在未定義的行為,編譯器可以他們想做的事情並生成他們想要的代碼。 簡而言之——沒有要求

因此,編譯器編寫者並不關心編譯器在未定義行為情況下會做什么。 在編譯此特定代碼時,兩個編譯器的構造不同,在此特定情況下的行為也不同。 這不是故意的 - 沒有人關心,所以一些無關領域的隨機無關決定導致了兩個編譯器的這種行為。

兩個編譯器的行為不同的具體原因將來自檢查兩個編譯器的源代碼。 在這種情況下,檢查llvm及其文檔gccgcc 開發人員選項將在此過程中有所幫助。

*q=p; 使用未初始化的q值; 訪問未初始化的變量是未定義的行為,允許編譯器以任何方式解釋該行代碼以及該行之前或之后的任何內容。

對於不同的優化級別,它也可能會給出不同的結果。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM