闲碎记事本 闲碎记事本
首页
  • JAVA
  • Cloudflare
  • 学完再改一遍UI
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

YAN

我要偷偷记录...
首页
  • JAVA
  • Cloudflare
  • 学完再改一遍UI
友链
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • python

  • vue

  • H5

  • C

    • C语言环境搭建
    • C语言
      • hello word
        • 输入/输出
        • 输出
        • 输入
        • 示例
        • 数据类型
        • 关键字
        • static
        • 不使用static
        • static 修饰局部变量
        • static 修饰全局变量
        • typedef
        • register
        • 表达式
        • sizeof
        • ,
        • ~
        • 变量
        • 全局变量
        • 局部变量
        • 常量
        • const
        • define 标识符常量
        • 枚举常量
        • 字符串
        • 数组
        • 选择结构 IF
        • 循环结构 While
        • 指针
        • 指针的声明
        • 指针的使用
        • 指针的类型
        • 指针的运算
        • 多级指针
        • 指针的注意事项
        • struct
        • 位域
        • union
        • 库
        • string.h
        • strlen
        • 分支和循环预计
        • 分支语句
        • if
        • switch
        • 循环语句
        • while
        • do-while
        • for
        • 函数
        • 函数调用
        • 函数声明
        • 头文件
        • 函数传参
        • 函数递归
        • 函数迭代
        • 函数指针
        • 函数可变参数
        • 数组
        • 一维数组
        • 二维数组
        • 三维数组
        • IO文件读取
        • 预处理器
        • 强制类型转换
        • 错误处理
        • 退出
        • 内存管理
        • 命令行参数
    • C++
  • 学习
  • C
YAN
2025-03-06
目录

C语言

基础语法 (opens new window)

# hello word

// std 标准(standard)
// i 输入
// o 输出
#include <stdio.h>

//main 函数,约定程序入口,有且只能有一个
int main() {
    //printf 是一个库函数,不带换行需要自己使用换行符
    printf("Hello, World!\n");
    //如果正常返回0,异常则非0
    return 0;
}

# 输入/输出

# 输出

#include <stdio.h> // 包含标准输入输出库

int main() {
    // 打印 整形
    printf("%d \n", 1);
    // 打印 字符
    printf("%c \n", 'h');
    // 打印 字符串(注意字符串要用双引号)
    printf("%s \n", "hello world");
    // 打印 float
    printf("%f \n", 1.1f);
    // 打印 double(注意格式说明符应为 %lf)
    printf("%lf \n", 1.1); // 不需要加 lf 后缀,1.1 默认为 double 类型
    return 0;
}

# 输入

需要在控制台输入

#include <stdio.h>

int a = 21;
int main(){
  //在函数内
  int a = 19;
  printf("在控制台输入一个数:\n");
  // &a 表示 变量a的地址
  scanf("%d",&a);
  //如果输入类型错误,将会打印出原来的值
  printf("%d \n",a);
  return 0;
}

# 示例

#include <stdio.h>

int main( )
{
   int c;

   printf( "示例1->请输入 :");
   //读取一个字符
   c = getchar( );
   //输出用户输入的第一个字符。
   putchar( c );

   printf( "\n==================\n");
   //循环读取并输出用户输入的字符,直到用户按下回车键('\n')或输入结束(EOF)。
   while (((c = getchar()))!= '\n' && c != EOF){
    printf( "%c  \n",c);
   }

   char str[100];
 
   printf( "示例2->请输入 :\n");
   gets( str );
   printf( "打印内容: ");
   puts( str );

   printf( "示例3->请输入 :\n");

   // fgets 会保留换行符
   fgets(str, sizeof(str), stdin);
   printf( "打印内容: ");
   puts( str );

   printf( "示例3->请输入 :\n");
   int i;
   // 遇到空格停止读取
   // 示例输入:ABC 1  -> 读取结果: ABC  和 1
   scanf("%s %d", str, &i);
   printf( "您输入了: %s %d ", str, i);
   return 0;
}

# 数据类型

类型 描述 大小 空间
char 字符型 1 8
shore 短整型 2 16
int 整型 2 16
long 长整型 , 4 (32 位)或 8 (64 位) 4 32 (32 位)或 64 (64 位)
long long 长长整型 8 64
float 单精度浮点型 4 32
double 双精度浮点型 8 64

# 关键字

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

# static

# 不使用static

下面代码打印10个2,因为局部变量在离开作用域的时候会被销毁

#include <stdio.h>

void test(){
    int a= 1;
    a ++;
    printf("%d \n",a);
}

int main() {
    int n =0;
    while (n <10)
    {
        n++;
        test();
    }
}
# static 修饰局部变量

提示

static修饰局部变量的时候,不会在域被销毁

所以如下代码打印2~11

#include <stdio.h>

void test(){
    //修饰局部变量
    static  int a= 1;
    a++;
    printf("%d \n",a);
}

int main() {
    int n =0;
    while (n <10)
    {
        n++;
        test();
    }
}
# static 修饰全局变量

示例1

const.c

int g_val = 2025;

main.c

#include <stdio.h>

