首页 > 其他分享 >C基础2

C基础2

时间:2023-07-15 17:14:16浏览次数:26  
标签:文件 定义 静态 代码 基础 编译 替换

字符数组

char c1[]={'c',' ','p','r','o','g'};

char c2[]="c prog";//末尾以'\0'为结束符

用字符串方式赋值比用字符逐个赋值要多占1个字节,用于存放字符串结束标志'\0';

'\0'由编译系统自动加上的

 

gcc编译过程

gcc -E hello.c -o hello.i 预处理

gcc -S hello.i -o hello.s 编译

gcc -c hello.s -o hello.o 汇编

gcc hello.o -o hello 链接

./hello 执行

 

宏定义

C语言中的宏定义是一种预处理指令,用于创建一个标识符的别名或者简单的函数替换。宏定义可以在编译之前被展开,将代码中的宏名替换为相应的内容。

宏定义的语法格式如下:

#define 宏名 替换文本
其中,宏名是你想要定义的标识符,替换文本是你想要将宏名替换成的内容。替换文本可以包含常量、表达式、其他宏等。

以下是一些宏定义的示例:

定义常量:
c
#define PI 3.14159
定义函数替换(带参宏):
c
#define MAX(a, b) ((a) > (b) ? (a) : (b))
这个宏定义了一个叫做MAX的函数替换,用于返回两个数中的较大值。

使用宏定义时要注意括号的使用,以避免优先级问题:
c
#define SQUARE(x) ((x) * (x))
该宏定义了一个计算平方的函数替换。

可以在宏定义中使用条件编译:
c
#define DEBUG
#ifdef DEBUG
   printf("Debug mode\n");
#endif
在编译时如果定义了DEBUG宏,那么就会打印"Debug mode"。

请注意,宏定义只是简单地进行文本替换,没有类型检查和作用域规则。因此,在使用宏定义时要特别注意替换后的代码是否符合预期,并避免出现潜在的错误。

参数传递方式: 带参宏:宏定义中的参数是简单地进行文本替换,没有传递参数的概念。宏替换时会将参数直接替换到宏定义中的相应位置。 带参函数:函数的参数通过栈或寄存器传递,具有类型和限定符,可以对参数进行类型检查和访问。 编译时期: 带参宏:宏定义在预处理阶段进行展开,替换原始代码。宏替换是在编译之前进行的,没有执行时开销。 带参函数:函数在编译后生成机器代码,并在运行时被调用执行。 类型和作用域: 带参宏:宏定义没有类型检查和作用域规则,仅仅进行简单的文本替换。宏定义可以在任何地方使用,不受作用域限制。 带参函数:函数具有类型检查和作用域规则,必须在合适的作用域内声明和定义。函数有返回值类型和参数类型,可以进行类型检查。 副作用和错误: 带参宏:宏替换是简单的文本替换,可能会引入一些副作用。例如,宏中多次使用参数时可能会导致不符合预期的结果。 带参函数:函数内部的代码是按顺序执行的,可以更好地控制副作用和错误。 综上所述,带参宏在展开时没有类型检查和作用域规则,并且只是简单的文本替换,适合进行简单的代码替换。而带参函数具有类型检查、作用域规则和运行时执行的特点,适合进行复杂的逻辑处理。

需要根据具体的需求来选择使用带参宏还是带参函数。如果只需要进行简单的文本替换,并且不需要类型检查和作用域规则,那么可以使用带参宏。如果需要进行复杂的逻辑处理,并且希望有类型检查和作用域规则,那么应该使用带参函数。

带参宏被调用多少次就会展开多少次,执行代码的时候没有函数调用过程,不需要压栈弹栈。所以带参宏,是浪费了空间,因为被展开多次,节省时间。

带参函数,代码只有一份,存在代码段,调用的时候去代码段取指令,调用的时候要压栈弹栈,有个调用的过程。所以说,带参函数是浪费了时间,节省了空间。

带参函数的形参是有类型的,带参宏的形参没有类型名

选择性编译

#ifdef A
 代码一
#else
 代码二
#endif
     具体的执行流程如下:
     如果宏A已经被定义,则编译器将会编译#ifdef A下面的代码一,并忽略#else和#endif之间的代码二。
