c语言的编译过程:

  • 预处理
  • 编译
  • 汇编
  • 链接

而预处理中有三种情况:

  • 文件包含( #include )
  • 条件编译(#if,#ifndef,#endif)

宏定义( #define )

宏就是预处理中的一种情况. 其实,宏的概念就是文本替换

宏的作用:

1.可维护性

2.可读性

宏还有其他作用比如:程序调试跟踪  等, 因为我也没试过那些,这里不写那些了

宏定义:

1.不带参数的宏定义

#define 标识符  字符串

2.带参数的宏定义

#define 标识符(参数表) 字符串

注意:标识符一般大写  ,

这里将不会解答宏中包含特殊符号:#、##的问题.

1.不带参数的宏:

源文件:C:\Users\Zero\Desktop\c\temp\macro.c

#include

 <stdio.h>

#define PI 3.1415    //定义常量PI

void main(){

    printf("PI常量的值:%f",PI);

}

结果:

原因:

宏是预编译中的一种情况(我在前面也有说到), 而预编译主要就是替换,宏就是文本替换

我们可以生成预编译后的文件来查看:

执行dos命令:

打开文件预编译后的文件macro.i文件:

由于加载预编译后的文本很长,这里我主要显示main主函数这部分:

wint_t __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) fgetwchar (void);
wint_t __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) fputwchar (wint_t);
int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) getw (FILE*);
int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) putw (int, FILE*);
# "macro.c" void main(){ printf("PI常量的值:%f",3.1415); }

我们可以看到原来的”PI”不见了,被替换成了3.1415

2.带参数的宏:

源文件:C:\Users\Zero\Desktop\c\temp\macro2.c

#include <stdio.h>

#define AREA(x,y) x*y

void main(){
int a=;
int b=;
int result; result=AREA(a+b,a-b); printf("result:%d",result);
}

结果:

原因:

也许有人会认为”result=AREA(a+b,a-b)”的运算过程是这样的:

result = (a+b)*(a-b)    -->    result= (6+5)(6-5)  --> result=11

不过,很遗憾它并不是函数,AREA(x,y) 不是函数 ,它不是函数!!!

开始就说过,宏就是替换 ,追其本质, 这里也是替换.

查看预编译后的文件:

执行dos命令:

打开文件预编译后的文件macro2.i , 主要显示main函数部分:

wint_t __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) fgetwchar (void);
wint_t __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) fputwchar (wint_t);
int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) getw (FILE*);
int __attribute__((__cdecl__)) __attribute__ ((__nothrow__)) putw (int, FILE*);
# "macro2.c" void main(){
int a=;
int b=;
int result; result=a+b*a-b; printf("result:%d",result);
}

我们发现原来的”AREA(a+b,a-b)”被替换成了“a+b*a-b”

result = (a+b)*(a-b)    -->    result= (6+5)(6-5)  --> result=11

result=a+b*a-b  –>  result=6+5*6-5  –>  result=31

因为是替换,并不是函数,所以最后的结果是31

关于宏的作用:

1.可维护性

源文件:C:\Users\Zero\Desktop\c\temp\effect.c

#include <stdio.h>
#define PI 3.14 void main(){
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
}

比如:我们在控制台输出10次PI的值, PI=3.14 , 后来,我发现3.14并不是我想要的结果,

我想要修改原来3.14的值, 改成3.1415 ,这个时候 我只需要修改PI这个常量就可以了.并不需要修改10次

#include <stdio.h>
#define PI 3.1415 void main(){
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
printf("控制台输出PI:%f\n",PI);
}

2.可读性

假如,一个常量为3.1415926, 你的代码也很长的时候,去找3.1415926是非常麻烦的,

#define PI=3.1415926  ,  你只需要找到PI就可以了

结构体

在实际问题中,一组数组往往具有不同的数据类型,这个时候就可以用到结构体

结构体:结构体是基本数组类型组成,里面可以存在不同类型的变量.像java中对象

结构体定义的是一个类型, 就好像自己定义了整形(int)一样

结构体的定义:

显示声明结构体:

struct 结构体名{

类型 变量名;

类型 变量名;

};

匿名声明结构体:

struct {

类型 变量名;

类型 变量名;

}结构体变量;

下面我将演示,及说明结构体的声明:

显示声明结构体:

源文件:C:\Users\Zero\Desktop\c\temp\struct.c

