国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

C語言——自定義類型結(jié)構(gòu)體_學(xué)習(xí)筆記

這篇具有很好參考價(jià)值的文章主要介紹了C語言——自定義類型結(jié)構(gòu)體_學(xué)習(xí)筆記。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

結(jié)構(gòu)體的基本概念

結(jié)構(gòu)體是一種用戶自定義的數(shù)據(jù)類型,可以包含多個(gè)不同類型的變量。通過使用結(jié)構(gòu)體,我們可以將相關(guān)聯(lián)的數(shù)據(jù)組織在一起,便于管理和使用。

結(jié)構(gòu)體的聲明

正常的結(jié)構(gòu)體聲明

在C語言中,結(jié)構(gòu)體(struct)指的是一種數(shù)據(jù)結(jié)構(gòu),是C語言中聚合數(shù)據(jù)類型的一類。
結(jié)構(gòu)體可以包含多個(gè)不同類型的數(shù)據(jù)成員,例如:int、float、char等。

結(jié)構(gòu)體的聲明方式如下:

struct 結(jié)構(gòu)體名 {
    類型1 數(shù)據(jù)成員1;
    類型2 數(shù)據(jù)成員2;
    ...
    類型n 數(shù)據(jù)成員n;
};

例如:

struct student {
    int id;
    char name[50];
    int age;
};                  //注意,結(jié)構(gòu)體定義后面的分號(hào)不要忘了

以上屬于是正常的結(jié)構(gòu)體聲明↑↑↑↑↑↑↑↑↑↑↑↑

匿名結(jié)構(gòu)體聲明

既然有正常的,那就有特殊的結(jié)構(gòu)體聲明了↓↓↓↓↓↓↓↓↓↓↓↓↓↓
在聲明結(jié)構(gòu)體的時(shí)候,可以不完全的聲明。(匿名結(jié)構(gòu)體類型)
如下:

//匿名結(jié)構(gòu)體類型
struct
{
	int a;
	char b;
	float c;
}x;
struct 
{
	int a;
	char b;
	float c;
}*p;
  • p是結(jié)構(gòu)體指針,這里可以看到,上下兩個(gè)結(jié)構(gòu)體的成員內(nèi)容都是一模一樣的,
  • 能不能實(shí)現(xiàn) p = &x 呢?
  • 答案是不能,編譯器會(huì)把上面的兩個(gè)聲明當(dāng)成完全不同的結(jié)構(gòu)體類型,所以p不能放x的地址!??!

上面兩個(gè)結(jié)構(gòu)體在聲明的時(shí)候省略掉了結(jié)構(gòu)體標(biāo)簽,這樣聲明也是可以的,但是!匿名的結(jié)構(gòu)體類型,只能在創(chuàng)建的時(shí)候定義結(jié)構(gòu)體變量,如果沒有對(duì)結(jié)構(gòu)體類型重命名的話,基本上只能使用一次。

typedef 結(jié)構(gòu)體起別名

可以使用typedef關(guān)鍵字為結(jié)構(gòu)體起別名,這樣一些名字很長的結(jié)構(gòu)體名字就可以簡化,這樣當(dāng)我們需要頻繁創(chuàng)建結(jié)構(gòu)體變量的時(shí)候就比較方便。
例如:

#include <stdio.h>

struct Chinese_Student {
    char name[50];
    int age;
    float score;
};

typedef struct Chinese_Student Student; // 為結(jié)構(gòu)體起別名Student

int main() {
    Student stu = {"Tom", 20, 88.5}; // 創(chuàng)建并初始化結(jié)構(gòu)體變量,其實(shí)就等價(jià)于Chinese_Student stu = {"Tom", 20, 88.5};
    printf("Name: %s\n", stu.name); // 訪問結(jié)構(gòu)體變量的成員
    printf("Age: %d\n", stu.age);
    printf("Score: %.1f\n", stu.score);
    return 0;
}

結(jié)構(gòu)體的自引用

