Initial commit: C language learning code
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user