目录
gcc
GNU编译器套件,能把人看得懂高级语言编译成计算机看得懂的二进制语言
预处理、编译、汇编、链接
预处理:
这个过程处理宏定义和include,去除注释,不会对语法进行检查。C 编译器对各种预处理命令进行处理,包括头文件包含、宏定义的扩展、条件编译的选择等;
gcc -E xxx.c 显示预处理结果,不生成任何文件 • gcc -E xxx.c -o xxx.i 生产 .i 结尾的预处理文件
编译: 把预处理文件(xxx.i)编译位汇编文件 (xxx.s)
将预处理得到的源代码文件,进行“翻译转换”,产生出机器语言的目标 程序,得到机器语言的汇编文件;
gcc -S xxx.i 生产以 .s 结尾的汇编文件
汇编: 把汇编文件(xxx.i) 翻译成二进制文件(xxx.o)
将汇编代码翻译成了机器码,但是还不可以运行;
gcc -c xxx.s 生产 .o 结尾的目标文件
链接:把前面目标文件和里面用到的一些系统组件等链接到一起。最后生成可执行文件
gcc xxx.o -o xxx 为什么需要链接? 在一个程序被分割成多个模块之后,这些模块之间如何组合形成一个单一的程序是必须解决的问题,模块之间如何组合的问题可以归结为模块之间如何通信的问题。最常见的属于静态语言的C/C++模块之间通信的两种方式: 模块间的函数调用 另外一种是模块间的变量访问。 函数访问必须知道目标函数的地址,变量访问必须知道目标变量的地址。所以两种方式都可以归结为一种方式:那就是模块间符号的引用。完成模块间符号引用的过程就是链接。链接的主要过程包括地址和空间分配,符号决议 和 重定位
进制
二进制到八进制
因为三个二进制能表示000--111范围即为0--7,刚好是八进制范围,所以能用三个二进制表示一个八进制 例: 二进制(101 010 101 101 111)转为八进制-> 052557(开头0代表八进制) 10 101 011 100 111 --> 025347 技巧:把二进制从低位到高位分为三个一组,每三个就是一个八进制 技巧:三个二进制从高位到低位 分别表示4 2 1
二进制转十六进制
技巧:把二进制从低位到高位分为四个一组,每四个就是一个十六进制 技巧:四个二进制从高位到低位 分别表示8 4 2 1 1101 1100 1111 1000 --> 13 12 15 8 ->0xDCF8(开头0x代表十六进制)
八进制到二进制
同理一个一个八进制可转为三个二进制 技巧:把八进制的每个数分解为 4x+2y+1z ,xyz即为二进制 如:0756 ->111 101 110
十六进制到二进制
同理一个一个十六进制可转为四个二进制 技巧:把十六进制的每个数分解为8q+4x+2y+z ,qxyz即为二进制 如:0xA5D6 ->1010 0101 1101 0110
变量
由字母、数字、下划线组成 不能以数字开头 不能和C关键词重名 程序运行时,变量占据储存空间大小由其数据类型决定 变量在内存空间中的首地址,称为变量地址
变量的说明
<存储类型> <数据类型> <变量名> auto 局部变量(系统默认,通常省略) register 寄存器变量(把变量放入寄存器中,此种变量过多反而会降低寄存器运行速率) 不能用&,取地址 static 静态变量未初始化,系统自动补零;静态变量系统运行前定义,结束后释放;其他文件无法使用 extern 外部变量,表明该变量要到其他文件找
常量
指在程序运行期间其数值不发生变化的数据, 整型常量 浮点常量 实数只有十进制,分单精度、双精度。两种表现形式:一般形式、指数形似。 指数常量 指数形式的实数:尾数部分、字母e、指数部分。实数为正可省略。 字符常量 单一字符,表现形式 ‘a’;可看成一个字节的正整数。 字符串常量 双引号括起来的一串字符表示的数据 标识常量(宏) 大写英文字母
数据类型
基本类型、构造类型、指针类型、空类型 1、基本类型: 整型{int(4) ;long int(8);short int(2(32位)/4(64位)); long long int(8)}、字符型{ char(1)}、浮点型{ float(4) ;double(8)}、枚举型(enum) 2、构造类型: 数组、结构体、共用体(union) 3、指针类型 4、空类型: void
关键字 | 字节 | 范围 |
---|---|---|
char | 1 | |
unsigned char | 1 | |
short int | 2 | |
unsigned short int | 2 | |
int | 4 | |
unsigned int | 4 | |
long int | 8(linux) | |
unsigned long int | 8(linux) | |
long long int | 8 | |
unsigned long long int | 8 | |
float | 4 | |
double | 8 | |
long double | 16 |
类型转换
隐式转换
1、算术运算中的隐式转换:
1、算术运算时,不同数据类型必须转换为相同数据类型才能进行运算,在没有强制转换时,部分情况系统会自动转换(隐式转换)。
2、整型提升:对于比 int 类型小的会提升为 int 类型。(char, unsigned char, short, unsigned short, ).
3、运算中存在float 或则double类型,其他类型会转换为 double 运算
4、运算中存在最长类型为 long。其它类型均会转为long。
5、运算中存在最长类型为 int。其它类型均会转为 int。
6、 有符号会转为无符号进行运算。
2、赋值转换
1、赋值时,运算符右边类型必须转为左边类型。如果右边类型大于左边会发生截断。
下面用一实例说明:
char ch; int i,result; float f; double d; result=ch/i+(f*d-i);
(1)首先计算 ch/i,ch → int型,ch/i → int型。
(2)接着计算 fd-i,由于最长型为double型,故f→double型,i→double型,fd-i→double型。
(3)(ch/i) 和(fd-i)进行加运算,由于fd-i为double型,故ch/i→double型,ch/i+(f*d-i)→double型。
(4)由于result为int型,故ch/i+(f*d-i)→double→int,即进行截断与舍入,最后取值为整型。
3、输出转换
1、程序中使用 printf 格式输出时,当要输出的数据类型与输出格式符不符合时,便自动进行类型转换。
注意:
1、较长数据类型换成短数据类型输出时,其值不能超出短型数据类型的范围。否则会出错。
强制转换
(type_name) expression
强制转换是程序员编程时,不依靠编译器的隐式转换而自己通过代码形式对数据进行转换。转换规则同隐式转换,代码上可读性更高。
运算符
算术运算符: + - * / % ++ ( float ,double 不能取余) 关系运算符: > >= < <= == != 逻辑运算符: ! && || 位运算符:(进行运运算时,负数使用补码运算且符号位参与运算。) ~ & | ^ >> << 赋值运算符: = -= += *= &= != ^= >>= <<= 特殊运算符 ?: 逗号运算符 (,,) 有括号 sizeof运算符: sizeof(类型或变量命) 它只针对数据类型,而不针对变量
输入输出
数据输出
1、字符输出函数 格式: putchar(c) 参数:c为字符常量、变量或表达式 功能: 把字符c输出到显示器上 返回值: 正常, 为显示的代码值 2、格式输出函数 格式: printf(“格式控制串”,输出表) 功能:按指定格式向显示器输出数据 输出表: 要输出的数据 格式控制串: 包含两种信息 格式说明:%【修饰符】格式字符,用于指定输出格式 普通子覅: 原样输出 格式符 i d 十进制 x X 十六进制无符号整数 o 八进制无符号整 u 十进制无符号整数 c 单一字符 s 字符串 e 指数形式浮点小数 f 小数形式浮点小数 g e和f中较短一种 %% 百分号本身 3、字符输入函数 格式: getchar() 功能: 从键盘读取一个字符 返回:正常,返回读取代码值;出错或结束键盘输入,返回-1 4、格式输入函数 格式: scanf("格式控制串",地址表) 功能: 按指定格式从键盘读入数据,存入地址表指定存储单元中,并按回车结束 返回值:正常,返回输入数据个数 地址表:变量的地址,常用取地址运算符& 遇空格、tab或回车 非法输入 宽度结束 结束
C]S8TBRHY7PO9`@6.png)
控制语句
for(表达式1;表达式2;表达式3) 表达式1可以省略,但循环前要给变量赋值 表达式2省略,将陷入死循环 表达式3省略,循环体中要增加使循环变量值改变的语句 if(表达式) ......else 表达式:逻辑表达式、关系表达式、任意类型。(非零为真,零为假) switch(表达式){ case 常量表达式 : 语块 break; default : 语快 } 1、每个常量表达式的值必须各不相同,否则会出错。 2、switch中表达式可以是 整型、字符型、枚举型表达式。 break 跳出循环 continue 结束本次循环 return 函数返回值,代表函数结束
数组
构造数据类型之一 数组是具有一定顺序关系的若干变量的集合,组成数组的各个变量称为数组元素 数组中各元素数据类型要相同 一维数组是指只有一个下标的数组,它在计算机的内存中是连续存储的 说明形式: <存储类型> <数据类型> <数组名> [<表达式>]; 数组名表示数组内存首地址,是地址常量。 sizeof(数组名) 是数组占用的总内存空间。 ******* c语言对数组不作越界检查 只能逐个引用数组元素,不能一次引用整个数组。 一维数组初始化 int a[5] = {1,2,3,4,5} 数组不初始化,其元素为随机值 对static数组不赋初值,系统会自动赋以0值 只给部分元素赋值,其余制动补0 冒泡排序 选择排序 插入排序 。。。。。。。。
二维数组
定义方式: 声明时列数不能省略,行数可以 数据类型 数组名【常量表达式】【常量表达式】 二维数组内存也是顺序存放。 int a【3】【3】 = {0}; a , a[0] ,a[1] ,a[2] 都是地址常量,其中前者是行地址(二维数组名),后三位为一维数组名。(常量不可改变!!!!!) sizeof(a) = 整个二位数组所占字节数 ,a+1 第二行地址 sizeof(a[0]) = 第一行(一维数组)所占字节数。 a[0]+1 第一行第二个元素空间地址。
字符数组
1、字符数组是指数组元素的数据类是字符类型的数组。 2、字符数组的初始化 char str[3]; 逐个字符赋值 str[0] = 'a'; str[1] = 'b'; str[2] = 'c'; 用字符串常量 str = "hello";
字符串
1、c语言中无字符串变量,用字符数组处理字符串。即以‘\0’结尾的字符数组就是字符串。
字符串函数
strlen 求字符串长度函数 不包括‘\0’ \xhh 表示十六进制代表的符号; \ddd 表示八进制 strcpy 字符串拷贝 strcat 字符串连接函数 strcmp 字符串比较函数
指针
指针作用 1、使程序简洁、紧凑、高效 2、有效地表示复杂的数据结构 3、动态分配内存 4、得到多余一个的函数返回值 在计算机中内存以字节为单位划分,每个单位有一个编号,这个编号就是地址。c语言中地址称为指针。存储地址的变量叫指针变量简称指针。
指针变量说明
<存储类型> < 数据类型> *< 指针变量>; char *p ## 存储类型指,指针变量本身的存储类型。 ## 指针的数据类型: 指针所指对象的数据类型 ## 指针的目标: 指针所指向的内存区域的数据,如果所指向的是一个变量的内存空间,则该变量称为指针的目标变量,简称为指针的目标。 p为一个指针: 1、p的内容就是地址量 2、*p,指针所指向的对象。内容是数据 3、&p 指针变量占用内存空间的地址,是个常量。
指针的运算
指针的运算是以指针变量所存放的地址量作为运算量而进行的运算。 实质就是地址的运算。 只能进行: 赋值运算、算术运算、关系运算 1、算术运算: + p+n 指针向地址大的方向移动n个数据 - ++ -- p1 - p2 两指针相减为所相隔 数据类型个数,而不是 字节数。 *** 不同数据类型的两个指针实行加减数运算 无意义 2、关系运算 两指针之间的关系运算表示它们指向的地址位置之间的关系,指向地址大的指针大于指向地址小的指针 指针与一般整数变量之间的关系运算没有意义。但可以和零进行等于或不等于的关系运算,判断指针是否为空。
指针的赋值运算:
指通过赋值运算符向指针变量送一个地址常量 *向指针变量赋值时,必须是一个地址常量或指针变量,不能是普通整数(除了赋零以外) 指针赋值运算形式: 1、把一个普通变量的地址赋给一个具有相同数据类型的指针: double x = 15, *p; p = &x ; 2、把一个已有地址的指针变量赋给具有相同数据类型的另一个指针: float a, *p1, *p2; p1 = &a; p2 = p1; 3、把一个数组的地址赋给具有相同数据类型的指针, int a[20], *p; p = a; // p = &a[0]; 32位系统中,指针占4个字节 64位系统中,指针占8个字节
数组指针
存储行地址的指针变量,叫做行指针变量。形式如下; 存储类型 数据类型 (*指针变量名)【表达式】 int a[3][3] = {0}; int (*p)[3] = a; 注意 *a 与*p的含义 a是二维数组名,本质上是行地址常量,这里的 ‘*’ 的作用不是取对应地址的内容,而是改变了a的性质:从行地址变为一级指针(常量)。从而 (a+1)与(*a+1)表达的含义也有区别:前者是行地址加一,代表地址移动二维数组里的一行(一个一维数组)。后者由于(*a)性质是一维地址,加一代表地址移动一个元素。 (p)与(a)最大区别在于,前者是变量而后者是常量。常量本身不可变。除此之外,其他性质相同,如(a+1)与(p+1)地址相同,(*a+1)与(*p+1)地址相同。同时指针宽度也一样。 #include <stdio.h> 8 9 int main(int argc, char *argv[]) 10 { 11 int a[3][3] = {1,2,3,4,5,6,7,8,9}; 12 int (*p)[3] = a; 13 printf("%8p %8p %8p \n",a[0],a[1],a[2]); 14 printf("%8p %8p %8p \n",p[0],p[1],p[2]); 15 16 printf("%8p %8p %8p \n",a,a+1,a+2);//行指针加一移动一行 17 printf("%8p %8p %8p \n",p,p+1,p+2);//行指针加一移动一行 18 19 20 printf("%8p %8p %8p \n",*a,*a+1,*a+2);//行指针变为一级指针(加一移动一个int) 21 printf("%8p %8p %8p \n",*p,*p+1,*p+2);//行指针变为一级指针(加一移动一个int) 22 23 24 printf("%d %d %d %d %d \n",**a,*(*a+1),*(*a+2),*(*a+3),*(*a+4)); 25 26 return 0; 27 } 运行结果: 0x7ffcb905fe30 0x7ffcb905fe3c 0x7ffcb905fe48 0x7ffcb905fe30 0x7ffcb905fe3c 0x7ffcb905fe48 0x7ffcb905fe30 0x7ffcb905fe3c 0x7ffcb905fe48 0x7ffcb905fe30 0x7ffcb905fe3c 0x7ffcb905fe48 0x7ffcb905fe30 0x7ffcb905fe34 0x7ffcb905fe38 0x7ffcb905fe30 0x7ffcb905fe34 0x7ffcb905fe38 1 2 3 4 5
字符指针与字符串
初始化字符指针只是把内存中字符串的首地址赋予指针,并不是把该字符串复制到指针中 char str[] = "hello world"; char *p = str; 注意: 当字符指针指向字符串常量时,不能使用指针改变常量。(本身常量就不能被改变) char *p = “hello ” ///*p = ‘a’;错误
指针和一维数组
* 和 & 互为 逆运算,同时存在可以抵消 * 和 [] 是等价的,两者可以相互转化 & 和 [] 互为逆运算,两者可以相互抵消 int a[N]; int * p =a; a[i] = *(p+i) = *(a+i) = p[i] 指针和一维数组没有必要的联系,只是在形式上有上述关系
指针与二维数组的关系
int a[3][3]; int *p = &a[0][0]; int *p = a[0]; 指针与二维数组也没有必要联系,在上述情况只是把二级指针看成一维数组来处理。
指针数组(和二维数组合没有直接关系)
定义:指由若干个具有相同储存类型和数据类型的指针变量的集合。 一般说明形式 存储类型 数据类型 *指针数组名【大小】; 指针数组名表示该指针数组的起始地址 int a[2][3] = {0}; int *p[2] = {a[0],a[1]}; p = a;//写法错误
数组指针
本质上是一个指针,指向数组的指针。 int a[5] = {1,2,3,4,5}; int (*p)[5] = &a; //&a表示数组a整个的地址 p+1: 移动20个字节 1 * 20(整个数组的大小)
多级指针
1、把一个指向指针变量的指针变量,称为多级指针变量。 2、对于指向处理数据的指针变量称为一级指针变量 2、把指向一级指针变量的指针变量称为二级指针变量 二级指针变量说明; 存储类型 数据类型 **指针名 多级指针的运算 指针变量加一,是向地址大的方向移动一个目标数据。类似道理,多级指针运算也是以其目标变量为单位进行偏移 如,int **p; p+1移动一个 int *变量所占内存空间。再比如 int ***p,p+1移动一个int**所占内存空间。