001-c语言复习

2021-03-05  本文已影响0人  千转军师

时间:2021年3月

0、新增内容

(1)函数strdup
strdup()函数是c语言中常用的一种字符串拷贝库函数,一般和free()函数成对出现。
strdup()在内部调用了malloc()为变量分配内存,不需要使用返回的字符串时,需要用free()释放相应的内存空间,否则会造成内存泄漏。该函数的返回值是返回一个指针,指向为复制字符串分配的空间;如果分配空间失败,则返回NULL值。
(2)函数strchr
在字符串里搜索某个字符
char *strchr(const char *str, int c)
(3)不定参数函数、vfprintf 和 fprintf函数例子(来自githu的netcwmp):

void cwmp_log_write(int level, cwmp_log_t * log, const char * fmt, va_list ap)
{
    cwmp_log_t * logger;

    if (log)
    {
        if (log->level < level )
        {
            return;
        }
        logger = log;
    }
    else
    {
        if (g_cwmp_log_file.level < level)
        {
            return;
        }
        logger = &g_cwmp_log_file;
    }
    logger = g_ot_log_file_ptr;
    vfprintf(logger->file, fmt, ap);
    fprintf(logger->file, "\n");
    fflush(logger->file);
}

(4)C 标准库的 ctype.h 头文件提供了一些函数,可用于测试和映射字符。
(5)snprintf 用法例子:

snprintf(buf, 256, "http://%s:%d", local_ip, port);

(6)关于头文件的引用
“”代表在编译的目录下搜寻
<openssl/ssl.h>表示在 gcc -I <目录> 提供的目录下查找openssl文件夹,然后找到该文件夹下的ssl.h

一、C语言的由来、特点和用途

1.1 由来

1.2 特点

1.3 用途

二、编译

2.1 搭建环境

使用gcc编译器来编译代码。在windows下安装 "MinGW-gcc",并把目录下的bin文件夹添加到windows的环境变量中。“Win+R”组合键,然后输入cmd,回车,就能打开cmd界面。命令“gcc <路径+文件名.c>"即可编译文件,生成可执行文件“a.exe”。

2.2 编译原理

2.2.1 过程

预处理、编译、汇编、连接

2.2.2 原理

【待续】

三、基础

3.1 程序结构

3.2 程序语句中的元素

3.2.1 标识符

3.2.2 运算符

3.2.3 成对的符号

[]:用于数组
{}:代码块或函数
():if语句、函数声明、for循环、while循环等

3.3 数据类型

变量的数据类型决定了变量的存储方式。

3.3.1 整型

类型 字节数 范围
signed char 1 -128~127(也即 -2^7 ~ 2^7-1)
unsigned char 1 0~255 (也即 0 ~ 2^8)
char 1
signed short 2
unsigend short 2
short 2
singed int 4
unsigned int 4
int 4
singned long 4
unsigned long 4
long 4
singed long long 8
unsigned long long 8
long long 8
无标题.png

3.3.2 浮点类型

类型 字节大小 范围 精度
float 4 6位小数
double 8 15位小数
long double 16 18位小数

修饰词:unsigned(带符号的)和signed(不带符号的)

3.3.3 void空类型

3.3.4 类型强转

强转前> 整形 浮点型 指针 结构体 共用体
整形 - 可以 可以(指针的地址值做为整形值) 错误
浮点型 可以 - 错误 错误
指针 可以(把整形值当做地址值) 错误 - 错误
结构体 错误 错误 不可(报错) 不同结构体不能互转(报错)
共用体 错误 错误 错误 错误

3.4 指针

3.4.1 指针的类型

#include <stdio.h>
char fun(int a)
{
  return a;
}
char (*fun_p)(int);

int main(void)
{
    
    fun_p=&fun;
    printf("%d\n", fun_p(10));
    return 0;
}

3.4.2 指针的定义

指向基本变量

//定义的时候,可以是
int *p;
int (*p);
int*p;
int* p;
//但是不能是
(int*) p;

指向一维数组

#include <stdio.h>
int main(void)
{
    char a[10];
    a[1] = 10;
    char *p = NULL;
    p=a;
    printf("%d  %d", p[1], *(p+1));
    return 0;
}

指向二维数组

#include <stdio.h>
int main(void)
{
    char a[10][10];
    a[0][1] = 10;
    char (*p)[10];
    p=a;
    printf("%d  %d", p[0][1], (*p)[1]);
    return 0;
}

