linux下的
是一种通用的、面向过程式的计算机编程语言
#include <stdio.h>        //#include 预处理命令,用来引用头文件, stdio.h 头文件 
int main()               //开始
{
    /* 一个注释 */
    printf("Hello, World! \n");
    return 0;           //返回
}
 
- 安装到vm中
 
-  
链接Xshell
 -  
安装gcc
 -  
测试
 -  
运行源码文件在linux中运行
$ gcc test1.c test2.c -o main.out //多个 $ ./main.out $ gcc hello.c //单个.c文件 
基本语法
-  
C的令牌(Token) 关键字、标识符、常量、字符串值,或者是一个符号
 -  
分号结尾
 -  
注释 // /* */
 -  
标识符
 -  
关键字
 
数据类型
基本类型
- 整数类型
 

- 浮点(1字节=8bit)

 
枚举类型
算术类型,定义在程序中只能赋予一定的离散整数值的变量
void类型
没有可用的值。
- 函数返回为空 void exit (int status);
 - 函数参数为空 int rand(void);
 - 指针指向void void *malloc( size_t size )
 
派生类型
指针、数组、结构、共用体、函数
变量
定义 type variable ; type variable=value;
变量声明
- int a 声明,也是定义 需要建立存储空间
 - extern int a 声明,不是定义 不需要建立存储空间,通过extern声明变量名而不定义它
 
左值/右值
- 左值lvalue :可以出现在赋值号 左边或右边 int a=0;
 - 右值rvalue :术语右值,存储在内存中某些地址的数值。只能出现在右边 10=20(报错)
 
常量
0 八进制
0x 十六进制
后缀U 无符号
后缀L 长整数
浮点常量 整数部分、小数点、小数部分和指数部分组成:使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e 或 E 引入的
定义常量
- #definde 预处理器 #define LENGTH 10 //之后LENGTH的值为定值10
 - const int var = 5; //不可以缺项之后LENGTH的值在函数内为定值10
 
存储类
- auto 默认存储类 修饰局部变量
 - register 存储类 在寄存器中不是在RAM的局部变量,(一个寄存器大小)
 - static 存储类 
  
- 在函数调用之间保持局部变量的值,每次调用该函数时,只初始化一次
 - 用于全局变量,作用域在声明它的文件内
 
 
static int a=10;//a是全局变量可直接调用
int main(){
   while(a--){
    fun();}
    return 0;
}
void fun(void){
    static int b=5;//局部变量,值初始化一次为5,后面不会再重置
    b++;
    print("%d,%d",a,b);//%输出""内的内容,
}
9,6
8,7
7,8
    .....
 
- extern 存储类 全局变量,对所有程序文件都可见, 
  
- 对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。
 - extern 是用来在另一个文件中声明一个全局变量或函数。
 
 

输出 count is 5
运算符
-  
逻辑运算符[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E10AOFNF-1676199814932)(D:\1ar\c笔记\逻辑运算符.png)]
 -  
位运算符
 

- 杂项
 

判断
-  
if else
 -  
switch
switch(expression){ case constant-expression : statement(s); break; /* 可选的 */ case constant-expression : statement(s); break; /* 可选的 */ /* 您可以有任意数量的 case 语句 */ default : /* 可选的 */ statement(s); } -  
A ? B : C //A真为B A假为C
 
循环
- while
 - for
 - do while
 - break //跳出循环
 - continue //告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。
 - goto //将控制转移到被标记的语句。 少用
 
函数
- 必有一个main()函数
 - 定义一个函数 return_type function_name( parameter list ); 
  
- 例:int max(int num1, int num2);
 
 
作用域
- 在函数或块内部的局部变量 局部和全局同名时用局部保存在栈
 - 在所有函数外部的全局变量 保存在静态存储单元
 - 在形式参数的函数参数定义中
 
数组
type arrayName [ arraySize ];//   int a[10];//10个int型数字
 
- 赋值 类似java
 
enum(枚举)
enum 枚举名 {枚举元素1,枚举元素2,……};//
 
-  
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN }; //第一个默认为整型0,但此赋值为1,后面依次加一 即 TUE=2... 
定义方式
可以与for一起用
- 先定义枚举类型,再定义枚举变量
 
enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
};//可以在函数内也可以在外
enum DAY day;//在函数内
 
- 定义枚举类型同时定义枚举变量
 
enum DAY
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
 
- 省略枚举名称,直接定义枚举变量
 
enum
{
      MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
 
-  
转换方式
enum day weekend; weekend = ( enum day ) a; //类型转换 
指针
type *var_name;
int *p;//定义指针变量
 
定义一个变量 var=10(会分配一个地址)
定义一个指针 p
指针访问var的地址,于是p指向了变量的地址(&访问地址符号)

- 指针常用操作 
  
- 定义一个指针变量
 - 把变量地址赋值给指针
 - 访问指针变量中可用地址的值
 
 
int main ()
{
   int  var = 20;   /* 实际变量的声明 */
   int  *ip;        /* 指针变量的声明 */
 
   ip = &var;  /* 在指针变量中存储 var 的地址 */
 
   printf("var 变量的地址: %p\n", &var  );//var的分配地址
 
   /* 在指针变量中存储的地址 */
   printf("ip 变量存储的地址: %p\n", ip );//var的分配地址
 
   /* 使用指针访问值 */
   printf("*ip 变量的值: %d\n", *ip );//var的值 20
 
   return 0;
}
 
- 在变量声明时,没有确切的地址,可以给指针赋值null
 - 菜鸟教程
 
| 概念 | 描述 | 
|---|---|
| 指针的算术运算 | 可以对指针进行四种算术运算:++、–、+、- | 
| 指针数组 | 可以定义用来存储指针的数组。 | 
| 指向指针的指针 | C 允许指向指针的指针。 | 
| 传递指针给函数 | 通过引用或地址传递参数,使传递的参数在调用函数中被改变。 | 
| 从函数返回指针 | C 允许函数返回指针到局部变量、静态变量和动态内存分配。 | 
指针的算术运算
#include <stdio.h>
 
const int MAX = 3;
 
int main ()
{
   int  var[] = {10, 100, 200};
   int  i, *ptr;
 
   /* 指针中的数组地址 */
   ptr = var;
   for ( i = 0; i < MAX; i++)
   {
 
      printf("存储地址:var[%d] = %p\n", i, ptr );
      printf("存储值:var[%d] = %d\n", i, *ptr );
 
      /* 指向下一个位置 */
      ptr++;
   }
   return 0;
}
存储地址:var[0] = e4a298cc
存储值:var[0] = 10
存储地址:var[1] = e4a298d0          //int   4位所以+4
存储值:var[1] = 100
存储地址:var[2] = e4a298d4
存储值:var[2] = 200
 
指针数组
int  var[] = {10, 100, 200};
int *ptr[MAX];
ptr[i] = &var[i]; /* 赋值为整数的地址 */
printf("var[%d] = %d\n", i, *ptr[i] );//*ptr[i]==*&var[i]==var[i]
var[0]=10
var[1]=100
var[2]=200
 
指向指针的指针
int **var;
 


#include <stdio.h>
 
int main ()
{
   int  V;
   int  *Pt1;
   int  **Pt2;
   V = 100;
   /* 获取 V 的地址 */
   Pt1 = &V;
   /* 使用运算符 & 获取 Pt1 的地址 */
   Pt2 = &Pt1;
   /* 使用 pptr 获取值 */
   printf("var = %d\n", V );      //V的值
   printf("Pt1 = %p\n", Pt1 );    //V的地址
   printf("*Pt1 = %d\n", *Pt1 );  //pt1指针指向的值
    printf("Pt2 = %p\n", Pt2 );   //指针pt1的地址
   printf("**Pt2 = %d\n", **Pt2); //pt2指向pt1的指针  pt1指针指向的值
 
   return 0;
}
var = 100
Pt1 = 0x7ffee2d5e8d8
*Pt1 = 100
Pt2 = 0x7ffee2d5e8d0     //一个int型值4位 + 一个int型指针 4位
**Pt2 = 100
 
传递指针给函数
C 传递指针给函数 | 菜鸟教程 (runoob.com)
从函数返回指针
C 从函数返回指针 | 菜鸟教程 (runoob.com)
函数指针
typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型
 
一个指向函数的指针
#include <stdio.h>
 
int max(int x, int y)
{
    return x > y ? x : y;
}
 
int main(void)
{
    /* p 是函数指针 */
    int (* p)(int, int) = & max; // &可以省略   p指向max这个函数存储的位置
    int a, b, c, d;
    printf("请输入三个数字:");
    scanf("%d %d %d", & a, & b, & c);
    d = p(p(a, b), c); // /* 与直接调用函数等价,d = max(max(a, b), c) */
    printf("最大的数字是: %d\n", d);
    return 0;
}
 
回调函数
函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。
简单讲:回调函数是由别人的函数执行时调用你实现的函数。
#include <stdlib.h>  
#include <stdio.h>
 
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
    for (size_t i=0; i<arraySize; i++)
        array[i] = getNextValue();
}
 
