结构类型struct,自定义数据类型typedef,程序结构和宏解析

it2023-03-03  76

枚举

枚举是一种用户定义的数据类型,它用关键字enum以如下语法来声明:

enum 枚举类型名字{名字0,...,名字n};

枚举类型的名字通常可以省略,我们使用的是花括号里的常量符号,它们的类型是int,值依次从0到n。

enum colors{red, yellow, green};

就创建了3个常量,red是0,yellow是1,green是2。

#include<stdio.h> enum colors{red, yellow, green}; void f(enum colors c); //enum在C语言中不可省略 int main(void) { enum colors t = red; scanf("%d", &t); f(t); return 0; } void f(enum colors c) { printf("%d\n", c); }

声明枚举量的时候可以指定值

enum colors{red = 1, yellow, green = 5};

结构类型

声明结构的形式

struct point{ int x; int y } p1, p2; //注意末尾有分号!!!

结构的初始化

#include<stdio.h> struct date { int month; int day; int year; }; int main(void) { struct date day = {07, 31, 2014}; struct date thismonth = {.month = 7, .year = 2014}; }

指向结构的指针

不同于数组,结构变量本身不是地址,所以要用&负号去取它的地址。

struct date{ int month; int day; int year; } myday; struct date *p = &myday; (*p).month = 12; p -> month = 12; //一般采取这种写法 **非常奇怪的是visual studio 2019调试不出来** #include<stdio.h> struct point{ int x; int y; }; struct point *GetStruct(struct point *); void output(struct point); void print(const struct point *); int main(int argc, char const *argc[]) { struct point y = {0, 0}; GetStruct(&y); output(y); output(*GetStruct(&y)); print(GetStruct(&y)); } struct point *GetStruct(struct point *p) { scanf("%d", &p -> x) //**这里为什么要加&号还没有理解** scanf("%d",&p -> y); printf("%d, %d", p -> x, p ->y); return p; } void output(struct point p) { printf("%d, %d", p.x, p.y); } void print(const struct point *p) { printf("%d, %d", p -> x, p ->y); }

类型定义

自定义数据类型 —— typedef

例如:

typedef int Length

上面的语句是的Length称为int的别名,即 int a 等价于 Length a。

接着可以拓展为:

typedef struct{ int month; int day; int year; } Date;

意思是,Date是struct中定义的这个结构的别名。我们甚至不用去为这个结构命名。

联合

typedef union{ int i; char ch[sizeof(int)]; }

union中的元素共用存储空间。

可以通过这种方式得到一个整数内部的各个字节,同样的也可以得到一个double或者float内部的字节。当我们要把一个整数以二进制的形式写到一个文件里的时候,这就是一个中间的媒介。

程序结构

全局变量

定义在函数外面的变量是全局变量全局变量具有全局的生存区和作用域 #include<stdio.h> int Q = 12; int main(void) { ... } int f(void) { ... }

不建议使全局变量之间产生联系

解释如下:

int Q = 12; int Q2 = Q;

以上程序报错:initializer element is not a compile-time constant 不是一个编译时刻的常量

如何解决:

const int Q = 12; int Q2 = Q;

虽然通过const可以解决,但是不建议使全局变量之间产生联系,这一点在大程序中可以体现。

如果函数内部存在与全局变量同名的变量,则全局变量被隐藏。这一点也可以推广为更小范围内定义的一个变量如果与更大范围内的变量重名的话,更大范围内的变量会被隐藏。

静态本地变量

#include<stdio.h> int Q = 12; int main(void) { ... } int f(void) { static int q = 13; ... }

静态本地变量拥有全局生存区和本地作用域

全局变量具有全局的生存区和作用域

本地变量具有本地的生存区和作用域

静态本地变量就是特殊的全局变量

返回指针的函数

int main(void) { int *p = f(); printf("*p = %d\n", *p); g(); printf("*p = %d\n", *p); return 0; } int* f(void) { int i = 12; return &i; } void g(void) { int k; printf("k = %d\n", k); }

输出结果为:

*p = 12 k = 24 *p = 24 返回本地变量的地址是危险的

本地变量是存储在栈里的,当函数执行时,其中的局部变量会执行压栈操作,当函数执行完毕后,栈中的数据会弹出,以便腾出栈空间。所以,当函数执行完毕后,指针指向的地址仍然是存在的,但是它指向的数据已经不在了。

以上解释引自

https://blog.csdn.net/dlutbrucezhang/article/details/9528885 返回全局变量或静态本地变量的地址是安全的返回在函数内malloc的内存是安全的,但是容易造成问题最好的办法是返回传入的指针

改进如下:

#include<stdio.h> int* f(int* m); void g(void); int main(void) { int n = 1; int *p = f(&n); printf("*p = %d\n", *p); g(); printf("*p = %d\n", *p); return 0; } int* f(int *m) { int i = 12; *m = i; return m; } void g(void) { int k = 24; printf("k = %d\n", k); }

*同时,有以下贴士:

不要使用全局变量在函数间传递参数和结果使用全局变量和静态本地变量的函数是线程不安全的*

宏定义

编译预处理指令:

#开头的是编译预处理指令他们不是C语言的成分,但C语言程序离不开它们#define用来定义一个宏

宏:

#define 名字 值注意结尾没有分号,因为这不是C的语句名字必须是一个单词,值可以是任何东西在编译开始之前,编译预处理程序会把程序中的名字换成值完全的文本替换如果一个宏中有其他宏的名字,也是会被替换的如果一个宏的值超过一行,*最后一行之前的行末需要加*宏的后面可以注释,不会被当成宏的一部分 #include<stdio.h> #define PI 3.14159 #define PI2 2*PI #define PRT printf("%f", PI); \ printf("%f\n", PI2) int main(void) { PRT; return 0; }

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4tRzXIfQ-1603174026234)(https://note.youdao.com/yws/api/personal/file/98ECF4720EBC4F268EFDE53ADEECB8A4?method=download&shareKey=0bf9328bd668fc13c3c807addc2fe9fa)]

没有值的宏:

#define _DEBUG

这类宏是用于条件编译的,后面有其他的编译预处理指令来检查这个宏是否已经被定义过了

预定义的宏:

_LINE_ //行号 _FILE_ //文件名 _DATE_ //日期 _TIME_ //时间 _STOC_ //

带参数的宏

#define CUBE(x) ((x)*(x)*(x))

原则:每个参数都要有括号!!,整个值都要有括号

可以带多个参数

#define MIN(a,b) ((a)>(b)?(b):(a)) 可以嵌套,组合使用其他宏不要加分号

补充

可以很复杂,如产生函数部分带参数的宏会被inline代替

大程序文件

新建项移除main添加新项,名称改为xxx.c

一个.c文件是一个编译单元

头文件:

把函数原型放到一个头文件(.h)结尾中,在需要调用这个函数的源代码文件(.c)文件中#include这个头文件。""还是<>: ““要求编译器首先在当前目录寻找这个文件,如果没有再到其他地方找;<>让编译器只在指定目录找。换句话说自己定义的头文件一般用””
最新回复(0)