每日一题(十四)

11.1 gcc输出文件类型

预编译

编译

汇编

链接

在这里插入图片描述

gdb:

在这里插入图片描述

11.2 符号中的空格

符号的中间一般不要加空格符、制表符、换行符,不然会引起歧义,比如---符号,空格的位置不同,运算结果也不同:

在这里插入图片描述

对于a --- b,编译器从左往右看,先读入一个-,再接着读入一个-,到这里编译器就认为这是一个自减运算符,再接着向右读入-,编译器认为---是先执行自减–-操作,然后执行减运算-的,所以a --- b在编译器看来就是a -- - b运算,得到结果为-1。

但是如果在第一个-之后就加上空格,即a - -- b,编译器就会认为第一个减号之后是程序员专门隔开的,所以先执行–-b再执行a - b,得到0。

再比如注释符/**/,如果遇到除数是指针指向的数据,可能会引起歧义:
在这里插入图片描述

如果之间在/后面加上*p,就会让编译器认为这是注释/**/的开始,所以加上空格就会明确了。

所以,以后在使用比较复杂的运算符组合的时候,切记要用空格来表达逻辑。

11.3 单双引号的区别

简单来说,单引号‘’括起来的字符代表一个整数,就是该字符的ASCII码值;

双引号“”括起来的字符串代表的是一个指向无名数组起始字符的指针,该无名数组被双引号包括的字符串以及一个\0字符初始化。

11.4 函数类型转换符——显示调用指定地址函数

如果我们要调出首地址为0xff位置的子函数,要想通过函数显式调用的方式,该怎么办?

通过类型转换符来进行强制类型转换,最终的语句为:(*void(*)() 0xff)();

构造这种表达式的核心思想就是:按照使用的方式来声明,也就是先了解要干什么,然后再看怎么做。

1.首先要明白C中变量的声明!!!

C中的变量声明由两部分组成:

  • 变量的数据类型,比如:int、float、char等等
  • 声明符,相当于变量的表达式,对声明符求值会返回一个声明中给的的数据类型的结果,比如:char *p中的*p,对*p求值会返回一个char类型的数据

比如:

char ff();:声明符ff()的求值结果是char,代表ff()函数的返回值是一个char类型的数据,即声明了ff是一个返回值为char的函数;

float *pf;:声明符*pf的求值结果是一个float类型的数据,即声明了pf是一个指向float的指针;

int *g();:声明符*g()的求值结果是一个int型的数据,()优先级比*高,所以可以看作*(g()),其中g是一个函数,(g())代表函数的返回值,*(g())代表对函数返回值的地址求值,得到一个int数据,也就是函数返回的是一个int型的指针;int *g()也可以写成int* g(),表示函数g的返回值是一个int类型的指针;

int (*h)():声明符中先执行(*h),表示这是一个指针,后面(*h)()表示这个指针指向h函数,所以表示h是一个指向返回值是int的函数,即h是一个函数指针;

2.已知函数指针,如何调用函数?
比如fp是一个函数指针,那么调用这个函数的方法就是:(*fp)();,也可以简写为:fp()

注意,不要把函数指针种指针的括号去掉,如果变为*fp(),就表示函数fp返回一个指针,*操作就是取得返回值的传递的值!

3.由变量声明推出相应的 类型转换符

了解声明之后,再看一下如何得到相应的类型转换符:把声明中的变量名和末尾的分号去掉,将剩余部分用括号封装起来就可以了。

比如我需要将一个指针强制转换为指向一个返回float的函数,首先写出此种函数的声明:

float (*f)();,然后去掉变量名和结尾分号,括号封装起来就是:(float (*)()),表示一个指向返回值是float型的函数的指针 的类型转换符。

言归正传,要调出首地址为0xff位置的子程序,就是要一个指针指向0xff位置的子程序!!!

具体方法是先写出这种指针的类型转换符,然后通过类型转换符声明一个指针指向0xff地址,过程如下:

  • 先借助声明,一个指向函数的指针:void (*f)();
  • 写出类型转换符,一个指向函数的指针的类型转换符:(void (*)())
  • 调用类型转换符创建指针指向0xff的子程序:(*(void (*)()) 0xff)()

4.调用函数

调用函数的方式就是通过函数指针fp:(*fp)();,也就是(*函数指针)代表的是被调用函数的地址,这里是0xff,但是单纯的0xff是不行的,我们需要对0xff进行类型转换!!!

对0xff地址进行类型转换得到函数指针fp:(void (*)()),使用类型转换符转换0xff地址时,fp相当于(void (*)()) 0xff

套进去得到调用0xff函数的操作:(*(void (*)()) 0xff)();

11.5 Linux下静态库与共享库的特点

库中实际上就是已编译好的函数代码,可以被程序直接调用。

Linux下的库一般的位置在/lib或者/usr/lib

静态库

静态库是复制拷贝到调用函数中的,函数运行的时候不再需要静态库,因为静态库是在链接的时候加进去的,所以当函数运行的时候,源库的改变对运行中的函数造成不影响。随之而来,当静态库升级之后,每一个调用静态库的程序都需要重新编译。

特点:

  • 链接静态库的时候,会把库中的相关代码拷贝到可执行文件中
  • 程序运行时不再需要静态库
  • 程序运行时不需加载库,运行速度快
  • 因为库中相关代码是拷贝到可执行文件中,所以占用了更多的磁盘和内存空间
  • 静态库升级之后需要重新编译链接

共享库

特点:

  • 共享库在链接的时候,仅仅是记录一下用到了哪个库中的哪个函数,并不复制库中相关代码
  • 多个程序可以同时调用一个共享库
  • 程序在运行的时候来加载共享库
  • 程序体积变小,程序本身没有包含库中的代码
  • 共享库升级之后,无需重新编译程序
posted @ 2020-11-12 07:56  Aspirant-GQ  阅读(47)  评论(0编辑  收藏  举报