指向函数
指向函数数组
注:函数数组指针,指向的对象为多个函数组成的数组。

#include <stdio.h>
int fun1(int a)
{
    return a;
}
int fun2(int a)
{
    return -a;
}
int (*(fun_p[2]))(int);

int main(void)
{
    fun_p[0] = fun1;
    fun_p[1] = fun2;
    printf("%d  %d  %d  %d", fun_p[0](10), fun_p[1](10), (*fun_p)(10), (*(fun_p+ 1))(10));
    return 0;
}

指向数组函数
返回值为数组的函数?没有这样的而函数。但是可以返回一位数组的指针来实现功能。

3.4.3 指针数值

为地址值,一般为4个字节,得到指针值的方法

printf("%p", p);

3.4.4 指针的大小

参考:https://www.cnblogs.com/noble/p/4144167.html

3.5 数组

一组相同数据类型的变量的集合。

3.5.1 定义

使用 [] 符号,句法
<数据类型> <标识符>[<不小于0的整形数字>];
例子:

int var[10];

3.6 结构体、共用体和枚举

3.6.1 结构体定义

#include <stdio.h>
//定义
typedef struct student{
    char *name;
    int num;
    double score;
}type_student;
//赋值
type_student st1={
    "xiaoming",
    53,
    80.2
};
//赋值2
type_student st2={
    .name = "xiaohong",
    .num = 20,
    .score = 91.2
};
int main(void)
{
    printf("%s  %d  %f\n", st1.name, st1.num, st1.score);
    printf("%s  %d  %f", st2.name, st2.num, st2.score);
    return 0;
}

3.6.2 共用体定义

#include <stdio.h>
//定义
union role
{
    int student_score;
    double teacher_salar;
};
typedef struct member{
    char *name;
    int age;
    union role r;
}type_member;
//赋值
type_member t1={
    "t1",
    29,
    .r.teacher_salar = 3400.9
};
type_member s1={
    "s1",
    17,
    .r.student_score = 80
};
int main(void)
{
    printf("%s  %d  %f\n", t1.name, t1.age, t1.r.teacher_salar);
    printf("%s  %d  %d", s1.name, s1.age, s1.r.student_score);
    return 0;
}

3.6.3 结构体位域

格式

//在结构体定义里面
type [member_name] : width ;

type 可以是int、unsigned int、signed int、char、unsigned char、signed char,不能是float、double等数据类型。

例子:

#include <stdio.h>
struct struct_t{
    int a:1;
    int c:10;
    int b;
};
int main(void)
{
    printf("%d", sizeof(struct struct_t));
    return 0;
}

3.6.4 结构体、共用体内存占用计算

#include <stdio.h>
int main(void)
{
    //数据类型对应占用的字节数
    printf("char\t%d\n", sizeof(char)); 
    printf("int\t%d\n", sizeof(int));
    printf("float\t%d\n", sizeof(float));
    printf("double\t%d\n", sizeof(double));
    int a;
    int b[3];
    char c;
    char *p;
    p = &c;
    //某个变量占用内存大小
    printf("a\t%d\n", sizeof(a));   
    //数组变量占用内存大小
    printf("b\t%d\n", sizeof(b));   
    //指针指向的内存大小
    printf("*p\t%d\n", sizeof(*p)); 
    //指针的大小
    printf("p\t%d\n", sizeof(p));   
    return 0;
}

结果

char    1
int     4
float   4
double  8
a       4
b       12
*p      1
p       4

共用体内存占用字节规则
若共用体某成员的占用字节数最大,那么这个字节数就是共用体的字节数。

#include <stdio.h>
union union_t{
    char a;
    int b;
    double c;   
};

int main(void)
{
    printf("%d\n", sizeof(union union_t));
    return 0;
}

结构体内存占用字节规则
(1)首先确定对齐字节:

(2)把对齐字节看做梯子的一个个等距横档,当成员累加到n时才超过横档,那么则把n放在这个横档作为起步,继续累加。
(3)例子:结构体有且其仅有成员a(2),b(2),c(4),d(8)这成员,后面的数字代表成员的对齐字节(成员也可能是嵌套的结构体),如果排序为abcd(即 2 + 2 + 4 + 8),那么计算过程为:

如果排序为adbc(即 2 + 8 + 2 + 4),那么计算过程为:

