目录
-----------------------------------------------------------------------------------------------------
初始化
char *p
的初始化
char *p
是一个指向字符的指针,通常用于指向一个字符数组(即字符串)或单个字符。你可以通过以下方式初始化 char *p
:
-
指向一个字符串字面量:
char *p = "Hello, world!";
注意,字符串字面量是常量,存储在字符串常量区,不能通过
p
修改它的内容。 -
指向字符数组:
char arr[] = "Hello, world!"; char *p = arr; // `p` 指向字符数组 `arr` 的首元素
char **p
的初始化
char **p
是一个指向指针的指针,通常用于指向一个字符串数组或动态分配的二维字符数组。初始化 char **p
的方式有几种:
-
指向字符串数组:
char *array[] = {"Hello", "world", "!"}; // 字符串数组 char **p = array; // `p` 指向 `array` 的首元素
总结
char *p
是指向字符的指针,可以指向字符数组、字符串字面量,或者通过动态分配内存来初始化。char **p
是指向字符指针的指针,通常用于处理二维字符数组或字符串数组,可以通过指针数组初始化,也可以通过动态分配内存来初始化。
const注意点:
char*可以被赋给const char*,但是char**不能被赋给const char**
char**被赋给const char**时,会得到警告assignment from incompatible pointer type
也就是类型不兼容,这是因为char *和const char*的指向类型都为char,所以它们可以合法的赋值
但是char **与const char**的指向分别为char*与const char*,这两个显然不兼容,所以会得到警告
在 C 语言中,void*
类型被称作“万能指针”因为它可以指向任何类型的对象。使用 void*
的原因是 C 语言没有多态性,所以不能将基类指针安全地转换为派生类指针。void*
是一个通用的指针类型,允许存储任何类型对象的地址,但需要显式转换才能使用。
万能指针void*
的规则:
-
不能直接操作
void*
指针:当你有一个void*
指针时,你不能直接通过*
运算符来访问它所指向的内存,因为它没有类型。 -
需要类型转换:从
void*
类型转换为其他任何类型的指针时,都是合法的。但是,不能从任何有类型的指针转换到void*
。
示例
#include <stdio.h>
int main() {
int a = 10;
void* ptr = &a;
// 错误:不能直接解引用 void* 指针
// int value = *ptr;
// 必须进行类型转换后才可解引用
int value = *((int*)ptr);
printf("Value of a: %d\n", value);
return 0;
}
在上面这个例子中,我们定义了一个 int
类型的变量 a
,并创建了一个指向 a
的 void*
指针。我们想通过 ptr
来访问变量 a
的值,但是由于 ptr
是 void*
,我们不能直接解引用它。相反,我们首先将 ptr
转换为 int*
类型,然后再解引用。
使用 void*
的注意事项
-
内存对齐(Memory Alignment):当你使用
void*
指针时,许多操作系统和编译器会对内存进行对齐,这意味着你不能保证任意类型的指针与void*
指针的地址是能够正确对齐的。直接读写void*
指针指向的内存地址可能会导致未定义行为。 -
void*
无类型:void*
指针不包含关于它所指向的数据类型的信息。也就是说,使用void*
指针的时候,需要开发者来确保通过void*
指针操作的对象的类型是正确的。 -
内存泄漏风险:由于
void*
指针不具备类型信息,使用其进行内存管理(如malloc()
,calloc()
,realloc()
,free()
)时,容易出现内存泄漏或者访问非法内存空间的情况。
结论
void*
类型作为一个通用的指针类型,经常用于风格各异的上下文中,比如内存管理或者回调函数的参数传递。然而,出于安全和清晰的考虑,应该尽量避免在自己的代码中使用它。当确实需要使用 void*
时,确保你已经理解了类型转换的语义和注意事项。
指针操作字符串数组
char s[10] = "hello";//存放字符串数据
char *p = "hello";
char s[10]; //s的数据类型 char[10]
char[10] s1[3]; //二维数组 //此时的二维数组的元素 s1[3] 是一个一维字符型数组
//定义一个存放 字符串 的一维数组
char *p = "hello"; //p的类型 char *
//char *的指针变量p 相当于代表一个字符串
char *p1 = "hello";
char *p2 = "world";
char *p3 = "china";
char* pstr[3]= {"hello","world","china"}; //数组 --- 数组中存放是 各个字符串的地址
//地址 ---存放这地址数据的数组 --- 指针的数组 ---指针数组
在程序中
char* pstr[3]= {"hello","world","china"}
for(i=0;i<3;i++)
{
printf("%s\n",pstr[i]);// 注意调用方式
}
指针操作二维数组
int a[3][4] = {1,2,3,4,5,6,7,8,9};
a 还是首元素的地址 a[0]、a[0][0]
int[4] a[3]; //a数组名 -- &a[0]
a<=>&a[0] //值的角度
a[0] //数据类型 int[4]
&a[0] //int[4] * 指针类型
//标准C语法: int (*)[4]
定义变量:
int (*p)[4]; //数组指针
//基类型 int[4]这种类型 ---数组类型,与原来的int 基类型对比
*p //等价与 a[0]
//相当于是 int[4] 这个一维数组的数组名
*(*(p+0)+0) <=> a[0][0]
*(*(p+i)+j) <=> a[i][j] //从内到外,先确定行,再确定列
指针操作函数
int shortcmp(const void *a,const void *b) //const修饰的形参变量则说明该形参只读不修改。
回调函数:
函数名 --- 代表函数的入口地址
int add(int a,int b) // 函数
// 函数名 - 代表函数的入口地址
// 函数名对应的数据类型
// int (int a,int b) //函数类型
// 代表一类函数, 返回值为int型
//带有两个int型的形参变量
说明:
1.可以定义一个函数类型的指针变量
来保存函数的入口地址
2.有了这个指针变量
通过指针变量 进行函数调用
注意:这里调用函数 *p/*****p/p 都是一样的效果。
优点
- 灵活性和可扩展性:只需更改传递的回调函数,即可改变行为,而不需要修改执行它的函数。
- 代码解耦:调用者和被调用者之间没有紧密的耦合,允许独立修改。
- 代码复用:相同的函数可以通过不同的回调函数达到不同的目的。
总之,回调函数是 C 语言中很有用的工具,使我们能够编写灵活且可维护的代码。了解和利用回调函数可以帮助你更好地处理各种编程任务,特别是在事件驱动和异步编程中。
42 int add(int a,int b)
43 {
44 return a+b;
45 }
46
47 int sub(int a,int b)
48 {
49 return a-b;
50 }
51 int mul(int a,int b)
52 {
53 return a*b;
54 }
55 int div(int a,int b)
56 {
57 return a/b;
58 }
59 void processData(int a,int b,int(*p)(int,int))//函数回调
60 {
61 printf("%d\n",p(a,b));
62 }
63 int main(int argc, const char *argv[])
64 {
65 int a[] = {9,8,7,6,5,4,3,2,1};
66 int len = sizeof(a)/sizeof(a[0]);
67
68 // choieSortN(a,len,func1);
69
70 // printArray(a,len);
71 processData(6,4,div);
72 return 0;
73 }
main
函数的定义
main
函数的标准定义如下:
int main(int argc, char *argv[]) {
// 程序代码
return 0;
}
或者:
int main(int argc, char **argv) {
// 程序代码
return 0;
}
//int main(int argc, const char *argv[])
//argc 命令行参数的个数
//argv 存放命令行参数的 字符串的指针数组
int main(int argc, const char *argv[])
{
printf("argc = %d\n",argc);
int i = 0;
for (i = 0; i < argc;++i)
printf("argv[%d] = %s\n",i,argv[i]);
return 0;
}
参数解释
-
argc
(Argument Count):argc
是一个整数,表示传递给程序的命令行参数的数量。它包括程序的名称本身,所以最少值为 1。
-
argv
(Argument Vector):argv
是一个指向字符指针数组的指针。每个argv[i]
是一个指向命令行参数的字符串的指针。argv[0]
通常是程序的名称,argv[1]
、argv[2]
等是用户提供的其他参数。
示例代码
以下是一个简单的示例,演示如何使用 argc
和 argv
:
#include <stdio.h>
int main(int argc, char *argv[]) {
printf("Number of arguments: %d\n", argc);
printf("Program name: %s\n", argv[0]);
for (int i = 1; i < argc; i++) {
printf("Argument %d: %s\n", i, argv[i]);
}
return 0;
}
运行示例
假设编译后的程序名为 example
,你可以用以下方式运行它,并传递参数
运行果将是:
Number of arguments: 4
Program name: ./example
Argument 1: arg1
Argument 2: arg2
Argument 3: arg3
通过了解和合理使用 argc
和 argv
,你可以使你的程序更加灵活,能够处理多种输入情况
总结:
指针 + 二维数组
1.二维数组本质 ---一维数组
2.数组的特点 //连续性,有序性,单一性
3.指针类型的定义
4.指针访问数组元素的 过程
*(*(p+i) + j)
指针 + 指针数组 //
指针 + 函数