当前位置: 代码迷 >> 综合 >> 2020-10-05 C语言重点知识总结
  详细解决方案

2020-10-05 C语言重点知识总结

热度:21   发布时间:2024-02-25 08:35:41.0

1Byte = 8 Bit
1KB = 1024Byte = 210Byte
1MB = 1024KB = 220Byte
1GB = 1024MB = 230Byte
1TB = 1024GB = 240Byte
1PB = 1024TB = 250Byte
1EB = 1024PB = 260Byte
一个字节BYTE=8位bit
数值是以补码表示的
正数的补码与原码相同
负数的补码:将该数的绝对值的二进制形式按位取反在加1
例
10的原码:00001010
取反:11110101
再加1,得-10的补码:11110110


说  明    字符型    短整型    整型    长整型    单精度浮点型    双精度浮点型
数据类型    char    short    int    long    float        double
长  度    1    2    4    4    4        8

指针固定占4个字节

获取某个数据类型的长度可以使用 sizeof 操作符
============================================================
数组:
同一个数组数据类型相同;下标从零开始A[0]/A[1]......
如果要数千亿有10个变量的数组必须用循环语句逐个输出各下标变量
for(i=0;i<10;i++)
{
printf("%d",a[i];
}
二维数组
a[3][4];
a[0]--------a00     a01     a02     a03
a[1]--------a10     a11     a12     a13 
a[2]--------a20     a21     a22     a23
int a[3][4]={ {1,2,3,4},{5,6,7,8},{9,10,11,12}};
==============================================================
结构体:
基本形式
struct 结构体名{
结构体所包含的变量或数组
};

struct stu{
char *name;//姓名
int num;//学号
int age;//年龄
char group;//所在学习小组
float score;//成绩
};

首先在C中定义一个结构体类型要用

typedef struct Student
{
    int a;
}Stu;
于是在声明变量的时候就可:Stu stu1;
如果没有typedef就必须用struct Student stu1;来声明。
这里的Stu实际上就是struct Student的别名。

结构体变量
struct stu stu1,stu2;
定义了两个变量stu1,stu2都是stu类型,都由5个成员组成

也可以
struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在学习小组
    float score;  //成绩
} stu1, stu2;

赋值方法
    //给结构体成员赋值
    stu1.name = "Tom";
    stu1.num = 12;
    stu1.age = 18;
    stu1.group = 'A';
    stu1.score = 136.5;
    //读取结构体成员的值
    printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", stu1.name, stu1.num, stu1.age, stu1.group, stu1.score);
    return 0;
}

//结构体
struct stu{
    char *name;  //姓名
    int num;  //学号
    int age;  //年龄
    char group;  //所在小组
    float score;  //成绩
} stu1 = { "Tom", 12, 18, 'A', 136.5 };
//结构体指针
struct stu *pstu = &stu1;

注意,结构体变量名和数组名不同,数组名在表达式中会被转换为数组指针,而结构体变量名不会,无论在任何表达式中它表示的都是整个集合本身,要想取得结构体变量的地址,必须在前面加&,所以给 pstu 赋值只能写作:
struct stu *pstu = &stu1;

  *pointer = &stu1;
获取结构体成员
pointer->memberName
指针->所取成员

(*pointer).memberName
指针。所取成员

======================================================
union 共用体名{
    成员列表
};     
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。

#include <stdio.h>
union data{
    int n;
    char ch;
    short m;
};
int main(){
    union data a;
    printf("%d, %d\n", sizeof(a), sizeof(union data) );
    a.n = 0x40;
    printf("%X, %c, %hX\n", a.n, a.ch, a.m);
    a.ch = '9';
    printf("%X, %c, %hX\n", a.n, a.ch, a.m);
    a.m = 0x2059;
    printf("%X, %c, %hX\n", a.n, a.ch, a.m);
    a.n = 0x3E25AD54;
    printf("%X, %c, %hX\n", a.n, a.ch, a.m);
   
    return 0;
}
运行结果
4, 4
40, @, 40
39, 9, 39
2059, Y, 2059
3E25AD54, T, AD54
==============================================================
关系运算符    含  义    
<           小于    
<=        小于或等于    
>        大于    
>=        大于或等于    
==        等于    
!=        不等于    
 与运算(&&)
