第二部分C语言重难点部分的理解
一.指针
-
指针定义的理解
#include
int main(void){ //定义一个char类型的指针,和int类型的变量 char * pc; int num =4; //此时的含义是pc指向了num的首地址 ; //指针的类型决定了取值的时候访问多少内存 // 0000 0001 0000 0001 // char * 获取数据, 0000 0001 // short * 获取数据, 0000 0001 0000 0001 pc = # // 指针可以参与运算,但是仅限于+和- // p+1表示指针向后移动类型单位长度 return 0; } -
指针的用处
-
用函数实现两个变量值的交换,注意只是交换了地址也并不能交换值,必须交换两个地址下的值才可实现:
//只交换两个变量的地址,也不能实现交换 void swap2(int *px,int *py){ int *ptemp; ptemp = px; px = py; py = ptemp; } //通过指针改变了两个值的内容,实现了交换 void swap3(int *px,int *py){ //px -->a py---->b int ptemp; ptemp = *px; // ptemp = 3; *px = *py; // a = b; a = 5,b = 5 *py = ptemp; // b = 3; b = 3; }
通过指针能够让函数有多个返回值----所谓的多个返回值并不是多个return,而是一次性的修改多个变量的值
-
/实现计算器
void calculator(int x,int y,int *psum,int *pjian,int *pcheng,float *pchu){
//把x+y的结果放到主调函数中 sum变量中
*psum = x+y;
*pjian = x-y;
*pcheng = x*y;
*pchu = x/(float)y;
}
-
二级指针---其实二级指针就是指向指针的指针
//定义一个变量 int a = 0; //定义一个一级指针指向a,假设a的地址为0xf2 int *p = &a; p == 0xf2; *p == *0xf2 == a; //定义一个二级指针指向p int* *p1 = &p; //此时p1的内存单元中存放的是p的地址,假设p的地址为0xf0 p1 == 0xf0; *p1 == *0xf0这个地址所指向的内容 == 0xf2 **p1 == **0xf0 == *0xf2 == a;
-
数组指针
-
指向数组元素的指针
指针pa进行+1或-1操作时不是内存地址的加减,而是指向下一个数组元素或上一个
-
同一个数组中的两个指针相减表示的是这两个指针之间差多少个元素
//定义一个数组 int a[] = {1,2,3,4,5}; //定义一个指针指向数组a int *ap = a; //利用该指针遍历数组a for (int i = 0; i < 5; i++){ printf("a[%d] = %d\n",i,*(ap+i)); }
-
指针变量之间的运算
- 表示两个指针之间相隔了几个元素 ,真实地地址差:(p1-p) * sizeof(int) //12
-
-
函数指针与指针函数的区别
函数指针是返回值是指针的函数; 如:char * getDay(int n){……}
-
指针函数是指向函数的指针;
//格式:被指向的函数的返回值类型 (*函数指针变量名)(被指的函数的参数); //定义了一个函数指针,指针变量名是pmax,它只能指向返回值是int 并且有两个int类型参数的函数 int (*pmax)(int x,int y); // 形参的名可以省略 int (*pmax2)(int ,int );
二.程序的编译及运行
- C 语言程序从编写源代码到运行经历哪些阶段?
- 编写源文件(源代码)就是新建一个文本文件,然后编写C语言源代码int main() {...}
- 编译(cc -c 文件名)利用命令cc -c 源文件名.c编译源文件,然后生成目标文件"原文件名.o"该编译过程需要检查语法等内容,该文件是二进制格式文件但是不能执行
- 链接(cc 目标文件) 将目标文件与库函数等链接到一起,使得程序可以执行使用命令:cc 目标文件名.o会默认生成 a.out(assembly output(来源于Unix), 表示二进制(binary)输出文件)cc 1.c
- 注意事项:
注意:在编写程序,编译的时候,如果检查语法有错误,就会报错,阻止编译继续进行但是函数比较特殊,如果函数调用,但是没有提供函数的定义,那么会隐式的提供函数声明.但是链接的时候,如果没有提供函数体,那么就会报错,阻止的链接的进行,进而不会生成可执行文件;
三.宏的概念及无参宏定义方法
- 宏的定义: 宏: 预处理指令,所有的以"#" 都是预处理指令,宏是一个特殊标识符,代表一个字符串、数字、表达式
- 宏的分类:
- 有参数的宏
- 无参数的宏
- 定义方式
- 无参数的宏的定义方式
- 格式: #define 宏名 宏所代表的内容
- 注意:
- 不需要使用分号结束,宏是一个预处理指令,不是语句
- 宏的名称一般是大写,以便区别普通变量
- 宏,一般我们写在文件的开头部分
- 有参数的宏的定义方式
- 格式:#define 宏名(形参) 宏代表的字符串
- 无参数的宏的定义方式
#define M(y) y*y+3*y
#define SUM(a,b) a+b
2. 使用注意事项
1. 有参宏的参数不会被分配内存空间,不需要指定类型
2. 有参宏展开的时候:
1. 把实参带入到宏的字符串中
2. 在出现宏名的地方用宏代表的字符串取替换
- 使用方式
- 宏可以参与运算可以成为表达式的一部分
- 使用的注意事项:
- 宏的替换 在程序编译之前,预处理程序 会把源代码中所有出现宏名的地方就用用宏代表的内容去替换,例外:当宏名出现在字符串中,此时不会替换
- 宏定义的时候,可以嵌套
- 在程序中出现宏名的地方,仅仅使用宏代表的值做替换
- 注意事项
-
typedef和#define的区别
//定义一个宏 #define INT2 int * //这是给 int* 起个别名 INT typedef int * INT; int main(int argc, const char * argv[]) { int num = 2; //使用宏定义指针变量 INT2 p,p5; // int *p,p5; //p是一个指针变量 //p5 就是一个整形变量 //用typedef(定义)方式,定义指针变量 INT p1,p6; //INT 原型 int * //int *p1,*p6; //p1 是指针变量 //p6也是指针变量 return 0; }
-
四.代码的练习
- 推箱子的小游戏
#define kRows 10
#define kCols 11
#include
//定义一个函数实现地图的绘制
void drawMap(char map[kRows][kCols]) {
for(int i = 0; i < kRows; i++){
printf("%s\n",map[i]);
}
}
//定义一个函数实现移动,当前位置元素和下一个位置的元素互换
void movel(char map[kRows][kCols],int oldX,int oldY,int newX,int newY){
char temp;
temp=map[oldX][oldY];
map[oldX][oldY]=map[newX][newY];
map[newX][newY]=temp;
}
int main(int argc ,const char * argv[]){
//1.定义二维数组,用来保存地图
char map[kRows][kCols]={
"##########",
"#O #### #",
"# X#### #",
"# #",
"###### #",
"# #### #",
"# #",
"# ######",
"# ",
"##########"};
//2.绘制地图
drawMap(map);
//3.提示游戏的玩法
printf("请控制小人移动:w.上 s.下 a.左 d.右 q.推出\n");
//4.定义一些变量,保存位置信息,保存玩家输入的方向
char direction; //保存玩家输入的方向
// 保存用户当前的位置
int personX = 1;
int personY = 1;
//初始化小人得下一个位置
int nextX = personX;
int nextY = personY;
char street = ' ';
char box = 'X';
//保存箱子的当前位置
int boxX=2;
int boxY=2;
//5.编写循环控制程序
while(1){
//1)接受玩家输入的方向 a\n d\n
scanf("%c",&direction);
getchar();
//防止穿墙
nextX=personX;
nextY=personY;
//2)根据玩家输入的方向,判断将要移动的方向
switch(direction){
case 'w':
case 'W':
//记录小人的下一个位置
nextX--;
break;
case 's':
case 'S':
//记录小人的下一个位置
nextX++;
break;
case 'a':
case 'A':
//记录小人的下一个位置
nextY--;
break;
case 'd':
case 'D':
//记录小人的下一个位置
nextY++;
break;
case 'q':
case 'Q':
printf("程序正在退出...\n");
printf("程序已经退出...\n");
return 0;
default:
break;
}
// 3)判断小人的将要移动的位置是否是路
if(map[nextX][nextY]==street){
//如果是路,让小人移动
movel(map,personX,personY,nextX,nextY);
//重新保存小人的最新位置
personX=nextX;
personY=nextY;
} else if(map[nextX][nextY]==box){
// 再判断小人的下一个位置是否是箱子
// 如果是箱子:
// 1) 计算箱子的下一个位置
int boxNextX = boxX+(boxX-personX);
int boxNextY = boxY+(boxY-personY);
// 2) 再判断 箱子的下一个位置是否是路
if (map[boxNextX][boxNextY] == street) {
// 如果是路:
// 1)箱子和箱子下一个位置交换
movel(map, boxX, boxY, boxNextX, boxNextY);
// 2)小人和箱子的位置进行交换
movel(map, personX, personY, boxX, boxY);
// 3)重新保存小人的位置,箱子的位置
boxX = boxNextX;
boxY = boxNextY;
personX = nextX;
personY = nextY;
}
}
drawMap(map);
if(boxY==9){
printf("哇哦啊,你推出来!");
break;
}
}
}