一、结构体
1.在C语言中可以使用结构体定义用户自己的数据类型,类似于数组,但是结构体中的成员可以是不同的数据类型。
1)直接定义结构体形式的变量
struct {若干成员;} 变量名;
例如:
struct {
char name[128]; // 成员
int age; // 成员
float score; // 成员
} student; // 结构体变量
struct {
char name[128];
int age;
float score;
} student2;
2)先定义结构体类型,然后用该类型定义变量。
struct 结构体类型名 {
若干成员;
};
struct 结构体类型名 变量名;
例如:
struct Student {
char name[128];
int age;
float score;
}; // 定义了一个名为Student的数据类型,该类型是一个结构,包括name、age、score三个成员
struct Student student; // 定义了一个名为student的变量,该变量是struct Student类型的
struct Student student2;
struct Student student3;
3)先用typedef定义结构体类型别名,再用该别名定义变量
typedef 原始类型 类型别名;
typedef unsigned int UINT;
UINT a; // 等价于unsigned int a;
typedef int BOOL;
typedef struct Student {
char name[128];
int age;
float score;
} STU;
STU student;
STU student2;
STU student3;
4)嵌套结构:一个结构体类型中的某个成员也是结构体类型的。例如:
struct Student {
char name[128];
int age;
float score;
struct Date {
int year;
int month;
int day;
} birthday;
};
2.结构型变量及数组的初始化
通过{}对结构型变量进行初始化s。
3.访问结构体的成员
1)通过变量访问成员:用.运算符,亦称成员访问运算符。
2)通过指针访问成员:用->运算符,亦称间接成员访问运算符。
4.结构类型的变量可以作为函数的参数,但是与基本类型参数的情况一样,虚实结合的过程只是值的复制,因此在函数内部对形参所做的修改,不会影响实参。如果希望函数能够改变实参的值,应该传入变量的地址。
5.即使是以读方式访问形参的函数,仅仅出于性能的考虑,也可以以地址方式传参,避免结构复制所带来的开销。为了防止在函数中意外地修改实参,可以用常量指针定义参数。
练习:定义2个结构,一个表示点,一个表示圆,提供如下函数:
计算圆的周长
计算圆的面积
判断点在圆上,在圆内,或者在圆外。
练习:录入10个学生的成绩单,每条记录包括:学生姓名、性别、年龄、学号、分数。放在结构数组中。实现一个分段打印成绩单记录的函数。
struct Record {
...
};
void enter (struct Record records[], size_t size);
void print (struct Record records[], size_t size, double from, double to);
6.内存对齐与补齐
1)对齐:结构中的每个成员在内存中的起始位置必须是其自身字节长度的整数倍,超过4字节的按4字节算。
2)补齐:结构体的字节长度必须是其字节数最多的成员的字节长度的整数倍,超过4字节的按4字节算。
7.位域
struct X {
int a : 1;
int b : 2;
int c : 5;
};
1)位域可以指定每个成员的大小(以位为单位),用以节省内存。
2)位域用法和结构一样,但是不能取成员的地址。
3)位域成员的类型必须是整型类(char/short/int/long/long long及其unsigned版本)
二、共用体(联合)
1.联合与结构的定义语法一致,只是其关键为union而不是struct。
2.联合中的成员在内存是共享的。
struct A {
int i;
char c[4];
double d;
};
IIIICCCCDDDDDDDD
----....~~~~~~~~
i c[4] d
union B {
int i;
char c[4];
double d;
};
3.联合的字节长度就是其字节数最大的成员的字节长度。
三、枚举
1.枚举是一个整型常量的列表,一般而言,其值为有限个,每个值都是枚举常量(整型)。枚举默认从0开始,但是也可以人为指定,没有显式指定的枚举常量,取上一个值加1。
2.C语言中的枚举类型其本质就是一个整型,可以接受任何针对整型变量的赋值,不做与枚举值域有关的检查。
3.增加代码的可读性,避免重复定义常量。
四、指针数组与数组指针
严格区分三个容易混淆的概念:
数组元素的指针
int a[10] = {...};
int* p = a;
printf ("%d", *p);
*p = 100;
p++;
指针组成的数组
int a, b, c;
int* arr[] = {&a, &b, &c};
*arr[0] = 100; // 100 -> a
printf ("%d", *arr[1]); // b
指向数组的指针
int a[10] = {...};
int (*p)[10] = &a;
五、二维数组与指针
六、多级指针
七、空指针(void*)
八、动态内存分配与释放
九、函数指针及其解引用
#include <stdio.h>
int main (void) {
int a[5] = {1,2,3,4,5};
printf ("%u\n", a);
printf ("%u\n", &a);
printf ("%u\n", &a[0]);
int* p1 = a;
printf ("%u,%u\n", p1, p1 + 1);
int (*p2)[5] = &a; // 数组指针
printf ("%u,%u\n", p2, p2 + 1);
printf ("%d\n", *((int*)(&a+1)-1));
int b[2][3] = {1,2,3,4,5,6,};
int (*p3)[3] = b;
/*
printf ("%d,%d,%d\n", (*p3)[0], *((*p3)+1), (*p3)[2]);
printf ("%d,%d,%d\n", (*(p3+1))[0], p3[1][1], *(*(p3+1)+2));
*/
size_t i, j;
for (i = 0; i < 2; i++) {
for (j = 0; j < 3; j++)
//printf ("%d ", b[i][j]);
//printf ("%d ", *(*(b+i)+j));
//printf ("%d ", *(*(p3+i)+j));
printf ("%d ", p3[i][j]);
printf ("\n");
}
return 0;
}
#include <stdio.h>
#include <string.h>
// 以二进制形式打印一个字节
void printb (char byte) {
size_t i;
for (i = 0; i < 8; i++)
printf ("%c", byte & 1<<7-i ? '1' : '0');
printf (" ");
}
// 以二进制形式打印一个内存块
void printm (char* buf, size_t size) {
size_t i;
for (i = 0; i < size; i++)
printb (buf[i]);
printf ("\n");
}
#pragma pack (1)
struct X {
int a : 1;
int b : 3;
int c : 6;
};
struct Y {
int a : 18;
short : 0; // 指定下一个成员的起始边界
int b : 3;
int c : 6;
};
struct Z {
int a : 1;
int : 3; // 空位
int c : 6;
};
#pragma pack ()
int main (void) {
//int a = 0x12345678;
//printm ((char*)&a, sizeof (a));
struct X x;
memset (&x, 0, sizeof (x));
x.a = 1; // 1
x.b = 2; // 10
x.c = 63; // 111111
printm ((char*)&x, sizeof (x));
struct Y y;
memset (&y, 0, sizeof (y));
y.a = 1;
y.b = 2;
y.c = 63;
printm ((char*)&y, sizeof (y));
struct Z z;
memset (&z, 0, sizeof (z));
z.a = 1;
z.c = 63;
printm ((char*)&z, sizeof (z));
return 0;
}
#include <stdio.h>
#include <math.h>
#define PAI 3.14159
typedef struct tag_Point {
double x;
double y;
} POINT, *LPPOINT;
typedef const POINT* LPCPOINT;
typedef struct tag_Circle {
POINT o;
double r;
} CIRCLE, *LPCIRCLE;
typedef const CIRCLE* LPCCIRCLE;
void input_point (LPPOINT pt) {
printf ("x:");
scanf ("%lf", &pt->x);
printf ("y:");
scanf ("%lf", &pt->y);
}
void input_circle (LPCIRCLE cr) {
input_point (&cr->o);
printf ("r:");
scanf ("%lf", &cr->r);
}
double cal_c (LPCCIRCLE cr) {
return 2 * PAI * cr -> r;
}
double cal_s (LPCCIRCLE cr) {
return PAI * cr -> r * cr -> r;
}
int in_circle (LPCPOINT pt, LPCCIRCLE cr) {
double x1 = pt -> x;
double y1 = pt -> y;
double x2 = cr -> o.x;
double y2 = cr -> o.y;
double d2 = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
double r2 = cr -> r * cr -> r;
if (fabs (d2 - r2) < 1E-6)
return 0; // 在圆上
else if (d2 < r2)
return -1; // 在圆内
else
return 1; // 在圆外
}
int main (void) {
CIRCLE cr;
input_circle (&cr);
printf ("周长:%lf\n", cal_c (&cr));
printf ("面积:%lf\n", cal_s (&cr));
POINT pt;
input_point (&pt);
switch (in_circle (&pt, &cr)) {
case 0:
printf ("在圆上。\n");
break;
case -1:
printf ("在圆内。\n");
break;
default:
printf ("在圆外。\n");
break;
}
return 0;
}
#include <stdio.h>
int main (void) {
struct A {
char c;
int i;
} a; // CXXXIIII
printf ("%d\n", sizeof (struct A)); // 8
printf ("%u,%u\n", &a.c, &a.i);
struct B {
char c;
short s[2];
int i;
} b; // CXSSSSXXIIII
printf ("%d\n", sizeof (struct B)); // 12
#pragma pack (1) // 按1字节对齐
struct C {
char c;
short s[2];
int i;
} c; // CSSSSIIII
#pragma pack () // 恢复到缺省对齐方式
printf ("%d\n", sizeof (struct C)); // 9
struct D {
int i;
char c;
} d; // IIIICXXX
printf ("%d\n", sizeof (struct D)); // 8
struct E {
double d;
char c;
} e; // DDDDDDDDCXXX
printf ("%d\n", sizeof (struct E)); // 12
return 0;
}
#include <stdio.h>
typedef struct tag_Record {
char name[128];
int gender;
int age;
int number;
double score;
} RECORD;
void enter (RECORD records[], size_t size) {
size_t i;
for (i = 0; i < size; i++) {
scanf ("%s", records[i].name);
scanf ("%d", &records[i].gender);
scanf ("%d", &records[i].age);
scanf ("%d", &records[i].number);
scanf ("%lf", &records[i].score);
}
}
void print (RECORD records[], size_t size, double from, double to) {
if (from < 0 && to < 0)
printf ("全部记录:\n");
else if (from < 0)
printf ("%g分以下:\n", to);
else if (to < 0)
printf ("%g分以上:\n", from);
else
printf ("%g分到%g分:\n", from, to);
size_t i;
for (i = 0; i < size; i++) {
if ((from < 0 || (from >= 0 && records[i].score >= from)) && (to < 0 || (to >= 0 && records[i].score < to)))
printf ("%8s%8s%8d%8d%8g\n",
records[i].name,
records[i].gender ? "男" : "女",
records[i].age,
records[i].number,
records[i].score);
}
}
int main (void) {
size_t size;
scanf ("%u", &size);
RECORD records[size];
enter (records, size);
print (records, size, -1, -1);
print (records, size, -1, 70);
print (records, size, 60, -1);
print (records, size, 55, 75);
return 0;
}
#include <stdio.h>
#include <string.h>
// 定义struct Date类型,及其别名
typedef struct Date {
int year;
int month;
int day;
} DATE;
// 定义struct Student类型
struct Student {
char name[128];
int age;
float score;
//struct Date birthday;
DATE birthday;
};
// 定义struct Student类型的别名
typedef struct Student STU;
void grow (STU* s) {
s->age++;
}
void show (const STU* s) {
printf ("姓名:%s\n", s -> name);
printf ("年龄:%d\n", s -> age);
printf ("学分:%g\n", s -> score);
printf ("生日:%d年%d月%d日\n", s -> birthday.year, s -> birthday.month, s -> birthday.day);
//strcpy (s -> name, "小二");//意外修改实参
}
int main (void) {
// 直接定义变量
struct {
char name[128];
int age;
float score;
struct { // 嵌套在一个结构中的结构
int year;
int month;
int day;
} birthday;
} s1, s2, s3;
// 用struct Student类型定义变量
struct Student s4, s5, s6;
// 用STU类型别名定义变量
STU s7, s8, s9;
// 结构体变量初始化
STU sa = {"张飞", 28, 85.5, {1984, 1, 1}};
// 结构体数组初始化
STU ss[] = {
{"赵云", 22, 95.5, {1990, 1, 1}},
{"关羽", 30, 90.5, {1982, 1, 1}}
};
// 通过.访问成员
printf ("%s\n", sa.name);
printf ("%d年%d月%d日\n", sa.birthday.year, sa.birthday.month, sa.birthday.day);
printf ("%s\n", ss[0].name);
printf ("%d年%d月%d日\n", ss[0].birthday.year, ss[0].birthday.month, ss[0].birthday.day);
// 通过->访问成员
STU* ps = &sa;
printf ("%s\n", ps->name);
//printf ("%s\n", (*ps).name);
printf ("%d年%d月%d日\n", ps->birthday.year, ps->birthday.month, ps->birthday.day);
printf ("%s\n", ss->name);
printf ("%d年%d月%d日\n", ss->birthday.year, ss->birthday.month, ss->birthday.day);
printf ("%d\n", sa.age);
grow (&sa);
printf ("%d\n", sa.age);
show (&sa);
return 0;
}
#include <stdio.h>
int main (void) {
union {
unsigned int un;
unsigned char uc[4];
} mb;
mb.un = 0x12345678;
size_t i;
for (i = 0; i < sizeof (mb.uc) / sizeof (mb.uc[0]); i++)
printf ("0x%x ", mb.uc[i]);
printf ("\n");
mb.uc[2] = 0x43;
printf ("0x%x\n", mb.un);
mb.un = 1;
if (mb.uc[0])
printf ("小端字节序。\n");
else
printf ("大端字节序。\n");
short a = 1;
if (*(char*)&a)
printf ("小端字节序。\n");
else
printf ("大端字节序。\n");
union A {
double d;
int i;
short s;
char c[9];
};
printf ("%d\n", sizeof (union A)); // 12
return 0;
}