参与运算的两个表达式都为真时,结果才为真,否则为假。例如:
5&&0
5为真,0为假,相与的结果为假,也就是 0。

2) 或运算(||)
参与运算的两个表达式只要有一个为真,结果就为真;两个表达式都为假时结果才为假。例如:
10 || 0
10为真,0为假,相或的结果为真,也就是 1。

3) 非运算(!)
参与运算的表达式为真时,结果为假;参与运算的表达式为假时,结果为真。例如:
!0

0 为假,非运算的结果为真,也就是 1。
!(5>0)
5>0 的结果是1,为真,非运算的结果为假,也就是 0。
==========================================================
switch 是另外一种选择结构的语句
switch(表达式){
    case 整型数值1: 语句 1;
    case 整型数值2: 语句 2;
    ......
    case 整型数值n: 语句 n;
    default: 语句 n+1;
}
例题:
#include <stdio.h>
int main(){
    int a;
    printf("Input integer number:");
    while(1)
    {
    scanf("%d",&a);
    switch(a)
    {
        case 1: printf("星期一\n"); break;
        case 2: printf("星期二\n"); break;
        case 3: printf("星期三\n"); break;
        case 4: printf("星期四\n"); break;
        case 5: printf("星期五\n"); break;
        case 6: printf("星期六\n"); break;
        case 7: printf("星期七\n"); break;
        default:printf("请重新输入\n"); break;
    }
}
    return 0;
}

break 是C语言中的一个关键字,专门用于跳出 switch 语句。
如果不用break,如果输入4,就会继续执行case4后面的所有语句

default 不是必须的。当没有 default 时,如果所有 case 都匹配失败,那么就什么都不执行
============================================================
max = (a>b) ? a : b;
该语句的语义是:如a>b为真,则把a赋予max,否则把b 赋予max

while(表达式){
    语句块
}
先计算“表达式”的值,当值为真(非0)时, 执行“语句块”;执行完“语句块”,再次计算表达式的值,如果为真,继续执行“语句块”……这个过程会一直重复,直到表达式的值为假(0),就退出循环,执行 while 后面的代码。

for 循环的一般形式为:
for(表达式1; 表达式2; 表达式3){
    语句块
}

运行过程为:
1) 先执行“表达式1”。
2) 再执行“表达式2”,如果它的值为真(非0),则执行循环体,否则结束循环。
3) 执行完循环体后再执行“表达式3”。
4) 重复执行步骤 2) 和 3),直到“表达式2”的值为假,就结束循环。
===========================================================
%d 十进制有符号整数 
%u 十进制无符号整数 
%f 浮点数 
%s 字符串 
%c 单个字符 
%p 指针的值 
%e 指数形式的浮点数 
%X 无符号以十六进制表示的整数 
%0 无符号以八进制表示的整数 
%g 自动选择合适的表示法
=======================================================
@一级指针与二级指针
例子:
一个&可以抵消一个*
int i = 10;
&i = 0x01;//设i的地址为0x01    
int* pi= & i=0x01;
*pi = *(&i) = i = 10;
&pi=0x02
int** ppi = &pi =0x02;
&ppi = 0x03;
*ppi = *(&pi) = pi =&i;
**ppi = *(*ppi) =*(&i) = i;
对一级指针而言,指针变量命,代表普通变量的地址
               *指针变量名,代表普通变量(值)
对二级指针变量而言,指针变量名,代表一级指针变量的地址
               *指针变量名,代表普通变量的地址
               **指针变量名,代表普通变量(值)
=======================================================
链表
链表是结构体变量于结构体变量连接在一起(前面的尾与后面的头相连)

动态创建一个链表:动态内存申请+模块化设计
1创建链表(创建一个表头表示整个链表)
2.创建节点
3.插入节点
4.删除节点
5.打印遍历链表(测试)

创建链表
#include <stdio.h>
#include <stdlib.h>
struct Node{
    int data;            //数据域
    struct Node *next  //指针域
};
//创建链表
struct Node* createList()
{
    struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
    //headNode 成为了结构体变量
    //变量使用前必须被初始化
    headNode->next = NULL;
    return headNode;
};
//创建节点
struct Node* createNode(int data)
{
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->data = data;
    newNode->next = NULL;
    return newNode;
};
//插入结点,参数:插入那个链表,插入结点的数据是多少 表头插入
void instertNodeByHead(struct Node* headNode,int data)
{
    //创建插入的节点
    struct Node *newNode = createNode(data);
    newNode->next = headNode->next;
    headNode->next = newNode;
}