#include

 <stdio.h>
#include <string.h> //导入strcpy()函数的头文件 //显示声明结构体
struct stu{
char name[]; //学生姓名
int age; //学生年龄
}; void main(){
//声明结构体变量
struct stu student; //struct stu: 这里是一个类型, student是一个变量 //赋值
strcpy(student.name,"张二"); //student.name="张二",写法是错误的
//因为字符数组定义之后,是不能直接赋值的 ,不过,可以用strcpy()方法赋值
student.age=; //控制台输出
printf("student.name:%s\n",student.name);
printf("student.name:%d\n",student.age);
}

编译文件:

结构体声明还是比较简单的,把”struct 结构体名”看成一个整体就可以了, 如下:

int 变量;    //定义整形

struct 结构体名 变量;  //定义结构体

两者的定义都是差不多的。关于访问结构体里的变量 ,一般都是通过 “结构体变量.变量” 访问

匿名声明结构体:

源文件:C:\Users\Zero\Desktop\c\temp\struct2.c

#include

 <stdio.h>
#include <string.h> //导入strcpy()函数的头文件 //匿名声明结构体
struct{
char name[]; //学生姓名
int age; //学生年龄
}student; void main(){
//赋值
strcpy(student.name,"张三");
student.age=; //控制台输出
printf("student.name:%s\n",student.name);
printf("student.age:%d\n",student.age);
}

编译文件:

我们发现匿名声明和显示声明还是有一些不同的,在void main()主函数里,我并没有定义student变量, 但是却可以正常执行, 原因是在匿名声明结构体的时候,我就声明了student变量, 创建匿名结构体的时候,是可以同时声明变量的,因为它是匿名的, 匿名结构体通常只能用一次。

typedef

typedef, 我感觉还是挺好用的,typedefine的简写, 顾名思义就是类型定义,它可以给类型取一个别名.

下面我们可以通过代码来观察:

源文件:C:\Users\Zero\Desktop\c\temp\typedef.c

#include

 <stdio.h>

typedef int Integer;    //给int类型取个别名Integer
typedef double Double; //给double类型取个别名Double void main(){
Integer i=; //定义整形变量
int j=; //定义整形变量
Double mark=23.3; //定义双精度浮点变量 printf("Integer i:%d\n",i);
printf("int j:%d\n",j);
printf("Double mark:%lf\n",mark);
}

即便是取了别名,原来的类型还是可以用的

编译文件:

typedef还可以用于结构体上:

源文件:C:\Users\Zero\Desktop\c\temp\typedef.c

显示声明:

#include

 <stdio.h>
#include <string.h> //导入strcpy函数的函数库 //显示定义学生结构体,别名stu_type
typedef struct stu{
char name[];//学生姓名
int age;//学生年龄
}stu_type; void main(){
stu_type student; //声明结构体变量student //赋值
strcpy(student.name,"赵七");
student.age=; //控制台输出
printf("student.name:%s\n",student.name);
printf("student.age:%d\n",student.age);
}

结果:

匿名声明:

源文件:C:\Users\Zero\Desktop\c\temp\typedef3.c

#include <stdio.h>
#include <string.h> //导入strcpy函数的函数库 //匿名定义学生结构体,别名stu_type
typedef struct{
char name[];//学生姓名
int age;//学生年龄
}stu_type; void main(){
stu_type student; //声明结构体变量student //赋值
strcpy(student.name,"赵九");
student.age=; //控制台输出
printf("student.name:%s\n",student.name);
printf("student.age:%d\n",student.age);
}

结果:

其实,匿名声明结构体加上typedef之后,就有名字 - -!,就可以使用多次了

好了,文章到了这里,也该结束了. 欢迎阅读我学C的那些年