extern int g_val;

void test(){
    int a= g_val;
    a++;
    printf("%d \n",a);
}

int main() {
    printf("全部变量 %d ",g_val);
    int n =0;
    while (n <10)
    {
        n++;
        test();
    }
}

打印 2026~2035

修改 const.c 如下

static int g_val = 2025;

则编译报错,因为:static在全局变量中的作用是限制该变量的链接属性,使得它只能在定义它的文件中使用,其他文件无法通过extern来引用

# typedef

提示

简化使用不方便的类型,可以理解为别名

示例1

#include <stdio.h>
#include <string.h>
typedef  unsigned char str;

int main() {
    str a []  = "Hello, World!";
    printf("|%s| 的长度是: %d\n", a, strlen(a));
    return 0;
}

示例2

#include <stdio.h>

typedef struct  Node
{
   int a;
   struct Node* next;
} MytNode;

int main() {
    struct Node a;
    MytNode b;
    printf("end");
    return 0;
}

# register

::: node 建议编译器将某个变量存储在CPU的寄存器中,而不是存储在主内存中。

寄存器是CPU内部的高速存储单元,访问速度远远快于访问主内存(RAM)。

因此,使用 register 可以提高变量的访问速度,尤其是在频繁访问的情况下。 :::


#include <stdio.h>

int main() {
    register int  n =0;
    while (n <10)
    {
        n++;
        printf("%d \n",n);
    }
}

# 表达式

用于计算变量、数据类型或表达式所占用的内存大小,单位是 字节(byte)
编译时计算,不会影响运行时的性能

# sizeof

#include <stdio.h>

int main() {
    char str[50] = "hello";
    printf("char =>%zu\n",sizeof(str));// 打印50
    
    printf("%d \n",sizeof 1);
    printf("char =>%zu\n",sizeof(char));
    printf("short => %zu\n",sizeof(short));
    printf("int => %zu\n",sizeof(int));
    printf("long =>%zu\n",sizeof(long));
    printf("long long =>%zu\n",sizeof(long long));
    printf("float =>%zu\n",sizeof(float));
    printf("double =>%zu\n",sizeof(double));
    
    return 0;
}

# ,

#include <stdio.h>

int main() {
    int a = 1;
    int b = 2;
    int c = 3;
    printf("%d \n", (c=a+b,b=c+10,a=b-4));
    return 0;
}

# ~

二进制取反

#include <stdio.h>

int main() {
    printf("%d \n",~1); //输出结果 -2
    printf("%d \n",~2); //输出结果 -3
    printf("%d \n",~3); //输出结果 -4
    printf("%d \n",~11); //输出结果 -12
    printf("%d \n",~-11); //输出结果 10
    printf("%d \n",~-4); //输出结果 3
    return 0;
}

0001 取反 1110
有符合整数的表现形式是补码,正数的补码与原码相同,而负数的补码是其原码取反后加1
1110 最高位为1 是负数
1110 取反-> 0001 -> +1 -> 0010 = 2 所以 1110 为 -2的补码

1110 = -2 0001 取反 //有符号值:0 为正数1 得到结果 ~-2 = 1

# 变量

# 全局变量

作用域,整个工程,生命周期为整个程序的生命周期

#include <stdio.h>

//在函数外
int a = 20;
int main(){
    printf("%d \n",a); //输出结果 20
    return 0;
}

# 局部变量

作用域,变量所在的局部区域,生命周期为:进入作用域开始,离开作用域结束

#include <stdio.h>

int main(){
  //在函数内
  int a = 20; 
  printf("%d \n",a); //输出结果 20
  return 0;
}

注意

全局变了和局部变量相同是局部优先

#include <stdio.h>

int a = 21;
int main(){
  //在函数内
  int a = 19; 
  printf("%d \n",a); //输出结果 19
  return 0;
}

# 常量

笔记

定义:

常变量是一个 不可修改的变量,在定义时必须初始化,且之后不能更改其值。
常变量本质上是一个变量,但它的值在运行时是固定的。

特点:

不可变性:常量值在定义后不能被修改。
直接替换:在编译时,常量的值通常会被直接替换到代码中(某些编程语言中)。
无存储空间:常量通常在编译时被处理,可能不会占用运行时的内存。
#include <stdio.h>

int main(){
  printf("%d \n",1); //1 就是常量
  //用常量声明
  char arr [4] = {"123"};
  printf("字符输出 : %s\n", arr);
  return 0;
}

# const

定义:

常变量是一个 不可修改的变量,在定义时必须初始化,且之后不能更改其值。
常变量本质上是一个变量,但它的值在运行时是固定的。

特点:

- 不可变性:常变量的值在定义后不能被修改。
- 存储空间:常变量在内存中有存储空间。
- 运行时定义:常变量的值在运行时生效(与常量不同)
#include <stdio.h>

int main(){
  //被 const 本质是变量,但是不能直接修改
  const int b = 1;
  //用常量声明
  //char arr [b] = {2};//代码异常
  printf("输出 =>%d ",b);
  return 0;
}

# define 标识符常量

#include <stdio.h>

