C语言笔记14

186 阅读6分钟

标识符作用域

这节很好理解 就不具体敲代码了

块作用域

被一个花括号包括起来的部分称为一个代码块
块作用域有两个界限

  • 上界限 标识符声明
  • 下界限 包含声明的代码块结束

全局变量自动初始化为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 charint8_t
typedef shortint16_t
typedef intint32_t
typedef long longint64_t
typedef unsigned charuint8_ t
typedef unsigned> shortuint16_ t
typedef unsigned intuint32_ 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(宏)