天天看點

一次緊急線上事故引發的嚴重問題

整個過程大概是這樣的:

甲方和乙方接口需要資料傳輸,比如 甲方AA傳遞到乙方BB,乙方想用甲方AA中的資料,由于某方後期修改了自己的定義,導緻sizeof不統一,整個系統發生了崩盤,引發了一次嚴重的線上事故。

甲方AA:

typedef struct aa
{
    int id;
    char a[6];
    enum day{
        one,
        two,
        three =4,
        four = 12
    };     
    double w;   
    float h;     
    long g;   
}AA;           

複制

乙方BB:

typedef struct bb
{
    int id;
    char a[3];
    enum day{
        one,
        two,
        three =4,
        four = 100
    }; 
    double w;           
    float h;    
    long g;
}BB;           

複制

雙方通信:

AA aa = {1,"wxh",2.2,3.3,4321};

  BB bb = *(BB*)(&aa);
  cout<<sizeof(AA)<<" "<<sizeof(BB)<<" "<<aa.h<<" "<<bb.h<<endl;

  memcpy(&bb,&aa,sizeof(aa));
  
  cout<<sizeof(AA)<<" "<<sizeof(BB)<<" "<<aa.h<<" "<<bb.h<<endl;           

複制

結果:

40 32 3.3 -1.58819e-23

40 32 3.3 -1.58819e-23

#pragma pack(1) //1位元組對齊

30 27 3.3 4.47049e-08

30 27 3.3 4.47049e-08

本來甲方傳了一個 3.3,而乙方得到的确是越界的負值 ,引發了系統崩潰。

一次緊急線上事故引發的嚴重問題
類型 32位 64位
char 1 1
int 4 大多數4,少數8
short 2 2
long 4 8
float 4 4
double 8 8
指針 4 8
enum 4 4

對齊原因:

1,性能問題:資料結構盡可能在自然邊界上對齊,若通路未對齊的記憶體,處理器需要作兩次記憶體通路,而對齊的記憶體隻要通路一次。

2,空間問題:沒有進行記憶體對齊的結構體或類會浪費一定空間,當建立對象越多時,消耗的空間也越多。

3,平台問題:不是所有的硬體平台都能通路任意位址上的任意資料的,某些硬體平台隻能在某些位址處取某些特定類型的資料,否則抛出硬體異常。

一次緊急線上事故引發的嚴重問題

對齊規則:

struct:

1,結構體變量中成員的便移量必須是成員大小的整數倍數;

2,結構體大小必須是最寬基本類型大小的整數倍。

union:

1,所有成員中最長的那個;

2,整個結構體大小必須是最寬成員大小的整數倍。

enum:

1, 定義一個常量集合,當int存儲,都為4。

一次緊急線上事故引發的嚴重問題

是以:在定義結構體成員時為節省記憶體,一般将同一類型定義在一起,長類型放在最開始或者最後。

附:struct與union互相嵌套的技巧

#include <stdlib.h>
#include <stdio.h>
 
// 參考 Glibc庫源碼 sig_info.h
typedef struct info{
  int age;
  union {
    int code;
    struct {
      int pid;
      int uid;
    } id;
    struct {
      int KM;
      char *addr_name;
    } address;
  } u;
} info_t;
 
#define code     u.code
#define pid     u.id.pid
#define uid      u.id.uid
#define KM      u.address.KM
#define addr_name   u.address.addr_name
 
int main(int argc, char **argv)
{
  info_t info;
  
  info.age   = 20;
  info.code   = 5;
  
  printf("age = %d\n", info.age);      // 20
  printf("code = %d\n", info.code);    // 5
  
  printf("----------------------\n");
  info.pid = 123;
  printf("age = %d\n", info.age);      // 20
  printf("code = %d\n", info.code);    // 123, 因為code變量與id變量共用一塊記憶體
  printf("pid = %d\n", info.pid);      // 123
 
  printf("----------------------\n");
  info.KM = 789;
  printf("age = %d\n",  info.age);    // 20
  printf("code = %d\n", info.code);    // 789, 因為code變量與address變量共用一塊記憶體
  printf("pid = %d\n",  info.pid);    // 789, 因為id變量與address變量共用一塊記憶體
  printf("KM = %d\n",   info.KM);      // 789
  return 0;
}           

複制