如果宏A没有被定义,则编译器将会忽略#ifdef A下面的代码一,而编译#else和#endif之间的代码二。
简而言之,这段代码的作用是根据宏A的定义与否来选择性地编译不同的代码。在编译时,可以通过在代码中定义或取消定义宏A来控制所要编译的代码部分。

需要注意的是,条件编译指令只会在预处理阶段进行处理,不是在运行时确定的。因此,在代码中对于宏的定义或取消定义需要在编译之前进行。
#ifndef B //这种方法常用在防止头文件重复包含
 代码一
#else
 代码二
#endif
     如果宏B没有被定义,则编译器将会编译#ifndef B下面的代码一,并忽略#else和#endif之间的代码二。
如果宏B已经被定义,则编译器将会忽略#ifndef B下面的代码一,而编译#else和#endif之间的代码二。
 
     

使用头文件保护(header guards)来防止头文件的重复包含。

在头文件中,可以在文件开头和结尾之间插入以下代码:

#ifndef HEADER_NAME_H
#define HEADER_NAME_H

// 头文件内容

#endif /* HEADER_NAME_H */

这里的 HEADER_NAME_H 是一个自定义的标识符,用于唯一标识这个头文件。当编译器遇到 #ifndef 指令时,它会检查 HEADER_NAME_H 是否已经被定义过了。如果没有被定义过,编译器就会执行 #define 指令来定义 HEADER_NAME_H

这样,在同一个编译单元中再次包含该头文件时,由于 HEADER_NAME_H 已经被定义了,编译器会忽略后续的头文件内容,从而避免了重复包含。

通过使用头文件保护机制,可以确保在同一个编译单元中只会包含一次头文件,避免了可能的编译错误和重定义问题。

#if 表达式

程序段一

#else

程序段二

#endif

如果表达式为真,编译第一段代码,否则编译第二段代码

选择性编译都是在预编译阶段干的事情

#define A
#if A
#else
#endif
#include <stdio.h>

#define A

int main() {
   #if defined(A)
       printf("宏 A 被定义了\n");
   #else
       printf("宏 A 未定义\n");
   #endif

   return 0;
}

静态编译 动态编译

静态编译和动态编译是编译器和链接器所执行的不同类型的代码生成和链接过程。

静态编译:gcc -static hello.c -o hello 在静态编译中,源代码(或者经过预处理的源代码)在编译阶段被翻译成与特定平台的机器码,并生成一个可执行文件。这个可执行文件已经包含了所有必要的代码和依赖项,在运行时不再需要其他的链接过程。静态编译的可执行文件可以在不同系统上独立运行,但会占用更多的磁盘空间。

动态编译(也称为动态链接):gcc hello.c -o hello 在动态编译中,源代码(或者经过预处理的源代码)在编译阶段被翻译成与特定平台的目标文件(通常是对象文件)。然后,这些目标文件在运行时被链接器进一步处理,与共享库(也称为动态链接库或共享对象)进行链接,以生成最终的可执行文件。在程序运行时,系统会根据需要加载共享库,并将其链接到内存中的进程,因此,动态编译允许多个程序共享相同的库,减少了磁盘空间的使用,并且可以方便地更新和替换共享库。

总结起来,静态编译生成一个独立的可执行文件,包含了所有代码和依赖项;而动态编译生成一个需要运行时加载和链接共享库的可执行文件。静态编译的优点是独立性和部署简单,但会占用更多的磁盘空间;动态编译的优点是共享性和可维护性,但需要在运行时加载和链接共享库。

在实际开发中,可以根据需求和考虑因素选择静态编译或动态编译。有些情况下,可能会同时使用两种编译方式,将一些常用的库进行静态编译,以提高程序的性能和独立性。

制作静态库:

gcc -c mylib.c -o mylib.o

ar rc libtestlib.a mylib.o

注意:静态库起名的时候必须以lib开头以.a结尾

ar 是一个用于创建,修改和提取静态库文件的命令行工具

r表示将文件插入到库中(或替换已经存在的文件)