在結(jié)構(gòu)體中包含一個(gè)類型為結(jié)構(gòu)體本身的成員可以嗎?
比如下面這段代碼:

struct Node
{
	int data;
	struct Node next;
};                      //error報(bào)錯(cuò)

這段代碼是會(huì)報(bào)錯(cuò)的,因?yàn)榻Y(jié)構(gòu)體中包含同一個(gè)類型的結(jié)構(gòu)體作為成員,這樣結(jié)構(gòu)體變量的大小就會(huì)無窮的大,是不合理的。

正確的結(jié)構(gòu)體自引用方式,使用結(jié)構(gòu)體指針

struct Node
{
	int data;
	struct Node* next;
};  

結(jié)構(gòu)體變量的創(chuàng)建和初始化

對(duì)于結(jié)構(gòu)體變量的創(chuàng)建和初始化,用示例代碼來進(jìn)行說明:

#include <stdio.h>

//創(chuàng)建結(jié)構(gòu)體變量前,要先對(duì)結(jié)構(gòu)體進(jìn)行聲明↓↓↓↓↓↓
struct student {
    char name[50];  //這里聲明一個(gè)student類型的結(jié)構(gòu)體,有三個(gè)成員
    int age;        //分別是字符類型姓名,整型類型年齡,浮點(diǎn)型類型分?jǐn)?shù)
    float score;
};

int main() {
   
    // 可以在創(chuàng)建結(jié)構(gòu)體變量的時(shí)候同時(shí)按聲明中的結(jié)構(gòu)體成員順序?qū)Y(jié)構(gòu)體變量進(jìn)行初始化操作↓↓
    struct student stu1 = {"Tom", 20, 88.5}; 
    
    //也可以用成員訪問操作符,不按照成員順序?qū)Y(jié)構(gòu)體成員進(jìn)行初始化操作↓↓↓↓↓
    struct student stu2 = {.age=18, .name="weil", .score=99.9}; 
    
	// 訪問結(jié)構(gòu)體變量的成員↓↓↓↓↓↓↓
    printf("Name: %s\n", stu2.name); 
    printf("Age: %d\n", stu2.age);
    printf("Score: %.1f\n", stu2.score);
    return 0;
}

結(jié)構(gòu)體內(nèi)存對(duì)齊

對(duì)于結(jié)構(gòu)體的大小,我們可以用sizeof 運(yùn)算符進(jìn)行計(jì)算
如下:

struct S1
{
	char c1;       //1字節(jié)
	int i;         //4字節(jié)
	char c2;       //1字節(jié)
};
int main()
{
	printf("%zd\n", sizeof(struct S1));
	return 0;
}

運(yùn)行結(jié)果為
C語言——自定義類型結(jié)構(gòu)體_學(xué)習(xí)筆記,C語言學(xué)習(xí)筆記,c語言,學(xué)習(xí),筆記
上面代碼的結(jié)構(gòu)體占的字節(jié)數(shù)大小為什么會(huì)是12個(gè)字節(jié)呢?

這就涉及到結(jié)構(gòu)體內(nèi)存對(duì)齊的知識(shí)點(diǎn)了↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

結(jié)構(gòu)體內(nèi)存對(duì)齊規(guī)定

  1. 結(jié)構(gòu)體的第一個(gè)成員對(duì)齊到相對(duì)結(jié)構(gòu)體變量起始位置偏移量為0的地址處
  2. 其他成員變量要對(duì)齊到某個(gè)數(shù)字(對(duì)齊數(shù))的整數(shù)倍的地址處。
    對(duì)齊數(shù)=編譯器默認(rèn)的一個(gè)對(duì)齊數(shù)與該成員變量大小的較小值。
    VS中默認(rèn)的值為8
    Linux中沒有默認(rèn)對(duì)齊數(shù),對(duì)齊數(shù)就是成員自身的大小
  3. 結(jié)構(gòu)體總大小為最大對(duì)齊數(shù)(結(jié)構(gòu)體中每個(gè)成員變量都有一個(gè)對(duì)齊數(shù),所有對(duì)齊數(shù)中最大的)的整數(shù)倍。
  4. 如果嵌套了結(jié)構(gòu)體的情況,嵌套的結(jié)構(gòu)體成員對(duì)齊到自己的成員中最大對(duì)齊數(shù)的整數(shù)倍處,結(jié)構(gòu)體的整體大小就是所有最大對(duì)齊數(shù)(含嵌套結(jié)構(gòu)體中成員的對(duì)齊數(shù))的整數(shù)倍。

