Initial commit: C language learning code

This commit is contained in:
2025-07-20 16:30:56 +08:00
commit 06e24173a6
139 changed files with 9303 additions and 0 deletions
+57
View File
@@ -0,0 +1,57 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>
// !指针函数
/*
* 函数指针
* C语言允许将函数的地址存储在指针变量中,这种指针称为函数指针。函数指针可以指向任何符合特定签名的函数。
* 例如,int (*func_ptr)(int, int) 表示一个指向接受两个int参数并返回int的函数的指针。
* 函数指针的声明和使用
* 1.声明函数指针
* int (*func_ptr)(int, int);
* 这表示func_ptr是一个指向接受两个int参数并返回int的函数的指针。
* 函数指针是一种数据类型,可以像其他指针一样使用。
* 函数指针变量不能进行算术运算,这是与数组指针变量不同的。数组指针变量加减一个整数可使指针移动,指向数组的其他元素,而函数指针的移动是毫无意义的。
1.动态灵活性
可在运行时动态绑定不同函数,实现同一接口调用不同逻辑(如根据配置选择算法),避免静态代码膨胀。
2.回调机制支持
便于实现回调函数(如事件监听、异步处理),使代码结构更松耦合,增强模块间交互能力。
3.函数作为数据存储
可将函数存入数组、结构体等数据结构,方便批量管理或按条件调度(如状态机、菜单系统)。
4.简化复杂逻辑
在算法(如排序、搜索)中通过函数指针传递比较规则,避免为不同规则重复编写代码,提升复用性。
5.底层编程优势
在操作系统、驱动开发等场景中,可直接操作函数地址,实现动态链接、热更新等底层功能。
*/
// 普通函数:加法
int add(int a, int b)
{
return a + b;
}
// 普通函数:乘法
int multiply(int a, int b)
{
return a * b;
}
int main()
{
// 声明函数指针,指向返回int、参数为(int, int)的函数
int (*op)(int, int);
// 动态赋值为加法函数
op = add;
printf("5 + 3 = %d\n", op(5, 3)); // 输出:8
// 动态切换为乘法函数
op = multiply;
printf("5 * 3 = %d\n", op(5, 3)); // 输出:15
return 0;
}
+80
View File
@@ -0,0 +1,80 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>
// !动态内存分配
/*
1.输入数据
如果输入数据时,先告诉你个数,然后再输入,要记录每个数据
C99可以用变量做数组定义的大小,C99之前呢?
int *a = (int*)malloc(n*sizeof(int));
2.malloc
#include <stdlib.h>
void* malloc(size_t size);
向malloc申请的空间的大小是以字节为单位的
返回的结果是void*,需要类型转换为自己需要的类型
(int*)malloc(n*sizeof(int))
float *pf;
pf=(float *)malloc(5*sizeof(float));
3.calloc
#include <stdlib.h>
void* calloc(size_t n, size_t size);
分配n个元素,每个元素的大小是size字节
calloc会把申请的空间初始化为0
malloc不会初始化
int *pi;
pi=(int *)calloc(5,sizeof(int));
4.free()
把申请得来的空间还给“系统”
申请过的空间,最终都应该要还
混出来的,迟早都是要还的
只能还申请来的空间的首地址
void free(void *p);
*/
int main(void)
{
/* 1.malloc */
int number;
int *a;
int i;
printf("请输入数据个数:");
scanf("%d", &number);
// int a[number];
// 定义可变数组
a = (int *)malloc(number * sizeof(int));
for (i = 0; i < number; i++)
{
scanf("%d", &a[i]);
}
for (i = number - 1; i >= 0; i--)
{
printf("%d ", a[i]);
}
free(a);
/* 2. */
void *p;
int cnt = 0;
while (p = malloc(100 * 1024 * 1024))
{
cnt++;
free(p);
}
printf("%d100MB的空间\n", cnt);
/* 3. */
/*void *p;
int cnt = 0;*/
cnt = 0;
p = malloc(100 * 1024 * 1024);
p++;
free(p);
return 0;
}
@@ -0,0 +1,40 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>
// !运算符
/*
scanf("%d", &i);里的&
获得变量的地址,它的操作数必须是变量
int i; printf("%x"&i);
地址的大小是否与int相同取决于编译器
int i; printf("%p", &i);
*/
int main()
{
// 变量的地址
int a = 10;
int b = 20;
printf("&a = %p\n", &a);
printf("&b = %p\n", &b);
// &操作符的sizeof
int x;
printf("sizeof(&x) = %zu\n", sizeof(&x)); // 32位系统输出4,64位系统输出8
// 数组的地址
int arr[3] = {1, 2, 3};
printf("arr地址 = %p\n", (void *)arr); // 0x7ffee3a45680
printf("&arr[0] = %p\n", &arr[0]); // 0x7ffee3a45680
printf("&arr = %p\n", &arr); // 0x7ffee3a45680(值相同但类型不同)
// 数组单元的地址
printf("&arr[0] = %p\n", &arr[0]); // 0x7ffee3a45680
printf("&arr[1] = %p\n", &arr[1]); // 0x7ffee3a45684(相差4字节)
printf("&arr[2] = %p\n", &arr[2]); // 0x7ffee3a45688(继续相差4字节)
return 0;
}
@@ -0,0 +1,43 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>
// !指针和const
/*
指针本身和所指的变量都可能const
1.指针是const
表示一旦得到了某个变量的地址,不能再指向其他变量
int *const q= &i; // q是const
*q=26; // OK
q++; // ERROR
2.所指是const
表示不能通过这个指针去修改那个变量(并不 能使得那个变量成为const)
const int *p = &i;
*p = 26; // ERROR! (*p)是 const
i=26; //OK
p=&j; //OK
3.判断:const在*前边,是表示不能通过这个指针去修改那个变量;const在*后边,是表示一旦得到了某个变量的地址,不能再指向其他变量
*/
// !const数组
/*
const int a[]= {1,2,3,4,5,6,};
数组变量已经是const的指针了,这里的const
表明数组的每个单元都是const int
所以必须通过初始化进行赋值
*/
// !保护数组值
/*
因为把数组传入函数时传递的是地址,所以那个函数 内部可以修改数组的值
为了保护数组不被函数破坏,可以设置参数为const
int sum(const int a[], int length);
*/
int main(void)
{
return 0;
}
+54
View File
@@ -0,0 +1,54 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>
// !数组是一种特殊的指针,数组名就是数组的首地址
/*
数组变量本身表达地址,所以
int a[10]; int*p=a;//无需用&取地址
但是数组的单元表达的是变量,需要用&取地址
a == &a[O]
[]运算符可以对数组做,也可以对指针做:
p[0] <==>a[0]
*运算符可以对指针做,也可以对数组做:
*a= 25:
数组变量是const的指针,所以不能被赋值
int a[] <==> int * const a=...
*/
void minmax(int a[], int len, int *max, int *min);
int main(void)
{
int c[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 16, 17, 21, 23, 55};
int min, max;
printf("min sizeof(c) = %lu\n", sizeof(c));
minmax(c, sizeof(c) / sizeof(c[0]), &max, &min);
printf("max = %d, min = %d\n", max, min);
int *p = &min;
printf("*p = %d\n", *p);
printf("p[0]=%d\n", p[0]);
return 0;
}
void minmax(int a[], int len, int *max, int *min)
{
int i;
printf("minmax sizeof(a) = %lu\n", sizeof(a));
*max = *min = a[0];
for (i = 1; i < len; i++)
{
if (a[i] < *min)
{
*min = a[i];
}
if (a[i] > *max)
{
*max = a[i];
}
}
}
+41
View File
@@ -0,0 +1,41 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>
// !指针函数
/*
一个函数不仅可以返回整型、字符型等值,也可在C语言中,以返回指针类型的值,返回指针类型值的函数就称为指针函数。
1.定义指针函数的形式如下:
类型标识符 *函数名(形参表)
{
函数体
}
2.指针函数的调用与普通函数的调用相同,只要注意其返口值是一个指针。
* int (*p)()是一个变量声明,表示p是一个指向函数入口地址的指针变量(函数指针),该函数的返回值是整型数据,“(*p)”两边的括号不能少
* int *p()则是函数声明,表示p是一个指针函数,其返回值是一个指向整型数据的指针,“*p”两边没有括号。作为函数声明,在括号内最好写入形式参数,这样便于与变量声明区别。
*/
// !指针函数:返回指向整数的指针
int *max(int *a, int *b)
{
if (*a > *b)
return a;
else
return b;
}
int main()
{
int x, y;
printf("Enter two integers: ");
scanf("%d %d", &x, &y);
// 调用指针函数
int *result = max(&x, &y);
printf("The larger number is %d\n", *result);
return 0;
}
@@ -0,0 +1,46 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>
// !指针变量
/*
变量的值是内存的地址
普通变量的值是实际的值
指针变量的值是具有实际值的变量的地址
*/
// !访问那个地址上的变量*
/*
*p: p指向的变量的值*是一个单目运算符,用来访问指针的值所表示的地址上的变量
可以做右值也可以做左值
int k= *p;
*p=k+1;
*/
void f(int *p);
void f2(int k);
int main(void)
{
int i = 6;
printf("i = %d\n", i);
printf("&i = %p\n", &i); // %p打印i地址
f(&i);
f2(i);
return 0;
}
void f(int *p)
{
printf("p = %p\n", p); // %p打印p地址
printf("*p = %d\n", *p); // %d打印p指向的变量的值
*p = 26; // 修改p指向的变量的值
}
void f2(int k)
{
printf("k = %d\n", k);
}
+63
View File
@@ -0,0 +1,63 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>
// !指针数组
/*
* 指针数组是一个数组,其每个元素都是指针变量(存储内存地址)。它的本质是 “存储指针的数组”,核心作用是批量管理多个指针,适用于需要集中操作多个地址的场景(如字符串集合、动态内存块管理等)
1.类型* 数组名[数组长度]; // !类型* 表示数组元素是该类型的指针
在 C 语言中,字符串本质是char*指针(指向字符数组的首地址)。因此,指针数组是存储多个字符串的高效方式(仅存储字符串的地址,而非复制整个字符串内容)
2.本质是数组,元素是指针。
存储多个独立指针(如多个变量地址)。
3.指针数组通过集中存储多个指针,实现了对多数据的高效管理,尤其在处理字符串集合、动态内存块或需要批量操作指针的场景中,能显著提升内存 利用率和代码灵活性。
4.
int a = 10, b = 20, c = 30;
int *ptr_arr[3]; // 声明一个包含3个int指针的数组
ptr_arr[0] = &a; // 存储a的地址
ptr_arr[1] = &b; // 存储b的地址
ptr_arr[2] = &c; // 存储c的地址
*/
int main()
{
// 基本类型指针数组
int num1 = 5, num2 = 10;
int *num_ptrs[2] = {&num1, &num2};
printf("Numbers: ");
for (int i = 0; i < 2; i++)
{
printf("%d ", *num_ptrs[i]); // 输出:5 10
}
printf("\n");
// 字符串指针数组
char *fruits[] = {"Apple", "Banana", "Cherry"};
printf("Fruits: ");
for (int i = 0; i < 3; i++)
{
printf("%s ", fruits[i]); // 输出:Apple Banana Cherry
}
printf("\n");
// 二维数组行指针数组
int table[3][2] = {{1, 2}, {3, 4}, {5, 6}};
int *row_ptrs[3] = {table[0], table[1], table[2]};
printf("Table:\n");
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 2; j++)
{
printf("%d ", row_ptrs[i][j]); // 输出每行元素
}
printf("\n");
}
// 输出:
// 1 2
// 3 4
// 5 6
return 0;
}
+81
View File
@@ -0,0 +1,81 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>
// !指针应用场景
/*
1.函数返回多个值,某些值不方便直接返回,只能通过指针返回,传入的参数实际上是需要保存带回的结果的变量
2.函数返回运算的状态,结果通过指针返回。常用的套路是让函数返回特殊的不属于有效范围内的 值来表示出错:-1或0(在文件操作会看到大量的例子)。当任何数值都是有效的可能结果时,就得分开返
*/
void swap(int *pa, int *pb);
void minmax(int c[], int len, int *max, int *min);
int divide(int a, int b, int *result);
int main(void)
{
/* 1.交换两个变量的值 */
int a = 5;
int b = 6;
swap(&a, &b);
printf("a = %d, b = %d\n", a, b);
/* 2.返回多个值*/
int c[] = {1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55};
int min, max;
minmax(c, sizeof(c) / sizeof(c[0]), &max, &min);
printf("max = %d, min = %d\n", max, min);
/* 3.返回运算的状态 */
int d=5;
int e=6;
int f;
if (divide(d,e, &f))
{
printf("%d/%d = %d\n", d, e, f);
}
return 0;
}
void swap(int *pa, int *pb)
{
int t = *pa;
*pa = *pb;
*pb = t;
}
void minmax(int a[], int len, int *max, int *min)
{
int i;
*max = *min = a[0];
for (i = 1; i < len; i++)
{
if (a[i] < *min)
{
*min = a[i];
}
if (a[i] > *max)
{
*max = a[i];
}
}
}
int divide(int a, int b, int *result)
{
if (b==0)
{
return 0;
}
else
{
*result = a / b;
}
return 1;
}
+85
View File
@@ -0,0 +1,85 @@
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <stdbool.h>
// !指针运算
/*
1.指针p+1
指针p的值会自动增加一个存储单元的大小(sizeof(数据类型)),即指向下一个元素的存储单元
给一个指针加1表示要让指针指向下一个变量
int a[10];
int *p = a;
*(p+1)—>a[1]
如果指针不是指向一片连续分配的空间,如数组,则这种运算没有意义
即*(p+n)<——>ac[n]
2.*p++
取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
*的优先级虽然高,但是没有++高
常用于数组类的连续空间操作
在某些CPU上,这可以直接被翻译成一条汇编指令
3.指针比较
<,<=, ==,>,>=,!=都可以对指针做
比较它们在内存中的地址
数组中的单元的地址肯定是线性递增的
4.0地址
当然你的内存中有0地址,但是0地址通常是不能随便碰的地址,所以你的指针不应该具有0值
因此可以用0地址来表示特殊的事情: 返回的指针是无效的,指针没有被真正初始化(先初始化为0)
NULL是一个预定定义的符号,表示0地址
有的编译器不愿意你用0来表示0地址
5.指针的类型
无论指向什么类型,所有的指针的大小都是一样的,因为都是地址
但是指向不同类型的指针是不能直接互相
赋值的 这是为了避免用错指针
6.指针的类型转换
void*表示不知道指向什么东西的指针
计算时与char*相同(但不相通)
指针也可以转换类型
int *p = &i;void*q = (void*)p;
这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量
我不再当你是int啦,我认为你就是个void!
7.用指针来做什么
需要传入较大的数据时用作参数
传入数组后对数组做操作
函数返回不止一个结果
需要用函数来修改不止一个变量
*/
int main(void)
{
/* 1.指针p+1 */
char ac[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
char *p = ac; // 指向数组首元素的指针,相当于char *p = &ac[0]
char *p1 = &ac[5];
printf("p = %p\n", p); // %p是以16进制输出地址
printf("p+1 = %p\n", p + 1);
printf("p1-p = %d\n", p1 - p);
int ad[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
int *q = ad;
int *q1 = &ad[6];
printf("q = %p\n", q); // %p是以16进制输出地址
printf("q+1 = %p\n", q + 1);
printf("q1-q = %d\n", q1 - q);
/* 2.*p++ */
char ae[] = {0,1,2,3,4,5,6,7,8,9,-1};
char *p2 = ae;
int i = 0;
for (i = 0; i < sizeof(ae) / sizeof(ae[0]); i++)
{
printf("%d\n", ae[i]);
}
for (p2 = ae;*p2!=-1;p2++) // *p2++先取值,再指向下一个元素
{
printf("%d\n", *p2);
}
/* 4.0地址 */
char ai[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
char *q3 = ai;
q3=p; //
return 0;
}