c表示创建一个库文件,如果库文件不存在则创建新的库文件

  • s表示创建一个包含索引的库文件。索引文件提供了一种快速查找库中的符号的方式。

     

  • t:列出静态库中包含的文件列表。例如, 将会列出 中的所有文件。ar t libexample.a``libexample.a

  • x:从静态库中提取文件。例如, 将会从 中提取 文件。ar x libexample.a example1.o``libexample.a``example1.o

  • d:从静态库中删除指定的文件。例如, 将会从 中删除 文件。ar d libexample.a example1.o``libexample.a``example1.o

  • q:快速追加文件到静态库末尾,不重新压缩整个静态库。这对于只添加少量文件时可以提高性能。例如, 将会将 文件追加到 的末尾。ar q libexample.a example3.o``example3.o``libexample.a

  • u:更新静态库中的文件。如果静态库中已经存在同名的文件,则用新的文件替换它。例如, 将会使用新的 文件更新 中原有的 文件。ar u libexample.a example1.o``example1.o``libexample.a``example1.o

ar 是一个用于创建、修改和提取静态库文件的命令行工具(Archiver)。它通常在 UNIX 和类 UNIX 系统上使用,例如 Linux 和 macOS。

ar 命令的基本语法如下:

ar [选项] [归档文件] [文件...]

常见的选项包括:

  • r:将指定文件插入到归档文件中。如果归档文件中已存在同名的文件,则会被替换。

  • c:创建一个新的归档文件。如果归档文件已存在,则会被覆盖。

  • s:创建一个包含索引的归档文件。索引文件可以加快在归档文件中查找符号的速度。

  • t:列出归档文件中包含的文件列表。

  • x:从归档文件中提取文件。

  • d:从归档文件中删除指定的文件。

  • q:快速追加文件到归档文件末尾,不重新压缩整个归档文件。

  • u:更新归档文件中的文件。如果归档文件中已经存在同名的文件,则用新的文件替换它。

这些选项可以根据具体需求进行组合使用,以执行不同的操作。在使用 命令时,还可以指定归档文件和要操作的文件的名称,以及其他一些选项。ar

注意, 命令一般用于处理静态库文件(通常以 扩展名结尾),而非动态链接库( 文件)。ar``.a``.so

root@r7ftf-virtual-machine:/home/tst# gcc main.c libteest.a -o main

root@r7ftf-virtual-machine:/home/tst# gcc libteest.a main.c -o main /usr/bin/ld: /tmp/cc8c3aBW.o: in function main': main.c:(.text+0x8f): undefined reference tomax' collect2: error: ld returned 1 exit status root@r7ftf-virtual-machine:/home/tst#

链接顺序不正确:在链接命令中,通常应该将库文件放在源文件之后,以确保编译器能够正确处理符号引用。因此,你应该像第一个示例那样将 libteest.a放在 main.c 之后,而不是像第二个示例那样放在 main.c 之前。

编译程序命令:

gcc -static main.c -o main -I../ -L../ -l teest

注意:-L是指定库文件的路径

-l(小写L)是指定那个库,指定的只要库文件名lib后面.a前面的部分

-I(大写i)指定头文件的路径

  • -static: 这个选项告诉编译器使用静态链接方式,即将所有的依赖库静态地链接到可执行文件中。这样生成的可执行文件不会依赖系统上已安装的共享库,具备更好的移植性。需要注意的是,静态链接可能会使得可执行文件的大小变大,并且不再能够享受系统上已存在的库的更新和修复。

可以将库文件及头文件存放到系统默认指定的路径下

库文件默认路径是/lib 或者是/usr/lib

头文件默认路径是/usr/include

制作动态库:

 

  1. gcc -c -fPIC example.c -o example.o

    在这个命令中, 选项表示只编译源代码文件而不进行链接, 选项表示生成位置无关代码, 是你的源代码文件, 是生成的目标文件。-c``-fPIC``example.c``example.o

  2. 链接目标文件:使用 GCC 编译器将目标文件链接为动态库。执行以下命令:

    复制代码gcc -shared example.o -o libexample.so
    //libexample.so 依然是lib开头但是.so结尾

    在这个命令中, 选项表示生成动态库, 是之前生成的目标文件, 是生成的动态库文件。-shared``example.o``libexample.so

    使用动态库:现在,动态库已经制作完成并安装到系统中。你可以在需要的程序中使用该动态库。在编译时,只需添加 选项和库名称以链接动态库,例如:-l

    gcc main.c -lexample -o main
-fPIC` 是 GCC(GNU Compiler Collection)编译器的选项之一。 表示生成位置无关代码(Position Independent Code,PIC)。位置无关代码是一种可以在内存中的任何位置加载和执行的机器代码,而不依赖于特定的内存布局或绝对地址。`-fPIC

