C程序设计总结

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) { }

函数类型 函数名 函数参数类型 函数参数 函数参数类型 函数参数 函数体

算法——程序的灵魂

一个程序包括两部分:

对数据的描述(数据结构):

对操作的描述(算法:要求计算机操作的步骤。广义的算法,解决一个问题采取的方法和步骤就是算法。

算法 + 数据结构 = 程序

算法的特性

  • 又穷性
  • 确定性
  • 有零个或多个输入
  • 有零个或多个输出
  • 有效性

表示一个算法

  • 自然语言

  • 流程图
    在这里插入图片描述

  • 三种基本结构

    1. 顺序结构

    2. 选择结构

    3. 循环结构
      当型(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 21512152152151
​ 存放的最小值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=0a,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循环

改变循环的执行状态

  • break

    break语句提前终止循环(跳出循环体,使流程跳出循环体外)

  • continue

    continue语句提前结束本次循环(希望提前结束本次循环)

  • break和continue的比较

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
 */
截屏2021-10-11 下午3.11.49
// 对交换函数进行变化,交换指针变量的值
#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)
  • 数据文件:供程序读写的文件。

错误分析

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值