C 语言基础知识

1.C 程序的基本构成

一个最基本C语言程序有头文件,主函数两部分组成

2.常用标准库函数

//输入输出函数
#include<stdio.h>
//数学函数
#include<math.h>
//字符串函数    
#include<string.h>

3.标识符

标识符由字母(A-Z,a-z)数字(0-9)下划线“_”组成,并且首字符不能是数字,但可以是字母或者下划线

4.注释

/*
多行注释
*/

//单行注释

5.进制转换

以二为桥梁 除二取余 按权相加

6.数据类型

C语言的基本数据类型为:整型字符型实数型

7.运算符与表达式

括号成员第一;全体单目第二;乘除余三,加减四;移位五,关系六;等于(与)不等排第七;位与异或和位或,“三分天下”八九十;逻辑或跟与,十二和十一;条件高于赋值;逗号运算级最低。

流程控制

流程图

椭圆 起止框

平行四边形 输入输出框

菱形 判断框

长方形 处理框

流程线 箭头

基本语句

表达式语句

由运算符将变量、常量、函数调用返回值结合而成。

a=3;(赋值语句)
空语句

只有一个分号。

复合语句

由一对花括号括起来的一组语句。

输入输出语句

输出

字符输出函数

putchar

格式输出函数

printf("格式字符",表达式表);
输入

字符输入函数

getchar();

格式输入函数

scanf("格式字符",变量地址表)

三种程序结构

顺序结构,选择结构,循环结构

数组

一维数组

main(){
//    一维数组定义
    int arr[]={1,2,3};
}
数组赋初值注意

1.可以只给部分元素赋初值。其它自动赋0值

2.只能给元素逐个赋值,不能给数组整体赋值

3.数组定义时可以不给出数组的长度。

二维数组

main(){
//    二维数组定义
    int arr1[][2]={1,1}; 
}
注意

1.可以只对部分元素赋初值,未赋初值的元素自动取0值

2.如对全部元素赋初值,则第一维的长度可以不给出

常用算法

冒泡排序
#include <stdio.h>
int main(){
    int nums[10] = {4, 5, 2, 10, 7, 1, 8, 3, 6, 9};
    int i, j, temp;

    //冒泡排序算法:进行 n-1 轮比较
    for(i=0; i<10-1; i++){
        //每一轮比较前 n-1-i 个,也就是说,已经排序好的最后 i 个不用比较
        for(j=0; j<10-1-i; j++){
            if(nums[j] > nums[j+1]){
                temp = nums[j];
                nums[j] = nums[j+1];
                nums[j+1] = temp;
            }
        }
    }
   
    //输出排序后的数组
    for(i=0; i<10; i++){
        printf("%d ", nums[i]);
    }
    printf("\n");
   
    return 0;
}
选择排序
#include<stdio.h>
int main() {
 int array[10] = { 6,9,7,8,5,3,4,0,1,2 };
    int temp, index;
    for (int i = 0; i < 9; i++) {
        index = i;
        for (int j = i; j < 10; j++) {
            if (array[j] < array[index])
                index = j;//保存满足条件的数组元素下标
        }
        temp = array[i];
        array[i] = array[index];
        array[index] = temp;,
    }
 //打印数组
 for (int i = 0; i < 10; i++)
  printf("%2d", array[i]);
 return 0;
}
最值
#include <stdio.h>
int main(){
    int a[10] = {4, 5, 2, 10, 7, 1, 8, 3, 6, 9};
    int i,j=0,k=0;

    for(i=0;i<10;i++){
        if(a[i]>a[j]){
              j=i;
        } 
        else if(a[i]<a[k]){
            k=i;
        }
    } 
    printf("最大值%d,下标为%d\n",a[j],j);
    printf("最小值%d,下标为%d\n",a[k],k);
    return 0;
}

函数

函数定义

确定三部分1.函数的返回值类型2.函数的名称3.函数的参数

