C程序设计
计算机程序和C语言
程序
一组计算机可以识别和执行的指令;
每一条指令对应计算机特定的操作;
计算机的一切操作都是由程序控制的。
语言
机器语言:计算机可以直接识别的二进制语言(0和1)
符号语言(汇编语言或符号汇编语言):汇编语言计算机不能直接识别,需要通过汇编程序进行转换,将符号语言指令转换成机器指令,转换过程叫做"汇编(assembly)"或“代真”,因此叫做汇编语言。不同型号的计算机其机器语言和汇编语言是不相通的,因此机器语言和汇编语言是完全面向机器的语言。
高级语言:与具体机器较远,因此叫做高级语言。高级语言也不能让计算机直接识别和执行,需要通过编译程序将高级语言的程序(源程序 source program)转换成机器语言指令的程序(目标程序 object program),然后计算机执行机器指令程序。
高级语言的发展历程:
1、非结构化语言
程序中的流程可以随意跳转
2、结构化语言
程序中的流程不允许随意跳转;
规定程序必须具有良好特性的基本结构(顺序结构、分支结构、循环结构);
程序由上而下依次执行。
C语言属于结构化语言
非结构化语言和结构化语言都是面向过程的语言
3、面向对象的语言
Java、C++、C#等属于面向对象的语言
简单程序
// 字符计数
#include <stdio.h>
int main(int argc, const char * argv[]) {
int nc;
nc = 0;
while ((getchar()) != EOF) {
++nc;
}
printf("%d\n", nc);
}
// 符号常量
// 摄氏温度和华氏温度转换表
#include <stdio.h>
#define LOWER 0
#define UPPER 300
#define STEP 20
int main(){
int fahr;
for(fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP)
printf("%3d\t%6.1f\n", fahr, (5.0/9.0)*(fahr - 32));
}
// 以每行一个单词的形式打印输出
#include <stdio.h>
int main(){
int c;
while ((c = getchar()) != EOF) {
if(c == ' ' || c == '\n' || c == '\t')
printf("\n");
putchar(c);
}
}
// 求两数之和
#include<stdio.h>
int main(){
int a, b, sum;
a = 123;
b = 456;
sum = a + b;
printf("sum is %d\n", sum);
return 0;
}
/**
sum is 579
Program ended with exit code: 0
*/
// 求两个整数中的较大者
#include <stdio.h>
int main(){
int a, b, c;
int max(int x, int y);
scanf("%d, %d", &a, &b);// “”中指定输入数据的格式,&a和&b表示变量a和b的地址;使用scanf函数,读入两个数,送到a和b的地址处,然后将这两个变量赋值给a, b。
c = max(a, b);
printf("max= %d\n", c);
return 0;
}
int max(int x, int y){
int z;
if (x > y) {
z = x;
return z;
} else{
z = y;
return z;
}
}
结构
一个C程序可以包括三部分:预处理指令、全局声明(main函数之前)、定义函数。
一个函数包括两部分:函数首部,函数体。没有参数写void或者不写。
int max (int x, int y) { }
函数类型 函数名 函数参数类型 函数参数 函数参数类型 函数参数 函数体
算法——程序的灵魂
一个程序包括两部分:
对数据的描述(数据结构):
对操作的描述(算法):要求计算机操作的步骤。广义的算法,解决一个问题采取的方法和步骤就是算法。
算法 + 数据结构 = 程序
算法的特性
- 又穷性
- 确定性
- 有零个或多个输入
- 有零个或多个输出
- 有效性
表示一个算法
-
自然语言
-
流程图
-
三种基本结构
-
顺序结构
-
选择结构
-
循环结构
当型(while)循环结构
直到型(util)循环结构
-
-
N-S流程图表示算法
-
用伪代码表示算法
// 表示5! begin (算法开始) 1=>t 2=>i while i<=5 { t * i => t i + 1 = i } print t end (算法结束)
-
用计算机语言表示算法
// 表示5! #include <stdio.h> int main() { int t=1, i = 2; while(i<=5){ t = t * i; i = i + 1; } printf("%d\n", t); return 0; } // 求1x2x3x4x5的值 # include <stdio.h> int main(){ int t = 1; int i = 2; while (i <= 5) { t = t * i; i ++; } printf("%d\n", t); } 120 Program ended with exit code: 0 // for 循环的实现 # include <stdio.h> int main() { int p = 1; for (int i = 2; i <= 5; i ++) { p = i * p; } printf("%d\n", p); } // 求1 x 3 x 5 x 7 x 9 x 11 # include <stdio.h> int main(){ int p, i; p = 1; i = 3; while (i <= 11) { p = i * p; i = i + 2; } printf("%d\n", p); return 0; } 10395 Program ended with exit code: 0 // for循环的实现 #include <stdio.h> int main() { int p = 1; for (int i = 3; i <= 11; i = i + 2) { p = i * p; } printf("%d\n", p); } // 有50名学生,要求输出成绩在80分以上的学生的学号,成绩。 // 结构体定义学生数组, 以5组学生为例 # include <stdio.h> struct student{ int num; float score; }stu[5]; int main() { for (int i=0;i<5;i++) { printf("请输入第%d个学生的信息\n",i+1); scanf("%d %f",&stu[i].num,&stu[i].score); } for (int i=0;i<5;i++) { if (stu[i].score>80) printf("%d, %.2f\n",stu[i].num,stu[i].score); } return 0; } // 判断是否是闰年,2000~2500年之间的每一年 /** *1、Y%4==0 并且 Y%100!=0是闰年 *2、Y%400==0是闰年 *不符合这两个条件的不是闰年。 */ #include <stdio.h> int main(){ int y = 2000; while (y<=2500) { // scanf("%d", y); if (y%4==0 && y%100!=0) { printf("%d:是闰年\n",y); } else if (y%400==0) { printf("%d:是闰年\n",y); } else{ printf("%d:不是闰年\n",y); } y = y + 1; } } // 求1 - 1/2 + 1/3 …… + 1/99 - 1/100. #include <stdio.h> int main() { int sign = -1; int sum = 1; int deno = 2; while (deno <= 100) { int term = sign * (1/deno); sum = sum + term; sign = (-1) * sign; deno = deno + 1; } printf("%d\n", sum); return 0; }
顺序程序设计
// 将华氏温度转换成摄氏温度
#include <stdio.h>
int main() {
float f, c;
f = 65;
c = (5.0 / 9)*(f - 32);
printf("转换为摄氏度为:%f\n", c);
return 0;
}
转换为摄氏度为:18.333334
Program ended with exit code: 0
数据表现形式及运算
常量、变量
常量
-
整型常量
-
实型常量
十进制小数形式(1.34)、指数形式(12.34e3)。
-
字符常量
普通字符’a’,‘b’、转义字符。
-
字符串常量
“abc”, “boy”
-
符号常量
#define PI 3.1416 // 行末没有分号。定义符号常量
变量
先定义后使用
常变量(有名字的不变量)
常变量与变量的异同:跟变量相同的是 有类型、占存储单元、但是不允许改变值。
const float pi = 3.1416; // 定义常变量
标识符
数据类型
整型数据
#include <stdio.h>
int main() {
int a = sizeof(short); // 2
int b = sizeof(int); // int类型占用4个字节
int c = sizeof(long); // 8
int d = sizeof(long long); // 8
printf("%d, %d, %d, %d\n", a, b, c, d);
}
基本整型(int)
存储方式:
正数 以二进制补码的方式存储(即仍是其二进制形式,负数不同)
容纳的数值范围计算:
如果给整型变量分配2个字节
存放的最大值01111111 11111111 第一位0是符号位,表示正数,后十五位全为1
最
大
值
:
2
15
−
1
;
最
小
值
:
−
2
15
;
因
此
范
围
为
:
−
2
15
~
2
15
−
1
最大值:2^{15}-1 ; 最小值:-2^{15}; 因此范围为:-2^{15}~2^{15}-1
最大值:215−1;最小值:−215;因此范围为:−215~215−1
存放的最小值10000000 00000000 第一位1是符号位,表示负数,后十五位全为0
超出这个范围就会出现溢出,输入的数据报错❌。
短整型(short int)
长整型(long int)
双长整型(long long int)
字符型
字符是以十进制数(字符的ASCII码)的形式存放在内存单元中的:
大写字母’A’:的ASCII码是十进制数65
小写字母’a’:的ASCII码是十进制数97
数字字符’1’:的ASCII码是十进制数49
-
注意‼️
字符’1’和整数1是不同的,'1’只是形状为1的符号,需要时按原样输出,在内存中一ASCII码的格式存储,占用一个字节;
1是以整数行书存储的(二进制补码的形式),占用2个或者4个字节。
整数运算 1 + 1等于2,但是’1’ + ‘1’并不等于整数2或者字符’2’.
浮点型
格式字符
%d:带符号整数
%c:字符
%f:浮点数
%s:字符串
字符数据输入输出
除了使用scanf()、printf()输入输出字符外还可以使用getchars()、put char()输入输出字符;
// 将接收到的字符输出。
#include<stdio.h>
int main() {
char a, b, c; // 定义字符变a, b, c
a = putchar(); // 键盘输入
b = putchar();
c = putchar();
getchar(a);
getchar(b);
getchar(c);
putchar('\n'); // 换行
return 0;
}
# include <stdio.h>
int main() {
putchar(getchar());
putchar(getchar());
putchar(getchar());
putchar('\n');
return 0;
}
选择程序设计
两种选择语句
1. if语句
实现两个分支的选择结构
求
a
x
2
+
b
x
+
c
=
0
的
方
程
根
。
假
设
a
,
b
,
c
的
值
任
意
。
求ax^{2} + bx + c = 0的方程根。假设a, b, c的值任意。
求ax2+bx+c=0的方程根。假设a,b,c的值任意。
#include <stdio.h>
#include <math.h> // 程序中需要调用disc求平方跟的函数sqrt
int main() {
double a, b, c, disc, x1, x2, p, q;
scanf("%lf,%lf,%lf", &a, &b, &c); // ⚠️格式字符后加“,”和不加“,”输入值的方式不同
disc = b * b - 4 * a * c;
if (disc < 0) {
printf("这个方程没有解");
} else {
p = -b/(2.0*a);
q = sqrt(disc)/(2.0*a);
x1 = p + q;
x2 = p - q;
printf("%lf, %lf", x1, x2);
}
return 0;
}
/**
2,4,1 // ⚠️
-0.29, -1.71
Program ended with exit code: 0
*/
#include <stdio.h>
#include <math.h> // 程序中需要调用disc求平方跟的函数sqrt
int main() {
double a, b, c, disc, x1, x2, p, q;
scanf("%lf%lf%lf", &a, &b, &c); // ⚠️
disc = b * b - 4 * a * c;
if (disc < 0) {
printf("这个方程没有解");
} else {
p = -b/(2.0*a);
q = sqrt(disc)/(2.0*a);
x1 = p + q;
x2 = p - q;
printf("%lf, %lf", x1, x2);
}
return 0;
}
/**
2 4 1 ⚠️
-0.29, -1.71
Program ended with exit code: 0
*/
// 输入两个实数,按代数值由大到小的顺序输出。
#include <stdio.h>
int main(){
double a, b, temp;
scanf("%lf%lf", &a, &b);
if (a > b) {
// 将a b的值互换
temp = a;
a = b;
b = temp;
}
printf("%2.4f, %2.4f\n", a, b);
return 0;
}
/**
4 2
2.0000, 4.0000
Program ended with exit code: 0
*/
// 输入3个实数,按由大到小的顺序输出。
#include <stdio.h>
int main(){
double a, b, c, temp;
scanf("%lf%lf%lf", &a, &b, &c);
if (a > b) {
temp = a;
a = b;
b = temp;
}
if (a > c) {
temp = a;
a = c;
c = temp;
}
if (b > c) {
temp = b;
b = c;
c = temp;
}
printf("排序好的顺序:%4.2f,%4.2f,%4.2f\n", a, b, c);
return 0;
}
/**
4 2 1
排序好的顺序:1.00,2.00,4.00
Program ended with exit code: 0
*/
逻辑运算符、逻辑表达式、逻辑型变量
三种逻辑运算符:&&(逻辑与AND)、||(逻辑或OR)、!(逻辑非NOT)
逻辑表达式:1为真;0为假,即将一个非0的值认作为真。
a = 4,!a为真;a = 4, b = 5 ,a && b为真。
逻辑型变量:
// 逻辑型变量
/**
使用_Bool表示
*/
#include <stdio.h>
int main()
{
float score;
scanf("%f", &score);
_Bool a, b; // 定义a, b定义为逻辑型变量
a = score >= 60; // 将关系型表达式score >= 60的值,赋值给逻辑型变量a
b = score <= 69; // 将关系型表达式score <= 69的值,赋值给逻辑型变量b
if (a && b) printf("this grade is C\n"); // 如果a、b均为真,则输出this grade is C
return 0;
}
/**
69
this grade is C
Program ended with exit code: 0
*/
条件运算符、条件表达式
适用条件:当执行一个条件语句,无论真假,最后都赋值给同一个变量。
表达式1?表达式2:表达式3;
if (a > b){
max = a;
} else {
max =b;
}
// 等同于
max = (a > b) ? a : b;
// 输入一个字符,判断它是否为大写字母,是大写,转换成小写。
#include <stdio.h>
int main() {
char ch;
scanf("%c", &ch);
// if (ch >= 'A' && ch <= 'Z') {
// ch = ch + 32;
// }
ch = (ch >= 'A' && ch <= 'Z') ? (ch + 32) : ch;
printf("%c\n", ch);
}
/*
A
a
Program ended with exit code: 0
*/
2. switch语句
实现多个分支的选择结构
// switch实现多分支选择结构
// 成绩等级
# include <stdio.h>
int main() {
char grade;
scanf("%c", &grade);
printf("你的成绩等级:");
switch (grade) {
case 'A':
printf("85~100\n");
break;
case 'B':
printf("60~85\n");
break;
case 'C':
printf("0~60\n");
break;
default:
printf("输入数据错误!!!\n");
break;
}
return 0;
}
/*
A
你的成绩等级:85~100
Program ended with exit code: 0
*/
// switch语句实现菜单处理命令。
#include<stdio.h>
int main() {
void action1(int, int), action2(int, int);
char ch;
int a = 32, b = 15;
ch = getchar(); // 输入一个字符
switch (ch) {
case 'a':
case 'A': // 输入a或者A执行action1函数
action1(a, b); // 执行A操作,调用action1函数
break;
case 'b':
case 'B': // 输入b或者B执行action2函数
action2(a, b); // 执行B操作,调用action2函数
break;
default:
putchar('\a'); // 输出警告⚠️
break;
}
return 0;
}
void action1(int x, int y) { // 执行加法的函数
printf("x + y = %d\n", x + y);
}
void action2(int x, int y){ // 执行乘法的函数
printf("x * y = %d\n", x * y);
}
/*
Ax + y = 47
Program ended with exit code: 0
*/
练习
// 选择结构嵌套使用
// 写一个程序判断某一年是否为闰年。
#include <stdio.h>
int main() {
int y, leap;
scanf("%d", &y);
if (y%4==0) {
if (y%100==0) {
if (y%400==0) {
leap = 1;
} else {
leap = 0;
}
} else {
leap = 1;
}
} else {
leap = 0;
}
if (leap == 1) {
printf("%d is leap year\n", y);
} else if (leap == 0) {
printf("%d is not leap year\n", y);
}
}
/*
2021
2021 is not leap year
Program ended with exit code: 0
2020
2020 is leap year
Program ended with exit code: 0
*/
// 使用逻辑表达式实现上述题目
#include <stdio.h>
int main(){
int y, leap;
scanf("%d", &y);
if ((y%4==0&&y%100!=0) || (y%400==0)) { // 使用逻辑表达式实现上述选择结构嵌套
leap = 1;
} else {
leap = 0;
}
if (leap == 1) {
printf("%d is leap year\n", y);
} else if (leap == 0) {
printf("%d is not leap year\n", y);
}
}
// 使用逻辑变量和逻辑常量true和false实现
#include <stdio.h>
#include <stdbool.h>
int main() {
int y;
_Bool leap;
scanf("%d", &y);
if (y%4==0) {
if (y%100==0) {
if (y%400==0) {
leap = true;
} else {
leap = false;
}
} else {
leap = true;
}
} else {
leap = false;
}
if (leap == true) {
printf("%d is leap year\n", y);
} else if (leap == false) {
printf("%d is not leap year\n", y);
}
}
循环程序设计
while循环
do…while循环
for循环
改变循环的执行状态
continue是只结束本次循环,而不结束整个循环。break结束真个循环。
利用数组处理批量数据
-
数组是一组有序数据的集合。数组中的各数据是有一定规律的。
-
数组中的每个元素属于同一个数据类型。
定义和引用一维数组
int a[10] // 定义了一个一维数组,数组中有10个元素
不可以在程序中临时输入数组的大小
#include<stdio.h>
int main(){
scanf("%d", &n);
int a[n]; // 企图临时确定数组的大小,这是错误的
}
可以通过实参,向调用函数的形参传值,确定数组大小。
void func(n) {
...
int a[2*n];
...
}
- 引用一维数组:通过下标的形式a[0]、a[7];
// 对10个数组元素依次赋值为0~9,并按逆序输出
#include<stdio.h>
int main(){
int i, a[10];
for (i = 0; i < 10; i++) { // 为每个数组元素赋值
a[i] = i;
}
for (i = 9; i >=0; i--) { // 按逆序循环输出
printf("%d,", a[i]);
}
printf("\n");
return 0;
}
/*
9,8,7,6,5,4,3,2,1,0,
Program ended with exit code: 0
*/
一维数组初始化
定义数组时给数组的每个元素赋值(为了使程序更加简洁),或者一部分元素赋值,
一维数组举例
// 用数组处理斐波拉契数列
#include <stdio.h>
int main(){
int f[20] = {1, 1}; // 对最前面两个元素赋初值为1
for (int i = 2; i < 20; i++) { // 循环计算每个数组元素,并赋值
f[i] = f[i - 1] + f[i - 2];
}
for (int i = 0; i <= 20; i++){
if (i%5==0) printf("\n"); // 5个数换行
printf("%12d,", f[i]); // 输出一个数
}
printf("\n"); // 最后换行
return 0;
}
/*
1, 1, 2, 3, 5,
8, 13, 21, 34, 55,
89, 144, 233, 377, 610,
987, 1597, 2584, 4181, 6765,
-730267627,
Program ended with exit code: 0
*/
// 有10个地区的面积,按从由小到大的顺序输出
// 冒泡排序
#include <stdio.h>
int main(){
int a[10];
int i, j, k;
// 循环输入10个数
for (i = 0; i < 10; i++) {
scanf("%d", &a[i]);
}
printf("\n");
// j从0变为9需要循环比较9次
for (j = 0; j < 9; j++){
// 每次循环需要比较(9 - j)次 [因为有0,所以比较9-j次]
for (i = 0; i < 9 - j; i++) {
if (a[i] > a[i+1]) {
k = a[i];a[i] = a[i+1];a[i+1] = k;
}
}
}
printf("The sorted number: \n");
// for循环输出排序好的顺序
for (i = 0; i < 10; i++){
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
/*
18 29 90 199 28 1 79 28 11 0
The sorted number:
0 1 11 18 28 28 29 79 90 199
Program ended with exit code: 0
*/
定义和引用二维数组
定义二维数组
float pay[3][6];
二维数组的存放是按行存放的,即在内存中先存放第一行的元素,再存放第二行的元素,顺序存放。在内存中存放是线性的,不是二维的,这只是逻辑上的为了方便记忆。
// 定义三维数组
float a[2][3][4]; // 定义了一个2页,3行,4列的三维数组
/*
多维数组元素在内存中的排列为第一维变化最慢,越往右变化越快
*/
二维数组的初始化
// 1.分行给二维数组初始化
int a[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
// 2.按数组元素在内存中的排列顺序给数组元素赋初值
int b[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
// 3.部分元素赋初值
int a[3][4] = {{1}, {5}, {9}};
// 4.定义数组时,可以对第一维的元素不赋值,对第二维的元素必须赋值。
int a[][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
二维数组程序举例
// 将一个二维数组的行列互换,存到另一个二维数组中
/**
a[2][3] = {1, 2, 3, 4, 5, 6};
b[3][2] = {1, 4, 2, 5, 3, 6};
*/
#include <stdio.h>
int main(){
int a[2][3] = {{1, 2, 3}, {4, 5, 6}}, b[3][2], i, j;
printf("array a:\n");
for (i = 0; i <= 1; i++) {
for (j = 0; j <= 2; j++) {
printf("%5d", a[i][j]); // 输出a数组元素
b[j][i] = a[i][j]; // 交换a, b数组行列
}
printf("\n");
}
printf("array b:\n");
for (i = 0; i <= 2; i++) {
for (j = 0; j <=1; j++) {
printf("%5d", b[j][i]);
}
printf("\n");
}
}
/*
array a:
1 2 3
4 5 6
array b:
1 2
4 5
2 3
Program ended with exit code: 0
*/
// 一个3 X 4的矩阵,求其最大值,并输出行列号。
#include <stdio.h>
int main(){
int i, j, row = 0, colum = 0, max;
int a[3][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
max = a[0][0];
for (i = 0; i <= 2; i++) {
for (j = 0; j <= 3; j++) {
if (a[i][j] > max) {
max = a[i][j];
row = i;
colum = j;
}
}
}
printf("max:%d\nrow:%d\ncolum:%d\n", max, row, colum);
return 0;
}
/*
max:12
row:2
colum:3
Program ended with exit code: 0
*/
字符数组
// 字符数组
#include <stdio.h>
int main() {
char a[8] = {'I',' ', 'a', 'm', ' ', 'b', 'o', 'y'};
int i;
for (i = 0; i <= 7; i++) {
printf("%c", a[i]);
}
printf("\n");
return 0;
}
/*
I am boy
Program ended with exit code: 0
*/
函数(实现程序模块化)
程序功能较多,规模较大,写在一个main函数中使得程序变得臃肿、难以维护。
函数就是功能。每个函数用来实现特定功能。
函数分类
从函数的形式角度分为: 有参,无参两种。
从用户使用的角度函数分为:库函数,用户自定义函数。
函数定义
- 指定函数的类型:函数返回值的类型;
- 指定函数的名称:方便调用;
- 指定函数的参数名称及参数类型:调用函数,便于传递参数;
- 指定函数的功能。
定义和声明不同。声明是把函数的基本信息包宿编译器。
函数举例
// 输入两个整数,求其最大值。要求用函数求较大的。
#include<stdio.h>
int max(int x, int y){ // 定义函数 , 有两个形参,接收赋值给它的实参
// if (x > y) {
// return x;
// } else {
// return y;
// }
int z;
z = x>y?x:y;
return z;
}
int main() {
int max(int x, int y); // 声明函数
int a, b, c;
scanf("%d%d", &a, &b);
c = max(a, b); // 调用函数 有两个实参,赋值给max函数的形参
printf("两者中较大的是:%d\n", c);
}
/*
4 5
两者中较大的是:5
Program ended with exit code: 0
*/
// 输出 :
// *******************
// Hello World!!
// *******************
/*
定义两个函数的时候,为void,表示无类型(无函数值)。即执行函数后不会返回任何值给main函数。
无返回值。
定义函数在main函数后,main函数需要使用时,需要声明「函数类型 函数名(参数及参数类型)」
*/
#include<stdio.h>
int main(){
void print_star(void); // 声明print_star函数
void print_message(void); // 声明print_message函数
print_star(); // 调用函数
print_message(); // 调用函数
print_star();
return 0;
}
void print_star(void){ // 定义函数print_star
printf("*******************\n");
}
void print_message(void) { // 定义函数prin_message
printf("Hello World!!\n");
}
/*
*******************
Hello World!!
*******************
Program ended with exit code: 0
*/
函数调用的过程
-
定义的函数中出现的形参,在没有出现函数调用的时候,不会分配内存。当函数调用的时候才会分配临时内存。
-
实参对应的值传递给形参。然后就可以使用形参进行运算了。
-
通过return语句将函数的值返回至主调函数。
-
调用结束后,形参单元会被释放。实参单元仍保持原值。函数调用中,形参和实参使用的是不同的存储单元,因此形参的值改变时不会改变实参的值。
// 输入两个整数,求其最大值。要求用函数求较大的。
// 要求:max函数中返回给主调函数的值的类型为float,与指定的函数类型不符⚠️
#include<stdio.h>
int max(int x, int y){ // 定义函数 , 有两个形参,接收赋值给它的实参
float z;
z = x>y?x:y;
return z;
}
int main() {
int max(int x, int y); // 声明函数
int a, b, c;
scanf("%d%d", &a, &b);
c = max(a, b); // 调用函数 有两个实参,赋值给max函数的形参
// 这里需注意⚠️:max函数返回到这里的值是float类型,而这里是int类型,按照赋值规则处理。
// 将z转成int类型,这就是最后的返回值。
printf("两者中较大的是:%d\n", c);
}
⚠️
/*
1.2 3.4
两者中较大的是:1
Program ended with exit code: 0
*/
// 输入两个数,求两个数之和
#include<stdio.h>
int main() {
float add(float x, float y);
float a, b, c;
printf("Please enter a and b:");
scanf("%f,%f", &a, &b); // ⚠️:%f后带\n的问题,而且scanf是输入函数,双引号直接不需要其他东西。
c = add(a, b);
printf("两个数的和为:%f\n", c);
return 0;
}
float add(float x, float y) {
float z;
z = x+y;
return z;
}
/*
Please enter a and b:2.1, 2.1
两个数的和为:4.200000
Program ended with exit code: 0
*/
函数的递归调用
在调用一个函数的时候,又出现间接或者直接的调用函数本身叫函数的递归调用
// 这是一个无终止的递归调用
#include<stdio.h>
int f(int x){
int y, z;
z = f(y);
return (2*z);
}
举例
// 有5名学生,第5个学生的年龄比第4个同学大2岁,第四个比第三个大2岁,第三个比第二个大2岁,第二个比第一个大2岁。第一个同学十岁。问第5个同学多少岁。
/*
递归问题的两个阶段:回溯和递推
*/
#include <stdio.h>
int main() {
int age(int n);
printf("NO.5, age:%d\n", age(5));
return 0;
}
int age(int n) {
int c;
if (n == 1) {
c = 10;
}else{
c = age(n - 1) + 2;
}
return c;
}
/*
NO.5, age:18
Program ended with exit code: 0
*/
// 求n的阶乘。
#include <stdio.h>
int fac(int n){
int c;
if (n < 0) {
printf("This data error!");
} else if (n == 0 || n == 1) {
c = 1;
} else {
c = n * fac(n - 1);
}
return c;
}
int main(){
int fac(int n);
int n;
scanf("%d", &n);
printf("%d! = %d\n", n, fac(n));
return 0;
}
/*
5
5! = 120
Program ended with exit code: 0
*/
数组作为函数参数
// 将数组作为函数实参
// 输入10个数,要求输出其中值最大的元素,和该数是第几个数
#include <stdio.h>
int main()
{
int max(int x, int y);
int a[10], i, m, n;
for (i = 0; i < 10; i++) { // 输入10个数,作为数组元素
scanf("%d", &a[i]);
}
for (i = 0, m = a[0], n = 0; i < 10; i++) {
if (max(m, a[i] > m)) { // 将数组作为函数的实参 判断如果a[i]大于m(假定为最大值的),将a[i]赋值给m,i赋值给原定的最大值的序列。
m = a[i];
n = i;
}
}
printf("数组中的最大值为:%d,排在的位置:%d\n", m, n+1);
}
int max(int x, int y) { // 求最大值
return x>y?x:y;
}
/*
1 2 3 4 5 6 7 8 9 10
数组中的最大值为:10,排在的位置:10
Program ended with exit code: 0
*/
指针
程序中定义一个变量,程序编译时,系统按照变量的类型分配相应的内存长度空间。int占用4个字节,单精度浮点型4个字节,字符型1个字节。内存区的每个字节都有一个编号,叫做地址。地址所标志的内存单元中存储的就是数据。
通过地址可以找到变量单元,即地址指向该变量单元。即地址即是“指针”。(通过指针可以找打以它为地址的内存单元)
对内存空间的直接访问(按照变量名访问内存单元的方式叫做直接访问):
printf("%d", i); // 通过变量名i找到存储单元的地址,从而对存储单元进行存取操作。程序编译后,变量名已经是变量的内存地址。对变量的存取都是通过地址进行的。
scanf("%d", j); // 将键盘输入的值送到地址从2004开始的整型存储单元。
k = i + j; // 从2000~2003内存单元中取出i的值,从2004~2006内存单元中取到j的值,相加的和存放到k所占用的2008~2010内存单元中。
对内存空间的间接访问():
就是将i变量的地址存放到另一个变量中,通过该变量访问i变量的地址,从而访问i变量。
// 指针变量 定义一个变量i_pointer存放i变量的地址
i_pointer = &i; // 将i变量的地址存放到i_pointer变量中,即变量i_pointer的值就是i变量占用内存的起始地址(2000)
// 这里i变量有两种访问方式:直接访问,间接访问(从i_pointer变量中取到i变量的起始地址,再从内存空间中找到2000字节开始的内存空间中找到i变量)
*i_pointer = 3; // 将变量3送到i_pointer变量所指向的存储单元。*i_pointer指的是i_pointer变量指向的存储单元。
/*
指向是通过地址来体现的(i_pointer变量存储着2000这个地址,指向地址为2000的存储单元)
指针:通过他可以找到以他为地址的内存单元。
*/
指针和指针变量
指向整型数据的指针(int *)读作:int指针;指向int的指针。
一个变量的指针的两层含义:1. 变量的地址(以存储单元编号表示的地址)
2. 变量的类型(指向的内存单元的数据类型)
-
指针:地址
-
指针变量:存放地址的变量
定义指针变量
变量类型 * 变量名
int c = 10;
int * a_pointer, * b_pointer, * c_pointer = &c; // 指针变量前面的*表示这个变量是指针类型的变量。指针变量名是a_pointer,
// b_pointer,c_pointer不是* a_pointer, * b_pointer, * c_pointer
引用指针变量
1)给指针变量赋值:
p = &a; // 将a的地址赋值给指针变量p ,p指向a,p指针变量的值,是a变量的地址。
2)引用指针变量指向的变量
p = &a; // 指针变量指向整型变量a
printf("%d", *p); // 以整数形式输出指针变量指向的变量的值,即a的值。
*p = 1; // 表示将1赋值给指针变量p所指向的变量。
3)引用指针变量的值
printf("%o", p);
// 作用是以八进制行书输出指针变量的值,若p指向a则输出a的地址。
指针变量的例子
// 通过指针变量访问整型变量
# include <stdio.h>
int main() {
int a = 10, b = 5;
int * a_pointer, * b_pointer; // 定义指向整型数据的指针变量a_pointer,b_pointer
a_pointer = &a; // 将a的地址赋值给指针变量a_pointer
b_pointer = &b; // 将b的地址赋值给指针变量b_pointer
// 这里是a_pointer = &a,而不是* a_pointer = &a
// 因为是将a的地址赋值给指针变量a_pointer,而不是*a_pointer(即变量a)
printf("a = %d,b = %d\n", a, b); // 直接访问
printf("* a_pointer = %d,* b_pointer = %d\n", * a_pointer, * b_pointer); // 间接访问
}
/*
a = 10,b = 5
* a_pointer = 10,* b_pointer = 5
Program ended with exit code: 0
*/
// 引用指针变量
// 输入a和b两个整数。按先大后小的顺序输出。
/*
⚠️这里需要注意的是,交换的是p1,p2所指向的内存单元(即p1, p2的值变化了),a,b的值没有交换。实际上是输出的b, a。
*/
#include <stdio.h>
int main() {
int *p1, *p2, *p, a, b;
scanf("%d,%d", &a, &b);
p1 = &a; // 使p1指向变量a
p2 = &b; // 使p2指向变量b
if (a < b) { // a<b时,使p1,p2的值交换
p = p1;
p1 = p2;
p2 = p;
}
printf("a = %d, b = %d\n", a, b); // 输出a, b的值
printf("max = %d, min = %d\n", * p1, * p2); // 输出p1, p2所指向的变量值
return 0;
}
/*
1,3
a = 1, b = 3
max = 3, min = 1
Program ended with exit code: 0
*/
// 用另一种方式处理上面题目。
// 用函数处理,并且用指针类型的数据作为函数参数。
#include <stdio.h>
int main() {
void swap(int *p1, int *p2);
int a, b;
int *pointer_1, *pointer_2;
scanf("%d, %d", &a, &b);
pointer_1 = &a;
pointer_2 = &b;
if (a < b) swap(pointer_1, pointer_2);
printf("a = %d, b = %d\n", a, b);
printf("max = %d, min = %d\n", *pointer_1, *pointer_2);
}
void swap(int *p1, int *p2) { // 交换*p1, *p2所指向的变量值
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
}
/*
1, 3
a = 3, b = 1
max = 3, min = 1
Program ended with exit code: 0
*/
// 对交换函数进行变化,交换指针变量的值
#include <stdio.h>
int main() {
void swap(int *p1, int *p2);
int a, b;
int *pointer_1, *pointer_2;
scanf("%d, %d", &a, &b);
pointer_1 = &a;
pointer_2 = &b;
if (a < b) swap(pointer_1, pointer_2);
printf("Max = %d, Min = %d\n", a, b); // a,b没有交换
return 0;
}
void swap(int *p1, int *p2) {
int *temp;
temp = p1;
p1 = p2;
p2 = temp;
}
/*
1, 2
Max = 1, Min = 2
Program ended with exit code: 0
*/
// 输入三个数,a,b,c按照从大到小的顺序输出。
// 用swap交换两个变量的值
// 用exchange改变三个变量的值
#include <stdio.h>
int main() {
void exchange(int *q1, int *q2, int *q3);
int a, b, c;
int *p1, *p2, *p3;
scanf("%d,%d,%d", &a, &b, &c);
p1 = &a; p2 = &b; p3 = &c;
exchange(p1, p2, p3); // 这里传进去不是指针变量指向的值,而是a,b,c所对应的地址。
printf("a = %d, b = %d, c = %d\n", a, b, c);
return 0;
}
void swap(int *pt1, int *pt2){ // 定义将两个变量的值交换的函数
int temp;
temp = *pt1;
*pt1 = *pt2;
*pt2 = temp;
}
void exchange(int *q1, int *q2, int *q3){ // 定义将三个变量的值交换的函数
void swap(int *pt1, int *pt2);
if (*q1 < *q2) swap(q1, q2);
if (*q1 < *q3) swap(q1, q3);
if (*q2 < *q3) swap(q2, q3);
}
/*
1,2,3
a = 3, b = 2, c = 1
Program ended with exit code: 0
*/
指针引用数组
数组元素的指针就是数组元素的地址。
// 可以使用指针变量指向数组元素
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int *p; // 定义p是指向整型变量的指针变量
p = &a[0]; // 把a[0]元素的地址赋值给指针变量p
// 使用指针可以使目标程序占用内存空间小,运行速度快。
// c语言中数组名,表示数组中的首个元素
p = &a; // 表示把a[0](数组a的首元素)元素的地址赋值给p指针变量。
int *p1;
p1 = &a[1];
// 上面两行等同于
int *p1 = &a[1];
如果指针变量p指向数组的任意一个元素,p+1表示指向同一数组的下一个元素;p-1指向同一数组的上一个元素。(这里的±是p的值(地址)简单的加减,而不是加上或者减去一个数组元素占用的字节数)。如果数组是int类型,则p是2000,p+1是2004。
-
注意⚠️如果指针变量p的初始值是&a[0],则p+i,和a+i是数组元素a[i]的地址。或者换种说法是 它们是指向数组a元素序号为i的个元素
-
注意⚠️a代表数组元素首元素的地址,a+i则是数组元素序号为i的地址。
-
注意⚠️***(a + i)和 (p + i)表示的是(a + i,)(p + i)所指向的数组元素。例如: (a + 5)表示a[5]
-
注意⚠️如果指针变量p1和p2指向同一个数组,执行p2 - p1,结果是p2 - p1的值除以数组元素的长度。int类型的数组,p1(2000),p2(2020),结果是(2020 - 2000)/4 = 5,表示p2和p1所指向的数组元素之间差5的数组元素。
通过指针引用数组元素
引用数组元素的两种方式:
- 下标法 a[i]
- 指针法 *(a + i);或者 *(p + i) 其中a是数组名,p是指向数组元素的指针。初值p = a
// 一个整型数组a,有十个元素,要求输出数组中的所有元素,使用下标法输出。
// 下标法
#include <stdio.h>
int main() {
int a[10];
int i;
for (i = 0; i < 10; i++)
scanf("%d", &a[i]);
for (i = 0; i < 10; i++)
printf("%d ", a[i]);
printf("\n");
return 0;
}
/*
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
Program ended with exit code: 0
*/
// 通过数组名计算数组元素的地址,找出元素的值。
#include <stdio.h>
int main() {
int a[10];
int i;
for (i = 0; i < 10; i++)
// scanf("%d", &a[i]);
scanf("%d", a + i);
for (i = 0; i < 10; i++)
printf("%d ", *(a + i));
printf("\n");
return 0;
}
/*
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
Program ended with exit code: 0
*/
// 用指针变量引用数组元素
#include <stdio.h>
int main() {
int *p, i;
int a[10];
for (i = 0; i < 10; i++)
scanf("%d", &a[i]);
for (p = a; p < a + 10; p++){
printf("%d ", *p); // 用指针指向当前的数组元素
}
printf("\n");
return 0;
}
/*
1 2 3 4 5 6 7 8 9 1
1 2 3 4 5 6 7 8 9 1
Program ended with exit code: 0
*/
// 通过⚠️指针变量输出整型数组a的十个元素。
// ⚠️注意 int a[10], *p = a; // 表示p的初始值是a(即a[0]的地址),指向a[0](数组元素的首元素)
#include <stdio.h>
int main() {
int a[10], *p, i;
p = a; // p的初值是a,指向a[0]
for (i = 0; i < 10; i++) {
scanf("%d", p++);
}
p = a; // 重新使指针变量p指向数组a首元素a[0]
// for (i = 0; i < 10; i++, p++) {
// printf("%d ", *p);
// }
// 等同于上面的程序。* 和 ++ 同优先级,自右而左执行。
for (i = 0; i < 10; i++) {
printf("%d ", *p++);
}
printf("\n");
return 0;
}
/*
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
Program ended with exit code: 0
*/
// 通过⚠️数组名计算元素地址,找出元素的值
#include <stdio.h>
int main() {
int a[10], i;
for (i = 0; i < 10; i++) {
scanf("%d", &a[i]);
}
for (i = 0; i < 10; i++) {
printf("%d ", *(a + i)); // 与上面的不同在于通过元素名+元素序号,计算出元素地址,再找到该元素
}
printf("\n");
}
/*
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
Program ended with exit code: 0
*/
数组名作为函数参数
fun(int arr[], int n)
fun(int *arr, int n)这两者是等价的。该函数被调用的时候,系统会在fun函数中建立一个指针变量arr用来存放从主调函数传递过来的实参数组首元素地址。
// 将数组a中10个元素按相反顺序存放
#include<stdio.h>
int main() {
void inv(int x[], n);
int i, a[10] = {2, 9, 10, 7, 8, 3, 5, 23, 12, 22};
for (i = 0; i < 10; i++) {
printf("%d ", a[i]); // 输出未交换顺序的数组各元素的值
}
printf("\n");
inv(a, 10); // 调用函数交换顺序
for (i = 0; i < 10; i++) {
printf("%d ", a[i]); // 输出交换顺序后的数组各元素的值
}
printf("\n");
return 0;
}
void inv(int x[], int n){ // 形参x是数组名
int temp, i, j, m = (n - 1)/2;
for (i = 0; i < m; i++) {
j = n - 1 - i;
temp = x[i]; x[i] = x[j]; x[j] = temp; // 将x[i]和x[j]进行交换
}
return;
}
/*
2 9 10 7 8 3 5 23 12 22
22 12 23 5 8 3 7 10 9 2
Program ended with exit code: 0
*/
对上面的程序进行改动。将形参改成指针变量。
// 对上面的程序进行改动。将形参改成指针变量。
#include <stdio.h>
int main() {
void inv(int *x, int n);
int i, a[10] = {2, 9, 10, 7, 8, 3, 5, 23, 12, 22};
for (i = 0; i < 10; i++) {
printf("%d ", a[i]);
}
printf("\n");
inv(a, 10);
for (i = 0; i < 10; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
void inv(int *x, int n){
int *p, temp, *i, *j, m = (n - 1)/2;
i = x; j = x + n - 1; p = x + m;
for (; i<= p; i++, j--) {
temp = *i; *i = *j; *j = temp;
}
}
/*
2 9 10 7 8 3 5 23 12 22
22 12 23 5 3 8 7 10 9 2
Program ended with exit code: 0
*/
自定义数据类型
文件的输入输出
文件主要有两类:
- 程序文件:源文件(.c文件),目标文件(.obj),可执行文件(.exe)
- 数据文件:供程序读写的文件。