//打印
void printList(struct Node* headNode)
{
    struct Node *pMove = headNode->next;
    while(pMove)
    {
        printf("%d\t",pMove->data);
        pMove = pMove->next;
    }
    printf("\n");
}
//删除链表
void delerNodeByAppoin(struct Node *headNode,int posData)
{
    struct Node* posNode = headNode->next;
    struct Node* posNodeFront = headNode;
    if(posNode == NULL)
        printf(" 无法删除链表为空\n");
        else
    {
            while(posNode->data != posData)
            {
                posNodeFront = posNode;
                posNode = posNodeFront->next;
                if(posNode == NULL)
                {
                    printf("没有找到相关信息,无法删除\n");
                    break;
                }
            }
            posNodeFront->next = posNode->next;
            free(posNode);
    }
}

int main()
{
    struct Node* list = createList();
    instertNodeByHead(list,1);
    instertNodeByHead(list,2);
    instertNodeByHead(list,3);
    printList(list);
    delerNodeByAppoin(list,2);
    printList(list);
    system("pause");
    return 0;
}

=======================================================
对于文件的操作
fopen(打开文件)
FILE *fopen(const char *path,const char *mode);//(文件地址,打开方式)
FILE *fp;
fp=fopen(“C:\\Users\\1.txt”,"r+")或者(“C:/Users/1.txt”,"r+");绝对路径
=======================================================
FILE *fp;//定义一个文件指针
fp=fopen("b.txt","r+")//相对路径
=========================================================
fclose(fp;)//关闭文件
========================================================
fopen函数需要两个参数 
1.代表要打开的文件路径
2.代表打来文件的方式(决定程序中可以对文件做什么操作)
打开方式有如下选择:
"r"  只能查看文件内容并且从文件头开始.要求文件已经存在,否则打开失败
"r+"  比"r"多了修改功能
"w"  只能修改文件内容并且从文件头开始.
        如果文件还不存在则创建文件,如果文件已经存在则删除里面所有内容. 
"w+"  比"w"多了查看功能(查看功能就是读操作,读就是可以用printf打印出来,如果w没有+printf就打印不出来)
"a"  只能在文件末尾追加新内容,如果文件不存在则创建新文件,如果文件存在不对原有内容做任何修改
"a+"  比"a"多了查看功能
"b"  也是一种打开方式.它可以和上面任何一种打开方式混合使用
       这种方式表示以二进制的方式对文件进行操作
注:所有文件都可以采用二进制方式进行操作.文本文件还可以采用另外一种方法进行操作(字符操作).
=================================================================
fread函数可以对文件采用二进制方式进行读操作
fwrite函数可以对文件采用二进制方式进行写操作(这两个函数可以处理任何文件,二进制和文本文件)
这两个函数都需要四个参数:
1.内存中第一个存储区的地址
2.内存中单个存储区的大小
3.希望操作的存储区个数
4.文件指针
它们的返回值表示实际操作的存储区个数
=================================================================
当要求一次存取一组数据(如,一个数组、一个结构体变量的值),fread和fwrite函数可以解决该类问题。
fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);

buffer:对于fread来说,指的是读入数据的存放地址;对于fwrite来说,是要输出数据的地址。
size:读写数据时,每笔数据的大小
count:读写数据的笔数
fp:文件指针