#define NAME "小明"
#define PLAY(name,event) ( name "在" event)

int main() {
    printf("%s\n", PLAY(NAME, "写代码"));
    return 0;
}

注意

  • 注意最后不要带;

  • 宏定义中如果涉及字符串拼接,需要使用 "" 直接连接,而不是 +。

# 枚举常量

注意

枚举值本质上是整数,默认从 0 开始递增。

#include <stdio.h>

enum Color {
   RED,
   GREEN,
   BLUE,
};

int main(){
    printf("color %d \n",RED);
    printf("color %d \n",GREEN);
    printf("color %d \n",BLUE);
    return 0;
}

打印出

color 0
color 1
color 2

# 字符串

#include <stdio.h>
#include <string.h>

int main(){
  char str  [] = "12345";
  char str2  [] = {'1','2','3','4','5','\0'};
  //字符串本质是个数组 ,末尾用\0结束,\0不计入长度
  printf("打印字符串1:%s => %d \n",str,strlen(str));
  printf("打印字符串2:%s => %d \n",str2,strlen(str2));

  //用常量声明
  char arr [4] = {"123"};
  printf("字符输出 : %s\n", arr);

  char arr2 [3] = {"123"};
  //因为123 长度是3 ,而arr2 最大长度也是3 ,不能正常存储\0字符,所以一下输出会错乱读取内存中的字符
  printf("字符输出 : %s\n", arr2);
  return 0;
}

# 数组

整数数组:必须使用 花括号 {} 进行初始化,不能使用方括号 []

数组下班从0开始

#include <stdio.h>

int main(){
    //声明
    int arr [4] = {1,2,3,4};
    //遍历
    for (int i = 0; i < 4; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    return 0;
}

# 选择结构 IF

#include <stdio.h>

int main(){
    printf("1: C\n");
    printf("2: Python\n");
    printf("3: JAVA\n");
    printf("请选择: \n");
    int cmd =0;
    scanf("%d",&cmd);
    if(cmd == 1){
        printf("开始学习C");
    }else if (cmd == 2)
    {
        printf("开始学习Python");
    }else{
        printf("开始学习Java");
    }
    return 0;
}

# 循环结构 While

#include <stdio.h>

int main(){
    int line = 0;
    while (line < 10){
        printf("开始学习 %d \n",line);
        line++;
    }
    printf("学习结束\n");
    return 0;
}

# 指针

指针是一个变量,用来存储另一个变量的内存地址。它的类型决定了它指向的变量类型。

  • 一个 int 指针用来存储 int 类型变量的地址。
  • 一个 char 指针用来存储 char 类型变量的地址。

# 指针的声明

声明指针时,需要在变量名前加上一个星号 *,语法如下:

#include <stdio.h>

type *pointer_name;

# 指针的使用

指针的主要用途是通过它访问或修改其指向的变量的值。

  • &:取地址运算符,用于获取变量的地址。
  • *:解引用运算符,用于访问指针指向的变量的值。
#include <stdio.h>

int main() {
    //定义变量
    int a = 10;
    //取地址
    printf("%s %p \n","a的内存地址",&a);
    //指针p记录a内存的内存地址
    int  *p = &a;
    // printf %p 打印指针地址
    printf("%s %p \n","指针p地址",*p);
    // *:解引用运算符
    // 通过指针 p 修改 a 的值
    *p = 20;
    printf("修改后,变量 a 的值: %d\n", a);
    return 0;
}

# 指针的类型

指针的类型必须与其指向的变量的类型一致。例如:

  • int 指针只能指向 int 变量。
  • char 指针只能指向 char 变量。
#include <stdio.h>

int main() {
    int a = 10;
    int *p = &a;       // 正确:p 是 int 指针,指向 int 变量 a
    float b = 3.14;
    float *q = &b;     // 正确:q 是 float 指针,指向 float 变量 b
    // 这里编译错误:p 是 int 指针,不能指向 float 变量
    p = &b;
    return 0;
}

# 指针的运算

#include <stdio.h>

int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    int *p = arr;      // p 指向 arr[0]
    int val = *p;
    printf("%s %d \n", "目前指针*p的值",val);
    p++;               // p 现在指向 arr[1]
    val = *p;
    printf("%s %d \n", "变化指针后*p的值",val);
    return 0;
}

# 多级指针

指针也可以指向另一个指针,形成多级指针(如二级指针、三级指针等)。

#include <stdio.h>

int main() {
    int a = 10;
    int *p = &a;        // p 是一个 int 指针,指向 a
    int **pp = &p;      // pp 是一个二级指针,指向 p

    printf("a 的值: %d\n", a);          // 输出 10
    printf("通过 p 访问 a: %d\n", *p);  // 输出 10
    printf("通过 pp 访问 a: %d\n", **pp); // 输出 10
    return 0;
}

# 指针的注意事项

  • 野指针:未初始化或释放后未置空的指针,可能导致程序崩溃。
#include <stdio.h>

