C语言

1
2
3
前情提要:
此学习博客更倾向于本人对于c中有什么不会、或是需要记忆的点
详细教学请跟学下放博客or 视频🥰

博客链接:https://www.runoob.com/cprogramming/c-tutorial.html

视频推荐:https://www.bilibili.com/video/BV1XZ4y1S7e1/?vd_source=bbdaef564df94fc8991902ba22c6761b

11.10

  • 关于单双精度浮点数

image-20251110223236897

核心思想科学计数法:

eg.123.456=1.23456*10^2^

故数值=符号+小数*指数

  • 常量

    • 整数常量: 0x or 0X前缀为16进制 0前缀为八进制 不带前缀默认十进制

      整数常量也可带后缀 eg.U 表示无符号整数(unsigned),L 表示长整数(long)

    • 浮点常量:注意在使用小数形式时—必须包含小数点、指数

    公式:[具体数字]*e/E*指数(其指数必须为整数) e/E前面必须要有数 字

    • 字符常量:注意记住几款常用转义序列

    image-20251110225926473

    • 字符串常量:注意字符串常量常以 null终止符 \0结尾

      1
      2
      3
      'a'=即为a的ASCII

      "a"='a'+'\0'
  • 定义常量

    1.使用 #define 预处理器: #define 可以在程序中定义一个常量,它在编译时会被替换为其对应的值。

  1. 使用 const 关键字:const 关键字用于声明一个只读变量,即该变量的值不能在程序运行时修改

两者都可用来定义常量,但#define只是简单替换值、const则是用来定义变量(在运行时会分配内存)

image-20251110231150224

  • 存储类

    • auto存储类

    ​ 相当于变量的默认定义(即无论打不打auto都是此类型)

    1
    2
    3
    4
    5
    {
    int mount;
    auto int month;
    }

    • register 存储类(不同于普通定义在内存RAM中,而是在寄存器中)
    1
    2
    3
    4
    {
    register int miles;
    }

    但因为变量不是在内存中故不可对变量使用取地址符 &

    • static 存储类

    image-20251110232211331

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #include <stdio.h>

    /* 函数声明 */
    void func1(void);

    static int count=10; /* 全局变量 - static 是默认的 */

    int main()
    {
    while (count--) {
    func1();
    }
    return 0;
    }

    void func1(void)
    {
    /* 'thingy' 是 'func1' 的局部变量 - 只初始化一次
    * 每次调用函数 'func1' 'thingy' 值不会被重置。
    */
    static int thingy=5;
    thingy++;
    printf(" thingy 为 %d , count 为 %d\n", thingy, count);
    }

    static修饰局部变量

    image-20251110232424394

    即在void func1(void)函数中,static int thingy=5只会被调用一次(即初始化一次)后续再调用此函数直接在上次函数调用后'thingy'值的基础上继续thingy++(跳过对static int thingy=5的再次声明)

    static修饰全局变量

    image-20251110233030951

    即于普通全局变量不同,被定义的变量只能在此程序/文件中访问 无法被 extern来声明被外部文件所使用

    • extern 存储类

    首先明确以下定义

    声明 vs 定义
    定义:创建变量并分配内存。例如 int count;
    声明:告诉编译器“这个变量/函数存在,它的类型是什么,但内存是在别处分配的”。使用 extern关键字就是进行声明。例如 extern int count;

    第一个文件:main.c

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>

int count ;
extern void write_extern();

int main()
{
count = 5;
write_extern();
}

第二个文件:support.c

1
2
3
4
5
6
7
8
#include <stdio.h>

extern int count;

void write_extern(void)
{
printf("count is %d\n", count);
}

在这里可运用代码 $ gcc main.c support.c

  • 使在support.c程序使用变量 int count ;

    • main.c文件中使用函数 void write_extern(void)

    详细说明:

image-20251110234113570

累了 23:42 。。。

11.11

请仔细阅读上方文章 真的真的做的好🤩

==注意!!!==