#include <stdio.h>
void max(int a, int b)
{
    if(a>b){
        printf("%d>%d",a,b);
    }
    else if(a<b){
        printf("%d<%d",a,b);
    }
    else{
        printf("%d=%d",a,b);
    }
}
int main(){
    max(1,2);//调用 

    return 0;
}
注意

​ 如果函数的定义是在调用函数的后面则需要在调用之前声明函数的定义,否则不需要事先声明。

高级知识

字符串

字符数组

用来存放字符的数组称为字符数组,例如:

char a[10];  //一维字符数组
char b[5][10];  //二维字符数组
char c[20]={'c', '  ', 'p', 'r', 'o', 'g', 'r', 'a','m'};  // 给部分数组元素赋值
char d[]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm' };  //对全体元素赋值时可以省去长度

字符数组实际上是一系列字符的集合,也就是字符串(String)。在C语言中,没有专门的字符串变量,没有string类型,通常就用一个字符数组来存放一个字符串。

C语言规定,可以将字符串直接赋值给字符数组,例如:

char str[30] = {"c.biancheng.net"};
char str[30] = "c.biancheng.net";  //这种形式更加简洁,实际开发中常用

数组第 0 个元素为'c',第 1 个元素为'.',第 2 个元素为'b',后面的元素以此类推。

为了方便,你也可以不指定数组长度,从而写作:

char str[] = {"c.biancheng.net"};
char str[] = "c.biancheng.net";  //这种形式更加简洁,实际开发中常用

给字符数组赋值时,我们通常使用这种写法,将字符串一次性地赋值(可以指明数组长度,也可以不指明),而不是一个字符一个字符地赋值,那样做太麻烦了。

这里需要留意一个坑,字符数组只有在定义时才能将整个字符串一次性地赋值给它,一旦定义完了,就只能一个字符一个字符地赋值了。请看下面的例子:

char str[7];
str = "abc123";  //错误
//正确
str[0] = 'a'; str[1] = 'b'; str[2] = 'c';
str[3] = '1'; str[4] = '2'; str[5] = '3';
字符串结束标志(划重点)

字符串是一系列连续的字符的组合,要想在内存中定位一个字符串,除了要知道它的开头,还要知道它的结尾。找到字符串的开头很容易,知道它的名字(字符数组名或者字符串名)就可以;然而,如何找到字符串的结尾呢?C语言的解决方案有点奇妙,或者说有点奇葩。

在C语言中,字符串总是以'\0'作为结尾,所以'\0'也被称为字符串结束标志,或者字符串结束符。

'\0'是 ASCII 码表中的第 0 个字符,英文称为 NUL,中文称为“空字符”。该字符既不能显示,也没有控制功能,输出该字符不会有任何效果,它在C语言中唯一的作用就是作为字符串结束标志。

C语言在处理字符串时,会从前往后逐个扫描字符,一旦遇到'\0'就认为到达了字符串的末尾,就结束处理。'\0'至关重要,没有'\0'就意味着永远也到达不了字符串的结尾。

" "包围的字符串会自动在末尾添加'\0'。例如,"abc123"从表面看起来只包含了 6 个字符,其实不然,C语言会在最后隐式地添加一个'\0',这个过程是在后台默默地进行的,所以我们感受不到。

需要注意的是,逐个字符地给数组赋值并不会自动添加'\0',例如:

char str[] = {'a', 'b', 'c'};

数组 str 的长度为 3,而不是 4,因为最后没有'\0'

当用字符数组存储字符串时,要特别注意'\0',要为'\0'留个位置;这意味着,字符数组的长度至少要比字符串的长度大 1。请看下面的例子:

char str[7] = "abc123";

"abc123"看起来只包含了 6 个字符,我们却将 str 的长度定义为 7,就是为了能够容纳最后的'\0'。如果将 str 的长度定义为 6,它就无法容纳'\0'了。

