簡體   English   中英

C 與 C++ 將結構放置在無符號字符緩沖區中

[英]C vs C++ placing structs in unsigned char buffer

C 是否有任何類似於 C++ 的東西,其中可以將結構放置在無符號字符緩沖區中,就像在 C++ 中所做的那樣,如標准sec 所示。 6.7.2

template<typename ...T>
struct AlignedUnion {
  alignas(T...) unsigned char data[max(sizeof(T)...)];
};
int f() {
  AlignedUnion<int, char> au;
  int *p = new (au.data) int;           // OK, au.data provides storage
  char *c = new (au.data) char();       // OK, ends lifetime of *p
  char *d = new (au.data + 1) char();
  return *c + *d;                       // OK
}

在 C 中,我當然可以將一個事物結構(或如上所示的 int)memcpy 到一個無符號字符緩沖區中,但隨后使用指向該結構的指針會遇到嚴格的別名違規; 緩沖區具有不同的聲明類型。

因此,假設有人想在 C 中復制上述 C++ 中的第二行。 一個人會做這樣的事情

#include<string.h>
#include<stdio.h>
struct Buffer {
  unsigned char data[sizeof(int)];
};

int main()
{ 
  struct Buffer b;
  int n = 5;
  int* p = memcpy(&b.data,&n,sizeof(int));
  printf("%d",*p);  // aliasing violation here as unsigned char is accessed as int
  return 0;
}

通常建議使用聯合,即union Buffer {int i;unsigned char b[sizeof(int)]}; 但是,如果緩沖區的目的是充當存儲(即在其中放置不同大小的類型,通過將指針推進到緩沖區中的空閑部分 + 可能更多一些以進行正確對齊),那么這並不是那么好。

您是否嘗試過使用union

#include <string.h>
#include <stdio.h>

union Buffer {
    int int_;
    double double_;
    long double long_double_;
    unsigned char data[1];
};

int main() {
    union Buffer b;
    int n = 5;
    int *p = memcpy(&b.data, &n, sizeof(int));
    printf("%d", *p);  // aliasing violation here as unsigned char is accessed as int
    return 0;
}

Buffer根據 alignment 要求最大的類型對齊data成員。

是的,由於嚴格的別名規則,這是不可能的。 因為不可能編寫符合標准的malloc()

您的緩沖區未對齊 - 需要添加 stdalign.h 中的stdalign.h alignas(int)

如果要防止編譯器優化,請:

  • 只需轉換指針並訪問它並使用-fno-strict-aliasing編譯,或使用volatile
  • 或者將緩沖區的訪問器移動到另一個沒有 LTO 編譯的文件,這樣編譯器就無法優化它。

// mybuffer.c
#include <stdalign.h>
alignas(int) unsigned char buffer[sizeof(int)];
void *getbuffer() { return buffer; }


// main.c
#include <string.h>
#include <stdio.h>
#include "mybuffer.h"
int main() {
  void *data = getbuffer();
  // int *p = new (au.data) int;           // OK, au.data provides storage
  int *p = data;
  // char *c = new (au.data) char();       // OK, ends lifetime of *p
  char *c = data;
  *c = 0;
  // char *d = new (au.data + 1) char();
  char *d = (char*)data + 1;
  *d = 0;

  return *c + *d;
}

6.5p6 中有效類型定義的編寫方式,不清楚它在所有極端情況下應該意味着什么——可能是因為委員會成員之間從未就如何處理所有極端情況達成共識。 缺陷報告通常會增加混亂而不是清晰,因為當標准和缺陷報告都沒有指定要設置或更改哪些操作時,它們使用諸如工會的“活躍成員”之類的術語。

如果想使用 object 或 static 或自動持續時間,就好像它是沒有聲明類型的緩沖區一樣,一種安全的方法應該是執行以下操作:

void volatile *volatile dummy_vp;

void test(void)
{
  union {
    char dat[1000];
    unsigned long force_alignment;
  } buffer;
  void *volatile launder = buffer.dat;
  dummy_vp = &launder;
  void *storage_blob = launder;
  ...
}

除非實現不遺余力地測試launder的讀取是否碰巧產生了與buffer.dat匹配的地址,否則它將無法知道該地址處的 object 是否具有聲明的類型。 如果地址恰好與buffer.dat的地址匹配,標准中的任何內容都不會禁止實現無意義的行為,但是性能改進證明檢查成本合理的情況不太常見,編譯器無法嘗試這種“優化” ”。

暫無
暫無

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

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