int main() {
    printf("%s \n","开始");
    // 未初始化,是野指针
    int *p;
    // 不可预测行为,可能破坏其他内存,这里不会崩溃
    *p = 10;
    int a = *p;
    printf("%s %d \n","打印:",a);
    return 0;
}
  • 空指针:使用 NULL 来表示指针未指向任何有效地址。
#include <stdio.h>

int main() {
    printf("%s \n","开始");
    // p 是一个空指针
    int *p = NULL;
    //解空指针,程序崩溃
    int a = *p;
    printf("%s %d \n","打印:",a);
    return 0;
}

# struct

结构体的内存大小是其所有成员的大小的总和,但可能会因为内存对齐而有所不同。

#include <stdio.h>
#include <string.h>

struct Person {
    char name[50];
    int age;
    float height;
} ;

int main() {
    struct Person person1;
    strcpy(person1.name, "John");
    person1.age = 25;
    person1.height = 1.75;
}

可以在声明时直接初始化结构体变量。

#include <stdio.h>

struct Person {
    char name[50];
    int age;
    float height;
} ;

int main() {
    struct Person person2 = {"Alice", 30, 1.65};
}

结构体可以作为函数的参数传递。

#include <stdio.h>

struct Person {
    char name[50];
    int age;
    float height;
} ;

void printPerson(struct Person p) {
    printf("Name: %s\n", p.name);
    printf("Age: %d\n", p.age);
    printf("Height: %.2f\n", p.height);
}

int main() {
    struct Person person = {"Alice", 30, 1.65};
    printPerson(person);
}

可以使用指针来访问结构体变量,通过指针访问结构体成员时需要使用 -> 运算符。

#include <stdio.h>

struct Person {
    char name[50];
    int age;
    float height;
} ;

void printPerson(struct Person p) {
    printf("Name: %s\n", p.name);
    printf("Age: %d\n", p.age);
    printf("Height: %.2f\n", p.height);
}

int main() {
    struct Person person1 = {"John", 25, 1.75};
    struct Person *p = &person1;

    printf("Name: %s\n", p->name);
    printf("Age: %d\n", p->age);
    printf("Height: %.2f\n", p->height);

    //通过指针赋值
    p-> height = 1.80;
    printf("Height: %.2f\n", p->height);
}

如果不需要给结构体命名,可以使用匿名结构体。

#include <stdio.h>
#include <string.h>


struct {
    char name[50];
    int age;
} person1;



int main() {
    person1.age = 30;
    printf("Age: %d\n", person1.age);

    struct {
        char name[50];
    } person2;
    strcpy(person2.name, "小明");
    printf("name: %s\n", person2.name);
}

使用 typedef 可以为结构体类型定义一个别名,从而简化代码。

#include <stdio.h>

typedef struct PP {
    char name[50];
    int age;
} Person;

void printPerson( struct PP p) {
    printf("Name: %s\n", p.name);
    printf("Age: %d\n", p.age);
}

int main() {
    Person person = {"小米", 25};
    printPerson(person);

    struct PP person2 = {"小红", 25};
    printPerson(person2);
}

# 位域

#include <stdio.h>
#include <string.h>

/* 定义简单的结构 */
struct{
  unsigned int widthValidated; //不指定位域 长度4 ,32位
  unsigned int heightValidated;
} status1;

/* 定义位域结构 */
struct{
  // type [member_name] : width ;
  // type	整数类型,决定了如何解释位域的值。类型可以是整型、有符号整型、无符号整型。
  // member_name	位域的名称。
  // width	位域(Bit Field 二进制)中位的数量。宽度必须小于或等于指定类型的位宽度。
  unsigned int widthValidated : 1;  //int 4字节 32位,设置位域1,及只能存储0和1
  unsigned int heightValidated : 1;
} status2;

struct{
  unsigned int age : 3; //int 4字节 32位,设置位域,及只能存储0~7
} Age;


int main( )
{
   printf( "内存大小 size : %d\n", sizeof(status1));
   printf( "内存大小 size : %d\n", sizeof(status2));

   Age.age = 4;
   printf( "Sizeof( Age ) : %d\n", sizeof(Age) );
   printf( "Age.age : %d\n", Age.age );

   Age.age = 7;
   printf( "Age.age : %d\n", Age.age );

   //二进制表示为 1000 有四位,超出
   //8  二进制-> 1000
   Age.age = 8;
   printf( "Age.age : %d\n", Age.age );

   return 0;
}

# union

共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。您可以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。

#include <stdio.h>
#include <string.h>

union Data
{
   int i;
   float f;
   char  str[20];
};

int main( ){
   union Data data;
   //共用体占用的内存应足够存储共用体中最大的成员
   printf( "Memory size occupied by data : %d\n", sizeof(data));

   data.i = 66;
   data.f = 66.66;
   strcpy( data.str, "hello world");

   //以下示例只会正常打印最后一个赋值的属性值
   printf( "data.i : %d\n", data.i);
   printf( "data.f : %f\n", data.f);
   printf( "data.str : %s\n", data.str);


    //以下示例只会正常打印每个属性值
   data.i = 66;
   printf( "data.i : %d\n", data.i);

   data.f = 66.66;
   printf( "data.f : %f\n", data.f);

   strcpy( data.str, "hello world");
   printf( "data.str : %s\n", data.str);

}