当字符串长度大于数组长度时,有些较老或者不严格的编译器并不会报错,甚至连警告都没有,这就为以后的错误埋下了伏笔,读者自己要多多注意。
练习

我们将 26 个大写英文字符存入字符数组,并以字符串的形式输出:

#include <stdio.h>
int main(){
    char str[30] = {0};  //将所有元素都初始化为 0,或者说 '\0'
    char c;
    int i;
    for(c=65,i=0; c<=90; c++,i++){
        str[i] = c;
    }
    printf("%s\n", str);
   
    return 0;
}
字符串的输入输出
输出

在C语言中,有两个函数可以在控制台(显示器)上输出字符串,它们分别是:

  • puts():输出字符串并自动换行,该函数只能输出字符串。
  • printf():通过格式控制符%s输出字符串,不能自动换行。除了字符串,printf() 还能输出其他类型的数据。
输入

在C语言中,有两个函数可以让用户从键盘上输入字符串,它们分别是:

  • scanf():通过格式控制符%s输入字符串。除了字符串,scanf() 还能输入其他类型的数据。
  • gets():直接输入字符串,并且只能输入字符串。

但是,scanf() 和 gets() 是有区别的:

  • scanf() 读取字符串时以空格为分隔,遇到空格就认为当前字符串结束了,所以无法读取含有空格的字符串。
  • gets() 认为空格也是字符串的一部分,只有遇到回车键时才认为字符串输入结束,所以,不管输入了多少个空格,只要不按下回车键,对 gets() 来说就是一个完整的字符串。换句话说,gets() 用来读取一整行字符串。

注意,就目前学到的知识而言,int、char、float 等类型的变量用于 scanf() 时都要在前面添加&,而数组或者字符串用于 scanf() 时不用添加&,它们本身就会转换为地址。

字符串长度

我们使用string.h头文件中的 strlen() 函数来求字符串的长度,它的用法为:

length strlen(strname);
//strname 是字符串的名字,或者字符数组的名字;
//length 是使用 strlen() 后得到的字符串长度,是一个整数。

预处理

预处理

使用库函数之前,应该用#include引入对应的头文件。这种以#号开头的命令称为预处理命令

C语言源文件要经过编译、链接才能生成可执行程序:

    1) 编译(Compile)会将源文件(`.c`文件)转换为目标文件。对于 VC/VS,目标文件后缀为`.obj`;对于GCC,目标文件后缀为`.o`。
编译是针对单个源文件的,一次编译操作只能编译一个源文件,如果程序中有多个源文件,就需要多次编译操作。

2)链接(Link)是针对多个文件的,它会将编译生成的多个目标文件以及系统中的库、组件等合并成一个可执行程序

宏定义

关于宏的一个常见应用就是,用它定义数值常量的名称:

#define         ARRAY_SIZE 100
double   data[ARRAY_SIZE];
//这两行代码为值 100 定义了一个宏名称 ARRAY_SIZE,并且在数组 data 的定义中使用了该宏
文件包含命令
/*
include叫做“文件包含命令”,用来引入对应的头文件(`.h`文件)
简单的理解:就是将头文件的内容插入到该命令的所在位置,
从而把头文件的内容和当前的源文件连接成一个源文件。
既:复制-粘贴。
*/

结构体

结构体

结构体是用户自定义的可用的数据类型,它允许您存储不同类型的数据项。

结构体类型的定义

以学生的基本信息为例,包括四个变量:姓名、年龄、性别、学号。

//定义了一个结构体STU
struct STU
{
    char name[20];
    int age;
    char sex;
    char num[20];
};
//定义结构体变量:
struct STU stu1,stu2;

上面也可以这样写

//定义了一个结构体STU
struct STU
{
    char name[20];
    int age;
    char sex;
    char num[20];
}stu1,stu2;
//此时还可以继续定义STU结构体变量如:
struct STU stu3;

typedef在结构体定义的基础上加上了别名stu1