//文本文件
#include<stdio.h>
#include<string.h>
int main()
{
         int arr[] = {5,2,85,27,945},num = 0;
         char buf[10] = {};
         FILE *p_file = fopen("a.txt","wb");   //二进制方式对文件操作
         if(p_file)
        {      
                for(num = 0;num <= 4;num++)
               {
                      //将数字转换成一组字符存在字符数组中 
                      sprintf(buf,"%d ",arr[num]);    
                      fwrite(buf,sizeof(char),strlen(buf),p_file);
               }
                //printf("%s\n",buf);
                fclose(p_file);    
                p_file = NULL;
         }
}
注:sprintf函数可以将数字按照格式打印在字符串中.
//二进制文件
#include<stdio.h>
int main()
{
          int arr[] = {45,23,943,426,345},size = 0;
          FILE *p_file = fopen("a.bin","wb");
          if(p_file)
         {
                 size = fwrite(arr,sizeof(int),5,p_file);   //size为返回值
                 printf("一共写入%d个存储区\n",size);
                 fclose(p_file);
                 p_file = NULL;
         }
          return 0;
}
示例程序:编写程序从二进制文件中得到所有数据并打印在屏幕上.
//从二进制文件中得到所有数据,并打印在屏幕上
#include<stdio.h>
int main()
{
             int arr[5] = {},size = 0,num = 0;
             FILE *p_file = fopen("a.bin","rb");
             if(p_file)
            {    
                      size = fread(arr,sizeof(int),5,p_file);
                      printf("一共获得%d个存储区\n",size);
                      for(num = 0;num <= 4;num++)
                     {
                            printf("%d ",arr[num]);   
                     }
                      printf("\n");
                      fclose(p_file);
                      p_file = NULL;
           }
            return 0;
}
 
=================================================================
文件操作练习:
1.编程程序从键盘得到多个人员信息并记录到文件中.人员信息包括id,salary和name.
   人员信息个数不确定,文件内容可以追加.
2.把文件中所有人员信息打印在屏幕上.
#include<stdio.h>
#include<string.h>

typedef struct
{
        int id;
        float salary;
        char name[10];
}person;
int main()
{
        int choice;
        person prs = {};
        FILE *p_file = fopen("person.bin","ab");   
        if(p_file)
       {
              do{
                         printf("请输入id:");
                         scanf("%d",&(prs.id));
                         printf("请输入工资:");
                         scanf("%g",&(prs.salary));
                         //清理回车换行
                         scanf("%*[^\n]");
                         scanf("%*c"); 
                         printf("请输入姓名:");
                         fgets(prs.name,10,stdin);//stdin从键盘获取输入 (10)获取N-1个字符
                         if(strlen(prs.name) == 9 && prs.name[8] != '\n')
                        {
                                scanf("%*[^\n]");
                                scanf("%*c");
                        }
                         fwrite(&prs,sizeof(person),1,p_file);
                         printf("是否需要输入下一个人员信息?0表示不需要,1表示需要\n");
                         scanf("%d",&choice);
                  }while(choice);
                  fclose(p_file);
                  p_file = NULL;
        }
        return 0;
}                                    
//把文件中所有人员信息打印在屏幕上
#include<stdio.h>
typedef struct
{
        int id;
        float salary;
        char name[10];
}person;
int main()
{
        person prs = {};
        FILE*p_file = fopen("person.bin","rb");
        if(p_file)
       {
             //fread函数返回值作逻辑表达式
              while(fread(&prs,sizeof(person),1,p_file)) 
             {
                     printf("id是%d\n",prs.id);
                     printf("工资是%g\n",prs.salary);
                     printf("姓名是%s\n",prs.name);
             }
             fclose(p_file);
             p_file = NULL;
      }
      return 0;
} 
=================================================================
fgets(...)从标准设备读数据。
gets(s,n,stdin);
 fgets(prs.name,10,stdin);// prs是一个数组 name是其中一个变量
s: 字符型指针,指向存储读入数据的缓冲区的地址。
n: 从流中读入n-1个字符
=================================================================
字符写入函数 fputc
int fputc ( int ch, FILE *fp );
ch 为要写入的字符,fp 为文件指针。
fputc() 写入成功时返回写入的字符,失败时返回 EOF,返回值类型为 int 也是为了容纳这个负数。
例如:fputc('a', fp);
或者:char ch = 'a';
          fputc(ch, fp);
例题
#include<stdio.h>
int main(){
    FILE *fp;
    char ch;
    //判断文件是否成功打开
    if( (fp=fopen("D:\\demo.txt","wt+")) == NULL )
    {
        puts("Fail to open file!");
        exit(0);
    }
    printf("Input a string:\n");
    //每次从键盘读取一个字符并写入文件
    while ( (ch=getchar()) != '\n' )
   {
        fputc(ch,fp);
    }
    fclose(fp);
    return 0;
}
while((ch=getchar())!='\n')意思就是输入的字符不是换行符,则为真,执行while里的语句块。
ch=getchar(),输入一个字符且将该值赋予给变量ch;ch=getchar())!='\n'
意思是ch不等于换行符。    ()圆括号的优先级>不等于。
getchar是读入函数的一种。它从标准输入里读取下一个字符,相当于getc(stdin)。返回类型为int型,为用户输入的ASCII码或EOF

