标识符作用域
这节很好理解 就不具体敲代码了
块作用域
被一个花括号包括起来的部分称为一个代码块
块作用域有两个界限
- 上界限 标识符声明
- 下界限 包含声明的代码块结束
全局变量自动初始化为0或NULL
如果在同一代码块中重复声明同一标识符 将出现标识符重定义报错
平级代码块
{
int n;
}
{
int n;
}
两个代码块中定义的变量n相互独立 指代的不是一个内存空间
上下级代码块
{
int n;
{
int n;
}
}
下级覆盖上级标识符 内层作用域覆盖外层作用域 两个同名标识符指代的不是同一个数据对象 不会报错
带括号的块作用域
函数 if while for 可以形成带括号的块作用域 参数列表中声明的标识符 作用域为整个函数
文件作用域
在代码块外声明的标识符 作用域从声明开始直到源文件结束
如果在函数中声明了同名标识符 处理方法和上下级代码块一样 两个同名标识符作用域嵌套的情况下 下级作用域将覆盖上级作用域
预处理指令
以#开头 放在最前面 预处理器会在编译前处理预处理指令 修改c语言源码
define
- 格式
#define 宏 替换体
- 预处理在程序中找到宏后 会用替换体替换宏
eg 符号常量
#define 符号常量 值
- 宏的命名与标识符命名规则相同:字母 下划线 数字 且首字符不能是数字
- 替换体不限于值 只要找到替换代码后 还能正常通过编译就行
- 宏的替换无差别 仅仅把代码当作文本处理
带参数的define
- 宏函数格式
#define 宏(参数1,参数2) 替换体
eg 求两个数平均值
#define mean(a,b) (a+b)/2//定义宏函数
int result;
result = mean(2, 4);//使用宏函数
result = (2 + 4) / 2;//预处理后被替换为
- 宏函数本质依然是将宏替换为对应的替换体
#define sq(n) n*n
int main()
{
int n = 2;
sq(n);//预期为4 实际为4
sq(n + 2);//预期为16 实际为8
100 / sq(n);//预期为25 实际为100
sq(++n);//预期为9 实际为结果未定义
}
//为啥呢 因为实际展开式如下
n* n;//正确
n + 2 * n + 2;//要达到预期结果 预处理应为#define sq(n) (n)*(n)
100 / n * n;//要达到预期结果 预处理应为#define sq(n) (n*n)
++n* ++n;//不能在同一个表达式中多次对同一个变量使用++运算符 会导致结果未定义
- 为保证宏函数按照预期运行 不被运算符优先级影响 应在参数和替换体两边加括号
#define sq(n) ((n)*(n))
宏函数中的运算符
井号#
若在替换体内参数前加# 替换后 会用双引号包括这个参数
#define pr(name)"the value of"name"is %d\n"//替换为"the value of"name"is %d/n"
两个字符串中出现了变量name 不符合printf第一个参数需要字符串的写法 无法编译通过
#define pr(name)"the value of"#name"is %d\n"//替换为"the value of""name""is %d/n"
相邻字符串自动连接成一个完整字符串 可正常编译并打印出结果
#include<stdio.h>
#define pr(name)"the value of"#name"is %d/n"
int main()
{
int num = 123;
printf(pr(num),num);//the value of num is 123
return 0;
}
双井号##
可以将替换体中的两个记号组合成一个记号
#define com(group,name) group##name//展开为groupname
#define com(group,name) group name//展开为group name 中间有空格
#define com(group,name) groupname//如果去掉替换体中的空格 则没有与前面参数对应的记号 无法通过编译
用法示例
#include<stdio.h>
#define pr(group,name) "the value of "#group #name" is %d\n"//展开为"the value of groupname is %d\n"
#define com(group,name) group##name//展开为groupname
int main()
{
//两组变量
int group1apple = 1, group1orange = 2;
int group2apple = 100, group2orange = 200;
//使用两组变量
printf(pr(group1, apple), com(group1, apple));
printf(pr(group1, orange), com(group1, orange));
printf(pr(group2, apple), com(group2, apple));
printf(pr(group2, orange), com(group2, orange));
return 0;
}
取消宏定义
格式
#undef 宏
eg
#define num 100
#undef num//取消宏定义
#define num 101//重新定义宏
typedef关键词
C语言没有规定整型数据类型的大小范围 同样的类型在不同编译器中所占字节和位可能不同 因此可表示的范围可能不同 为了增强代码的可移植性 可以给数据类型定义别名 通过别名的数字判断数据类型所占字节和位 然后可以像使用普通类型一样使用类型别名 typedef关键词可以实现此功能
- 格式:声明变量后在前面加个typedef 变量名就变成这种数据类型的别名
typedef 数据类型 别名;
- 别名命名规则和标识符命名规则一样 字母数字下划线 且首字符不能是数字
- 作用域从声明到代码块结束
- typedef也常用于结构变量
typedef struct {
char name[20];
int gender;
double height;
double weight;
}person;//person就是这个结构变量的别名了
person p={"timmy",1,170.00,65.00}
- typedef与#define区别
typedef只能用来给类型区别名 不能用于值
typedef由编译器解释 而不是预处理器
typedef在某些情况下比#define好用
头文件stdint.h
在vs中
| typedef 整型类型 | 别名 |
|---|---|
| typedef signed char | int8_t |
| typedef short | int16_t |
| typedef int | int32_t |
| typedef long long | int64_t |
| typedef unsigned char | uint8_ t |
| typedef unsigned> short | uint16_ t |
| typedef unsigned int | uint32_ t |
| typedef unsigned long long | uint64_ t |
头文件inttypes.h
在vs中
| 有符号 | |
| #define PRId8 | "hhd" |
| #define PRId16 | "hd" |
| #define PRId32 | "d" |
| #define PRId64 | "lld" |
| 无符号 | |
| #define PRIu8 | "hhu" |
| #define PRIu16 | "hu" |
| #define PRIu32 | "u" |
| #define PRIu64 | "llu" |
用法示例
#include <stdio.h>
#include<stdint.h>
#include <inttypes.h>
int main()
{
int32_t n = 123;
printf("n=%" PRId32 "\n", n);//被转换拼接为"n=%d\n",n
return 0;
}
条件编译
预处理中的分支结构
#if
if从C语言关键词变成了预处理指令 在编译前由预处理器修改代码 保留走向分支的代码 删除跳过分支的代码
- 格式:#if 常量表达式
- 需要#endif标记指令块结束
#else和#elif
用法含义类似else和else if
#include<stdio.h>
#define n 0
//预处理前
int main()
{
#if n==1//常量表达式 无需括号
printf("111\n");
#elif n==2
printf("222\n");
#else
printf("000\n");
#endif //必须用#endif标记指令块结束
printf("***\n");
return 0;
}
//预处理后
int main()
{
printf("***\n");
return 0;
}
#ifdef和#ifndef
前者是if和defined的缩写 意思是是否定义了某宏
如果定义了该宏(甚至不要求宏有替换体)则保留代码块内的代码 否则删除代码块内的代码
- 格式:#ifdef 宏
- 需要#endif标记指令块结束
#include<stdio.h>
#define n 0
//预处理前
int main()
{
#ifdef n
printf("aaa\n");
#endif
#ifndef m
printf("bbb\n");
#endif
return 0;
}
//预处理后
int main()
{
printf("aaa\n");
printf("bbb\n");
return 0;
}
测试一个宏是否被定义过也可以使用另一种写法
#if define(宏)