1
2
/* 本人有关于 break 和 continue 的原先易错点这里着重写出来
记住他们都是适用于循环语句 但 break 可用于 switch 中/*

break是跳出循环,如果为嵌套循环 即 break 跳出内层循环

continue则是跳出本次循环(即跳出本次循环后面的语句,开始新的一轮循环),但要注意区分 for( ; ; ) eg. for(i=1;i<=n;i++) 前者continue可跳出i++语句(如果在此for语句中continue后面有i++) 后者则需具体分析

1
2
3
4
5
6
7
for(int i=1; i<=5; i++) {
if(i == 3) {
continue; // 不是跳过 i++,而是直接去执行 i++
}
printf("%d ", i);
}
// 输出:1 2 4 5

image-20251111123010103

1
/* 即你可以认为在后者的情况下continue 已经跳出本次循环了 但开始下一次循环的结束必须以i++为结尾,可看成i++不在for循环的程序里面(话说好像本来也不在😅) 然后再自加完后程序开始第二次循环 再在此基础上开启循环条件的判断(eg.i<5)之类的 如果不符再跳出循环

还要注意 break continue跳出中只管跳出循环语句 不管这个循环语句里面包括几个for 语句

break
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

int main ()
{
/* 局部变量定义 */
int a = 10;

/* while 循环执行 */
while( a < 20 )
{
printf("a 的值: %d\n", a);
a++;
if( a > 15)
{
/* 使用 break 语句终止循环 */
break;
}
}

return 0;
}
continue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

int main ()
{
/* 局部变量定义 */
int a = 10;

/* do 循环执行 */
do
{
if( a == 15)
{
/* 跳过迭代 */
a = a + 1;
continue;
}
printf("a 的值: %d\n", a);
a++;

}while( a < 20 );

return 0;
}

image-20251111124117306

不要有break或continue怎么不是跳出if语句的错误理念哦(过来人的经验🫠)

11.17

函数

定义函数

定义函数的一般形式,由函数头函数主体组成

1
2
3
4
return_type function_name( parameter list )
{
body of the function
}
  • return_type — 指函数的返回值的类型(eg.int /float….) 若函数执行但不返回值,那么此时return_typevoid
  • function_name + parameter list — 函数名称
  • parameter — 参数(可写可不写)
  • 函数主体 —执行语句

函数参数

image-20251117205743613

  • **传值调用:**只是将实际参数的值复制给函数内的参数,其函数内部参数的改变不会影响主函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>

/* 函数声明 */
void swap(int x, int y);

int main ()
{
/* 局部变量定义 */
int a = 100;
int b = 200;

printf("交换前,a 的值: %d\n", a );
printf("交换前,b 的值: %d\n", b );

/* 调用函数来交换值 */
swap(a, b);

printf("交换后,a 的值: %d\n", a );
printf("交换后,b 的值: %d\n", b );

return 0;
}

image-20251117210134529

即在函数内部改变了a、b的值,但回到主函数时实际值并没有改变

  • 引用调用:相当于指针调用函数,此时形式参数为实际参数的指针,对形式参数的操作就为对实际参数的操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>

/* 函数声明 */
void swap(int *x, int *y);

int main ()
{
/* 局部变量定义 */
int a = 100;
int b = 200;

printf("交换前,a 的值: %d\n", a );
printf("交换前,b 的值: %d\n", b );

/* 调用函数来交换值
* &a 表示指向 a 的指针,即变量 a 的地址
* &b 表示指向 b 的指针,即变量 b 的地址
*/
swap(&a, &b);

printf("交换后,a 的值: %d\n", a );
printf("交换后,b 的值: %d\n", b );

return 0;
}

image-20251117210658726

作用域规则

  • 局部变量:在某个函数内定义的变量(故此变量也只能在此函数内使用)
  • 全局变量:在程序顶部定义的变量 (顾名思义即可以在程序内全局都可以使用)
1
2
3
4
5
6
7
8
9
#include <stdio.h>

/* 全局变量声明 */
int g;