==================================================================================
冒泡排序法
是把数字两两比较;
数字大的下沉,数字小的上浮,经过第一轮排序最后取出下沉到最下面的数字,剩下的数字继续比较,如此循环直到最后一个元素;
#include <stdio.h>
#define ARR_LEN 255 //数组长度上限

例题:
//冒泡排序
//从当前元素起,向后依次比较每一对相邻元素,若逆序则交换
//对所有元素均重复以上步骤,直至最后一个元素
// arr[]:排序目标数组;int len:元素个数
void bubbleSort(int arr[],int len)
{
    int temp;
    int i,j;
    for(i=0;i<len-1;i++)//外循环为排序趟数,len个数进行len-1趟
        for(j=0;j<len-1-i;j++)//内循环为每趟比较的次数,第一趟比较len-1次
    {
        if(arr[j]>arr[j+1])   //相邻元素比较,若逆序则交换(升序为左,降序为右)
        {
          temp = arr[j];
          arr[j]=arr[j+1];
          arr[j+1]=temp;
        }
    }
}

int main()
{
    int arr[ARR_LEN] = {3,5,1,-7,4,9,8,10,4,6};
    int len = 10;
    int i;
    bubbleSort(arr,len);
    for(i=0;i<len;i++)
        printf("%d\t",arr[i]);
    putchar('\n');
    return 0;
}
================================================================================================
其他排序方法
1.直接选择排序(简单选择排序):
是把序列中最小的元素比较出来放在序列头部,然后剩下的元素按此方法继续比较选择;

2.直接插入排序
其方法是将序列XXXXXX(X代表元素)看作两部分,用A隔离这两个部分
如XaXXXX,a为已经排好位置的元素,A后为未排好位置的元素,然后依次取出a后的元素与a前元素比较,并放在合适的位置
初始为XaXXXX;之后为XXaXXX;

3.希尔排序(缩小增量排序)//直接插入改进版
方法是取间隔a的数进行比较,这个a称为增量,若前数大于后数,则交换位置,从头到尾完成后;
再按照a/2进行比较,若a为奇数,则按照(a+1)/2计算,知道增量为a=1时,比较完a=1这一趟结束排序

4.快速排序(在冒泡法上进行改进)
序列arr,标记头尾为i ,j,设标记量temp = arr[i] 也就是arr[0]
j开始 --,直到有arr[j]<temp,此时j找到位置了,站在此处不动,i开始动
i开始++,直到有arr[i]>temp,此时i找到位置了,站在此处不动,j再开始动
直到j与i相遇了,哎此时相遇位置的数与temp进行交换
第一次排序结束,相遇位置左侧元素便均小于temp,右方均大于temp
利用这种方法对两侧进行递归操作,直到temp实在无法拆分左右停止,次数的序列便排序完成。


6.堆排序,堆排序是基于堆的概念上设计的排序算法
 堆,一般常用的也就是二叉堆,二叉堆的可以形象的比喻成一颗 完全二叉树。
而二叉堆中分为大顶堆和小顶堆。
   大顶堆是节点处的值大于等于左右孩子,小顶堆是节点处的值小于等于左右孩子。
    一般升序采用大顶堆,降序采用小顶堆
    将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
    说白了,先将序列看作是一个完全二叉树,从最后一个非叶子节点开始,
    (1)将结点及其叶子,进行比较,把最大的换到结点处,这样一层层的把最大的节点拱到了堆顶
    (2)将堆顶元素跟末尾元素进行交换,这样就将最大值放到了序列最后
    (3)此时将剩余数看作完全二叉树进行如此计算,一步一步的将最大值拱出来,剩下一个节点无左右孩子的时候,排序完成。

7.桶排序,又称桶排序
其方法主要是:
(1)从序列中找出最大值N,设置一个数组arr,并使其所有值为0,长度为N+1,其数组下标0--N也就代表了这N+1个桶
(2)将所有数据放入他所匹配的下标桶,并在放入时使得此处arr得值加1表示计数
(3)根据桶的下标取出所存在的元素,若某个桶得值为0,则直接跳过,通过桶得下标排序,通过桶的值判断有多少元素在这个桶里存放
(4)排序结束,出现结果