编译器在给每个不同数据类型分配内存空间大小时与电脑所装操作系统位数(32位/64位)无关,与所用的编译器有关.
内存的分配由编译器决定的而与运行的环境无关.
各个编译器对C语言的标准不一样,不同的编译器对C语言标准的支持不一样,有的编译器仅支持C89标准,而有的编译器能支持C99标准.
gcc编译器对C语言支持得很好,如下程序,使用gcc编译器可以编译并运行,但用一些其他的编译器便无法运行.
#include <stdio.h>
int main()
{
int a[0];
printf("hello world!\n");
}
C89不支持 //注释.
不同编译器对C语言标准不一样,而且各个编译器对C语言的标准有扩展.
1.描述算法
2.操作系统开发
3.桌面软件
4.Web服务器,网络编程
5.游戏开发
6.开发编译器
7.数据库编程
8.实现新的语言
9.图形编程
1.编写以下代码
/*file : test.c*/
#include <stdio.h>
int a = 3;
int b = 4;
int c;
int main()
{
c = a + b;
}
2.通过gcc编译器执行以下命令编译成汇编文件
gcc -S -o test.s test.c
3.打开目录下生成的test.s汇编文件,可以看到入口main函数,各个变量的定义声明和内存偏移和指令.
4.通过gcc编译器执行以下命令编译成目标文件
gcc -c -o test.o test.s
编译完成后生成目标文件,打开该文件全是二进制代码.
5.通过gcc编译器执行以下命令链接成可执行文件
gcc -c -o test.exe test.o
6.编译完成后即可运行.
1.二进制文件储存在外部储存器.
2.当程序加载时从外部存储器将程序拷贝到内存区域中.
3.CPU中的控制中的指针指向程序的第一条指令,然后送到运算器依次执行.
1.裸机系统的程序烧写在FLASH
2.操作系统的程序由文件系统进行管理,安装在某一目录
简单地说,未定义行为是指C语言标准未做规定的行为。编译器可能不会报错,但是这些行为编译器会自行处理,所以不同的编译器会出现不同的结果,什么都有可能发生,这是一个极大的隐患,所以我们应该尽量避免这种情况的发生。
1.用来标识程序中某个对象的名字.
2.标识符只能以数字,字母,下划线组成.
3.第一个必须是字母或下划线.
4.标识符长度.C89-31个有效字符.C99-63个有效字符.
5.标识符大小写敏感.
6.尽量不要以下划线开头(编译系统专用).
1.C语言保留的一些特定意义的标识符.
2.这些关键字被编译系统本身使用,用户不能再使用.
3.C89有32个关键字,C99新增5个关键字.
1.数据类型关键字
char
int
double
short
long
float
signed
unsigned
struct
union
enum
void
2.控制语句关键字
if
else
switch
default
while
do
break
continue
for
return
goto
3.储存类型关键字
auto
register
static
extern
4.其他关键字
const
sizeof
typedef
volatile
5.C99新增关键字
_Bool
_Complex
Imaginary
inline
restrict
6.其他编译器扩展关键字
far
near
bit
sbit
sfr
sfr16
data
bdata
idata
pdata
xdata
code
interrupt
reetrant
using
_irq
__svc_indirect
…
C语言系统中预先定义的标识符
1.系统类库名.
2.系统常量名(func/FILE).
3.系统函数名(printf/scanf).
预定义标识符和关键字类似,但不是关键字.
与宏定义不同,预定义标识符遵循代码块作用域规则.
用户可以把预定义标识符作为普通标识符使用,但会混淆违背系统规定的意愿,使用不当甚至会出错.
在程序不能被改变的量称为常量.
1.直接常量
整型 / 字符型 / 浮点型 / 符号常量
2.标识符
变量名 / 函数名 / 数组名 / 文件名 / 类型名的有效字符序列
3.符号常量
宏
可以被改变和重复赋值的量称为变量.
1.在运行过程中值可以改变.
2.有自己的类型,名字.
3.在内存中有自己的存储空间.
4.程序编译时会给每个变量分配空间互位置.
5.不同类型的变量在内存中占用的空间大小不同.
计算机内部数据都是采用二进制储存.
二进制满二进一.
C语言无法表示二进制数,可以通过移位表示.
int main() {
int a = 10;//十进制数
int b = 0XAB;//十六进制数
int c = 0123;//八进制转十进制
int d = 0x01 << 1;//通过移位表示二进制
}
二进制转十进制
- 除二取余法
十进制转二进制
- 乘二取整法
十进制转八进制
- 除八取余法
八进制换十进制
- 乘八取整法
十进制转十六进制
- 除十六取余法
十六进制转十进制
- 乘16取整法
在计算机中数据分为有符号数和无符号数
无符号数中的所有位来表示数据.
有符号数的最高位表示符号位,正负 0表示正数,1表示负数.
原码
有符号数的原码
1 >>> 0000 0001
-1>>> 1000 0001
反码
正数的反码是其本身
负数的反码最高位符号位保持不变其余取反
原码 -1 >>> 1000 0001
反码 -1 >>> 1111 1110
补码
正数的补码是其本身
负数的补码最高位不变,其余的取反加一
原码 -1 >>> 1000 0001
补码 -1 >>> 1111 1111
补码转原码
先减一,再取反,或者做一次补码运算.
int main() {
unsigned int a = -10;
signed int b = 5;
if(a > b) {
printf("a > b\n");
}
else if(b > a) {
printf("b > a\n");
}
}
//运行结果为
a > b
//将a以无符号的形式打印会出现一个很大的值
//printf("a = %u\n",a);
-128-1结果为 127
127+1结果为 -128
+0 >>> 0000 0000
-0 没有-0,最小的数为 -128 >>> 1000 0000
大端模式的1在内存中中
高地址------------低地址
0X00-0X00–0X00-0X01
小端模式的1在内存中中
高地址------------低地址
0X01–0X00–0X00-0X00
//判断当前系统是大端模式还是小端模式
int main() {
union check
{
int i;
char ch;
}c;
c.i = 1;
if(c.ch == 1)
printf("小端模式");
else
printf("大端模式");
}
/* 边界判断问题 */
int main() {
for(unsigned char i = 0;i >= 0 ;i--)
printf("%d\n",i);
}
//该程序将死循环
int main() {
printf("%s\n",__func__);//打印当前函数
printf("%s\n",__FILE__);//打印函数所在文件
printf("%d\n",__LINE__);//打印当前行
#line 200 //指定下一行的函数为200
printf("%d\n",__LINE__);//打印出200
printf("%s\n",__DATE__);//打印编译时的日期
printf("%s\n",__TIME__);//打印编译时的时间
}
后缀
数字后面加 L 或 l 表示长整型
数字后面加 U 或 u 表示无符号型
数字后面加 UL, LU, ul, lu 表示无符号长整型
int 默认为 signed int
long long a; — long long int a; ---- signed long long int a;
变量的本质:一块连续的存储单元(内存,寄存器),变量不一定有地址.
变量有名字,用来表示存储单元的内容.
数据对象没有名字,是一种具有数据类型的连续存储单元(例:malloc申请的内存).
不同类型变量占据内存空间不同
计算机中任何有符号数都是以补码形式进行存储和运算,为了简化硬件.
不同编译器类型的大小也有所不同.
宏定义INT_MAX和INT_MIN代表有符号整型的最大值和最小值,同样还有CHAR_MAX,SCHAR_MAX等,这些宏定义在 limits.h 文件中.
ASCII码:用7位二进制表示128个字符.
最高位是校验位(奇校验和偶校验).
- 0~31是控制字符或通信专用字符(不可显示).
- 32~126是可显字符,包括数字,字母,标点符号等.
字符型数据在内存以ASCII码形式储存.
转义字符:
制表符 \t
回车符 \r
换行符 \n
每个变量占1个字节
允许对字符变量赋整数值,低八位赋值给字符变量,高八位截断.
字符和整数之间可以直接进行数值运算.
与浮点型进行运算时会自行转换 char >> int >> float
浮点数:可近似表示任意实数.
浮点型:float. double. long double,
float 占4字节
double占8字节
long double 占12字节
浮点数全是有符号数,最高位代表符号位
在内存的分布为 [符号位] [移码] [尾码(移位后剩余的小数)]
例:100.125(十进制)----1100100.001(二进制)
阶码为正数的二进制位数减一 ----1100100(7位-1) 最终阶码为6
阶码表示小数点向前移动多少位后使整数位为1.
移码 = 阶码 + 127 = 6 + 127 = 133(十进制) = 10000101(二进制).
最终内存分布为
[符号位][0]
[移码][10000101]
[尾码]100100001
最终100.125在内存中表示为0 1000101 100100001 = 0X42C84000(十六进制)
int main() {
union{
float f;
int i;
}t;
t.f = 100.125;
printf("%x\n",t.i);
}
特别注意:不能直接比较浮点数是否相等.
int main() {
float a = 0.1;
float b = 0.1;
if(a == b)//错误
;
#define EPSILON 1E-6
if(abs(a-b)<EPSILON)//正确
;
}
C语言在进行数据运算时要求操作符两边数据类型相同.
编译器会自动进行类型转换,称为隐式转换.
当算术或逻辑表达式中的操作数类型不同.
当赋值运算符两侧类型不匹配.
函数调用过程中实参和形参类型不匹配.
return语句中的表达式类型与函数返回类型不匹配.
C99新增数据类型_Bool.
C99之前(包括C99)中没有bool、ture、false关键字.
但stdbool.h中通过宏定义让C语言能使用bool,true,false.使其兼容C++.
//复数的使用
#include <stdio.h>
#include <complex.h>//需要包含该文件才能使用复数
#include <stdlib.h>
int main() {
double _Complex z1 = 1.5 + 7.2 * I;//定义一个复数.
double _Complex z2 = 3.2 + 8.1 * I;//定义一个复数.
double _Complex z3 = z1 + z2;
printf("%lf + %lfi\n",creal(z3),cimag(z3));
}
表达式就是由运算符和操作数组成的一个符合C语法规则的式子序列.
单个常量或变量也是一个表达式.
根据运算符种类, 可分为不同种类表达式(算术表达式,关系表达式,逻辑表达式).
表达式运算后的结果为表达式的值.
值的类型为表达式.
表达式+; =语句.
语句是对计算机的指令:声明语句、赋值、函数调用、结构化语句、空语句、复合语句…
语句可分为简单语句和复合语句,简单语句以;结束
单目运算符
双目运算符
三目运算符
C语言一共48中预算符
ANSI C:44种运算符.
C90:增加了一元+运算符;区分前缀后缀 ++/–.
C99:增加了复合字面运算符.
表达式中,优先级高的运算符先选择操作对象.
优先级用来确定运算符的操作对象,而不是用来确定运算次序的,后者是由编译器去决定的.
C语言优先级一共16级,16级最高,1级最低
记忆方法
[]、()、、->、++/–(后)、(类型名) {值列表}
++/-(前缀)
单目运算符、算术运算符、关系运算符、逻辑运算符、条件运算符、赋值运算符、逗号顺序求值
位逻辑运算符优先级高于逻辑运算符
结合性:
结合性就是一串操作符是从左往右依次执行还是从右往左依次执行
当表达式中运算符优先级相同,那么运算符就会根据结合性来挑选操作数。
左结合与右结合:
左边的运算符先挑运算对象,依次向右执行
右边的运算符先挑运算对象,依次向左执行
求值顺序和结合性无关,和编译器有关
右结合型的运算符:
单目运算符
赋值运算符
条件运算符
C99中 / 的结果总是向零取整.
C99中 % 的值的符号跟被除数相同.
逻辑运算符优先级:!>算术运算符>关系运算符> && >||> 赋值运算符
1.printf() 函数输出格式化的字符串.
2.sprintf() 函数把格式化的字符串写入变量中.
3.fprint() 函数把格式化的字符串写入指定的输出流(文件/流/数据库).
4 vprintf() ,vsprintf() ,vfprint(),这三个函数和上边三个一样,只是加v,参数放到数组里了。
1.scanf() 从输入终端读取数据.
2.fscanf() 从指定流或文件读入数据.
3.sscanf() 从参数所给的字符串中读取字符.
4.vsscanf() ,vscanf() ,vswscanf(),
- stdin 标准输入流
- stdout 标准输出流
- stderr 标准错误
函数
类型标识符 函数名(类型1 形参1,类型2 形参2,…)
传递参数—函数体运算—返回值
void fun(void) {
}
一些需要注意的地方
C89标准中若没有明确声明将默认为int型(返回类型,参数声明).
C99中不再支持隐含int.
C89标准中返回类型非void的函数可以使用不带返回值的return语句.
C99标准中返回类型非void的函数必须使用带返回值的return语句.
int fun(int num) {
assert(num != 0);
int k = 100/num;
return k;
}
一个函数在函数体能调用该函数本身.
递归是嵌套调用的一种特殊的情况.
优点与缺点
递归函数需要注意的一些地方
函数本质上默认是外部的:extern
外部声明后可以被其他文件调用
内部函数:使用static修饰
只能被本文件的函数调用
可以与其他文件同名
用inline修饰
int inine fun(void) {
}
优点
缺点
总结
与一般函数和宏的区别
变量在程序中的作用范围.
外部变量(局部变量)
局部变量
外部变量和局部变量的作用域
变量作用域的屏蔽
在程序的链接过程中可能出现各个代码段有相同名字的变量,这时候需要根据变量的链接属性来区分.
外部链接属性extern
内部链接属性internal
无连接属性none
变量的本质
声明变量的意义
程序代码区(代码区)
静态存储区(数据区)
动态存储区
自动变量:auto
静态变量:static
寄存器变量:register
外部变量:extern
告诉编译器,该变量随时会发生变化,每次使用该变量直接到内存中去取而不是采用暂存在寄存器中的值.
变量同时使用const和volatile
- 状态寄存器
被const和volatile同时修饰的变量相当于状态寄存器,只有硬件能对其进行修改.
标准C = C语言标准 + 标准库函数
– 库函数源代码不可见,一般通过头文件引出
– 库函数一般分为两种
1.根据C标准实现的库函数
2.编译器自己扩展的库函数
不同编译器对标准库实现有差异
– 底层环境,软硬件方面存在差异
– 不同架构的CPU的I/O实现上的差异
动态链接库与静态链接库
– 静态链接库在程序编译链接的时候和用户代码链接在一起
– 动态链接库在程序运行的时候有效
– 使用静态链接库链接的代码体积较大
C89标准中一共有15个头文件
#include
//设定插入点
#include//字符处理
#include//定义错误码
#include//浮点数处理
#include//文件输入/输出
#include//参数化输入/输出
#include//数据流输入/输出
#include//定义各种数据类型最值常量
#include//定义本地化函数
#include//定义数学函数
#include//定义输入/输出函数
#include//定义杂项函数及内存分配函数
#include//字符串处理
#include//基于数组的输入/输出
#include//定义关于时间的函数
新增
- complex.h
- fenv.h
- inttypes.h
- iso646.h
- stdbool.h
- stdint.h
- tgmath.h
- wchar.h
- wctype.h
/* 将a[0]赋初始100,a[9]赋初始900,其他均为0 */
int a[10] = {[0] = 100,[9] = 900};
main(void) {
int a[10];
//若a的地址为0,则&a+1结果为40.
//因为&a表示的是整个数组,此时应当做一个数组的元素,且该元素大小为40字节.
}
首元素
的地址若定义数组 int a[10];
需要注意的地方
strlen返回字符串长度(字符长度,不包括’\0’)
直接引用传入
传递的是一个地址,或者说是常量指针
//注意:传入的是数组的地址,修改数组会影响实参.
//int fun(int a[])//不需要加具体长度也可以,因为传入的是一个指针.
int fun(int a[10]) {
for(int i=0;i<10;i++) {
a[i] = i;
}
}
//用const修饰数组,防止数组被修改
int fun(const int a[]) {
}
//int fun)(int a[][10])
int fun(int a[2][10]) {
}
//用static修饰表示告诉编译器该数组元素至少为5,此时传进来的不止是地址,还会把元素从传入
//节省程序从内存中读取元素的时间,用于提高程序的效率
//C99标准
int fun(int a[static 5]) {
}
C99标准中数组定义时数组大小可以是一个变量
//一维变长数组作为函数的参数
//变长数组作为函数的参数时,数组长度变量必须声明在变长数组之前,即int n要在int a[n]之前
int fun(int n, int a[n]) {
}
//int fun(int rows, int cols, int a[][cols])
//int fun(int rows, int cols, int a[][*])//C99标准可以这样使用,但GCC没有实现该功能
int fun(int rows, int cols, int a[rows][cols]) {
}
指针的重要性
指针变量的大小由编译器决定,而不是由系统(32位,64位)决定
int a[10];
int *p = a;//指向数组首元素
int (*p2)[10] = a;//指向长度为10的数组,指向整个数组a,加括号是因为方括号的优先级高于*
int [10](*p2) 和 int (*p2)[10]相同
可以 int (*p2)[10] = a;也可以 int (*p2)[10] = &a;但后者因为类型不同会出现警告
#include <stdio.h>
int main(int argc, char *argv[])
{
int a[2][3] = {{1,2,3},{4,5,6}};
int (*p1)[3] = a;//指向二维数组的指针
int (*p2)[3] = a[0];
printf("p1 = %x\n",p1);
printf("p2 = %x\n",p2);
printf("p1[0] = %x\n",p1[0]);
printf("p2[0] = %x\n",p2[0]);
printf("p1+1 = %x\n",p1+1);
printf("p2+1 = %x\n",p2+1);
printf("p1[0][0] = %x\n",p1[0][0]);
printf("p2[0][0] = %x\n",p2[0][0]);
printf("(*p1)[0] = %x\n",(*p1)[0]);
printf("(*p2)[0] = %x\n",(*p2)[0]);
printf("(*p1+1)[0] = %x\n",(*p1+1)[0]);
printf("(*p2+1)[0] = %x\n",(*p2+1)[0]);
return 0;
}
int a[2][3];
//int fun(int rows, int a[2][3])
//int fun(int rows, int a[][3])
int fun(int rows, int (*p)[3])
{
}
//每个元素都是字符串指针
//定义一个数组,该数组的每个元素都是一个指向char *类型的指针
//sizeof(p)的结果为20
//*(p+1) == p[1]
char *p[5] = {
"123",
"hello",
"world",
"12345",
"111111111111"
};
void fun(int len, char *p[]) {
for(int i = 0; i < len; i++)
printf("%s\n",p[i]);
}
int *a[10];//指针数组
int (*a)[10];//数组指针
//定义一个函数
int fun(void) {}
返回类型 (*函数名)(形参) {
}
int (*funp)(void) {
}
//两种赋值方式结果一样
funp = fun;
funp = &fun;
//无论取多少次地址都是一样的结果
funp();
(*funp)();
(**funp)();
#include <stdio.h>
int fun(void) {
printf("fun\n");
}
int main(int argc, char *argv[])
{
int (*funp)(void) = fun;
fun();
funp();
(*funp)();
(**funp)();
(******funp)();
(*fun)();
(***fun)();
printf("fun = %x\n",fun);
printf("&fun = %x\n",&fun);
printf("funp = %x\n",funp);
printf("*funp = %x\n",*funp);
printf("**funp = %x\n",**funp);
printf("*****funp = %x\n",*****funp);
unsigned int *p = (unsigned int *)fun;
printf("p = %x\n",p);
printf("*p = %x\n",*p);
return 0;
}
void fun(int (*funp)(int a, int b)) {
}
int a = 0;
//const修饰了*p,此时*p为一个常量无法被修改
//指向常量的指针
const int *p = &a;
int const *p = &a;//与上面相同
//可以对指针p进行修改,但不能对p所指向的常量进行修改
#include <stdio.h>
int main(int argc, char *argv[])
{
int a = 10;
int b = 10;
const int *p = &a;
int const *t = &a;
a = 100;
printf("a = %d\n",a);
printf("*p = %d\n",*p);
printf("*t = %d\n",*t);
/*
因为*p和*t被const所修饰,所以*p和*t所指向一个常量类型(const int)的地址,
而常量不能被修改,所以以下代码无法被编译通过.
*p = 20;
*t = 20;
p = &b;
t = &b;
*p = 20;
*t = 20;
printf("b = %d\n",b);
printf("*p = %d\n",*p);
printf("*t = %d\n",*t);
*/
const const const const int const const const *k;
return 0;
}
int a = 10;
int b = 20;
int * const p = &a;
*p = 100;
/*
因为p被const所修饰,所以p为一个常指针,该指针无法被修改,
只能指向定义时初始化的地址.,所以以下代码无法被编译通过.
但是可以对指向的地址的内容进行修改.
p = &b;
*/
int a = 10;
int const * const p = &a;
/*
无法对指针p进行修改操作,也无法对所指向的内容进行修改.
但依然可以读取.
*/
printf("p = %x\t*p = %d\n",p,*p);
int const arr[10];
/*
存放的元素都为常量,无法赋值,相当于指向常数的指针
arr[0] = 10;
*/
int main(void) {
}
//int main(char *argc, char *argv[])
int main(char *argc, char **argv) {
}
int a[10];
int * __restrict__p = (int*)malloc(10);
int *q = a;
for(int i = 0; i < 10; i++) {
a[i] += 10;
q[i] -= 10;
p[i] += 5;
a[i] *= 10;
p[i] += 10;//编译器会优化成p[i] += 15;
}
– 提醒用户要使用满足restrict要求的参数
格式一
struct tagPhone
{
char A;
int B;
short C;
}Phone;
格式二
struct tagPhone
{
char A;
short C;
int B;
}Phone2;
格式三
struct tagPhone3
{
char A;
char B[2];
char C[4];
}Phone3;
//结构体大小为char的大小,结构体成员分别占cahr的一定位宽
//下列a占内存的第1位(最低位),b占用2-3位,c占用第4位(最高位)
//若k.c = 1;则k的内存中的值为8,若k.a = 1;则k的内存中的值为1.
struct data {
char a:1;
char b:2;
char c:1;
}k;
#include
union {
struct data_strcut {
char a:1;
char b:2;
char c:1;
}k;
char data;
}test;
int main(int argc, char *argv[])
{
printf("test size = %d\n",sizeof(test));
test.data = 0;
printf("test.data = %d\n",test.data);
test.k.c = 1;
printf("test.data = %d\n",test.data);
return 0;
}
//这样嵌套定义可以使代码更加紧凑,比定义多个结构体更节省空间.
struct student {
int name;
int grade;
}
struct teacher {
int name;
int NO;
}
union union_data {
struct student stu;
struct teacher teh;
}
struct person {
int age;
int sex;
union union_data u;
}
//这样嵌套使用可以模拟一个寄存器,可以进行字节寻址,也可以位寻址
union lcd_reg {
struct lcd_control {
int lcd_clr:2;
int lcd_on:1;
int lcd_cor:5;
}lcd_bit;
int lcd_data;
}reg;
reg.lcd_data = 0;//此处对整个寄存器清零
reg.lcd_bit.lcd_on = 1;//此处对寄存器的某一位置位
//枚举中saturday类型中saturday的值默认为0,后面的成员依次递增
//若(saturday = 2,)时,saturday 的初始值为2,后面的成员也依次递增
//若(wednesday = 10,)时,wednesday 的初始值为10,后面的成员依次递增
enum day
{
saturday,//(saturday = 2,)
sunday,
monday,
tuesday,
wednesday,//(wednesday = 10,)
thursday,
friday
} workday;
int a = 1;
enum day weekend;
weekend = friday;
weekend = (enum day)a; //类型转换
//weekend = a; //错误
//为结构体定义新的别名
typedef struct {
}student;
student a;
//为数组定义新的别名
typedef int arr_10[10];//等价 typedef int [10] arr_10;
arr_10 a;
sizeof(a);//结果为10*4 = 40
//为指针定义别名
typedef char * charp;
charp a = "hello world";
puts(a);
//typedef int (*myfun)(int a, int b);//等价 typedef int (*)(int a, int b) myfun;
int new_fun(int a, int b);
myfun p;
p = new_fun;
struct k{
int a;
int b;
};
int fun(struct num) {
int sum;
sum = num.a + num.b;
return sum;
}
//传参
//类似java匿名内部类
fun((struct k){2,3});
void fun(int len, int a[10]) {
}
//传参
fun(10, (int [10]){0,1,2,3,4,5,6,7,8,9});
#include
#define
#undef
- 预定义的宏
LINE
FILE
…
#if
#endif
#elif
#else
#ifdef
#ifndef
重置行号和文件名#line
产生错误信息#error
修改编译设置#pragma
用一些标识符作为宏名来代替一些符号或常量的命令
在预处理阶段,预处理器会将程序中所有出现的宏名用宏定义中的字符串替换,这个过程称为宏替换或宏展开
宏定义格式
#define 宏名 字符串
#define SUM(A,B) printf("A+B = %d\n",A+B)
#define SUM2(A,B) printf(""#A"+"#B" = %d\n",A+B)
SUM(A,B);
->printf("A+B = %d\n",2+3)
SUM2(A,B);
->printf("""2""+""3"" = %d\n",2+3)
#define INT(n) a##n
int INT(1);//int a1;
#define INT(n) a##n
#undef INT
int INT(1);//出错无法通过
– #undef的使用
重置行号和文件
1.#line 1000 "hello"
2.printf("行号 = %d\n",__LINE__);
3.printf("文件名 = %s\n",__FILE__);
结果
行号 = 1000
文件名 = hello
#define PI 3.13
#if PI
#else
#error "NO define PI!\n"
#endif
//编译的时候显示的信息
#pragma message("这是自己添加的编译信息")
#pragma warning( warning-specifier : warning-number-list [; warning-specifier : warning-number-list…]
#pragma warning( push[ ,n ] )
#pragma warning( pop )
主要用到的警告表示有如下几个:
once:只显示一次(警告/错误等)消息
default:重置编译器的警告行为到默认状态
1,2,3,4:四个警告级别
disable:禁止指定的警告信息
error:将指定的警告信息作为错误报告
参考:https://blog.csdn.net/gggg_ggg/article/details/42006019
设置对齐模数
_Pragma 是一个操作符
_Pragma ("pack(4)") 等价于 pragma pack(4)
堆与栈的区别
FILE *fp;
fp = fopen("test.c","wt");
fclose(fp);