int main ()
{
/* 局部变量声明 */
int a, b;

but 若全局变量与局部变量名称相同,则会优先使用局部变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>

/* 全局变量声明 */
int g = 20;

int main ()
{
/* 局部变量声明 */
int g = 10;

printf ("value of g = %d\n", g);

return 0;
}

image-20251117211448009

  • 形式参数:(函数的参数or 形式参数)会被当成局部变量使用,注意事项与上方相同。
1
2
/* 添加两个整数的函数 */
int sum(int a, int b)

作为函数参数的 int a int b 被当成局部变量使用

数组

声明数组

基本格式

1
type arrayName [ arraySize ];

arrayName 为数组名称

type 可为任意有效类型 如int or float

arraySize 为一个大于0的整数常量

否则:

b12717f3e5d7c69af6123c929a211162_720

或者🤐

2f370dc91cdab110c47eec7bd9d40293_720

(其余自行学习)

enum (枚举)

C 函数指针与回调函数 | 菜鸟教程

类似于数组有一堆数值,但却是来定义一堆离散型的数值

  • 定义枚举类型的格式
1
enum 枚举名 {枚举元素1,枚举元素2,……};
  • 初始化枚举元素

默认第一个元素的值为0(没有定义量的前提),后续后一个比前一个多一(也是没有定义的前提下)

1
enum season {spring, summer=3, autumn, winter};

image-20251117213550248

  • 定义枚举变量

image-20251117213700400

枚举类型: enum Day

枚举变量: enum Day day

函数指针与回调函数

函数指针

定义:即指向函数的指针(可用于调用函数 or 传递参数)

1
typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型

eg.下面例子声明了函数指针p,用来指向函数max

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <stdio.h>

int max(int x, int y)
{
return x > y ? x : y;
}

int main(void)
{
/* p 是函数指针 */
int (* p)(int, int) = & max; // &可以省略
int a, b, c, d;

printf("请输入三个数字:");
scanf("%d %d %d", & a, & b, & c);

/* 与直接调用函数等价,d = max(max(a, b), c) */
d = p(p(a, b), c);

printf("最大的数字是: %d\n", d);

return 0;
}

这里运用函数参数 与直接运用函数一样

image-20251117214706249

回调函数

能够使 函数指针用来作为某个函数的参数

image-20251117215009676

下面具体分析某个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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);
for(int i = 0; i < 10; i++) {
printf("%d ", myarray[i]);
}
printf("\n");
return 0;
}
  • void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
    • 第三个参数 int (*getNextValue)(void)为一个函数指针
    • 它指向一个不接受参数 (void) 且返回 int 的函数
    • 这个就可每次调用函数来获值
  • populate_array函数
1
2
3
4
5
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
for (size_t i = 0; i < arraySize; i++)
array[i] = getNextValue(); // 调用回调函数获取值
}

每次循环都调用函数 getNextValue()来获取一个整数值

  • 回调函数 getNextRandomValue
1
2
3
4
int getNextRandomValue(void)
{
return rand(); // 返回随机数
}

每次都调用此函数来随机返回一个值给populate_array函数

字符串

字符串,即以 \0结尾的一堆字符数组,故 \0用来标记数组的结束(不用特地在字符串后面加上 \0 C编辑器在初始化数组时就会自动把 \0放在末尾

image-20251117222431421

在C中用来操作字符串的函数

image-20251117222601061

  • strcpy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <stdio.h>
#include <string.h>

int main ()
{
char str1[14] = "runoob";
char str2[14] = "google";
char str3[14];
int len ;

/* 复制 str1 到 str3 */
strcpy(str3, str1);
printf("strcpy( str3, str1) : %s\n", str3 );

image-20251117222722430

  • strcat
1
2
3
/* 连接 str1 和 str2 */
strcat( str1, str2);
printf("strcat( str1, str2): %s\n", str1 );

image-20251117222858864

  • strlen
1
2
3
4
5
6
   /* 连接后,str1 的总长度 */
len = strlen(str1);
printf("strlen(str1) : %d\n", len );

return 0;
}

image-20251117223011530

  • strchr(s1,ch) —用来指向s1字符中ch第一次出现的位置

即用来查找单个字符

1
2
3
4
5
6
7
8
int main() {
char str[] = "Hello, World!";
char ch = 'o';
char *result;

// 查找字符 'o' 第一次出现的位置
result
= strchr(str, ch);
  • strstr(s1,s2) 在字符串中寻找子字符串
1
2
3
4
5
6
7
8
int main() {
char str[] = "Hello, World! Welcome to C programming.";
char substr[] = "World";
char *result;

// 查找子字符串 "World" 第一次出现的位置
result
= strstr(str, substr);