為什么存在內(nèi)存對(duì)齊

大部分的參考資料都是這樣說的:

1.平臺(tái)原因(移植原因):

不是所有的硬件平臺(tái)都能訪問任意地址上的任意數(shù)據(jù)的;某些硬件平臺(tái)只能在某些地址處取某些特定類型的數(shù)據(jù),否則拋出硬件異常。

2.性能原因:

數(shù)據(jù)結(jié)構(gòu)(尤其是棧)應(yīng)該盡可能地在自然邊界上對(duì)齊。原因在于,為了訪問未對(duì)齊的內(nèi)存,處理器需要作兩次內(nèi)存訪問;而對(duì)齊的內(nèi)存訪問僅需要一次訪問。

假設(shè)一個(gè)處理器總是從內(nèi)存中取8個(gè)字節(jié),則地址必須是8的倍數(shù)。如果我們能保證將所有的double類型的數(shù)據(jù)的地址都對(duì)齊成8的倍數(shù),那么就可以用一個(gè)內(nèi)存操作來讀或者寫值了。否則,我們可能需要執(zhí)行兩次內(nèi)存訪問,因?yàn)閷?duì)象可能被分放在兩個(gè)8字節(jié)內(nèi)存塊中。

總體來說:結(jié)構(gòu)體的內(nèi)存對(duì)齊是拿空間來換取時(shí)間的做法。

舉例解釋

struct S1
{
    char c1;
    char c2;
    int i;
};
struct S2
{
	char c1;
	int i;
	char c2;
};
int main()
{
	printf("%zd\n", sizeof(struct S1));
	printf("%zd\n", sizeof(struct S2));
	return 0;
}

以上代碼運(yùn)行結(jié)果(VS 2022):
C語言——自定義類型結(jié)構(gòu)體_學(xué)習(xí)筆記,C語言學(xué)習(xí)筆記,c語言,學(xué)習(xí),筆記
結(jié)構(gòu)體中的成員類型相同,不同的排列順序也會(huì)影響結(jié)構(gòu)體變量的大小,這就是結(jié)構(gòu)體內(nèi)存對(duì)齊的奧妙,接下來畫內(nèi)存布局圖來進(jìn)行解釋:
C語言——自定義類型結(jié)構(gòu)體_學(xué)習(xí)筆記,C語言學(xué)習(xí)筆記,c語言,學(xué)習(xí),筆記

那在設(shè)計(jì)結(jié)構(gòu)體的時(shí)候,我們既要滿足對(duì)齊,又要節(jié)省空間,就要讓占用空間小的成員盡量集中在一起

結(jié)構(gòu)體數(shù)組

↓↓↓↓↓↓↓↓↓↓↓↓↓創(chuàng)建結(jié)構(gòu)體數(shù)組 & 遍歷訪問結(jié)構(gòu)體數(shù)組↓↓↓↓↓↓↓↓↓↓↓↓↓
代碼示例如下:

#include <stdio.h>

// 定義一個(gè)結(jié)構(gòu)體類型
struct Student {
    char name[20];
    int age;
    float score;
};

int main() {
    // 創(chuàng)建結(jié)構(gòu)體數(shù)組并初始化
    struct Student students[] = {
        {"Tom", 20, 88.5},
        {"Jack", 21, 92.0},
        {"Alice", 19, 95.5}
    };

    // 遍歷訪問打印結(jié)構(gòu)體數(shù)組中的所有內(nèi)容
    int len = sizeof(students) / sizeof(struct Student);  // 計(jì)算數(shù)組長度
    for (int i = 0; i < len; i++) {
        printf("Name: %s\n", students[i].name);
        printf("Age: %d\n", students[i].age);
        printf("Score: %.1f\n", students[i].score);
    }

    return 0;
}

