学习C语言 8.5 预处理和指针

目录

一、预处理

1.宏定义 

2.文件包含  

3.条件编译 

二、指针

1.指针变量定义 

2 指针类型 

3. 指针变量的引用


 

一、预处理

预处理:
    编程流程:
    1.编辑 
    2.编译   gcc main.c  生成可执行文件a.out 
    3.运行 
    4.调试 
     
编译过程:
  1.预处理  
    //预编译 -- 将 代码中相关 预处理命令执行 最终生产一个 只包含c语言代码的文件

指令:gcc -E file.c hello.i
  2.编译 
    //编译   -- 对语法进行检查,将这个c的源代码 生产 汇编代码

指令:gcc -S file.c -o hello.s
  3.汇编 
    //汇编   -- 表示将 汇编源代码 最终生成 机器代码 //object

指令:gcc -c file.s -o hello.o
  4.链接
    //链接   -- 将使用到的其它代码了链接到一起 生成 最终可执行文件

 

1.宏定义 

语法:             
    #define 宏名   宏值  
   
   注意:
    1. 预处理命令 都是以 # 开头的 
    2. 宏名 命名规则 和 之前标识符命名规则一致 
       注:
          宏名一般都写大写 ,以区别与普通变量名 
    3. 预处理 实际上 是将 宏名 用 宏值(预处理阶段的 字符串) 原样替换 //文本替换 
       注意:
           c语言字符串中出现的 "宏名" 不会被替换,如下列代码中的printf引号中的N就不会被替换成10

如:

#include <stdio.h>
#define N 10

int main(void)
{
    printf("N=&d\n",N);

    return 0;
}


    4. 应用 
       a. 提高代码可读性   
       b. 一改全改,方便代码修改 
     
    5. 宏名的 作用域

        #undef 宏名   
            表示 取消 宏名的 定义 
    
    注意:
       只是在预处理阶段发挥作用。
       
       作用范围:
       从定义处开始,到 #undef 结束

编写代码时,可以使用宏定义,给一个宏名表示数字

#include <stdio.h>
#define ARRAY_SIZE 10

int main(void)
{
	int a[ARRAY_SIZE]={1,2,3,4,5,6,7,8,9,10};

	int i=0;
	for(i=0;i<ARRAY_SIZE;++i)
	{
		printf("%d ",a[i]);
	}
	putchar('\n');

	return 0;
}

注意宏定义的时候,后面不要加分号,否则会报错

e7079672412c427ba830c3813b933533.png

我们可以从.i文件里看出它为什么会出错

2289f50c398a49efb5f1ab8c89309dac.png

可以看出,如果宏定义时带分号,文本原样替换时就会把分号带上

 

带参宏定义: 
语法:
    #define 宏名(参数) 宏值 
    
    
eg:
   #define ADD(a,b) a+b 
 

#include <stdio.h>
#define ADD(a,b) a+b
#define MUT(a,b) a*b
#define JIAN(a,b) a-b
#define CHU(a,b) a/b

int main(void)
{
	int x=8;
	int y=2;

	printf("+ %d\n",ADD(x,y));
	printf("* %d\n",MUT(x,y));
	printf("- %d\n",JIAN(x,y));
	printf("/ %d\n",CHU(x,y));

	return 0;

}


注意:
   1.带参宏 和 函数有本质的区别
     a.处理阶段不一样 
       宏定义 --预处理阶段 
       函数   --编译阶段 
     b.使用不一样
       宏     -- 预处理阶段  就使用结束了 
                 宏的使用,本质上,是文本的原样替换
                 参数 
                     宏的参数,只是进行 文本替换用,不进行语法检查                  
          函数   -- 调用时,进行使用 
                 函数的使用,本质上是函数代码的调用 
                 参数 
                     函数的参数,是有类型的,编译阶段是要进行类型检查的 
                     
    c.应用 
           一般对于一些短小代码 ,考虑写成带参宏
           不超过5行的代码         
           
    d.宏的副作用 

如:

#include <stdio.h>
#define MUT(a,b) a*b


int main(void)
{
	printf("* %d\n",MUT(1+2,3+4));

	return 0;

}

我们的本意原来是计算表达式之后,两个数相乘,应该为3*7=21,但是宏定义实际上只是文本原样替换,所以会变成1+2*3+4=11,这就是宏的副作用。


       处理:
           会变的都加括号,整体也加括号。