// 获取随机值
int getNextRandomValue(void)
{
    return rand();
}
 
int main(void)
{
    int myarray[10];
    /* getNextRandomValue 不能加括号,否则无法编译,因为加上括号之后相当于传入此参数时传入了 int , 而不是函数指针*/
    populate_array(myarray, 10, getNextRandomValue);//此处getNextRandomValue作为int (*getNextValue)(void)形式的参数
    for(int i = 0; i < 10; i++) {
        printf("%d ", myarray[i]);
    }
    printf("\n");
    return 0;
}
 
字符串
在 C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。
 
结构体
一种用户自定义的可用的数据类型
C 结构体 | 菜鸟教程 (runoob.com)
共用体
在相同的内存位置存储不同的数据类型。以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
//union结构体
union Data
{
   int i;
   float f;
   char  str[20];
};//所占空间大小为,类型中占位最大的
调用
    union Date data;
    data.i=20;
//如果同时调用data.f   ,data.i会损坏
 
位域
struct packed_struct {
  unsigned int f1:1;//4位现只需要1位
  unsigned int   :1;//空域,占位,该1位不能使用
  unsigned int f3:1;//从下一个单元开始存放
  unsigned int f4:1;
  unsigned int type:4;
  unsigned int my_int:9;
} pack;
调用
位域变量名.位域名      // 给位域赋值(应注意赋值不能超过该位域的允许范围)
位域变量名->位域名
 	pbit=&bit;    /* 把位域变量 bit 的地址送给指针变量 pbit */
    pbit->a=0;    /* 用指针方式给位域 a 重新赋值,赋为 0 */
    pbit->b&=3;    /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */
    pbit->c|=1;    /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */
 
typedef
用来为类型取一个新名字
typedef struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} Book;
/*类型位Book*/
 
对比typedef #define
- #define是C指令,为个数据类型定义别名
 - #define不仅可以为类型定义别名,也能为数值定义别名,typedef只能为类型定义符号名称
 - typedef由编译器执行解释,#define由预编译器处理
 
i/o
C 语言中的 I/O (输入/输出) 通常使用 printf() 和 scanf() 两个函数。

%d整型 **f%**浮点型
getchar() putchar()
-  
int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。可以在循环内使用这个方法,从屏幕上读取多个字符。
 -  
int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。可以在循环内使用这个方法,以便在屏幕上输出多个字符。
 -  
#include <stdio.h> int main( ) { char str[100]; printf( "Enter a value :"); gets( str ); printf( "\nYou entered: "); puts( str ); return 0; } 

scanf() print()
-  
int scanf(const char *format, …) 函数从标准输入流 stdin 读取输入,并根据提供的 format 来浏览输入。
 -  
int printf(const char *format, …) 函数把输出写入到标准输出流 stdout ,并根据提供的格式产生输出。
 -  
format 可以是一个简单的常量字符串,但是您可以分别指定 %s、%d、%c、%f 等来输出或读取字符串、整数、字符或浮点数。
 
#include <stdio.h>
int main( ) {
 
   char str[100];
   int i;
 
   printf( "Enter a value :");
   scanf("%s %d", str, &i);
 
   printf( "\nYou entered: %s %d ", str, i);
   printf("\n");
   return 0;
}
 

 
32765 我不知道是不是5个backspce的原因
文件读写
/*在Linux系统中*/
#include <stdio.h>
 
