C高级学习笔记第1天

1. GCC 编译器 

gcc 编译c文件的流程 

 (1) 预处理   处理 头文件展开  宏替换等 动作    不会进行任何语法检查

  •         gcc hello.c -E -o hello.i  
  •         -E 只进行预处理操作 
  •         -o 重命名 输出文件名字 
  •         hello.i  .i后缀 表示只进行预处理后的文件     


    (2) 编译   使用C编译工具  将预处理后的 C代码 编译为 汇编代码 

  •         gcc hello.i -S -o hello.s 
  •         -S 只进行 C编译为 汇编  
  •         .s 后缀 用于表示 汇编文件 
  •         
  •         该动作需要进行语法检查 


    (3) 汇编    使用汇编器  将汇编代码 编译为 二进制目标文件  不能单独运行 

  •         gcc  hello.s  -c -o hello.o 
  •         -c  仅进行 汇编, 生成 二进制目标文件
  •         .o  表示 二进制目标文件


    (4) 链接  将多个二进制目标文件 与库链接 生成可以执行的 二进制文件

  •         gcc hello.o -o hello.out
  •         
  •         gcc 中的其他的选项  
  •         -I   用于指定头文件的搜索路径 
  •             gcc hello.c -c -o hello.o -I./inc
  •         -l   用于指定 链接第三方库的名字 
  •         -L   用于指定 链接第三方库的路径 
  •         -O   用于指定 编译优化等级  -O0 不优化  -O1 -O2 -O3    volatile 防止编译器优化
  •         -g   用于编译时 附加 调试数据  主要用于gdb调试 
  • GCC组件: 有4个组成部件 
  •     编译器   : C ---> 汇编 
  •     汇编器   : 汇编---> 二进制机器码
  •     链接器   : 链接多个二进制机器码 和 库 构成 可以运行的二进制程序 
  •     C库      : 一些由gcc提供的 常用的一些函数 

    编译报错:

   1. 头文件错误    预处理阶段出现   通常为头文件路径错误或文件名写错了 

hello.c:5:19: fatal error: inc/a.h: 没有那个文件或目录
compilation terminated.

   2. 语法错误   在 编译阶段出现  通常为 标点符号或中文问题 

hello.c: In function ‘main’:
hello.c:10:2: error: expected ‘;’ before ‘func’
  func();
  ^

    中文符号问题 

hello.c: In function ‘main’:
hello.c:9:2: error: stray ‘\357’ in program
  printf("PI=%.2f\n", PI);
  ^
hello.c:9:2: error: stray ‘\274’ in program
hello.c:9:2: error: stray ‘\233’ in program
hello.c:10:2: error: expected ‘;’ before ‘func’
  func();
  ^

    3. 未定义标识符    变量/函数没有定义或声明就使用  

hello.c: In function ‘main’:
hello.c:12:19: error: ‘pelpe’ undeclared (first use in this function)
  printf("%.2d\n", pelpe);
                   ^
hello.c:12:19: note: each undeclared identifier is reported only once for each function it appears in

    4. 归档错误/ 链接错误   引用了其他文件或库中的函数 而没有声明 或 函数名字错误 

/tmp/cclYp5VM.o:在函数‘main’中:
hello.c:(.text+0x42):对‘func’未定义的引用
collect2: error: ld returned 1 exit status

作业:  输入一个数  分解质因数 
    20=2*2*5
    11=11

思路:  短除法  

5.逻辑错误:  bug  程序可以运行 但 没有达到预期效果甚至出现错误

  • 调试工具:  软件工具 与 硬件工具 
  •     printf打印法调试 

    
    GDB 调试器: 软件调试工具  字符界面调试功能 

    1. 编译时添加-g 选择  附加调试信息 

    gcc zhishufenjie.c -g -o gdb.out

    2.     使用gdb运行调试程序 

gdb gdb.out
GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1
Copyright (C) 2016 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from gdb.out...done.
(gdb) 

    gdb 命令   

  •     l  查看程序源码  
  •     设置断点    断点: 程序 运行到这里就会暂停 
  •     b 行号  
  •     info b  查看所有的断点 
  •     
  •     运行代码
  •     r 
  •     查看程序中变量的值 
  •     
  •     单步运行:   一次执行一行C代码  
  •     (gdb) n     运行一行
  •     (gdb) s      若这一行是函数调用,s则进入函数
  •     恢复程序运行 
  •     (gdb) c
  •     
  •     删除断点 
  •     del  1   删除断点1 
  •     
  •     quit  退出gdb  
  •     

C语言中  变量的存储类型 与 特性

指针:  C语言的灵魂   手术刀 

  • 本质就是 一个直接访问内存 的工具   只有C语言有   
  • 程序的本质:      就是操作内存      
    指针是啥? 
        1. 指针变量    :   一个变量  存放一个内存的地址   
        2. 一个内存地址   指针常量 

指针变量的定义: 

  • 存储类型  指针指向的空间的数据类型 *  变量名;
  • 示例: 
  • aotu     int *     p;