在這個(gè)示例中,我們首先定義了一個(gè)名為Student的結(jié)構(gòu)體類型,包含三個(gè)成員變量:name、age和score。然后,我們在main函數(shù)中創(chuàng)建了一個(gè)Student類型的結(jié)構(gòu)體數(shù)組students,并初始化其中的元素。接著,我們使用一個(gè)for循環(huán)遍歷訪問數(shù)組中的每個(gè)元素,并使用printf函數(shù)打印出每個(gè)學(xué)生的姓名、年齡和分?jǐn)?shù)。

在訪問結(jié)構(gòu)體數(shù)組元素時(shí),我們使用了結(jié)構(gòu)體變量名加上索引的方式來訪問,例如students[i].name表示訪問第i個(gè)元素的name成員變量。

結(jié)構(gòu)體指針

↓↓↓↓↓↓↓↓↓↓↓↓↓結(jié)構(gòu)體指針的創(chuàng)建 & 初始化 & 成員訪問↓↓↓↓↓↓↓↓↓↓↓↓↓

#include <stdio.h>

// 定義一個(gè)結(jié)構(gòu)體類型
struct Student {
    char name[20];
    int age;
    float score;
};

int main() {
    // 創(chuàng)建結(jié)構(gòu)體變量并初始化
    struct Student stu1 = {"Tom", 20, 88.5};
    struct Student stu2 = {"Jack", 21, 92.0};
    struct Student stu3 = {"Alice", 19, 95.5};

    // 創(chuàng)建結(jié)構(gòu)體指針變量
    struct Student *stuPtr;

    // 將結(jié)構(gòu)體變量的地址賦給結(jié)構(gòu)體指針變量
    stuPtr = &stu1;

    // 遍歷打印結(jié)構(gòu)體指針中的內(nèi)容
    printf("Name: %s\n", stuPtr->name);
    printf("Age: %d\n", stuPtr->age);
    printf("Score: %.1f\n", stuPtr->score);

    // 將結(jié)構(gòu)體指針變量指向下一個(gè)結(jié)構(gòu)體變量
    stuPtr = &stu2;

    // 遍歷打印結(jié)構(gòu)體指針中的內(nèi)容
    printf("Name: %s\n", stuPtr->name);
    printf("Age: %d\n", stuPtr->age);
    printf("Score: %.1f\n", stuPtr->score);

    // 將結(jié)構(gòu)體指針變量指向下一個(gè)結(jié)構(gòu)體變量
    stuPtr = &stu3;

    // 遍歷打印結(jié)構(gòu)體指針中的內(nèi)容
    printf("Name: %s\n", stuPtr->name);
    printf("Age: %d\n", stuPtr->age);
    printf("Score: %.1f\n", stuPtr->score);

    return 0;
}

在這個(gè)示例中,我們首先定義了一個(gè)Student結(jié)構(gòu)體類型,包含了學(xué)生的姓名、年齡和分?jǐn)?shù)。在main函數(shù)中,我們創(chuàng)建了三個(gè)Student類型的結(jié)構(gòu)體變量,并分別初始化它們。

然后,我們創(chuàng)建了一個(gè)Student類型的指針變量stuPtr,用于指向這些結(jié)構(gòu)體變量。我們將第一個(gè)結(jié)構(gòu)體變量的地址賦給了stuPtr,并使用->運(yùn)算符通過指針訪問結(jié)構(gòu)體中的數(shù)據(jù),并打印出學(xué)生的信息。接著,我們將指針變量指向下一個(gè)結(jié)構(gòu)體變量,重復(fù)上述操作,直到遍歷完所有的結(jié)構(gòu)體變量。文章來源地址http://www.zghlxwxcb.cn/news/detail-726525.html

