第二阶段(c语言)

gcc编译过程

在GCC(GNU Compiler Collection)的编译过程中,源代码从高级语言(如C或C++)转换为可执行文件或库文件,这个过程涉及多个阶段,主要包括预处理(Preprocessing)、编译(Compilation)、汇编(Assembly)和链接(Linking)。下面是对这四个阶段的详细解释:

1. 预处理(Preprocessing)

预处理是编译过程的第一个阶段。在这一阶段,预处理器(cpp)读取源代码文件(如.c.cpp文件),并处理所有的预处理指令,这些指令以#字符开始。预处理指令包括:

  • 宏定义#define):定义宏,可以是常量或宏函数。
  • 条件编译#ifdef#ifndef#endif等):根据条件包含或排除代码部分。
  • 文件包含#include):将其他文件的内容插入到当前位置。
  • 其他预处理指令:如#undef(取消宏定义)、#pragma(向编译器发送指令)等。

预处理后,生成的文件仍然是文本文件,但包含了所有已展开的宏定义和包含的文件内容,不再包含预处理指令。

2. 编译(Compilation)

编译是将预处理后的文本文件转换成汇编代码的过程。编译器(如gcc的cc1或c++的cc1plus)将源代码转换为一种更低级的、面向特定处理器的中间表示,即汇编代码。在这一阶段,编译器会进行词法分析、语法分析、语义分析,并生成中间代码(IR),最后再将中间代码转换为汇编代码。

3. 汇编(Assembly)

汇编阶段是将汇编代码转换为机器代码的过程。汇编器(as)读取汇编代码文件(通常是.s.asm文件),并将其转换成机器语言指令,这些指令可以直接被计算机的CPU执行。生成的机器代码保存在目标文件(通常是.o.obj文件)中。

4. 链接(Linking)

链接是将多个目标文件以及库文件合并成最终的可执行文件或库文件的过程。链接器(ld)负责处理这一过程。链接时,链接器会解决程序中所有外部符号(如函数名、变量名)的引用,即将程序中调用的函数和使用的变量与它们在库文件或其他目标文件中的定义关联起来。链接过程还可能包括地址空间的分配、重定位以及资源的合并等。

  • 静态链接:在编译时将所有需要的库直接链接到最终的可执行文件中。
  • 动态链接:在运行时才将需要的库链接到程序中,这样可以减小可执行文件的大小,但需要在系统中存在相应的动态链接库(DLL或.so文件)。

内存:一块临时存储区域

        虚拟内存 vm

        物理内存  pm 

        内存单元:一个内存单元的大小是1byte

        内存块:连续多个内存单元

        内存地址:相当于是教室的门牌号

        内存中的值:相当于是教室里面所存放的东西

==================================================================

