结构体声明及定义

格式

声明

成员用分号隔开成员,最后加分号

定义

成员用逗号隔开,最后加分号。

结构体数组

成员用逗号隔开,最后加分号。

例程

struct stru{

    int  a;

    char b;

};

int main(int argc, char **argv)

{

    struct stru test1 = {

      .a = 3,

      .b = 4,

    };

    struct stru test2 = {5, 6};

    struct stru test3[2] = {

        {

            .a = 7,

            .b = 8,

        },

        {

            .a = 9,

            .b = 10,

        }

    };


    printf("test1.a = %d\n", test1.a);

    printf("test2.a = %d\n", test2.a);

    printf("test3[0].a = %d\n", test3[0].a);

    printf("test3[1].a = %d\n", test3[1].a);

    return 0;

}

执行结果

test1.a = 3

test2.a = 5

test3[0].a = 7

test3[1].a = 9


结构体高级用法

1.结构体可以 直接赋值

    struct my_struct{

        int a;

        char b;

    };

    struct my_struct stru1 = {2,1};

    struct my_struct stru2 = stru1;

2.结构体可以拷贝

    可以用memcpy将一个结构体的内容拷贝到另一个结构体

3.结构体数组可以拷贝

例如:

struct stru{

    int a;

    char b;

};

int main(int argc, char **argv)

{

    struct stru stru1[5], stru2[5];

    stru1={{11,'a'}, {22, 'b'}};

    memcpy(stru2,stru1,(sizeof(struct stru))*2));

    printf("%d, %c\n",stru2[0].a, stru2[0].b);

    printf("%d, %c\n",stru2[1].a, stru2[1].b);

}

运行结果为:

11, a

22, b

4.将连续的寄存器写到一个结构体中,避免逐个iomap

   例:10th_lcd中:

   struct lcd_regs {

        unsigned long lcdcon1;

        unsigned long lcdcon2;

        ...

        unsigned long reserved[9];

        ...

    };

    static volatile struct lcd_regs* lcd_regs;

    lcd_regs = ioremap(0x4D000000, sizeof(struct lcd_regs));

    lcd_regs->lcdcon1  = (4<<8) | (3<<5) | (0x0c<<1);

    lcd_regs->lcdcon2  = (3<<24) | (319<<14) | (1<<6) | (0<<0);

    ...

    如果要方便读取这个ioremap之后的结构体指针所指向的结构体的内容,可用以下办法:

    static struct lcd_regs lcd_regs_backup;

    unsigned long *dest = (unsigned long *)&lcd_regs_backup;

    unsigned long *src    = (unsigned long *)lcd_regs;

    int i;

    for(i = 0; i < (sizeof(lcd_regs_backup) / sizeof( unsigned long)); i++)

    {

        dest[i] = src[i];

    }


5.  结构体的指针操作

    例:alsa/裸板  wav.c中:read_wav中指针的操作:

    struct WAVE_FORMAT

    {

       WORD wFormatTag;

       WORD wChannels;

       DWORD dwSamplesPerSec;

       DWORD dwAvgBytesPerSec;

       WORD wBlockAlign;

       WORD wBitsPerSample;

    };

    struct FMT_BLOCK

    {

       char  szFmtID[4]; // 'f','m','t',' '

       DWORD  dwFmtSize;

       struct WAVE_FORMAT wavFormat;

    };

    struct DATA_BLOCK

    {

       char szDataID[4]; // 'd','a','t','a'

       DWORD dwDataSize;

    };

    void read_wav(unsigned int wav_buf, int *fs, int *channels, int *bits_per_sample, int *wav_size)

    {

        struct FMT_BLOCK *fmtblk;

        struct DATA_BLOCK *datblk;


        nand_read(0x60000, (unsigned char *)wav_buf, 0x200000);

        fmtblk = (struct FMT_BLOCK *)(wav_buf + sizeof(struct RIFF_HEADER));

        datblk = (struct DATA_BLOCK *)&fmtblk[1];  //因为fmtblk下边紧挨着就是datblk

        *wav_size = datblk->dwDataSize;

      }

头文件结构体交叉包含

一、普通情况

struct dma_device;

struct dma_ops{

    struct dma_device *dev;

    ...

};

struct dma_device{

     struct mutex *lock;

     struct dma_ops *ops;

};

二、typedef情况

struct VideoDevice;

struct VideoOpr;

typedef struct VideoDevice T_VideoDevice, *PT_VideoDevice;

typedef struct VideoOpr    T_VideoOpr, *PT_VideoOpr;

typedef struct VideoDevice{

    ...

    PT_VideoOpr ptVideoOpr;

}T_VideoDevice, *PT_VideoDevice;

typedef struct VideoOpr{

    ...

    int (*StartDevice)  (PT_VideoDevice ptVideoDevice);

}T_VideoOpr, *PT_VideoOpr;

结构体对齐问题

1.结构体对齐问题

#include

struct test_t {

    int a;         //0 - 3

    char b;        //4 

    int c;        //8 - 11

    char d;        //12

    short f;    //14 - 15 (而不是13-14)

    long t;        //16 - 23

    char x;        //24 (25-31)

};

int main(int argc,char **argv)

{

    struct test_t t1;

    printf("sizeof struct test_t = %ld\n", sizeof(struct test_t));

    printf("addr of a = %ld\n", &(t1.a));

    printf("addr of d = %ld\n", &(t1.d));

    printf("addr of f = %ld\n", &(t1.f));

    return 0;

}

