嵌入式-C语言-8-字符指针数组/大型程序实现

本文详细介绍了字符指针数组的概念与应用,包括指针数组的定义、取值和常见场景。同时深入探讨了C语言预处理,涵盖了头文件包含、宏定义、条件编译等内容,并强调了头文件卫士的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、字符指针数组

1.1.明确:[]运算符的计算步骤:

      1.先求地址

      2.后取值

      char a[] = {'a', 'b'};

      char *p = &a;

      p[1]:先求地址:p+1,然后取值:*(p+1)

1.2.指针数组(实际开发很常用)

a)指针数组概念:数组中每个元素都是一个指针(地址),元素只能是地址,不能是普通的数据

b)指针数组定义的语法格式:

    数据类型  *数组名[元素个数/数组长度] = {地址列表};

    例如:

    int a = 10, b = 20, c = 30;

    int *p[3] = {&a, &b, &c};

    结果:p[0] = &a, p[1] = &b, p[2] = &c;

    等价于:*(p+0) = &a, *(p+1) = &b, *(p+2) = &c;

    取值:*p[0] = 10, *p[1]=20, *p[2] = 30;

    等价于:**(p+0) = 10, **(p+1) = 20, **(p+2) = 30;

元素个数 = sizeof(数组名)/sizeof(数组名[0])

c)应用场景:如果将来需要定义大量的一堆堆的指针变量,反而让代码看起来极其繁琐,采用指针数组来进行统一,类似数组来替换大量的变量,现在用指针数组替换大量的指针变量

例如:

int a = 10, b = 20, c = 30, d = 40, e = 50; ...

int *pa = &a, *pb = &b, *pc = &c, *pd = &d, *pe = &e; ..

指针数组来优化:

int *p = {&a, &b, &c, &d, &e ...};

1.3.字符指针数组

a)概念:字符指针数组是特殊的指针数组,每个元素是一个字符串的首地址

   例如:

   char  *p[] = {"abc", "efg"}; //第0个元素是字符串"abc"的首地址

 第1个元素是字符串'efg'的首地址

   等价于

   char *p1 = "abc";

   char *p2 = "efg";

   char *p[] = {p1, p2}; 

   结果:

   p[0] = p1 = "abc" 

   p[1] = p2 = "efg";

b)应用场景:需要用到大量字符串指针变量此种情形!

二、预处理

2.1.回顾C程序编译三步骤

a)预处理:gcc -E -o xxx.i xxx.c

b)只编译不链接:gcc -c -o xxx.o xxx.i/xxx.c

c)链接:gcc -o xxx xxx.o    

2.2.预处理指令(以#开头,不分分号;)分类:

a)头文件包含指令:#include,又分两类:

#include <stdio.h> //告诉gcc直接到/usr/include目录下找stdio.h并且把stdio.h的内容全部拷贝过来

#include "stdio.h" //告诉gcc先到当前目录下找stdio.h,如果没有找到再去/usr/include目录下找stdio.h

   问:如果包含的头文件不在当前目录下,也不在/usr/include目录下,怎么找到包含的头文件呢?

   答:可以通过-I选项指定头文件所在的路径(核心,开发常用)

   编译命令格式:gcc -E -o xxx.i xxx.c -I /home/xxxxxxxx

b)宏定义指令:#define

1.建议:宏的名称用大写,续行用\

2.作用:提高代码的可移植性,让代码将来改起来方便

3.宏定义指令又分两类:常量宏和参数宏(宏函数)

3.1.常量宏

语法格式:#define  宏名    (值)

         例如:#define   PI   (3.14)

         效果:将来gcc会将程序中所有的宏PI替换成3.14

3.2.参数宏(又称宏函数)

a)语法格式:#define  宏名(宏参数)   (宏值)   

    注意:宏值里面的宏参数不要少圆括号

              如果宏参数有多个,用逗号来分区

    例如:#define   SQUARE(x)  ((x)*(x))

              #define  SUB(x, y)   ((x) - (y))

    优点:代码的执行效率比函数要高   不需要传参,拷贝之类的

b)gcc替换过程:gcc编译器先将宏参数替换成实际值, 然后将代码中宏名最终全部替换成宏值

c)#(转字符串指令)和##(粘贴指令)

1.#作用:将后面跟的宏参数转换为字符串

                例如:#N替换成了"N"

2.##作用:将其后面的宏参数进行替换然后与前面的部分粘连在一起,最终作为宏值替换