int num = 0;

        error:&&num:不要针对地址进行取址操作

        &num:获取num所在内存的起始地址

        num:内存中所存取的内容

       error: *num:不能针对非地址值进行取值(对地址值取值(*ptr)

int* ptr = null; 

        error:&&ptr:不要针对地址进行取址操作

        &ptr:获取ptr这个指针变量所在内存的起始地址

        ptr:内存中所存取的内容(地址)

       error: *ptr:获取指针变量的这个地址值对应的内存其所存储的内容

        error:**ptr:通常也不能针对一个一级指针进行二次取值

int** q = &ptr;

浮点类型内存存储

 

 alloca申请的内存在栈上

malloc calloc申请的内存在堆上

const int* n;       //值不可变,地址可变

int* const n;        //地址不可变,值可变

consr int* const n;        //地址和值都不能改变

int main()
{
    const int num = 10;//值不可变
    int* ptr = (int*)&unm;
    *ptr = 1000;
    printf("%d\n",num);
    return 0;
}

=========================================================================

练习:

练习1:
以下程序输出结果

void swap_1(int a, int b)
{
    int c;
    c = a;
    a = b;
    b = c;
}

void swap_2(int& a, int &b)
{
    int c;
    c = a;
    a = b;
    b = c;
}

int main()
{
    int a = 100;
    int b = 200;
    
    swap_1(a, b);
    printf("%d, %d", a, b);    //100,200
    swap_2(a, b);
    printf("%d, %d", a, b);
}
练习3:
下面的函数实现在一个固定的数上加一个数,有什么错误?请改正。
int add_n ( int n )
{
    static int i = 100;
    i += n;               //i不是一个固定值
    return i;
} 
//i是一个静态局部变量,含义:当前的i只能初始化一次
当程序结束时,静态局部变量被释放
=======================================================
int main(int argc,char *argv[])
{
    int num = 10;
    printf("%d\n",add_n(num));//110
    printf("%d\n",add_n(num));//120
    printf("%d\n",add_n(num));130
    printf("%d\n",add_n(num));140
}
练习4:
写出下列程序的运行结果。
int a; //全局变量未初始化系统默认为0
int b; 
int c;
void F1() 
{ 
    b=a*2; 
    a=b; 
}

void F2() 
{ 
    c=a+1; 
    a=c; 
}

int main() 
{ 
    a=5;  // 如果int a = 5;打印结果为5,局部变量
    F1(); 
    F2(); 
    printf("a=%d/n",a); //11
    return 0;
}    
练习5:
下面程序能否编译通过,如果不能请修改? 其运行结果是多少,为什么?
在64位没问题,在32位有问题
64位:
#includ <stdio.h>

int main()
{
    unsigned long long a = 0xAAAAAAAABBBBBBBB;
    unsigned int b = 0xDDDDDDDD;
    printf("valuea:%lx, valueb:%lx\n", a, b);
}
//结果为:aaaaaaaabbbbbbbb,dddddddd
32位:
#includ <stdio.h>

int main()
{
    unsigned long long a = 0xAAAAAAAABBBBBBBB;    8位
    unsigned int b = 0xDDDDDDDD;
    printf("valuea:%lx, valueb:%lx\n", a, b);    32位中:lx:4位,llx为8位,所以存在问题
}
   小端存储,所以结果为:bbbbbbbb,aaaaaaaa 
原因:对于printf存在缓存区,printf相当于栈,参数从后往前入栈
//注意点:
1、输出缓存区是一个栈
2、函数参数入栈的顺序
3、printf输出时要严格按照前面的类型输出
练习6:
改错:
int main(void)
{
    int **p;
    //
    int arr[100];

    p = &arr;

    return 0;
}
//p中存放arr的地址时,*p为空,不能跨级操作

练习7:
char str1[] = "abc";
char str2[] = "abc";

const char str3[] = "abc";
const char str4[] = "abc";

const char *str5 = "abc";
const char *str6 = "abc";

char *str7 = "abc";
char *str8 = "abc";

printf("%d\n", str1 == str2);不相等,都在栈上
printf("%d\n", str3 == str4);不相等,const强调值不可变,地址不同
printf("%d\n", str5 == str6);
printf("%d\n", str7 == str8);
练习8:
char* a = "ABC";//a指向的是字符串常量,不可修改
puts(a);
a[0] = 'D';
puts(a);
练习9:
请问以下代码有什么问题:
int main()
{
    char a;
    char *str=&a;
    strcpy(str,"hello");//str只有一个字节的空间,hello在str中存放不下
    printf(str);
    return 0;
}
练习10:
     16进制:15 CD 5B 07 00 
    int a = 123456789;
    printf("%lu\n", strlen(&a));//输出4
//str寻找‘\0’
int main()
{
    int a[5] = {1,2,3,4,5};
    //&a+1偏移了整个数组
    int *ptr = (int*)(&a + 1);
    //ptr只是一个整型指针,所以其-1代表向前偏移一个元素
    printf("%d,%d",*(a + 1),*(ptr - 1));//5

    return 0;
}

int a[2][2] = {9,8,7,6};
printf("%p,%p\n",&a,&a+1);
printf("%p,%p\n",a,a+1);
printf("%p,%p\n",a[0],a[0]+1);

&a + 1
a + 1
a[0] + 1

外部存储器

        也称为辅助存储器或外部存储设备,是指除计算机内存及CPU缓存以外的储存器。此类储存器一般断电后仍然能保存数据,主要用于存储大量数据,并扩展计算机的存储容量。

        外部存储器(定义):外部存储器是指独立于计算机主机之外,用于存储数据和程序的设备

排序:

数据结构:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值