#include <stdio.h>
struct struct_t1{
    char a;
    char b;
    int c;
    double d;
};
struct struct_t2{
    char a;
    double d;
    char b;
    int c;
};
int main(void)
{
    printf("%d    %d\n", sizeof(struct struct_t1), sizeof(struct struct_t2));
    return 0;
}

3.6.5 结构体和共用体的互相嵌套

结论:两者可以互相嵌套

#include <stdio.h>
struct struct_t1{
    char a;
    char b;
    int c;
    double d;
};
struct struct_t2{
    char a;
    double d;
    char b;
    int c;
};
union union_t{
    struct struct_t1 t1;
    struct struct_t2 t2;
};
struct struct_t3{
    char a;
    union union_t b;
    char c;
};
int main(void)
{
    printf("%d\t%d\t%d\t%d\n", sizeof(struct struct_t1), 
    sizeof(struct struct_t2), sizeof(union union_t),
    sizeof(struct struct_t3));
    return 0;
}
//打印: 16      24      24      40

3.7 函数

//函数定义
return_type function_name( parameter list )
{
   body of the function
}
//函数声明
return_type function_name( parameter list );
//函数调用
function_name( parameter value )

3.7.1 传参特点

3.7.2 函数递归

例子1:递归实现数字排列组合(多层循环)

//递归实现数字排列组合
#include <stdio.h>
#define recourse_n 3
int para_list_t[] = {5, 4, 3};
int record[recourse_n];
int fun(int n, int *para_list)
{
    int i;
    if(n == 0)
    {
        for(i = 0; i < recourse_n; i ++)
        {
            if(i != 0)
            {
                printf("-");
            }
            printf("%d", record[i]);
        }       
        printf("\n");
        return 0;
    }
    else 
    {
        int ret;
        for(i = 0; i < *para_list; i++)
        {
            record[recourse_n - n] = *para_list - i;            
            ret = fun(n - 1, para_list + 1);
            if(ret == -1)
            {
                return -1;
            }
        }
    }
    return 0;
}

int main(void)
{
    fun(recourse_n, para_list_t);
    return 0;
}

3.7.3 回调函数

把另一个函数fun_other()的指针当做本函数fun()的参数,函数fun_other()当做回调函数。
作用:可以根据情况更改函数的接口。

3.8 流程控制语句

流程语句包括判断、选择、循环、跳转,相应的语句有 if、switch、for、do-while、goto语句

四、基础2

4.1 代码注释风格

4.2 关键字

(32个)

关键字 说明 关键字 说明
auto 声明自动变量 break 跳出当前循环
case 开关语句分支 char 声明字符型变量或函数返回值类型
const 定义常量,如果一个变量被 const 修饰,那么它的值就不能再被改变 continue 结束当前循环,开始下一轮循环
default 开关语句中的"其它"分支 do 循环语句的循环体
double 声明双精度浮点型变量或函数返回值类型 else 条件语句否定分支(与 if 连用)
enum 声明枚举类型 extern 声明变量或函数是在其它文件或本文件的其他位置定义
float 声明浮点型变量或函数返回值类型 for 一种循环语句
goto 无条件跳转语句 if 条件语句
int 声明整型变量或函数 long 声明长整型变量或函数返回值类型
register 声明寄存器变量 return 子程序返回语句(可以带参数,也可不带参数)
short 声明短整型变量或函数 signed 声明有符号类型变量或函数
sizeof 计算数据类型或变量长度(即所占字节数) static 声明静态变量
struct 声明结构体类型 switch 用于开关语句
typedef 用以给数据类型取别名 unsigned 声明无符号类型变量或函数
union 声明共用体类型 void 声明函数无返回值或无参数,声明无类型指针
volatile 说明变量在程序执行中可被隐含地改变 while 循环语句的循环条件

C99和C11有新增的关键词。

4.2.1 个别关键字讲解

4.2.2 static和const使用

#include <stdio.h>
int main(void)
{   
    const int b1;
    int const b2;
    static int b3;
    int static b4;
    int const static c1;
    int static const c2;
    const int static c3;
    const static int c4;
    static int const c5;
    static const int c6;
    return 0;
}

4.3 变量

4.4 常量

4.4.1 整形常量

4.4 宏

参考: https://blog.csdn.net/qq997843911/article/details/62042697

#define PRINT(x) printf("%d", x);

用于打印参数值;