int main()
{
   FILE *fp = NULL;
 
   fp = fopen("/tmp/test.txt", "w+");
   fprintf(fp, "This is testing for fprintf...\n");
   fputs("This is testing for fputs...\n", fp);
   fclose(fp);
}
 

预处理器
C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。(cpp)


参数化的宏
可以使用参数化的宏来模拟函数
int square(int x) {
   return x * x;
}
/*可用#define square(x) ((x) * (x))写上面的代码*/
#include <stdio.h>
 
#define MAX(x,y) ((x) > (y) ? (x) : (y))
 
int main(void)
{
   printf("Max between 20 and 10 is %d\n", MAX(10, 20));  
   return 0;
}
 
头文件.h
包含了 C 函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。需要使用 C 预处理指令 #include 来引用它。
int x;
#include "header.h"/*引用语法时要引用头文件*/
int main (void)
{
   puts (test ());//引用头文件中的操作
}
 
/*从多个不同的头文件中选择一个引用到程序中if elif*/
#if SYSTEM_1
   # include "system_1.h"
#elif SYSTEM_2
   # include "system_2.h"
#elif SYSTEM_3
   ...
#endif
 
强制类型转换
(type_name) expression
 
强制类型转换运算符的优先级大于除法,

错误处理
errno 、perror()、strerror()
- perror() 函数显示您传给它的字符串,后跟一个冒号、一个空格和当前 errno 值的文本表示形式。
 - strerror() 函数,返回一个指针,指针指向当前 errno 值的文本表示形式。
 
递归(参考数据结构)
可变参数
int func(int, ... ) 
{
   .
   .
   .
}
 
int main()
{
   func(2, 2, 3);
   func(3, 2, 3, 4);
}
 
请注意,函数 func() 最后一个参数写成省略号,即三个点号(…),省略号之前的那个参数是 int,代表了要传递的可变参数的总数。为了使用这个功能,您需要使用 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。具体步骤如下:
- 定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。
 - 在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
 - 使用 int 参数和 va_start 宏来初始化 va_list 变量为一个参数列表。宏 va_start 是在 stdarg.h 头文件中定义的。
 - 使用 va_arg 宏和 va_list 变量来访问参数列表中的每个项。
 - 使用宏 va_end 来清理赋予 va_list 变量的内存。
 
内存管理

动态分配内存
定义一个指针,指向未定义所需内存大小的字符,再根据需求来分配内存
 description = (char *)malloc( 200 * sizeof(char) );
/*通过调用函数 realloc() 来增加或减少已分配的内存块的大小*/
/* 使用 free() 函数释放内存 */
   free(description);
 
命令行参数
都可
int main( int argc, char *argv[] )
int main( int test_argc, char *test_argv[] )  
 
排序算法(数据结构)
冒泡
#include <stdio.h>
void bubble_sort(int arr[], int len) {
    int i, j, temp;
    for (i = 0; i < len - 1; i++)
        for (j = 0; j < len - 1 - i; j++)
            if (arr[j] > arr[j + 1]) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
}
int main() {
    int arr[] = { 22, 34, 3, 32, 82, 55, 89, 50, 37, 5, 64, 35, 9, 70 };
    int len = (int) sizeof(arr) / sizeof(*arr);
    bubble_sort(arr, len);
    int i;
    for (i = 0; i < len; i++)
        printf("%d ", arr[i]);
    return 0;
}
 
选择排序
void selection_sort(int a[], int len) 
{
    int i,j,temp;
 
    for (i = 0 ; i < len - 1 ; i++) 
    {
        int min = i;                  // 记录最小值,第一个元素默认最小
        for (j = i + 1; j < len; j++)     // 访问未排序的元素
        {
            if (a[j] < a[min])    // 找到目前最小值
            {
                min = j;    // 记录最小值
            }
        }
        if(min != i)
        {
            temp=a[min];  // 交换两个变量
            a[min]=a[i];
            a[i]=temp;
        }
        /* swap(&a[min], &a[i]);  */   // 使用自定义函数交換
    }
}
 
/*
void swap(int *a,int *b) // 交换两个变量
{
    int temp = *a;
    *a = *b;
    *b = temp;
}
*/
 