如上所示运行结果为:

    sizeof struct test_t = 32

    addr of a = 140734639618176

    addr of d = 140734639618188

    addr of f = 140734639618190

分析:

  int a:    占4字节

  char b:   占1字节,char类型放哪都可以对齐,因为它只占1个字节

  int c:    占4字节,其开始地址必须是4的倍数

  char d:   占1字节

  short f:  占2字节,其开始地址必须是2的倍数

  ...

  最后,结构内最长的是long型,占8个字节,所以虽然只有25个字节,还是扩充为32个字节(sizeof(t1)的结果为32)。

  另外,如果在结构体的声明里边添加一个char g[13],则打印出来sizeof(t1)的结果为40。说明数组的长度算到结构体的长度。

变长数组

1.全局的变长数组是不允许的,如:

    static int n;

    static char str[n]

    int main(int argc, char **argv)

    { .. }

2.函数内的变长数组是允许的,如:

    a. int main(int argc, char **argv)

       {

           int n;

           char str[n];

       }

    b. void test(int n)

       {

           char str[n];

       }

3.分析:

    c/c++中全局变量的存储空间是编译器分配的, 不是在运行期分配的, 大小必须在编译期能确定, 因此变长数组不能是全局变量。

    变长数组是指用整型变量或表达式声明或定义的数组,而不是说数组的长度会随时变化,变长数组在其生存期内的长度同样是固定的。例如:

    void test(int n)

    {

         char str[n];

         n = n+10;

         printf("sizeof str = %d\n", sizeof(str))

    }

    长度仍然是传进去时的参数,而不是n+10

结构体中长度为0的数组

        长度为0的数组在标准c和c++中是不允许的,如果使用长度为0的数组,编译时会产生错误,提示数组长度不能为0。但在GNUc 中,这种用法却是合法的。它的最典型的用法就是位于数组中的最后一项,如上面所示,这样做主要是为了方便内存缓冲区的管理。如果你将上面的长度为0的数组换为指针,那么在分配内存时,需采用两步:首先,需为结构体分配一块内存空间;其次再为结构体中的成员变量分配内存空间。这样两次分配的内存是不连续的,需要分别对其进行管理。当使用长度为0的数组时,则是采用一次分配的原则,一次性将所需的内存全部分配给它。相反,释放时也是一样的。

代码示例​:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "test.h"
#include <math.h>

#define LENGTH 5

struct stru1{
int a;
int *b;
};

struct stru2{
int a;
int b[0];
};

struct stru3{
int a;
int b[];
};

struct stru4{
int a;
int b[1];
};

int main(int argc, char **argv)
{
struct stru1 *test1;
struct stru2 *test2;
struct stru3 *test3;
struct stru4 *test4;
int i;

test1 = (struct stru1 *)malloc(sizeof(struct stru1));
test2 = (struct stru2 *)malloc(sizeof(struct stru2));
test3 = (struct stru3 *)malloc(sizeof(struct stru3));
test4 = (struct stru4 *)malloc(sizeof(struct stru4));

test1->a = 1;
test1->b = (int *)malloc(sizeof(int));
*(test1->b) = 2;

test2->a = 2;
for(i = 0; i < LENGTH; i++){
test2->b[i] = i;
}

test3->a = 3;
for(i = 0; i < LENGTH; i++){
test3->b[i] = LENGTH-i;
}

test4->a = 4;
(test4->b)[0] = 4;

printf("struct stru1 length:%d\n", sizeof(struct stru1));
printf("struct stru2 length:%d\n", sizeof(struct stru2));
printf("struct stru3 length:%d\n", sizeof(struct stru3));
printf("struct stru4 length:%d\n", sizeof(struct stru4));

printf("test1 addr = %#x\n", test1);
printf("test1->b addr = %#x\n", test1->b);
printf("test2 addr = %#x\n", test2);
printf("test2->b addr = %#x\n", test2->b);
printf("test3 addr = %#x\n", test3);
printf("test3->b addr = %#x\n", test3->b);
printf("test4 addr = %#x\n", test4);
printf("test4->b addr = %#x\n", test4->b);

printf("val of test2->b: ");
for(i = 0; i < LENGTH; i++){
printf("%d ", test2->b[i]);
}
printf("\n");

printf("val of test3->b: ");
for(i = 0; i < LENGTH; i++){
printf("%d ", test3->b[i]);
}
printf("\n");

free(test1->b);
free(test1);
free(test2);
free(test3);
free(test4);
return 0;
}

运行结果(64位系统)

struct stru1 length:16

struct stru2 length:4

struct stru3 length:4

struct stru4 length:8

test1    addr = 0xb63010

test1->b addr = 0xb63090

test2    addr = 0xb63030

test2->b addr = 0xb63034

test3    addr = 0xb63050

test3->b addr = 0xb63054

test4    addr = 0xb63070

test4->b addr = 0xb63074

val of test2->b: 0 1 2 3 4 

val of test3->b: 5 4 3 2 1

总结


项目



长度为0的数组(没写长度的数组)



指针方式



占用空间



数组变量不占用空间



指针变量占用空间



申请与释放



 一次性申请与释放



申请与释放都要分别进行



地址连续性



数组变量值紧跟结构体的上一变量。



指针变量值与其他变量无关。



扩展空间



直接给新地址赋值即可。



需要重新分配空间


结构体内变长数组注意

linux中默认结构体对齐,所以此问题可以忽略

    char[0]不是你想用就能用