#include <stdio.h>
#define COMMOND(x) {#x, x ## _commond}

int local_var = 0;
int print_commond(int num, void *para_list)
{
    printf("local_var=%d", local_var);
    return 0;
}
int set_commond(int num, void *para_list)
{
    local_var = *(int *)para_list;
}
struct struct_t{
    char *name;
    int (*fun)(int, void *);
};
struct struct_t cmd[]={
    COMMOND(set),
    COMMOND(print)
};
int main(void)
{
    int para = 100;
    int tmp;
    cmd[0].fun(0, (void *)(&para));
    cmd[1].fun(0, &tmp);
    return 0;
}

4.6 内存管理

参考:https://blog.csdn.net/u014630142/article/details/82428028

4.6.1 程序对内存的使用

分区 描述
堆区(stack) 动态内存使用(管道:先进先出)
栈区(heap) 对自动变量、函数形参的操作(杯子:先进后出)
静态存储区(static area) 静态变量、全局变量存储区域
代码区(code area) 存放可执行代码(程序代码指令、常量字符串等)

4.6.2 栈区的使用

4.6.2 堆区的使用(动态内存分配)

int *p = (int *)malloc(8);  //分配8个字节的动态内存,并把地址赋给指针p

五、拓展

5.1 c标准库列表

参考:https://www.runoob.com/cprogramming/c-standard-library.html

5.2 文件IO(在文件stdio.h中)

参考“linux基础学习3”:https://www.jianshu.com/p/82f2bf75c49c

FILE *fopen(const char *path, const char *mode);    //打开文件
int fclose(FILE *fp);   //关闭文件
int fgetc(FILE *stream);    //字符写入
int fputc(int c, FILE *stream); //字符读取
char *fgets(char *s, int size, FILE *stream);   //按行读取
int fputs(const char *s, FILE *stream);     //按行写入
char *fgets(char *s, int size, FILE *stream);   //按行写入

5.2.1 文件夹

示例

#include <stdio.h>
#include "dirent.h"  
#define  FilePath "./"
int main()  
{     
int i = 0;
int filesize = 0;  
    DIR *dir = NULL;  
    struct dirent *entry;  
  
    if((dir = opendir(FilePath))==NULL)  
    {  
        printf("opendir failed!");  
        return -1;  
    }
else  
    {  
         while(entry=readdir(dir))  
        {  
            i++;
            printf("filename%d = %s",i,entry->d_name);  //输出文件或者目录的名称
            printf("\tfiletype = %d\n",entry->d_type);  //输出文件类型   
        }  
  
         closedir(dir);    
    }  
    return 0;    
}  

5.3 字符串处理系列(string.h)

void *memchr(const void *str, int c, size_t n)

5.4 常用算法

5.4.1 几个排序的方法

(1)冒泡法
那一个数两两比较其他所有未进行冒泡的数,根据情况来决定是否交换位置。
复杂度来看,n个数的排序有 0.5n^2 次比较,以及 0~0.5n^2 次范围的交换动作。
例子:

//冒泡排序从小到大
#include <stdio.h>
int num_list[]={
    1,7,8,70,56,32,77
};
#define member_n 7
void show(void)
{
    int i;
    for(i = 0; i < member_n; i ++)
    {
        printf("%d ", num_list[i]);
    }
    printf("\n");
}
int main()  
{     
    int i,j;
    show();
    for(i = 0; i < member_n - 1; i++)
    {
        for(j = i + 1; j < member_n; j++)
        {
            if(num_list[i] > num_list[j])
            {
                num_list[i] ^=  num_list[j];
                num_list[j] ^=  num_list[i];
                num_list[i] ^=  num_list[j];
            }   
        }
    }
    show();
    return 0;    
}  

(2)选择法
选一个数,与所有数比较,然后得出最小的那位数的索引,与序号为0的数交换,当做第一个数;然后按同样的方法取得其他数的最小数,与序号为1的数交换;以此类推。
复杂度来看,n个数有 0.5*n^2 次比较, 以及0~n次范围的数字交换。
例子:

//选择排序,从小到大
#include <stdio.h>
int num_list[]={
    1,7,8,70,56,32,77,100,4,32
};
#define member_n 10
void show(void)
{
    int i;
    for(i = 0; i < member_n; i ++)
    {
        printf("%d ", num_list[i]);
    }
    printf("\n");
}
//选出最小的数,得到它的序号,然后和前面的未排序的数
//进行交换
int main()  
{     
    int i,j;
    int min_idx;
    show();
    for(i = 0; i < member_n - 1; i++)
    {
        min_idx = i;
        for(j = i + 1; j < member_n; j++)
        {
            if(num_list[min_idx] > num_list[j])
            {
                min_idx = j;
            }   
        }
        if(i != min_idx)
        {
            num_list[i] ^= num_list[min_idx];
            num_list[min_idx] ^= num_list[i];
            num_list[i] ^= num_list[min_idx];
        }
        
    }
    show();
    return 0;    
}  

(3)插入排序
先把部分排序好,后面的数一个个插入到合适的位置。
判断次数和交换次数一样,n个数排列的话,在 n-1 ~ 0.5*n^2 之间

//插入排序,从小到大
#include <stdio.h>
int num_list[]={
    132,7,8,70,56,32,77,100,4,32
};
#define member_n 10
void show(void)
{
    int i;
    for(i = 0; i < member_n; i ++)
    {
        printf("%d ", num_list[i]);
    }
    printf("\n");
}
int main()  
{     
    int i,j;
    show();
    for(i = 0; i < member_n - 1; i++)
    {
        for(j = i + 1; num_list[j] < num_list[j - 1]; j--)
        {
            if(j == 0)break;
            num_list[j] ^= num_list[j - 1];
            num_list[j - 1] ^= num_list[j];
            num_list[j] ^= num_list[j - 1];
        }       
    }
    show();
    return 0;    
}  

(4)希尔排序
插入排序的改进版。
刚开始从 1/2 处序号开始插入排序,依次为 1/4 、1/8 等序号处插入排序,直到开始的序号为0

//参考菜鸟教程
void shell_sort(int arr[], int len) {
    int gap, i, j;
    int temp;
    for (gap = len >> 1; gap > 0; gap = gap >> 1)
        for (i = gap; i < len; i++) {
            temp = arr[i];
            for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
                arr[j + gap] = arr[j];
            arr[j + gap] = temp;
        }
}

(5)(6)参考: https://www.runoob.com/cprogramming/c-sort-algorithm.html
(5)归并排序

(6)快速排序
在区间中随机挑选一个元素作基准,将小于基准的元素放在基准之前,大于基准的元素放在基准之后,再分别对小数区与大数区进行排序。

5.5 链表

参见 https://git.lug.ustc.edu.cn/bigbang/code/-/tree/master/%E5%B7%A5%E7%A8%8B/%E9%93%BE%E8%A1%A8

包括 单向非循环链表、单向循环链表、双向非循环链表、双向循环链表

六、应用例子

6.1 字母排列组合

//遍历26个字母的排类组合
/*
例如26个字母,任选5个字母,进行排列组合,结果有26^5种可能(字母可以
重复)。那么遍历数字0到26^5 - 1,把数字转换为26进制数,显然5位数即可
表示完全,每个位置上的数,组成一个序列,例如
100=0*26^4+0*26^3+0*26^2+3*26^1+22*26^0,换言之是0、0、0、3、22,
于是对应字母序号得出aaadw
*/
#include <stdio.h>
char letter[]={
    'a','b','c','d','e','f','g','h','i','j','k','l','m',
    'n','o','p','q','r','s','t','u','v','w','x','y','z'
};

int mypow(int a, int b)
{
    int ret = 1;
    int i;
    for(i = 0; i < b; i++)
    {
        ret *= a;   
    }
    return ret;
}
int getQuenue(int num, int basic, int shift_num, int *result)
{
    int i;
    for(i = 0; i <= shift_num - 1; i++)
    {
        *(result + i) = (num % mypow(basic, shift_num - i)) / mypow(basic, shift_num - i - 1);
    }
    return 0;
}
int main(void)
{   
    int i, j;
    int basic = 26, shift_num = 5;
    int result_t[shift_num];
    int cnt;
    cnt = 0;
    for(i = 0; i < mypow(basic, shift_num); i++)
    {
        getQuenue(i, basic, shift_num, result_t);
        for(j = 0; j < shift_num; j++)
        {
            printf("%c", letter[result_t[j]]);          
        }
        printf("  ");
        cnt++;
        if(cnt >= 15)
        {
            cnt = 0;
            printf("\n");   
        }       
    }   
    return 0;
}
上一篇下一篇

猜你喜欢

热点阅读