目录
字符指针
字符指针指向字符串
使用字符指针
例子
指针数组
数组指针
数组名和&数组名
数组名
sizeof(数组名),&数组名
&数组名
&数组名错误使用方法
数组名和&数组名用于一维数组
例子1
例子2
数组名和&数组名用于二维数组
例子1
例子2
函数指针
通过函数指针调用函数
字符指针
在C语言中,字符指针是一种特殊的指针类型,用于指向字符数据的内存地址。
字符指针的声明形式为:
char *字符指针名=字符串/字符串;
这种形式在c语言是允许的,但是c++禁止char*指针指向常量字符串
字符指针指向字符串
char* str = "Hello,World!";
这意味着将字符串的首字符的地址赋给字符指针
我们可以验证一下
#include<stdio.h>
int main()
{
char* str = "Hello,World!";
printf("%p\n", str);
printf("%p\n", "Hello,World!");
printf("%p\n", &str[0]);
printf("%c\n", str[0]);
}
结果
事实证明确实如此
使用字符指针
使用字符指针时,有几个需要注意的地方:
-
字符指针的初始化:在定义字符指针时,可以直接赋值一个字符串常量,也可以通过动态内存分配来初始化指针。例如:
char *str1 = "Hello World"; // 字符指针指向字符串常量 char *str2 = malloc(20); // 字符指针指向动态分配的内存
注意,字符串常量是不可修改的,所以使用字符指针指向字符串常量时要谨慎,确保不会试图修改字符串。
-
字符指针的解引用:使用字符指针访问字符串中的字符时,可以使用指针自增运算符
++
来实现。例如:char *str = "Hello"; while (*str) { printf("%c", *str++); }
这里的
*str++
先取出str
指针指向的字符,然后将指针自增,指向下一个字符。 -
字符指针的比较:可以使用比较运算符
==
、!=
、<
、>
、<=
、>=
来比较字符指针的大小。例如:char *str1 = "apple"; char *str2 = "banana"; printf("%d", str1 < str2); // 输出1,表示str1在str2之前
字符指针的比较是基于内存地址的,指向内存地址较低的字符指针比较小。
-
字符指针的修改:字符指针可以通过赋值来修改指向的字符串。但是,如果字符指针指向的是字符串常量,则不能修改指针指向的内容。例如:
char *str = "Hello"; str[0] = 'h'; // 错误,试图修改字符串常量
如果需要修改字符串,可以使用字符数组来替代字符指针。
-
字符指针的空指针检查:在使用字符指针之前,建议先进行空指针检查。例如:
char *str = NULL; if (str == NULL) { printf("Pointer is NULL\n"); }
避免在空指针上进行解引用操作,以防止程序崩溃或产生未定义行为。
例子
可以使用字符指针来访问字符串和字符数组,例如:
#include <stdio.h>
int main() {
char *str = "Hello, World!"; // 字符串常量
char arr[] = "Hello, World!"; // 字符数组
printf("%s\n", str); // 输出字符串
printf("%c\n", *str); // 输出第一个字符
printf("%s\n", arr); // 输出字符数组
printf("%c\n", arr[0]); // 输出第一个字符
return 0;
}
结果
指针数组
在C语言中,指针数组是指一个数组,其每个元素都是指针类型的变量。
指针数组可以用于存储一组指针,每个指针可以指向不同的数据。
指针数组的声明方式为:
指针类型 *数组名[数组大小]
以下是一个指针数组的示例:
int main() {
int num1 = 10, num2 = 20, num3 = 30;
int *ptrArray[3]; // 声明一个包含3个int类型指针的数组
ptrArray[0] = &num1; // 指针数组的第一个元素指向num1
ptrArray[1] = &num2; // 指针数组的第二个元素指向num2
ptrArray[2] = &num3; // 指针数组的第三个元素指向num3
printf("%d\n", *ptrArray[0]); // 输出10,访问指针数组的第一个元素所指向的值
printf("%d\n", *ptrArray[1]); // 输出20,访问指针数组的第二个元素所指向的值
printf("%d\n", *ptrArray[2]); // 输出30,访问指针数组的第三个元素所指向的值
return 0;
}
在上述示例中,声明了一个包含3个int类型指针的指针数组ptrArray。然后,通过将指针数组的元素指向各个变量,可以实现对这些变量的间接访问。通过解引用操作符 * 和数组下标,可以访问对应元素所指向的值。
指针数组在实际应用中有很多用途,比如可以用来存储字符串数组、函数指针数组等。
数组指针
在C语言中,数组指针是指一个指向数组的指针变量。
数组指针可以用来访问数组元素,也可以用来处理多维数组。
数组指针的声明方式为:
数据类型 (*指针变量名)[数组大小];
数组名和&数组名
在C语言中,&数组名
和数组名
的运算结果是不同的。
数组名
在C语言中,数组名是一个特殊的标识符,大多数情况下它表示数组的首元素的地址。
当使用数组名时,它会自动转换为指向数组第一个元素的指针。这意味着可以通过数组名来访问数组中的元素。
下面是一个示例代码来说明数组名的用法:
#include <stdio.h>
int main() {
int numArray[5] = {1, 2, 3, 4, 5};
printf("%p\n", numArray); // 输出数组的首地址
printf("%p\n",&numArray[0]);
return 0;
}
输出结果如下:
0x7fff5a40ce90
0x7fff5a40ce90
可以看到,numArray
输出的是数组的首地址。
在一些情况下,可以将数组名用作指针来访问数组元素。例如,可以使用numArray[0]
来访问数组的第一个元素,使用numArray[1]
来访问数组的第二个元素,依此类推。
需要注意的是,数组名作为指针时是常量,不能对其进行赋值操作。也就是说,不能将数组名指向另一个数组或另一个地址。
sizeof(数组名),&数组名
绝大多数情况下,数组名代表数组首元素地址
但是有两种例外情况,sizeof(数组名)和&数组名,这里的数组名都代表整个数组
int numArray[5] = { 1, 2, 3, 4, 5 };
printf("%d\n", sizeof(numArray));
printf("%p\n", &numArray);// 输出整个数组的地址
运行结果是
&数组名
&数组名
:取得是整个数组的地址,也就是数组的首地址。
当使用&数组名
时,它返回的是整个数组的指针,指向整个数组的存储空间。
这与使用数组名(
返回的是指向数组第一个元素的指针)有所不同。
&数组名一般用于二维指针
对于二维数组,&数组名
表示整个二维数组的地址,二维数组的地址是数组首元素的地址,二维数组首元素是个数组。
所以&数组名,表示都是指向数组的指针。
下面是一个示例代码来说明&数组名
的用法:
#include <stdio.h>
int main() {
int matrix[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
printf("%p\n", &matrix); // 输出整个二维数组的地址
printf("%p\n", matrix); // 输出二维数组的首行地址,也即第一个子数组的地址
printf("%p\n", &matrix[0]); // 输出二维数组的首行地址,与上一行等效
printf("%p\n", matrix[0]); // 输出二维数组的首行地址,与上两行等效
printf("%p\n", &matrix[0][0]); // 输出二维数组的第一个元素的地址
return 0;
}
输出结果如下:
0x7fff5a40ce90
0x7fff5a40ce90
0x7fff5a40ce90
0x7fff5a40ce90
0x7fff5a40ce90
可以看到,&matrix
、matrix
、&matrix[0]
、matrix[0]
和&matrix[0][0]
都输出了相同的地址,即整个二维数组的地址。
需要注意的是,二维数组的地址类型是指向一维数组的指针,即int (*)[3]
。这是因为在内存中,二维数组是按行存储的,每行是一个一维数组。
因此,对于二维数组,&数组名
表示整个数组的地址,数组名
表示首行的地址,&数组名[0]
和数组名[0]
表示第一行的地址,&数组名[0][0]
表示第一个元素的地址。
总结起来,&数组名
表示获取整个数组的地址,返回的是指向整个数组的指针。
&数组名错误使用方法
我们看个例子
int a[5]={1,2,3,4,5};
int(*p)[5]=&a;
printf("%d",*p);//这是错误的
p是指向数组的,*p相当于数组名,数组名又相当于数组首元素首地址,以%d打印出来就是一堆乱码
数组名和&数组名用于一维数组
例子1
下面是一个示例代码来说明这两者的区别:
#include <stdio.h>
int main() {
int numArray[5] = {1, 2, 3, 4, 5};
printf("%p\n", &numArray); // 输出数组的首地址
printf("%p\n", numArray); // 输出数组的首地址
printf("%p\n", &numArray[0]); // 输出数组的首地址
printf("%p\n", &numArray + 1); // 输出整个数组占用的内存空间后面的地址
printf("%p\n", numArray + 1); // 输出数组第二个元素的地址
printf("%p\n", &numArray[1]); // 输出数组第二个元素的地址
return 0;
}
输出结果如下:
0x7fffc7f12680
0x7fffc7f12680
0x7fffc7f12680
0x7fffc7f12694
0x7fffc7f12684
0x7fffc7f12684
可以看到,&numArray
、numArray
和&numArray[0]
的值都是相同的,都是数组的首地址。而&numArray + 1
是整个数组占用的内存空间后面的地址,而numArray + 1
和&numArray[1]
是数组第二个元素的地址。
需要注意的是,当数组作为函数参数传递时,实际上传递的是数组的指针(首地址),而不是整个数组。这是因为数组在参数传递过程中会自动退化为指针。因此,函数名
和&函数名
的结果是相同的,都是函数的地址。
总结起来,&数组名
表示整个数组的地址,而数组名
表示数组的首元素的地址。在大多数情况下,使用数组名
会自动退化为指向第一个元素的指针。
例子2
以下是一个数组指针的示例:
int main() {
int numArray[5] = {1, 2, 3, 4, 5};
int (*ptr)[5]; // 声明一个指向包含5个int类型元素的数组的指针变量
ptr = &numArray; // 数组指针指向numArray数组的首地址
printf("%d\n", (*ptr)[0]); // 输出1,访问数组指针指向的数组的第一个元素
printf("%d\n", (*ptr)[1]); // 输出2,访问数组指针指向的数组的第二个元素
printf("%d\n", (*ptr)[2]); // 输出3,访问数组指针指向的数组的第三个元素
return 0;
}
在上述示例中,声明了一个包含5个int类型元素的数组numArray。然后,声明了一个指向包含5个int类型元素的数组的指针变量ptr,并将其指向numArray数组的首地址。
通过解引用操作符 * 和圆括号来定义操作符优先级,可以访问数组指针指向的数组的元素。在示例中,使用(*ptr)[index]的方式访问数组元素,其中index表示要访问的元素的下标
数组名和&数组名用于二维数组
int a[2][4]={{1,2,3,4},{5,6,7,8}};
int (*b)[4]=a;
二维数组数组名代表它第一行的地址
此时a代表二维数组首元素地址,但二维数组首元素是个数组啊,所以此时b指向了一个一维数组
我们再看下面这种情况
int a[2][4]={{1,2,3,4},{5,6,7,8}};
int (*c)[4]=&a;
此时&a代表整个二维数组的地址,也就是说b接受了一个二维数组的地址,但是b是一维数组指针,所以&a被隐式转换为a第一个子数组的地址
我们可以来验证一下
我们可以看到啊,b,c完全是一样的,均指向a[0]这个数组的地址。
a的类型是int[2][4],赋给b时存在隐式类型转换
&a代表整个二维数组的地址,是int(*)[2][4],这与c的int(*)[4]不同,所以赋给c时也存在隐式转换
事实上
下面这种情况也是一样的道理
int a[2][4] = { {1,2,3,4},{5,6,7,8} };
int(*d)[2][4] = a;
int(*e)[2][4] = &a;
例子1
数组指针可以用于处理多维数组。例如,可以声明一个指向二维数组的指针来访问二维数组的元素。
int main() {
int twoDimArray[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*ptr)[3]; // 声明一个指向包含3个int类型元素的数组的指针变量
ptr = twoDimArray; // 数组指针指向twoDimArray二维数组的首地址
printf("%d\n", (*ptr)[0]); // 输出1,(*ptr)代表twoDimArray[0]
printf("%d\n", (*ptr)[1]); // 输出2,访问数组指针指向的二维数组的第二个元素
printf("%d\n", (*ptr)[2]); // 输出3,访问数组指针指向的二维数组的第三个元素
return 0;
}
这里的*ptr代表*(ptr+0),即twoDimArray[0]
在上述示例中,声明了一个包含2行3列的二维数组twoDimArray。然后,声明了一个指向包含3个int类型元素的数组的指针变量ptr,并将其指向twoDimArray数组的首地址。通过(*ptr)[index]的方式访问二维数组的元素。
数组指针在C语言中是一个灵活而强大的工具,可以用来处理数组和多维数组的访问和操作。
例子2
#include<stdio.h>
int main()
{
int twoDimArray[2][3] = { {1, 2, 3}, {4, 5, 6} };
int(*ptr)[3]; // 声明一个指向包含3个int类型元素的数组的指针变量
ptr = &twoDimArray; // 这里存在隐式转换
printf("%d\n", (*ptr)[0]); // 输出1,访问数组指针指向的二维数组的第一个元素
printf("%d\n", (*ptr)[1]); // 输出2,访问数组指针指向的二维数组的第二个元素
printf("%d\n", (*ptr)[2]);//输出3
}
这里的*ptr代表*(ptr+0),即twoDimArray[0]
结果
1
2
3
和上面那个例子一样
函数指针
C语言函数指针是指向函数的指针变量。它可以用来存储函数的地址,并且可以通过函数指针调用函数。
对于函数而言,函数名和&函数名都是函数的地址
函数指针的声明格式为:
返回类型 (*指针变量名)(参数类型1, 参数类型2, ...);
以下是一个简单的例子:
#include <stdio.h>
// 函数声明
int add(int a, int b);
int main() {
// 定义函数指针变量
int (*ptr)(int, int);
// 将函数的地址赋值给指针变量
ptr = add;
// 通过指针调用函数
int result = ptr(3, 4);
printf("Result: %d\n", result);
return 0;
}
// 函数定义
int add(int a, int b) {
return a + b;
}
在上面的例子中,我们声明了一个名为ptr
的函数指针变量,类型为int (*)(int, int)
,即指向一个具有两个int
类型参数并返回int
类型的函数。然后,我们将add
函数的地址赋值给ptr
,并通过ptr
调用add
函数,将结果打印出来。输出结果为7
。
函数指针在C语言中常用于实现回调函数、函数式编程等高级技术。它可以动态地决定需要调用哪个函数,提高了程序的灵活性和可扩展性。
通过函数指针调用函数
在C语言中,可以通过函数指针调用函数。具体步骤如下:
-
声明函数指针:首先需要声明一个函数指针,指定函数的返回类型和参数类型。例如,要声明一个指向返回类型为
int
,参数类型为int
的函数的指针,可以这样写:int (*ptr)(int);
。 -
赋值函数地址:将要调用的函数的地址赋值给函数指针变量。例如,如果有一个名为
add
的函数,可以将其地址赋值给ptr
:ptr = add;
。 -
通过函数指针调用函数:通过函数指针变量调用函数,可以像调用普通函数一样使用函数指针。通过使用指针变量的名称(加上括号和参数),可以调用所指向的函数。例如,可以使用
ptr(3)
来调用函数add(3)
。
下面是一个简单的例子来说明如何通过函数指针调用函数:
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
int main() {
int (*ptr)(int, int); // 声明函数指针
ptr = add; // 将add函数的地址赋给指针变量
int result = ptr(3, 4); // 通过指针变量调用add函数
printf("Result of add: %d\n", result);
ptr = multiply; // 将multiply函数的地址赋给指针变量
result = ptr(3, 4); // 通过指针变量调用multiply函数
printf("Result of multiply: %d\n", result);
return 0;
}
输出结果为:
Result of add: 7
Result of multiply: 12
在上面的例子中,我们声明了一个名为ptr
的函数指针变量,类型为int (*)(int, int)
,即指向一个具有两个int
类型参数并返回int
类型的函数。然后,我们将add
函数和multiply
函数的地址分别赋值给ptr
,并通过ptr
调用这两个函数。最后,将结果打印出来。