# 库

# string.h

# strlen
#include <stdio.h>
#include <string.h>

int main() {
    char str[50] = "hello";
    printf("|%s| 的长度是 |%d|\n", str, strlen(str));
    return 0;
}

# 分支和循环预计

# 分支语句

# if
#include <stdio.h>

int main() {
    int a= 6;
    if(a==1){
        printf("等于1");
    }else if (a>2 && a<6)
    {
        printf("大于2小于6");
    }
    else{
        printf("不等于1且大于等于6");
    }
}
# switch
#include <stdio.h>

int main() {
    int a= 1;
    switch (a)
    {
        case 1:
            printf("选择了1");
            break;
        case 2:
            printf("选择了2");
            break;
        case 3:
            printf("选择了3");
            break;
        default:
            printf("默认选择: %d \n",a);
            break;
    }
}

# 循环语句

# while

while 循环:只有满足while里的条件时才会执行
break是用于永久的终止循环
continue 跳过本次循环后面的代码,直接去判断部分,进行下一次循环的判断

示例

#include <stdio.h>

int main() {
    int a= 0;

    while (a<10)
    {
        //如果为5跳过后续代码
        a++;
        if(a == 5){
            printf("跳过:%d \n",a);
            continue;
        }
        printf("当前:%d \n",a);
    }
}

输出

当前:1 
当前:2 
当前:3
当前:4
跳过:5
当前:6
当前:7
当前:8
当前:9
当前:10
# do-while

示例1

#include <stdio.h>

int main() {
    int a= 0;
    do{
        a++;
        //如果为5跳过后续代码
        if(a == 5){
            printf("跳过:%d \n",a);
            continue;
        }

        //如果大于10结束循环
        if(a > 10){
            printf("结束:%d \n",a);
            break;
        }
        printf("当前:%d \n",a);
        //不等于0的时候执行
    }while (a!=0);
}

输出

当前:1 
当前:2 
当前:3
当前:4
跳过:5
当前:6
当前:7
当前:8
当前:9
当前:10
结束:11

示例2

更换条件while (a==0);

#include <stdio.h>

int main() {
    int a= 0;
    do{
        a++;
        //如果为5跳过后续代码
        if(a == 5){
            printf("跳过:%d \n",a);
            continue;
        }
        //如果大于10结束循环
        if(a > 10){
            printf("结束:%d \n",a);
            break;
        }
        printf("当前:%d \n",a);
        //不等于0的时候执行
    }while (a==0);
}

输出

当前:1

提示

do-while循环:无论是否满足while里的条件都会执行一次

# for
#include <stdio.h>

int main() {
    //size_t 是无符号的,这意味着它不能表示负数。它的取值范围从 0 开始,一直到它能表示的最大正整数。
    for (size_t i = 0; i < 10; i++)
    {
        printf("打印:%d \n",i);
    }
}

# 函数

# 函数调用

#include <stdio.h>

int data() {
    printf("Hello, World!\n");
    return 0;
}

int main() {
    //printf 是一个库函数,不带换行需要自己使用换行符
    printf("Hello, World!\n");
    //如果正常返回0,异常则非0
    return data();
}

# 函数声明

笔记

如果函数调用在实现之前,需要在调用前申明

#include <stdio.h>

//调用前申明
int data();

int main() {
    //printf 是一个库函数,不带换行需要自己使用换行符
    printf("Hello, World!\n");
    //如果正常返回0,异常则非0
    return data();
}

int data() {
    printf("Hello, World!\n");
    return 0;
}

# 头文件

hello.h

#ifndef HELLO_H  // 如果 HELLO_H 未定义
#define HELLO_H  // 定义 HELLO_H
    int hello(char* name );
#endif  // 结束条件编译

hello.c

#include <stdio.h>

int hello(char* name ){
    printf("你好啊! %s \n",name);
}

test.c

// <> 只在 标准路径 或 系统路径 中查找头文件。
#include <stdio.h>
// "" 先在 当前源文件所在的目录 中查找,找不到再去 标准路径 查找。
#include "magic.h"


int main() {
    hello("小明");
}

# 函数传参

真实传给函数的参数,叫实参。
实参可以是:常量、变量、表达式、函数等
无论实参是何种类型的量,在进行的数调用时,它们都必须有确定的值,以便把这些值传送给形参

形式参数是指函数名后括号中的变量
因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。
形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效

#include <stdio.h>

//当实参传递给形参的时候,形参是实参的一份临时拷贝//对形参的修改不能改变实参
void swap(int a ,int b ){
    int c;
    c = a;
    a = b;
    b = c;
}

//指针传递的是内存地址,可以有效变更值
void swap2(int* a ,int* b ){
    int c = 0;
    c = *a;
    *a = *b;
    *b = c;
}

int main() {
    int a =2,b=1;
    printf("初始化 A:%d B:%d \n",a,b);
    //传值调用 因为形参的原因,函数内外无联系
    swap(a,b);
    printf("交换1 A:%d B:%d \n",a,b);
    //传址调用 因为形参类型是指针指向真实参数,函数内外有联系
    swap2(&a,&b);
    printf("交换2 A:%d B:%d \n",a,b);
}