d)编译器已经定义好的宏(直接使用,无需#define),实际开发非常常用!

   注意:都是两个下划线

   __FILE__:表示当前文件名,%s

   __LINE__:表示当前行号,%d

   __FUNCTION__:表   示当前所在的函数名,%s,等价于__func__

   __DATE__:文件创建的日期,%s

   __TIME__:文件创建的时间,%s

   实际开发使用:

   printf("这里出错了:%s, %s, %s, %s, %d,出现了一个野指针的访问错误.\n",
__DATE__, __TIME__, __FILE__, __FUNCTION__, __LINE__);

e)用户预定义宏:通过gcc的-D选项来预定义一个宏

   作用:程序在编译的时候可以给程序传递一个值

   注意:如果宏是一个字符串,那么-D后面的宏值需要用\"说明

   例如:gcc -DSIZE=50  -DWELCOME=\"小陈\" 

   结果代码中可以直接使用SIZE和WELCOME宏,并且他们的值分别是50和"小陈"

   printf("%d %s\n", SIZE, WELCOME);

f)条件编译命令 (实际开发用的非常多)

   #if //如果,例如:#if A==1    

   #ifdef //如果定义了....

   #ifndef  //如果没有定义 ....

   #elif //否则如果

   #else //否则

   #endif //和#if,#ifdef,#ifndef配对使用

   #undef //取消定义,和#define死对头,例如:#define PI (3.14)  #undef PI

   实际开发的演示代码:

   利用条件编译让一套代码支持不同的CPU(X86,ARM,POWERPC,DSP)

   //vim 
 A(void)
   {
#if  ARCH==X86
//只编译X86相关代码
#elif ARCH==ARM 
//只编译ARM相关代码
//...
   }

三、大型程序实现

3.1.掌握:头文件卫士

a)结论:以后编写任何头文件编码框架必须如下:

      vim A.h 添加

      #ifndef  __A_H  //头文件卫士,宏名一般跟头文件名同名

      #define __A_H

      头文件的代码区域

      #endif //和#ifndef配对

      头文件卫士作用:防止头文件内容重复定义

b)问:头文件卫士如何保护呢?

   例如:

   //没有头文件卫士的情形:

   vim a.h 添加

   int a = 250; //定义变量

   保存退出

 

   vim b.h 添加

   #include "a.h"

   保存退出

 

   vim main.c 添加

   #include <stdio.h>
   #include "a.h"
   #include "b.h"
   int main(void)
   {
printf("%d\n", a);
return 0;
   }

  保存退出

  gcc -E -o main.i main.c

  vim main.i

  结果:

  int a = 250; //重复定义了

  int a = 250; //重复定义了

  问:如何解决?

  答:添加头文件卫士

  //添加头文件卫士情形:

  vim a.h 添加

  #ifndef __A_H

  #define __A_H

   int a = 250; //定义变量

  #endif

   保存退出

    

   vim b.h 添加

   #ifndef __B_H

   #define __B_H

   #include "a.h"

  #endif

   保存退出

 

   vim main.c 添加  

 #include <stdio.h>
   #include "a.h"
   #include "b.h" 
   int main(void)
   {
printf("%d\n", a);
return 0;
   }

  保存退出

  gcc -E -o main.i main.c

  vim main.i

  结果:没有重定义的错

3.2.实际开发产品代码组成部分:三部分

a)代码开发原则:不要用一个源文件搞定,实际产品代码不可能一个源文件,否则造成代码的维护极其的繁琐,实际产品代码应该按照功能将一个源文件分拆成多个源文件来实现,提高了代码的可维护性。

b)实际产品代码分拆三部分:

   1.头文件部分:负责变量的声明,自定义数据类型的声明和函数的声明

         作用:将来别的源文件只需包含头文件即可访问变量和函数

         例如:dog.h,duck.h,chick.h

   2.源文件部分:负责变量的定义,自定义数据类型变量的定义,函数的定义

         例如:dog.c描述狗,duck.c描述鸭子,chick.c描述鸡

   3.主文件部分:负责统领头文件和源文件,它里面只有main函数,它负责调用其他源文件的变量和函数

          例如:main.c

   4.建议:一个源文件对应一个头文件,并且源文件包含自己对应的头文件

   5.建议:如果头文件中有相同的内容,建议摘出来之后放到一个公共的头文件中,其他头文件只需包含功能的头文件即可

                vim common.h

                两只眼

                vim dog.h

                #include "common.h"

                vim duck.h

                #include "common.h"

   6.注意:static

案例:实现加,减,清0,置1

分析:

头文件:add.h  sub.h  set.h  clear.h

源文件:add.c  sub.c  set.c clear.c

主文件:main.c

分步编译:

gcc -E -o add.i add.c

gcc -E -o sub.i sub.c

gcc -E -o set.i set.c

gcc -E -o clear.i clear.c

gcc -E -o main.i main.c

gcc -c -o add.o add.i

gcc -c -o sub.o sub.i

gcc -c -o set.o set.i

gcc -c -o clear.o clear.i

gcc -E -o main.o main.o

gcc -o main main.o set.o clear.o sub.o add.o

./main

一步到位:

gcc -o main main.c sub.c add.c set.c clear.c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值