簡體   English   中英

GCC可以警告我修改C99中const結構的字段嗎?

[英]Can GCC warn me about modifying the fields of a const struct in C99?

我在嘗試制作常量正確的代碼時偶然發現了一個小問題。

我本來想寫一個函數,它接受一個const結構的指針,告訴編譯器“請告訴我,如果我正在修改結構,因為我真的不想”。

我突然想到編譯器會允許我這樣做:

struct A
{
    char *ptrChar;
};

void f(const struct A *ptrA)
{
    ptrA->ptrChar[0] = 'A'; // NOT DESIRED!!
}

這是可以理解的,因為實際上const是指針本身,而不是它指向的類型。 我想讓編譯器告訴我,我正在做一些我不想做的事情,但是,如果可能的話。

我使用gcc作為我的編譯器。 雖然我知道上面的代碼應該是合法的,但我仍然檢查它是否會發出警告,但沒有任何結果。 我的命令行是:

gcc -std=c99 -Wall -Wextra -pedantic test.c

有可能解決這個問題嗎?

如果需要,一種設計方法的方法是對同一對象使用兩種不同的類型:一種讀/寫類型和一種只讀類型。

typedef struct
{
  char *ptrChar;
} A_rw;

typedef struct
{
  const char* ptrChar;
} A_ro;


typedef union
{
  A_rw rw;
  A_ro ro;  
} A;

如果函數需要修改對象,則將讀寫類型作為參數,否則采用只讀類型。

void modify (A_rw* a)
{
  a->ptrChar[0] = 'A';
}

void print (const A_ro* a)
{
  puts(a->ptrChar);
}

為了使調用者界面更加一致並使其保持一致,您可以使用包裝器函數作為ADT的公共接口:

inline void A_modify (A* a)
{
  modify(&a->rw);
}

inline void A_print (const A* a)
{
  print(&a->ro);
}

使用此方法, A現在可以實現為opaque類型,以隱藏調用者的實現。

這是一個實現與接口或“信息隱藏” - 或者說非隱藏;-)-問題的示例。 在C ++中,只需將指針設為私有,並定義合適的公共const訪問器。 或者可以使用訪問者定義抽象類 - “接口”。 正確的結構將實現這一點。 不需要創建結構實例的用戶只需要查看該接口。

在C中,可以通過定義一個函數來模擬它,該函數將指向struct的指針作為參數並返回指向const char的指針。 對於不創建這些結構實例的用戶,甚至可以提供一個“用戶頭”,它不會泄漏結構的實現,而只是定義操縱函數(或返回,如工廠)指針。 這使結構保持不完整的類型(因此只能使用指向實例的指針)。 這種模式有效地模擬了C ++在幕后使用this指針所做的this

這是C語言的已知問題,不可避免。 畢竟,您沒有修改結構,而是通過從結構中獲取的非const限定指針修改單獨的對象。 const語義最初的設計圍繞着需要將內存區域標記為物理上不可寫的常量,而不是圍繞防御性編程的任何顧慮。

也許如果您決定使用C11,您可以實現一個Generic宏,它引用同一成員的常量或變量版本(您還應該在結構中包含一個union)。 像這樣的東西:

struct A
{
    union {
        char *m_ptrChar;

        const char *m_cptrChar;
    } ;
};

#define ptrChar_m(a) _Generic(a, struct A *: a->m_ptrChar,        \
                                 const struct A *: a->m_cptrChar)//,  \
                                 //struct A: a.m_ptrChar,        \
                                 //const struct A: a.m_cptrChar)

void f(const struct A *ptrA)
{
    ptrChar_m(ptrA) = 'A'; // NOT DESIRED!!
}

聯盟為一個成員創建了2個解釋。 所述m_cptrChar是一個指向恆定炭和m_ptrChar到非恆定。 然后宏根據它的參數類型決定引用哪個。

唯一的問題是宏ptrChar_m只能與這個結構的指針或對象一起工作,而不能同時工作。

我們可以隱藏一些“訪問者”功能背后的信息:

// header
struct A;           // incomplete type
char *getPtr(struct A *);
const char *getCPtr(const struct A *);

// implementation
struct A { char *ptr };
char *getPtr(struct A *a) { return a->ptr; }
const char *getCPtr(const struct A *a) { return a->ptr; }

不,除非您將結構定義更改為:

struct A
{
    const char *ptrChar;
};

保持舊結構定義不變的另一個復雜解決方案是定義具有相同成員的新結構,其相關指針成員設置為:指向const類型。 然后,您調用的函數將更改為采用新結構。 定義了一個包裝函數,該函數接受舊結構,通過成員復制將成員復制到新結構並將其傳遞給函數。

GCC可以警告我修改C99中const結構的字段嗎?

您沒有修改const結構的字段。

struct A的值包含指向非const char的指針。 ptrA是指向const結構A的指針。因此,您無法在* ptrA處更改struct A值。 所以你不能在(* ptrA).Char aka ptrA-> ptrChar更改指向char的指針。 但是你要改變ptrA-> ptrChar指向的值,即*(ptrA-> Char)的值,也就是ptrA-> Char [0]。 這里唯一的結果是結構為,你沒有改變結構A,那么什么是“不希望”?

如果您不想允許更改struct A的Char字段指向的值(通過該結構A),則使用

struct A
{
    const char *ptrChar; // or char const *ptrChar;
};

但也許你認為你在f中做的事情是這樣的

void f(const struct A *ptrA)
{
    const char c = 'A';
    ptrA->ptrChar = &c;
}

哪個會從編譯器中獲得錯誤。

暫無
暫無

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

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