typedef struct STU
{
    char name[20];
    int age;
    char sex;
    char num[20];
}stu1;
//也可省略结构体名
typedef struct 
{
    char name[20];
    int age;
    char sex;
    char num[20];
}stu1;
//定义结构体变量 stu1为别名
stu1 stu2;
结构体数组的定义
struct STU
{
    char name[20];
    int age;
    char sex;
    char num[20];
};
//定义结构体变量
struct STU stu[10];
//初始化
struct STU stu[3]={
    {"LiuHu",18,'F',"beijing road"},
    {"NaoDan",17,'M',"shanghai road"},
    {"MaHuangTeng",19,'F',"shenzhen road"}
};
//引用
printf("%s",stu[0].name);

文件操作

打开(fopen函数)

fopen() 函数用来打开一个文件,它的原型为:

FILE *fopen(char *filename, char *mode);

filename为文件名(包括文件路径),mode为打开方式,它们都是字符串。fopen() 会获取文件信息,包括文件名、文件状态、当前读写位置等,并将这些信息保存到一个FILE类型的结构体变量中,然后将该变量的地址返回。

FILE是在stdio.h头文件中定义的一个结构体,用来保存文件信息。

如果希望接收 fopen() 的返回值,就需要定义一个 FILE 类型的指针。例如:

FILE *fp = fopen("demo.txt", "r");

表示以“只读”方式打开当前目录下的 demo.txt 文件,并使 fp 指向该文件,这样就可以通过 fp 来操作 demo.txt 了。fp 通常被称为文件指针。又如:

FILE *fp = fopen("D:\\demo.txt","rb");

表示以二进制方式打开 D 盘下的 demo.txt 文件,允许读和写。

打开方式(mode)有多种,见下表:

打开方式说明
r以只读方式打开文件,只允许读取,不允许写入。该文件必须存在。
r+以读/写方式打开文件,允许读取和写入。该文件必须存在。
rb+以读/写方式打开一个二进制文件,允许读/写数据。
rt+以读/写方式打开一个文本文件,允许读和写。
w以只写方式打开文件,若文件存在则长度清为0,即该文件内容消失,若不存在则创建该文件。
w+以读/写方式打开文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a以追加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留(EOF符保留)。
a+以追加方式打开可读/写的文件。若文件不存在,则会建立该文件,如果文件存在,则写入的数据会被加到文件尾后,即文件原先的内容会被保留(原来的EOF符 不保留)。
wb以只写方式打开或新建一个二进制文件,只允许写数据。
wb+以读/写方式打开或建立一个二进制文件,允许读和写。
wt+以读/写方式打开或建立一个文本文件,允许读写。
at+以读/写方式打开一个文本文件,允许读或在文本末追加数据。
ab+以读/写方式打开一个二进制文件,允许读或在文件末追加数据。
常用程序
if( (fp=fopen("D:\\demo.txt","rb") == NULL ){
    printf("Error on open D:\\demo.txt file!");
    getch();
    exit(1);
}
/*
这段程序的意义是,如果返回的指针为空,表示不能打开D盘根目录下的 demo.txt 文件,
并给出提示信息“error on open D:\\demo.txt file!”。
第3行getch()的功能是从键盘输入一个字符,但不在屏幕上显示。
在这里,该行的作用是等待,只有当用户从键盘敲任一键时,程序才继续执行,
因此用户可利用这个等待时间阅读出错提示。敲键后执行exit(1)退出程序。
*/
关闭(fclose函数)

文件一旦使用完毕,应该用fclose()函数把文件关闭,以释放相关资源,避免数据丢失。fclose() 的原型为:

int fclose(FILE *fp);

fp 为文件指针。例如:

fclose(fp);

文件正常关闭时,fclose() 的返回值为0,如果返回非零值则表示有错误发生。

指针(重点)

指针与数组

指针与字符串

指针与函数

如果觉得我的文章对你有用,请随意赞赏