函数传递的是形参

输出

初始化 A:2 B:1 
交换1 A:2 B:1 
交换2 A:1 B:2

# 函数递归

对于数字 123456,依次打印每一位

#include <stdio.h>

void print_num(unsigned int num){
    if(num > 9 ){
        print_num(num /10);
    }
    printf("%u",num%10);
}

int main() {
    unsigned int num = 12234;
    print_num(num);
}

编写函数不允许创建临时变量,求字符串的长度。

#include <stdio.h>

int my_strlen(char* name){
    if(*name == '\0'){
        return 0;
    }
    name++;
    return 1+my_strlen(name);

}

int main() {
    printf("字符长度 %u",my_strlen("123456789"));
}

# 函数迭代

求第N个 斐波那契数列

#include <stdio.h>


int fib(int n ){

    int n1 =0;
    int n2 =1;
    int sum =0;
    for (size_t i = 1; i <=n; i++)
    {
        //   n1           n2
        //1  0            1
        //2  1            (0+1)
        //3  (0+1)        (1 + (0+1))
        //5  (1 + 0+1)    (0+1)+(1 + (0+1))
        sum = n1+n2;
        n1 = n2;
        n2 =  sum;

    }
    return sum;
}


int fib2(int n ){
    int n1 =1;
    int n2 =2;
    if(n == 1){
        return n1;
    }
    if(n == 2){
        return  n2;
    }
    int sum =0;
    for (size_t i = 3; i <=n; i++)
    {
        sum = n1+n2;
        n1 = n2;
        n2 =  sum;

    }
    return sum;
}


int main() {
    //1 2 3 5 8 13 21
    printf("fib :%u \n",fib(6));
    printf("fib2 :%u \n",fib2(7));
}

# 函数指针

#include <stdio.h>

//将指针函数作为参数传递 (回调接口)
int max(int a ,int b,void (*log)(int,int)){
    log(a,b);
    return a>b?a:b;
}

void print_info(int a,int b){
    printf("比较 %u 和 %u \n",a,b);
}

int main() {
    int a = 3;
    int b = 4;
    //指针函数 max 本身指向一个地址 可省略 &
    // int (*fun)(int,int,void (*log)(int,int)) = &max;
    int (*fun)(int,int,void (*log)(int,int)) = max;
    //print_info 同理 可省略 print_info
    printf("最大值%u \n",fun(fun(a,10,&print_info),b,print_info));
}

# 函数可变参数

#include <stdio.h>
//提供了一组宏来支持可变参数的功能
#include <stdarg.h>

// 定义一个函数,计算固定数量整数的平均值
/**
 * count 可变参数数量 为了方便提取
 * param  在示例中为普通固定参数
 * ...  可变参数
 */
double print_args(int count,char *param, ...) {
    va_list args;
    //va_start(va_list ap, last_fixed_arg);
    //第一个参数是  va_list
    //第二个参数是 最后一位固定参数,这里是 name
    va_start(args, param);

    int arg_arry [3];
    for (int i = 0; i < count; i++) {
        //va_arg 提取一个参数,并移动 va_list 指针至下一位
        arg_arry[i] = va_arg(args, int);
        printf("%s 第%d个参数值为:%d \n",param,i+1,arg_arry[i]);
    }
    //清理va_list,结束可变参数的获取。
    va_end(args);
}

int main() {
    print_args(3,"TEST", 10, 20, 30);
    return 0;
}

# 数组

# 一维数组

#include <stdio.h>


int main() {

    int array [10] = {1,2,3};
    printf("占用 %d \n",sizeof(array));
    printf("长度 %d \n", sizeof(array) / sizeof(array[0]));
    
    printf("打印 %d \n",array[0]);
    printf("打印 %d \n",array[1]);
    printf("打印 %d \n",array[2]);
    printf("======================\n");


    //变长数组(VLA)是 C99 标准引入的特性,确保你的编译器支持 C99 或更高版本。
    int n = 3;
    //变长数组不能直接初始化
    int array2 [n];
    array2[0] = 4;
    array2[1] = 5;
    array2[2] = 6;
    printf("占用 %d \n",sizeof(array2));
    printf("长度 %d \n", sizeof(array2) / sizeof(array2[0]));
    printf("打印 %d \n",*array2);
    printf("打印 %d \n",array2[0]);
    printf("打印 %d \n",array2[1]);
    printf("打印 %d \n",array2[2]);
    printf("======================\n");

    //如果不指定长度,实际长度为内容长度
    char array3 [] = {'A','B','C'};
    printf("占用 %d \n",sizeof(array3));
    printf("长度 %d \n", sizeof(array3) / sizeof(array3[0]));
    printf("打印 %c \n",*array3);
    printf("打印 %c \n",array3[0]);
    printf("打印 %c \n",array3[1]);
    printf("打印 %c \n",array3[2]);

}

# 二维数组

#include <stdio.h>

