1.字符指针变量
#include <stdio.h>
int main()
{
const char* pstr = "hello bit.";
printf("%s", pstr);
return 0;
}
这里是把字符串hello bit.首字符的地址放在pstr中,就是把一个常量字符串的首字符h的地址存放到指针变量pstr中。
让我们看一道题:
#include <stdio.h>
int main()
{
char str1[] = "hello bit.";
char str2[] = "hello bit.";
const char* str3 = "hello bit.";
const char* str4 = "hello bit.";
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if (str3 == str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
//结果是str1 and str2 are not same
// str3 and str4 are same
解释:
在str1和str2(字符数组)在栈上各自分配独立的内存空间,每个数组都会完整拷贝字符串"hello bit."的内容,str1和str2是不同的内存地址。
在str3和str4(字符指针)指向字符串常量(存储在只读数据段),编译器会对相同的字符串字面量进行优化,让他们共享同一块内存,str3和str4指向相同的内存地址。
2.数组指针变量
指针数组是一种数组,数组中存放的是地址(指针)。而数组指针是指针变量。
数组指针变量存放的应该是数组的地址,能够指向数组的指针变量。
int *p1[10];
int (*p2)[10];
在这段代码中:p1是指针数组—存放指针的数组;p2是指针变量,指向的是数组—数组指针。
所以数组指针的正确格式是:
int (*p)[10];
解释:p先和*结合,说明p是一个指针变量,然后指针指向的是一个大小为10个整型的数组。所以p是一个指针,指向一个数组,叫数组指针。
注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。
我们要对数组指针变量进行初始化:
int arr[10] = {0};
int (*p) [10] = &arr;
| | |
| | |
| | p指向数组的元素个数
| p是数组指针变量名
p指向的数组的元素类型
3.二维数组传参的本质
二维数组起始可以看做每个元素是一维数组的数组,也就是二维数组的每个元素是一个一维数组。那么二维数组的首元素就是第一行,是个一维数组。
那么根据数组名是数组首元素的地址,二维数组的数组名表示的就是第一行的地址,是一个一维数组的地址,那就意味着二维数组传参的本质上也是传递了地址,传递的是第一行这个一维数组的地址。那么形参也可以写成指针形式。如下:
#include <stdio.h>
void test(int(*p)[5], int r, int c)//int(*p)[5]也可以写成int[3][5]
{
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
test(arr, 3, 5);
return 0;
}
总结:二维数组传参,形参的部分可以写成数组,也可以写成指针形式。
4.函数指针变量
函数指针变量是用来存放函数地址的,未来通过地址能够调用函数的。
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("test: %p\n", test);
printf("&test: %p\n", &test);
return 0;
}
//结果是:test: 003D13CF
// &test: 003D13CF
从这个代码可以看出函数是有地址的,函数名就是函数的地址,也可以通过&函数名的方式获得函数的地址。
如果我们要将函数的地址存放起来就得创建函数指针变量:
void test()
{
printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)() = test;
int Add(int x, int y)
{
return x + y;
}
int (*pf3)(int, int) = Add;
int (*pf4)(int x, int y) = &Add;//x和y写上或者省略都可以
函数指针类型解析:
int (*pf3) (int x, int y)
| | ____________
| |
| | |
| | pf3指向函数的参数类型和个数
| 函数指针变量名
pf3指向函数的返回类型
int (*) (int x, int y) //pf3函数指针变量的类型
函数指针变量的使用:
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int (*pf3)(int x, int y) = Add;
printf("%d\n", (*pf3)(2, 3));
printf("%d\n", pf3(3, 5));
return 0;
}
//结果:5
// 8
5.typedef关键字
typedef是用来类型重命名的,可以将复杂的类型简单化。
例如:
typedef unsigned int uint
//将unsigned int 重命名为uint
对于指针类型我们要将int*重命名为ptr_t
typedef int* ptr_t;
但是对于数组指针和函数指针重命名的方式就有点区别了
比如我们有数组指针类型int(*)[5],需要重命名为parr_t
typedef void(*parr_t)(int);
//新的类型名必须在*的右边
函数指针类型的重命名也是一样的,比如:将void(*)(int)类型重命名为pf_t
typedef void(*pf_t)(int);
//新的类型名必须在*的右边
6.函数指针数组
如果要把一个函数的地址存到一个数组中,那这个数组就叫函数指针数组。
函数指针数组的定义:
int (*parr1[3])();
parr先和[]结合,说明parr1是数组,数组的内容是int (*)()类型的函数指针。
7.回调函数
回调函数的定义:回调函数是一个通过函数指针(或引用)来调用的函数。如果你把函数的指针(或引用)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
简单来说:你自己定义的,但你不直接调用,而是交给另一个函数去调用的函数。
例如:
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
void calc(int(*pf)(int, int))
{
int ret = 0;
int x, y;
printf("输⼊操作数:");
scanf("%d %d", &x, &y);
ret = pf(x, y);
printf("ret = %d\n", ret);
}
int main()
{
int input = 1;
do
{
scanf("%d", &input);
switch (input)
{
case 1:
calc(add);
break;
case 2:
calc(sub);
break;
case 3:
calc(mul);
break;
case 4:
calc(div);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
8.qsort函数
qsort函数的作用是对数组进行快速的排序,它是一个通用高效的排序函数,可以排序任何类型的数据。
void qsort (void* base, size_t num, size_t size,
int (*compar)(const void*,const void*));
参数:
base:指向要排序数组的第一个元素指针。
num:数组中元素个数。
size:每个元素的大小。
compar:指向比较两个元素的函数的指针。重复调用此函数以比较两个元素。它应遵循以下原型:qsort
int compar (const void* p1, const void* p2);
通过比较函数返回值:
负数:第一个参数小于第二个参数。
零:两个参数相等。
正数:第一个参数大于第二个参数。
例如:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char name[50];
int age;
float score;
} Student;
// 按姓名排序
int compare_by_name(const void *a, const void *b) {
Student *s1 = (Student*)a;
Student *s2 = (Student*)b;
return strcmp(s1->name, s2->name);
}
// 按年龄排序
int compare_by_age(const void *a, const void *b) {
Student *s1 = (Student*)a;
Student *s2 = (Student*)b;
return s1->age - s2->age;
}
// 按分数排序(降序)
int compare_by_score(const void *a, const void *b) {
Student *s1 = (Student*)a;
Student *s2 = (Student*)b;
if (s1->score > s2->score) return -1;
if (s1->score < s2->score) return 1;
return 0;
}
int main() {
Student students[] = {
{"Alice", 20, 85.5},
{"Bob", 22, 92.0},
{"Charlie", 19, 78.5},
{"David", 21, 88.0}
};
int n = sizeof(students) / sizeof(students[0]);
// 按姓名排序
qsort(students, n, sizeof(Student), compare_by_name);
printf("按姓名排序:\n");
for (int i = 0; i < n; i++) {
printf("%s, %d, %.1f\n", students[i].name, students[i].age, students[i].score);
}
// 按分数排序
qsort(students, n, sizeof(Student), compare_by_score);
printf("\n按分数降序排序:\n");
for (int i = 0; i < n; i++) {
printf("%s, %d, %.1f\n", students[i].name, students[i].age, students[i].score);
}
return 0;
}

被折叠的 条评论
为什么被折叠?



