结构, union, enum, typedef
struct structname
{
int i; //除了static,其余不可在定义struct时赋值.
char buf[32];
double f;
} ; //定义struct structname
structname a; //定义变量 a
structname a = {0, “123”, 1.2}; //初始化
a.i = 10;
支持初始化指定项目(C99):
structname a = {.i = 1};
不同平台的寻址方式不同,有的可能同偶地址开始读取, 编译器会将struct对齐, 以成员函数size的最大位对齐.
对于
struct structname
{
int i;
char buf[32];
} a; //sizeof(a) = 36; 以4位对齐
struct structname
{
double f;
} b; //sizeof(b) = 8; 以8位对齐
struct structname
{
int i;
char buf[32];
double f;
} c; //sizeof(c) = 48; 以8位对齐
#progma pack (2) 可以指定对齐位数, 此时最终对齐值为自身对齐值和指定对齐值中的较低的那个.
对于空结构体,size == 1;因为必须保证结构体的每一个实例在内存中都有独一无二的地址.
结构体的静态成员不对结构体的大小产生影响,因为静态变量的存储位置与结构体的实例地址无关.
struct structname
{
} a, *pstr; //定义struct和变量 a
和数组不同,一个结构的名字不是该结构的地址,必须使用&运算符.
memset(&a, 0, sizeof(structname));
struct 允许赋值,数组不可.
structname b;
a = b;
伸缩型数组成员(C99)
struct structname
{
int i;
double arr[]; //未分配任何内存,必须至少有一个其他成员,必须是最后//的一个数组成员.
} *p;
p = (structname *)malloc(sizeof(structname) + sizeof(double) * 5);
union
在同一存储空间里(但不同时)存储不同类型数据的数据类型,同一时间只能存储一个成员值,
union unionname
{
int i;
double f;
char c
} a;
a.i = 10;
支持初始化指定项目(C99):
union a = {.i = 1};
union允许赋值,数组不可.
union b;
a = b;
enum
enum enumname
{
one, //0
two = 3, //注意,以逗号隔开
three //4,注意没有逗号
} a;
a = two;
typedef
typedef struct structname
{
int i;
}nickname;
nickname a;
typedef char (* func())[5]; //typedef函数类型,无参数,返回值为指向char []的指针.
typedef char [5] *(* func)(); //typedef函数指针,无参数,返回值为指向char []的指针.
void func(char buf[]);
int main(int argc, _TCHAR* argv[])
{
typedef void (* pfunc)(char buf[]);
typedef void fun(char buf[]);
pfunc p = func;
fun *a= func;
return 0;
}
void func(char buf[])
{
}
C预处理器和C库
编译器用一个空格字符代替每一个注释
int/*===*/a; //等同于 int a;
系统会删除反斜线和换行符的组合,但是编译其会有缩进,所以要注意喔.
预处理指令
#define
#define X 1
#define fun(n) n*n //func(4+2);最后结果为14,4+2*4+2
预处理器不进行计算,只是进行文字替换操作,实际计算过程是在编译阶段.
#运算符
#define PRINT(n) printf(#n" = %d\n", n); //#n可以吧参数转换为相应的字符串
#define PRINT1(n) printf("n = %d\n", n); //#n可以吧参数转换为相应的字符串
int main(int argc, _TCHAR* argv[])
{
int x = 2;
PRINT1(2); //n = 2
PRINT(x); //x = 2
PRINT(2); //2 = 2
return 0;
}
##运算符
#define X(n) x##n
#define PRINT(n) printf("x"#n"=%d\n", x##n);
int main(int argc, _TCHAR* argv[])
{
int X(1) = 2; //等同于x1 = 2;
PRINT(1); //x1 = 2
return 0;
}
可变宏 …和__VA_ATGS__
#define X(n) x##n
#define PR(X,...) printf("hi, "#X": " _ _VA_ATGS_ _) //…必须放在最后
int main(int argc, _TCHAR* argv[])
{
int X(1) = 2;
PR(1, "x1 = %d", x1); //输出:hi, 1: x1 = 2
return 0;
}
#define MYC
#ifdef MYC //或者 #ifndef MYC
...
#else
...
#endif
#define MYC
#if defined(MYC) //同上面的代码意义相同,#if后面跟常量整数表达式,如果表达式为非0,则表达式为真
...
#elif defined(YOURC)
...
#else
...
#endif
#pragma来启动修改对编译器的设置
#pragma C99 on
内联函数inline,编译器没有预留给它的单独带麻快,所以无法获得内联函数的地址(如果获得地址,编译器会产生非内联函数),编译时候优化内联函数,所以内联函数的定义和调用必须在同一文件中,故在头文件中定义内联函数,具有内部链接,所以编译不会产生问题.
int atexit(void (* pfunc)(void));
注册函数,在exit执行时,注册函数会执行.函数按先进后出顺序执行.
可变参数:stdarg.h
int i = 2;
char c = 'A';
char buf[32] = "abc";
func("%d%c%s", i, c, buf);
void func(char *format, ...); //…必须为最后一个参数
va_list ap;//存放参数
va_start(ap, format); //把ap初始化为参数列表,参数是从…开始的.
int i = va_arg(ap, int); //第n次调用va_arg,返回参数列表的第n个参数.va_arg不提供退回之前参数的方法,可用va_copy(apcopy, ap)先保存参数列表.
va_end(ap); //释放动态分配的用于存放参数的内存
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· Windows编程----内核对象竟然如此简单?