我学C的那些年[ch02]:宏,结构体,typedef的更多相关文章

  1. 函数定义从零开始学C++之从C到C++(一):const与#define、结构体对齐、函数重载name mangling、new/delete 等

    今天一直在学习函数定义之类的问题,下午正好有机会和大家共享一下. 一.bool 类型 逻辑型也称布尔型,其取值为true(逻辑真)和false(逻辑假),存储字节数在不同编译系统中可能有所不同,VC+ ...

  2. 指针直接赋值为整型AND利用宏定义求结构体成员偏移量

    首先我们要更正一个很熟悉的概念,那就是指针不仅仅是“地址”,指针还有一个很重要的特性,那就是“类型”. 指针初始化时,“=”的右操作数; 除外,该语句表示指针为空): 所以 ; 这样的代码是不允许的. ...

  3. 结构体中使用#define定义宏

    struct  hostent {   char    *h_name;        /* official name of host */   char    **h_aliases;    /* ...

  4. 【原创】只学到二维数组和结构体,不用链表也能写一个C贪食蛇?(四)

    全系列Index: [原创]只学到二维数组和结构体,不用链表也能写一个C贪食蛇?(一) [原创]只学到二维数组和结构体,不用链表也能写一个C贪食蛇?(二) [原创]只学到二维数组和结构体,不用链表也能 ...

  5. 结构体指offsetof宏详细解析

    1.#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE*)0)->MEMBER)     (include/linux/stddef.h) ...

  6. linux中查看结构体和宏

    1.进入目录/usr/include cd /usr/include/ 2.生成ctags文件sudo make ctags -R 3.vim -t 结构体(宏)名称 4.找到相应的宏或者结构体 5. ...

  7. 标C编程笔记day04 预处理、宏定义、条件编译、makefile、结构体使用

    预处理:也就是包括须要的头文件,用#include<标准头文件>或#include "自己定义的头文件" 宏定义,如:#define PI 3.1415926 查看用宏 ...

  8. Linux中的两个经典宏定义:获取结构体成员地址,根据成员地址获得结构体地址;Linux中双向链表的经典实现。

    倘若你查看过Linux Kernel的源码,那么你对 offsetof 和 container_of 这两个宏应该不陌生.这两个宏最初是极客写出的,后来在Linux内核中被推广使用. 1. offse ...

  9. 对offsetof、 container_of宏和结构体的理解

    offsetof 宏 #include<stdio.h> #define offsetoff(type, member)      ((int)&((type*)0)->me ...

随机推荐

  1. ASP.NET- LinkButton 传递多个参数

    在使用LinkButton时可能会遇到需要传递多个参数的问题,而LinkButton的用来传递参数的属性commandargument需要传递的是一个string类型的值.因而传递多个参数时需要进行一 ...

  2. mysql的优化措施,从sql优化做起

    http://geeksblog.cc/2016/06/11/mysql-optimize/ 优化sql的一般步骤 通过show status了解各种sql的执行频率 定位执行效率低的sql语句 通过 ...

  3. Linux 内核使用的 GNU C 扩展

    gcc核心扩展linuxforum(转)=========================== Linux 内核使用的 GNU C 扩展 =========================== GNC ...

  4. JSP/Servlet 中的事件处理

    写过AWT或Swing程序的人一定对桌面程序的事件处理机制印象深刻:通过实现Listener接口的类可以在特定事件(Event)发生时,呼叫特定的方法来对事件进行响应. 其实我们在编写JSP/Serv ...

  5. 高效 jquery 的奥秘

    当你准备使用 jQuery,我强烈建议你遵循下面这些指南: 1. 缓存变量 DOM 遍历是昂贵的,所以尽量将会重用的元素缓存. // 糟糕 h = $('#element').height(); $( ...

  6. Android(java)学习笔记180:Android MediaPlayer 播放prepareAsync called in state 8解决办法

    使用android MediaPlayer播放音频文件时,有时会出现prepareasync called in state 8错误. 以下方法可以避免这个异常出现.  第一种方法: private ...

  7. View事件分发机制

    所谓的事件分发,其实就是对MotionEvent事件的分发过程,即当一个MotionEvent产生后,系统需要把这个事件传递给一个具体的View,而这个传递的过程就是分发过程. 点击事件的分发由3个方 ...

  8. Windows Python 2.7 安装 Numpy

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/4846093. ...

  9. 利用jQuery npoi插件 asxh一般处理文件实现excel的下载

    最近开发的过程中遇到这么一个问题,利用ajax和ashx文件实现下载功能.发现代码调试走完之后并没有弹出下载框. 研究了一段时间之后发现解决这种问题有两种方法,1.ajax获取数据集在前台做处理实现导 ...

  10. php读取操作大文件

    在php中,对于文件的读取时,最快捷的方式莫过于使用一些诸如file.file_get_contents之类的函数,简简单单的几行代码就能 很漂亮的完成我们所需要的功能.但当所操作的文件是一个比较大的 ...