在制作动态库时,使用 选项编译源代码文件非常重要,因为动态库的代码将在不同的进程空间中加载和共享。如果没有使用位置无关代码,当动态库加载到内存中的某个特定位置时,其中的绝对地址会发生偏移,导致代码无法正确映射到新的地址,从而引发错误或段错误。-fPIC

通过使用 选项,GCC 会生成适用于位置无关代码的目标文件。它会使用相对寻址或基于全局偏移表(Global Offset Table,GOT)的方式来访问全局变量和函数。这样,即使动态库加载到内存中的不同位置,也能够正确解析符号和进行地址重定位。-fPIC

总结来说,使用 选项编译源代码文件是制作动态库时的一项重要操作,它确保生成的代码是位置无关的,可以在不同的内存位置加载和执行。-fPIC

制作动态链接库: gcc -shared mylib.c -o libtestlib.so

 

标签:文件,定义,静态,代码,基础,编译,替换
From: https://www.cnblogs.com/ptm2/p/17556497.html

相关文章

  • sql注入基础
    sql注入           意义是:用户在提交表单时输入恶意的sql语句,欺骗后端把其当作正常的数据执行 注入方式分类有两种         按照注入方式分:union注入、布尔盲注、时间注入、报错注入         按照注入点类型分:字符型、数字型  一、按......
  • shell脚本基础
    1.shell基础1.1简介shell介绍shell应用场景:系统管理,文件操作等1.2解释器解释器是一种命令解释器,主要作用是对命令进行运行和解释,将需要执行的操作传递给操作系统内核并执行指定解释器#!/usr/bin/python3不指定,默认就是这个#!/bin/bash#这是一个shell脚本#byliech......
  • Java基础
    跨平台原理Java的跨平台基于编译器和JVM。编译器把源文件编译成与平台无关的字节码class文件,JVM把该文件解释成与平台有关的机器码指令,在平台上执行。Java面向对象的4个特征1抽象提取对象的共性,构成抽象类或接口,由继承抽象类的类或接口的实现类来重写抽象方法。2继承子类继承......
  • Pycharm基础
    Pycharm基础1.添加解释器2.安装包的两种方式3.插件汉化插件以及一种使各分层括号颜色一致的插件......
  • Java基础--day02
    变量作用域类变量、实例变量、局部变量 publicclassDemo03{/***类变量static*/staticdoublesalary=89561.36;/***实例变量*从属于对象*不初始化,会变成默认类型*00.0布尔值默认false*除了基本类......
  • python魔术方法大全 基础篇、比较篇
    魔术方法大全魔术方法官方名称叫specialmethod,所谓的魔术方法就是python让用户客制化一个类的方式,顾名思义就是定义在类里面的一些特殊的方法。这些specialmethod的特点就是它的method的名字,前后都有两个下划线,所以这些方法也被称为Dundermethod。基础篇比较篇属性篇类......
  • MySQL基础
    1.数据库登录  mysql-uroot-p  mysql-uroot-pmysql  mysql-uroot-p-h192.168.213.200  备份  mysqldump-uroot-p--all-dtabases>all_databasees_20210305.sql      //备份所有库  mysqldump-uroot-p--databasesmysql>mysql_20......
  • 汇编基础
    汇编语言的组成汇编指令(机器码的助记符)伪指令(由编译器执行)其他符号(由编译器识别)汇编语言的核心是汇编指令,它决定了汇编语言的特性存储器指令和数据在存储器中存放,也就是平时所说的内存磁盘不同于内存,磁盘中的数据或程序如果不读入内存,就无法被cpu使用指令和数据在......
  • Python基础day45
    SQL注入问题importpymysql#连接MySQL服务端conn=pymysql.connect(host='127.0.0.1',port=3306,user='root',password='123',database='db8_3',charset='utf8',autocommit=True#针对增......
  • 构建强大算力基础,奠定AI时代基石!
    7月6日,2023世界人工智能大会在上海拉开序幕。由中国电信主办的“算网一体融创未来”主题论坛也于当日在上海世博中心举行。上海市经济和信息化委员会副主任戎之勤、上海市金山区人民政府副区长葛钧、中国电信数字智能科技分公司副总经理何忠江、中国电信上海公司副总经理胡伟良、......