int main() {
    //二维数组创建
    int array [3][3] = {1,2,3,4,5,6,7,8,9};
    //二维数组理解成一个内容是数组的一维数组,*array 得到第一个数组,**array 得到第一个数组的第一个内容
    printf("打印 %d \n",**array);
    printf("打印 %d \n",array[1][2]);
    printf("======================\n");
    int array2 [2][3] = {{1,2,3},{4,5,6},{7,8,9}};
    printf("打印 %d \n",**array2);
    printf("打印 %d \n",array2[1][2]);
    printf("======================\n");
    int array3 [3][3] = {1,2,3,4,5,6,7};
    printf("打印 %d \n",**array3);
    printf("打印 %d \n",array3[1][2]);
    printf("======================\n");
    int array4 [][3] = {1,2,3,4,5,6,7};
    printf("打印 %d \n",**array4);
    printf("打印 %d \n",array4[1][2]);
    printf("======================\n");
    //二维数组可以省略行,不能省略列
    // int array5 [2][] = {1,2,3,4,5,6,7};
}

# 三维数组

public class Object extends  余胜军{
    
}

# IO文件读取

打开

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

关闭

int fclose( FILE *fp );

写入字符

int fputc( int c, FILE *fp );

写入字符串

int fputs( const char *s, FILE *fp );
int fprintf(FILE *fp,const char *format, ...) 
模式 描述
r 打开一个已有的文本文件,允许读取文件。
w 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。
a 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。
r+ 打开一个文本文件,允许读写文件。
w+ 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。
a+ 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。

如果处理的是二进制文件,则需使用下面的访问模式来取代上面的访问模式:
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"

#include <stdio.h>

int main( )
{

    FILE *f= fopen("test.txt","w+");
    if (f == NULL) {
        printf("文件不存在或打开失败\n");
        return 1;
    }

    fprintf(f,"hello world %s","小明");
    char *content = "\n这是内容";
    fputs( content, f );

    //重置指针开始读取
    rewind(f);

    // char * buffer;
    char  buffer [100];

    //scan形式读取 遇到第一个空格停止读取
    fscanf(f, "%s", buffer);
    printf("%s\n", buffer );
 
    //重置指针 确保能完整读取
    rewind(f);

    //每次读取一行,直到全部读完
    while (fgets(buffer, sizeof(buffer), f) != NULL) {
        printf("%s", buffer);
    }

    fclose(f);
}

# 预处理器

指令 描述
#define 定义宏
#include 包含一个源代码文件
#undef 取消已定义的宏
#ifdef 如果宏已经定义,则返回真
#ifndef 如果宏没有定义,则返回真
#if 如果给定条件为真,则编译下面代码
#else #if 的替代方案
#elif 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个 #if……#else 条件编译块
#error 当遇到标准错误时,输出错误消息
#pragma 使用标准化方法,向编译器发布特殊的命令到编译器中

预设宏

宏 描述
DATE 当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。
TIME 当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。
FILE 这会包含当前文件名,一个字符串常量。
LINE 这会包含当前行号,一个十进制常量。
STDC 当编译器以 ANSI 标准编译时,则定义为 1。
// 带< 从系统库中获取
#include <stdio.h>
#include <string.h>
// 带 "" 在当前目录(即 .c 文件所在目录)查找,如果找不到:回退到 系统默认路径(和 <header.h> 一样)
#include "magic.h"

// 定义常量。
#define MAX_CONNECT 100
//取消 MAX_CONNECT 定义
#undef MAX_CONNECT
//重新定义
#define MAX_CONNECT 100

#define SYSTEM_NAME "LINUX"
//SYSTEM_NAME 平台
#if !defined (SYSTEM_NAME)
  #define SYSTEM_NAME "WINDOWS"
#endif


//windows 平台 1 是
#define PLATFORM_WINDOWS 1
//不是 linux 平台 0 否
#define PLATFORM_LINUX 0

//如果不存在 ENV 变量 ,设置为 dev
#ifndef ENV
   #define ENV "dev"
#endif

//设置debug参数
#define DEBUG  1

//如果有Debug参数
#ifdef DEBUG
   #define USER "TEST"
#endif

//如果有Debug参数
#if defined (DEBUG)
    // \ 宏延续运算符:一个宏通常写在一个单行上。但是如果宏太长,一个单行容纳不下,则使用(\)
    #define my_log(user,msg) \
        printf("log [%s]:%s %s %s %d %d : %s\n",user, __FILE__ ,__DATE__,__TIME__,__LINE__,__STDC__,msg);
#else
    #define my_log(user,msg)
#endif


// # 字符串常量化运算符: 当需要把一个宏的参数转换为字符串常量时,则使用字符串常量化运算符(#)
#define env_info(a,b) my_log(USER,(#a ":" b))

