【无标题】c语言基础

本文详细介绍了C语言的GCC编译器工作流程,包括预处理、编译、汇编和链接四个阶段。同时,深入探讨了不同进制之间的转换方法,如二进制到八进制、二进制到十六进制等。此外,还讲解了变量、数据类型、类型转换(隐式和显式)、运算符、输入输出、控制语句、数组、指针等相关概念,特别强调了指针与数组、二维数组及字符串的交互。文章还涉及到了C语言中的数组指针和多级指针的使用。
摘要由CSDN通过智能技术生成

目录

gcc

预处理、编译、汇编、链接

进制

二进制到八进制

二进制转十六进制

八进制到二进制

十六进制到二进制

变量

变量的说明

常量

数据类型

类型转换

隐式转换

1、算术运算中的隐式转换:

2、赋值转换

3、输出转换

强制转换

运算符

输入输出

数据输出

控制语句

数组

二维数组

字符数组

字符串

字符串函数

指针

指针变量说明

指针的运算

指针的赋值运算:

数组指针

字符指针与字符串

指针和一维数组

指针与二维数组的关系

指针数组(和二维数组合没有直接关系)

数组指针

多级指针


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

关键字字节范围
char1
unsigned char1
short int2
unsigned short int2
int4
unsigned int4
long int8(linux)
unsigned long int8(linux)
long long int8
unsigned long long int8
float4
double8
long double16

类型转换

隐式转换

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**所占内存空间。


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值