指针变量的 赋值:  类型匹配才可以赋值 

  • 物理意义: 与指定的内存  建立联系
  • 示例:
  • int a;
  • *p = &a; 
  • &地址运算:   变量可取地址  有内存的量
  •     得到变量的内存地址   
  • 野指针:  一个指针  指向的内存 不可知 或  不确定 或  非法(不可访问) 
  • 空指针:  一个指针  指向内存 0地址(不允许访问)  即这个指针中的值是0   int *p = NULL;
  • 宏 NULL 空指针   (void *)0  通常在程序中 表示一个指针 没有建立任何指向关系 
  • void * 类型 : 表示 万能指针, 一个可以指向 任意类型的指针 
  • #include <stdio.h>
    
    char a;
    
    int main()
    {
    	printf("a=%p\n",&a);
    	int *p;//野指针
    	int *q = NULL;//空指针
    	void *w;//万能指针
    	printf("p=%p\n",p);
    	printf("q=%p\n",q);
    	printf("w=%p\n",w);
    	//printf("*p=%d\n",*p); //野指针不可如此操作 段错误
    	//printf("*q=%d\n",*q);//空指针也不可如此操作 0地址不可访问 段错误
    	//printf("*w=%d\n",*w);//语法错误 无法确定类型
    	return 0;
    }
    

指针的访问:  * 指针取值运算符

  • *指针变量  即等同于 指针指向的目标(对象)
  •     
  •     1  * 只能对指针操作
  •     2 指针必须有建立指向关系   不能对空指针 野指针进行*操作 
  •             *空指针  段错误/空指针异常
  •             *野指针  结果未知   大概率段错误 
  •             void * 类型的指针  可以赋值  void *p = &a;    *p 不行   *(int*)p 可以的
  •                   不能*操作   通常是进行强制类型转换 为指定类型的  *
  •             
  • int b;
  • int *p = &b;
  • *&b; 等同于 b 
  •       * 是 & 的逆运算 
  • &*p; 等同于 &b
  •       & 是 * 的逆运算  

指针的算数运算: 

  • 指针 +或- 一个整数    其结果仍然是一个指针 
  •         其运算本质就是  地址的移动 + 向大地址方向移动  - 小地址方向移动 
  •         其移动的字节大小  由其指向的类型长度决定  +1 / -1 移动一个指向的类型 
  •     指针的 ++ -- 运算
  •     
  •     指针 与 指针的 运算 
  •     1. 大地址 - 小地址  结果是一个整数  其大小为 这两个地址之间相隔的 指向的类型个数 

关系运算   本质是 判断 这个两个地址 在内存中的位置

  •     p  >  q   p地址 在 q地址的 大方向  
  •     p  <  q   p地址 在 q地址的 小方向
  •     p ==  q   p和q指针指向同一个内存 
  •     p !=  q   p和q指针指向不同的内存 
  •     p  >= q   p地址 在 q地址的 大方向  或 相同
  •     p  <= q   p地址 在 q地址的 小方向  或 相同

    一级指针和一维数组: 

  •     数组名可以当指针用 
  •     指针变量 可以做数组用 
  •     区别在于  指针变量 可以重新赋值   而数组名不行 
  •     int arr[5] = {1,2,3,4,5};
  •     int *p = arr; // p = &arr[0]  等同 
  •     p[0]  == arr[0]  == *arr  == *p    //等同 
  •     p[1]  == arr[1]  == *(arr+1)  == *(p+1)
  •   练习:  用指针方式遍历数组  写函数实现
  • void show_arr(int *arr, int len)   //要求使用arr和len完成遍历 不再定义其他变量
    {
        while(len--) printf("%d ",*(arr++));
    }
    //*arr++   ==  *(arr++)  ==  先*arr用  然后 arr=arr+1; 
    int main()
    {
        int arr[] = {1,2,3,4,5};
        show_arr(arr,5);
        return 0;
    }
  • 有如下定义:
  • int a[10]={0,1,2,3,4,5};
  • int *p = a;
  • 假设每个表达式中p=a;求表达式代表的值?
  • *p=?    a[0]   == 0 
  • *a=?    a[0]   == 0
  • p[0]=?  a[0]   == 0 
  • a[0]=?  
  • *p++=?  ==  *(p++) == 先*p用 作为表达式的值, 然后 p=p+1
  • *(p++)=?
  • *p+1=?     a[0] + 1 == 1
  • *(p+1)=?   a[1]  == 1
  • *a+1=?     a[0] + 1 == 1
  • *(a+1)=?   a[1]  == 1
  • 练习: 数组倒叙 
  • void arr2rra(int *start, int *end)   //倒叙 从start到end之间的 数组元素, 不能定义其他变量
  • {
  • }
  • int main()
  • {
  •     int arr[] = {1,2,3,4,5};
  •     show_arr(arr,5);
  •     arr2rra(arr, arr+4);
  •     show_arr(arr,5);
  •     return 0;
  • }

指针与字符串:

  • char s[] = "hello";  //s是一个数组 将"hello"复制一份到s数组中
  • cosnt char *p = "hello";  // p是一个指针变量 指向常量区字符串 
  •     *p 
  • 求字符串的长度:
  • int len(const char *s)
  • {
  •     char *p = s;
  •     while(*p) p++;
  •     return p-s;
  • }

const 修饰指针: 

  •     cosnt char  *p;              *p 只读   p 可读可写 
  •     char * const p;              *p 可读可写  p 只读 
  •     const char * const p ;      *p 只读   p 只读 
  • 作业: 使用指针完成
  •     1. 输入一个字符串  倒叙输出 字符串     
  •     2. 大数加法 
  • a = "128346197325817632587412435878032743712098317867293728651279"  '9' - '0' = 9
  • b =                   "239878950493849938744869530984603429835934"  '4' - '0' = 4
  • c = a+b                                                   ...7213   '3'  =(9+4+进位标记)%10+'0'
  • c 字符串 char c[200];  

    
预习:  指针变量 占用内存的大小  sizeof(p);
    1. 指针数组     数组指针  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值