到了這里,關(guān)于C語言——自定義類型結(jié)構(gòu)體_學(xué)習(xí)筆記的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 數(shù)據(jù)結(jié)構(gòu)(c++語言版) 鄧俊輝 第五章:二叉樹學(xué)習(xí)筆記

    數(shù)據(jù)結(jié)構(gòu)(c++語言版) 鄧俊輝 第五章:二叉樹學(xué)習(xí)筆記

    5.1二叉樹及其表示 ????????樹是由節(jié)點(diǎn)和邊組成的。 1.有根樹 ??????? 樹是由頂點(diǎn)(vertex)和邊(edge)組成。樹的每個(gè)頂點(diǎn)也叫節(jié)點(diǎn)(node)。 2.深度與層次 ????????由樹的連通性,每一節(jié)點(diǎn)與根都有一條路徑相連:根據(jù)樹的無環(huán)性,由根通往每個(gè)節(jié)點(diǎn)的路徑必然唯一。 ?

    2024年02月13日
    瀏覽(26)
  • C語言學(xué)習(xí):8、深入數(shù)據(jù)類型

    C語言中,如果需要用的整數(shù)大于int類型的最大值了怎么辦? 我們知道int能表示的最大數(shù)是2147483647,最小的數(shù)是-2147483648,為什么? 因?yàn)樽?2位系統(tǒng)中,寄存器是32位的,寄存器中最高位表示符號(hào)位,0表示整數(shù),1表示負(fù)數(shù); 所以32位系統(tǒng)中int的最大值可以表示為0111 1111 1111

    2024年02月09日
    瀏覽(17)
  • 《綜合與Design_Compiler》學(xué)習(xí)筆記——第一章綜合綜述 第二章verilog語言結(jié)構(gòu)到門級(jí)的映射 第三章 使用DC進(jìn)行綜合

    《綜合與Design_Compiler》學(xué)習(xí)筆記——第一章綜合綜述 第二章verilog語言結(jié)構(gòu)到門級(jí)的映射 第三章 使用DC進(jìn)行綜合

    2023.6.25 2023.6.27 和之前學(xué)的芯動(dòng)力mooc中很多內(nèi)容相似,這篇整理的邏輯更好些 將RTL代碼轉(zhuǎn)換到基于工藝庫的門級(jí)網(wǎng)表。一般分為如下三個(gè)步驟。 (1)邏輯級(jí)綜合 設(shè)計(jì)被描述成 布爾等式 的形式,觸發(fā)器、鎖存器這樣的基本單元采用元件例化(instantiate)的方式表達(dá)出來,下面是

    2024年02月12日
    瀏覽(23)
  • C語言入門教程,C語言學(xué)習(xí)教程(第三部分:C語言變量和數(shù)據(jù)類型)二

    C語言入門教程,C語言學(xué)習(xí)教程(第三部分:C語言變量和數(shù)據(jù)類型)二

    前面我們多次提到了字符串,字符串是多個(gè)字符的集合,它們由 \\\" \\\" 包圍,例如 \\\"http://c.biancheng.net\\\" 、 \\\"C語言中文網(wǎng)\\\" 。字符串中的字符在內(nèi)存中按照次序、緊挨著排列,整個(gè)字符串占用一塊連續(xù)的內(nèi)存。 當(dāng)然,字符串也可以只包含一個(gè)字符,例如 \\\"A\\\" 、 \\\"6\\\" ;不過為了操作方

    2024年01月17日
    瀏覽(21)
  • 【JAVA】數(shù)據(jù)類型與變量(主要學(xué)習(xí)與c語言不同之處)

    【JAVA】數(shù)據(jù)類型與變量(主要學(xué)習(xí)與c語言不同之處)

    ?作者簡介:大家好,我是橘橙黃又青,一個(gè)想要與大家共同進(jìn)步的男人???? ??個(gè)人主頁:橘橙黃又青-CSDN博客 目標(biāo): 1. 字面常量 2. 數(shù)據(jù)類型 3. 變量 在上節(jié)課 HelloWorld 程序中, System.Out.println(\\\"Hello World\\\") ; 語句,不論程序何時(shí)運(yùn)行,輸出的都是 Hello World,其實(shí) \\\"Hello Wo

    2024年03月28日
    瀏覽(23)
  • 【go語言學(xué)習(xí)筆記】05 Go 語言實(shí)戰(zhàn)

    【go語言學(xué)習(xí)筆記】05 Go 語言實(shí)戰(zhàn)

    在做項(xiàng)目開發(fā)的時(shí)候,要善于借助已經(jīng)有的輪子,讓自己的開發(fā)更有效率,也更容易實(shí)現(xiàn)。 1. RESTful API 定義 RESTful API 是一套規(guī)范,它可以規(guī)范如何對(duì)服務(wù)器上的資源進(jìn)行操作。和 RESTful API 和密不可分的是 HTTP Method。 1.1 HTTP Method HTTP Method最常見的就是POST和GET,其實(shí)最早在

    2024年02月13日
    瀏覽(23)
  • Go語言學(xué)習(xí)筆記

    注:安裝教程 注:上一篇筆記 注:下一篇筆記 2.6、流程控制 2.6.1、條件語句 2.6.2、選擇語句 2.6.3、循環(huán)語句 2.6.4、跳轉(zhuǎn)語句 goto語句跳轉(zhuǎn)到本函數(shù)內(nèi)的某個(gè)標(biāo)簽 2.7、函數(shù) 2.7.1、函數(shù)定義 函數(shù)構(gòu)成代碼執(zhí)行的邏輯結(jié)構(gòu)。函數(shù)的基本組成為:func、函數(shù)名、參數(shù)列表、返回值

    2024年02月06日
    瀏覽(24)
  • 6.Go語言學(xué)習(xí)筆記-結(jié)合chatGPT輔助學(xué)習(xí)Go語言底層原理

    6.Go語言學(xué)習(xí)筆記-結(jié)合chatGPT輔助學(xué)習(xí)Go語言底層原理

    1、Go版本 2、匯編基礎(chǔ) 推薦閱讀:GO匯編語言簡介 推薦閱讀:A Quick Guide to Go\\\'s Assembler - The Go Programming Language 精簡指令集 數(shù)據(jù)傳輸: MOV/LEA 跳轉(zhuǎn)指令: CMP/TEST/JMP/JCC 棧指令: PUSH/POP 函數(shù)調(diào)用指令: CALL/RET 算術(shù)指令: ADD/SUB/MUL/DIV 邏輯指令: AND/OR/XOR/NOT 移位指令: SHL/SHR JCC有條件跳轉(zhuǎn): JE

    2024年02月04日
    瀏覽(29)
  • lua腳本語言學(xué)習(xí)筆記

    lua腳本語言學(xué)習(xí)筆記

    Lua 是一種輕量小巧的腳本語言,用標(biāo)準(zhǔn)C語言編寫并以源代碼形式開放, 其設(shè)計(jì)目的是為了嵌入應(yīng)用程序中,從而為應(yīng)用程序提供靈活的擴(kuò)展和定制功能。 因?yàn)槲覀兪褂胷edis的時(shí)候一般要寫lua腳本,這篇文章就介紹一下lua腳本語言的基礎(chǔ)用法。 window操作系統(tǒng)下可以使用SciTE來

    2024年02月16日
    瀏覽(23)
  • go語言學(xué)習(xí)筆記1

    go語言學(xué)習(xí)筆記1

    ? GoLang是一種靜態(tài)強(qiáng)類型、編譯型、并發(fā)型,并具有 垃圾回收 功能的編程語言;它可以在不損失應(yīng)用程序性能的情況下極大的降低代碼的復(fù)雜性,還可以發(fā)揮多核處理器同步多工的優(yōu)點(diǎn),并可解決面向?qū)ο蟪绦蛟O(shè)計(jì)的麻煩,并幫助程序設(shè)計(jì)師處理瑣碎但重要的內(nèi)存管理問題

    2024年02月12日
    瀏覽(18)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包