// 标记粘贴运算符(##): 会合并两个参数。它允许在宏定义中两个独立的标记被合并为一个标记。例如:
#define os_info(a) \
        char buffer[100]; \
        snprintf(buffer, sizeof(buffer), "当前系统[%s]: %d\n", #a, PLATFORM_##a); \
        my_log(USER, buffer);


int main(){
    //打印 ENV:dev
    env_info(ENV,ENV);

    my_log(USER,"当前是debug环境");

    #if PLATFORM_WINDOWS == 2
        os_info(WINDOWS);
    #elif PLATFORM_LINUX
        os_info(LINUX);
    #else
        my_log(USER,"未知平台");
    #endif

    printf("当前环境%s \n",ENV);
    printf("最大连接数:%d \n",MAX_CONNECT);

    return 0 ;
}


# 强制类型转换

int -> unsigned int -> long -> unsigned long -> long long > unsigned long long > float > double > long double

#include <stdio.h>

int main()
{
   int sum = 17, count = 5;
   double mean;

   mean = (double) sum / count;
   printf("Value of mean : %f\n", mean );
}

# 错误处理

打印错误

//提供了输入输出功能,如fprintf和perror
#include <stdio.h>

int main ()
{
   FILE * pf;
   pf = fopen ("unexist.txt", "rb");
   if (pf == NULL){
      perror("通过 perror 输出错误");
   }
   else
   {
      fclose (pf);
   }
   return 0;
}

记录错误

//提供了输入输出功能,如fprintf和perror
#include <stdio.h>
//提供了errno 全局变量,用于存储错误号
#include <errno.h>
// 提供了字符串处理函数,如 strerror
#include <string.h>

extern int errno ;

int main ()
{
   FILE * pf;
   //声明了一个整数变量errnum,用于存储错误号。
   int errnum;
   pf = fopen ("unexist.txt", "rb");
   if (pf == NULL){
      //如果文件打开失败,将errno的值赋给errnum,以便稍后使用。
      errnum = errno;
      //使用fprintf将错误号输出到标准错误流(stderr)。
      fprintf(stderr, "错误号: %d\n", errno);
      //使用perror函数输出与errno相关的错误信息。perror会自动在输出信息前加上传入的字符串。
      perror("通过 perror 输出错误");
      //使用strerror函数将错误号转换为错误信息字符串,并通过fprintf输出到标准错误流。
      fprintf(stderr, "打开文件错误: %s\n", strerror( errnum ));
   }
   else
   {
      fclose (pf);
   }
   return 0;
}

# 退出

#include <stdio.h>
#include <stdlib.h>

int main()
{
   int dividend = 20;
   int divisor = 5;
   int quotient;

   if( divisor == 0){
      fprintf(stderr, "除数为 0 退出运行...\n");

      exit(EXIT_FAILURE);
   }
   quotient = dividend / divisor;
   fprintf(stderr, "quotient 变量的值为: %d\n", quotient );

   //正常退出 0
   exit(EXIT_SUCCESS);
}

# 内存管理

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main()
{
   char name[100];
   strcpy(name, "小明");
   printf("Name = %s \n", name );

   char *description;
   /* 动态分配内存 */
   description = malloc(3);
   if( description == NULL ){
      fprintf(stderr, "无法分配需要的内存\n");
   }
   else{
       //写入内存
      strcpy( description, "12");
      printf("Description: %s \n", description );

      //调整内存大小
      description = realloc(description,11);
      if( description == NULL ){
        fprintf(stderr, "无法分配需要的内存\n");
        free(description);
        exit(1);
     }

      //重新写入
      strcpy( description, "1234567890");
      printf("Description: %s \n", description );
   }
   printf("释放内存\n");
   free(description);

   //由于内存被释放,这里打印错误的值
   printf("Description: %s \n", description );
   return 0 ;
}

# 命令行参数

#include <stdio.h>

//argc 是指传入参数的个数,
//argv[] 是一个指针数组,
int main(int argc, char *argv[]) {
    // p 指向 argv 数组的首元素(即 argv[0])
    char **p = argv;

    // *p 是 argv[i] 的字符串(即第 i 个参数的字符串)
    // *p + 3 是 argv[i] 字符串的第 4 个字符的地址(索引从 0 开始)
    // *(*p + 3) 是 argv[i][3] 的值(即第 4 个字符)
    printf("argv[n] = %s -> index[3]:%c\n", *p, *(*p + 3));

    // 指针++操作,p 指向 argv[1]
    p++;
    // *p 是 argv[1] 的字符串
    // **p 是 argv[1][0] 的值(即第 1 个参数的首字符),等效写法 *(*p),*p[0]
    printf("argv[n] = %s -> index[0]:%c\n", *p, **p);

     // 指针++操作,p 指向 argv[2]
    p++;
    // *p 是 argv[2] 的字符串
    // **p 是 argv[2][0] 的值(即第 2 个参数的首字符),等效写法 *(*p),*p[0]
    printf("argv[n] = %s -> index[0]:%c\n", *p, **p);


    for (int i = 0; i < argc; i++) {
        printf("argv[%d] = %s\n", i, argv[i]);
    }
    return 0;
}

上次更新: 2025/05/22, 07:52:48
C语言环境搭建
C++

← C语言环境搭建 C++→

最近更新
01
Caddy操作指南
04-25
02
虚拟机磁盘扩展
04-22
03
Swap空间
04-22
更多文章>
Theme by Vdoing | Copyright © 2022-2025 YAN | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式