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、算术运算时,不同数据类型必须转换为相同数据类型才能进行运算,在没有强制转换时,部分情况系统会自动转换(隐式转换)。
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语言中无字符串变量,用字符数组处理字符串。即以‘ ’结尾的字符数组就是字符串。字符串函数
strlen 求字符串长度函数 不包括‘ ’ 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
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**所占内存空间。



