结构体声明及定义
格式
声明
成员用分号隔开成员,最后加分号
定义
成员用逗号隔开,最后加分号。
结构体数组
成员用逗号隔开,最后加分号。
例程
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]不是你想用就能用