如下列处理

#include <stdio.h>
#define MUT(a,b) ((a)*(b))


int main(void)
{
	printf("* %d\n",MUT(1+2,3+4));

	return 0;

}

这样计算就是:((1+2)*(3+4))=21


    e.注意
      宏定义 必须 写在同一行 
      如果要换行,需要每行最后加上续行符\,并且续行符后面不能有空格

注意:在用宏定义时后面不能加;但是如果在宏定义出写do while(0),就可以加分号

1b23450421de4cdc85dc910996753eca.png

如18行位置处,就可以加分号

 

2.文件包含 
 

#include <文件名>
#include "文件名"

  <> 与 "" 
 区别:
    在于,查找头文件的方式不一样 
   <>  //到系统默认的路径寻找对应的头文件 
   ""  //表示先到当下目录下寻找头文件,如没有,再到系统默认路径下寻找 

 

3.条件编译 

       
  (1). 
  #ifdef 标识符
    程序段 1
  #else
    程序段 2
  #endif
  
  
  含义:
     如果 定义了 标识符 
     则 预处理程序段1  //就是将程序段1的代码 保留 
     否则 
        保留程序段2

  用途:
      //1.调试代码 
      //2.设计头文件 
  (2).
     #ifndef 标识符
        程序段1
     #else
        程序段2
     #endif
      
    含义:
     如果 没有定义了 标识符 
     则 预处理 程序段1  //就是将程序段1的代码 保留 
     否则 
        处理 程序段2
        
  (3).
    #if 表达式
      程序段1
    #else
      程序段2
    #endif
     
   含义:
       表达式为真 处理程序段1 表达式为假 处理程度段2     
eg:
  #if 0
   ...
  #endif

#ifndef __DAYS_H
#define __DAYS_H

int days(int year,int mouth);

#endif

#ifndef防止多次包含,带来重复定义的问题 

 

二、指针

指针概念:
   地址 内存单元的编号 
   指针 就是 地址 
  
指针 也是一种数据类型 
        指针类型 
        这种数据类型 是专门用来处理 地址 这种数据
    

1.指针变量定义 

基类型 * 指针变量名
   (1)基类型    
            //数据类型 
            整型
            浮点型 
            字符型 
            数组类型 
            指针类型 
            ....
            //结构体类型 ,函数类型 
           作用:            
               表示该指针类型 所指向的内存空间 存放什么类型的数据 
  (2).*   
          //定义时,表示此时定义的是一个 指针类型 的变量
  (3).指针变量名 
          //符合标识符命名规则 

int * p; //pointer 
  int a = 10; //a所在的空间是用来存放 int(整型)类型的数据的

&a //表示获得a所在空间的首地址 
     //表示 获得了一块 可以存在int型数据的内存空间的地址 

 

2 指针类型 

int *p;  //int *  ---整体叫指针类型 


  数据类型 变量名;
  int*    p; 
          //int* 含义 首先表示是一个 指针类型 
                     //表示指向int型数据的指针类型 

 

3. 指针变量的引用

   int a = 10;
   int *p = &a; //p指向a  ---因为p中保存了a的地址
   
   * //指针运算符   
     //单目运算 
     //运算对象 --- 只能指针(地址)
   
         //通过a可以修改这块地址内的值,也就是通过a访问的 -- 直接访问

 
 *p //表示访问 p所指向的 基类型的 内存空间,通过*p是间接访问
      step1: 首先拿出p中地址,到内存中定位 
      step2: 偏移出sizeof(基类型)大小的一块空间 
      step3: 将偏移出的这块空间,当做一个基类型变量来看   //*p运算完的效果  
      
*p //运算效果 相当于就是一个基类型的变量 
                
可以认为*p与a等价 
 
   int a; // 
        a 的数据类型 int 
       &a 的数据类型 int* //地址这种数据---对应到一种数据类型 --指针类型 
                          //
   float b;
         &b //float*

#include <stdio.h>

int main(void)
{
	int a=0x12345678;
	short *p=(short*)&a;

	printf("a=%#x\n",a);
	printf("*p=%#x\n",*p);

	return 0;

}

注意类型匹配,或者进行强制转换,如第6行,因为a是个int型,4个字节,而short是2个字节,所以需要强转,电脑是小端存储的,所以*p=0x5678

 

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值