插入排序
void insertion_sort(int arr[], int len){
    int i,j,temp;
    for (i=1;i<len;i++){
            temp = arr[i];
            for (j=i;j>0 && arr[j-1]>temp;j--)
                    arr[j] = arr[j-1];
            arr[j] = temp;
    }
}
 
希尔排序
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;
        }
}
 
归并排序
- 迭代法
 
int min(int x, int y) {
    return x < y ? x : y;
}
void merge_sort(int arr[], int len) {
    int* a = arr;
    int* b = (int*) malloc(len * sizeof(int));
    int seg, start;
    for (seg = 1; seg < len; seg += seg) {
        for (start = 0; start < len; start += seg + seg) {
            int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len);
            int k = low;
            int start1 = low, end1 = mid;
            int start2 = mid, end2 = high;
            while (start1 < end1 && start2 < end2)
                b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
            while (start1 < end1)
                b[k++] = a[start1++];
            while (start2 < end2)
                b[k++] = a[start2++];
        }
        int* temp = a;
        a = b;
        b = temp;
    }
    if (a != arr) {
        int i;
        for (i = 0; i < len; i++)
            b[i] = a[i];
        b = a;
    }
    free(b);
}
 
- 递归法
 
void merge_sort_recursive(int arr[], int reg[], int start, int end) {
    if (start >= end)
        return;
    int len = end - start, mid = (len >> 1) + start;
    int start1 = start, end1 = mid;
    int start2 = mid + 1, end2 = end;
    merge_sort_recursive(arr, reg, start1, end1);
    merge_sort_recursive(arr, reg, start2, end2);
    int k = start;
    while (start1 <= end1 && start2 <= end2)
        reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
    while (start1 <= end1)
        reg[k++] = arr[start1++];
    while (start2 <= end2)
        reg[k++] = arr[start2++];
    for (k = start; k <= end; k++)
        arr[k] = reg[k];
}
void merge_sort(int arr[], const int len) {
    int reg[len];
    merge_sort_recursive(arr, reg, 0, len - 1);
}
 
快速排序
- 迭代法
 
typedef struct _Range {
    int start, end;
} Range;
Range new_Range(int s, int e) {
    Range r;
    r.start = s;
    r.end = e;
    return r;
}
void swap(int *x, int *y) {
    int t = *x;
    *x = *y;
    *y = t;
}
void quick_sort(int arr[], const int len) {
    if (len <= 0)
        return; // 避免len等於負值時引發段錯誤(Segment Fault)
    // r[]模擬列表,p為數量,r[p++]為push,r[--p]為pop且取得元素
    Range r[len];
    int p = 0;
    r[p++] = new_Range(0, len - 1);
    while (p) {
        Range range = r[--p];
        if (range.start >= range.end)
            continue;
        int mid = arr[(range.start + range.end) / 2]; // 選取中間點為基準點
        int left = range.start, right = range.end;
        do
        {
            while (arr[left] < mid) ++left;   // 檢測基準點左側是否符合要求
            while (arr[right] > mid) --right; //檢測基準點右側是否符合要求
 
            if (left <= right)
            {
                swap(&arr[left],&arr[right]);
                left++;right--;               // 移動指針以繼續
            }
        } while (left <= right);
 
        if (range.start < right) r[p++] = new_Range(range.start, right);
        if (range.end > left) r[p++] = new_Range(left, range.end);
    }
}
 
- 递归法
 
void swap(int *x, int *y) {
    int t = *x;
    *x = *y;
    *y = t;
}
void quick_sort_recursive(int arr[], int start, int end) {
    if (start >= end)
        return;
    int mid = arr[end];
    int left = start, right = end - 1;
    while (left < right) {
        while (arr[left] < mid && left < right)
            left++;
        while (arr[right] >= mid && left < right)
            right--;
        swap(&arr[left], &arr[right]);
    }
    if (arr[left] >= arr[end])
        swap(&arr[left], &arr[end]);
    else
        left++;
    if (left)
        quick_sort_recursive(arr, start, left - 1);
    quick_sort_recursive(arr, left + 1, end);
}
void quick_sort(int arr[], int len) {
    quick_sort_recursive(arr, 0, len - 1);
}
 
C 语言实例 | 菜鸟教程 (runoob.com)
C 语言经典100例 | 菜鸟教程 (runoob.com)



















