一、什么是指针?
指针,也就是内存地址,指针变量是用来存放内存地址的变量。就像其他变量或常量一样,您必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
type *var_name;
在这里,type是指指针的基本类型,它可以是任意基本数据类型,但需要注意的是,指针变量p本身也是一个变量,也需要占据存储空间(32位系统下占4个字节,64位系统下占8个字节),同样也有地址。这里对指针的使用进行演示:
#include <stdio.h>
int main() {
int a = 10;
int* p;//定义一个整型指针变量p
p = &a;//将整型变量a的地址赋给指针p,&为取地址符
printf("%d\n", *p);//*为解引用符,与指针p结合使用可以找到p地址里存放的内容
//这里的打印结果为a的值:10
printf("%p\n", p);//打印结果为此时a的地址:000000A857CFFB94
//(地址随编译环境改变而改变,不唯一)
printf("%p\n", &a);//打印结果同上,为:000000A857CFFB94
}
有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作。
二、指针的运算
1.指针的加一与减一
在C中,指针加一指的是增加一个存储单元,对数组而言,这意味着加一后的地址是下一个元素的地址,而不是下一个字节的地址。
存储单元的意思是对应类型的空间,比如int类型的指针,一个存储单元是四字节空间。对于字符串变量,里面的每一个字符都独立占用一个对应类型的存储空间,用指针表示法定义一个字符串变量后,指针++就是下一个元素的地址。而且对于自增这种写法,只有指针表示法可以用。
例如:
#include <stdio.h>
int main() {
int a[10] = { 1,2,3,4,5 };
int* p = a;//将数组的首地址赋给指针p
p++;//p的加一能力
printf("%d\n", *p);//打印结果为a[1]的值,即:2
}
指针的减一能力与加一原理相同,使用时注意不要让指针越界。
2.指针与指针的加减
加法运算:两个指针不能进行加法运算,这是非法操作,因为两个地址进行加法后,得到的结果指向一个不知所向的地方,而且毫无意义。
减法运算:两个指针可以进行减法操作,但必须类型相同,一般用在数组方面。如果两个指针变量都指向同一数组元素,则两个指针变量值之差是两个指针之间的元素个数。在这里也同样需要注意不要让指针轻易越界,使用减法要求两个指针必须是指向同一数组元素或者指向数组元素最后一个元素之后的第一个元素,只有在这种情况下,两个指针的减法运算才有意义。
下面进行代码演示:
#include <stdio.h>
int main() {
int a[10] = { 1,2,3,4,5 };
int* p1 = &a[1];
int* p2 = &a[4];
printf("%d\n", p2 - p1); //打印结果为元素间隔数:3
}
三、指针的类型
1.指针数组
指针数组的一般声明为:
type *p[n];
[ ]的符号优先级高,先与p结合成为一个数组,再由type*说明这是一个什么类型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
指针数组用于存储多个指针,例如,可以存储多个字符串的指针。
int *ptrArr[10]; // 定义一个包含 10 个指向整数的指针的数组
ptrArr
是一个指针数组,其中每个元素都是一个 int
类型的指针。例如,ptrArr[0]
是一个 int*
指针
2.数组指针
数组指针是指向数组的指针。具体来说,它指向整个数组的起始地址,通常用于处理多维数组。
数组指针的一般声明为:
type (*pointer)[size]
这里的 type
是数组中元素的类型,size
是数组的大小。
用法:用于指向一个特定大小的数组。例如,int (*ptr)[10]
是一个指向包含 10 个 int
元素的数组的指针。
int arr[10]; // 定义一个包含 10 个整数的数组
int (*ptr)[10] = &arr; // ptr 是一个指向包含 10 个整数的数组的指针
通过 ptr
可以访问 arr
数组中的元素。例如,(*ptr)[0]
就是 arr[0]
。
数组指针与指针数组的区别:
- 通过数组指针可以访问整个数组,或对其进行迭代。
- 通过指针数组可以访问存储在每个指针中的不同数据或指向的不同内存地址。
3.函数指针
函数指针是 C 语言中的一种特殊类型的指针,用于指向函数的地址。它们允许你在运行时选择要调用的函数,这为动态函数调用和回调函数提供了灵活性。以下是函数指针的定义:
return_type (*pointer_name)(parameter_types);
return_type
是函数的返回类型。pointer_name
是函数指针的名字。parameter_types
是函数接受的参数类型列表。
定义和初始化函数指针:
#include <stdio.h>
// 定义一个函数
int add(int a, int b) {
return a + b;
}
int main() {
// 定义一个函数指针并将其指向函数 add
int (*func_ptr)(int, int) = add;
// 使用函数指针调用函数
int result = func_ptr(5, 3); // 相当于调用 add(5, 3)
printf("Result: %d\n", result);
return 0;
}
函数指针作为参数:函数指针可以作为参数传递给其他函数,这使得函数调用
#include <stdio.h>
// 定义一个函数
int add(int a, int b) {
return a + b;
}
int subtract(int a, int b) {
return a - b;
}
// 接受函数指针作为参数的函数
void perform_operation(int (*operation)(int, int), int x, int y) {
int result = operation(x, y);
printf("Result: %d\n", result);
}
int main() {
perform_operation(add, 5, 3); // 使用 add 函数
perform_operation(subtract, 5, 3); // 使用 subtract 函数
return 0;
}
函数指针的应用:
-
回调函数:函数指针常用于实现回调函数,允许你在特定事件发生时调用不同的函数。这在事件驱动编程和库函数中很常见。
-
动态函数调用:你可以在运行时决定调用哪个函数,通过函数指针动态地选择函数。
-
实现策略模式:在需要在不同的情况下执行不同策略时,函数指针可以用来选择具体的实现。
函数指针为 C 语言程序提供了很大的灵活性,使得函数的调用和执行能够根据程序的需求进行动态调整。
四、用指针进行大小端判断
在计算机系统中,“大小端”是指数据在内存中存储的顺序。大小端的概念主要涉及到多字节数据类型(如 int
、float
等)在内存中的存储方式。
大端模式(Big-endian):数据的高字节存储在低地址,低字节存储在高地址。例如,0x12345678
在内存中的存储顺序是 12 34 56 78
,其中 12
存储在最低地址。
小端模式(Little-endian):数据的低字节存储在低地址,高字节存储在高地址。例如,0x12345678
在内存中的存储顺序是 78 56 34 12
,其中 78
存储在最低地址。
实现代码为:
#include <stdio.h>
int main() {
// 定义一个整数并初始化
unsigned int num = 0x1; // 0x1 在内存中只有一个字节
// 获取该整数的字节地址
unsigned char *bytePtr = (unsigned char *)#
// 判断字节序
if (bytePtr[0] == 1) {
printf("System is Little-endian\n");
} else {
printf("System is Big-endian\n");
}
return 0;
}
这里通过强转数据类型来获取字节地址:
unsigned char *bytePtr = (unsigned char *)#
通过将 num
的地址强制转换为 unsigned char*
类型,bytePtr
就是一个指向 num
变量首地址的 unsigned char
指针,这样可以逐字节访问内存内容。
如果输出 System is Little-endian
,则表示系统使用小端模式。
如果输出 System is Big-endian
,则表示系统使用大端模式。
五、const与指针的结合
在 C 语言中,const
关键字与指针结合使用可以确保数据不被修改,提供更安全的编程方式。const
修饰符用于表示数据的只读性,与指针结合时可以有多种用法。下面是几种常见的 const
与指针结合的应用方式:
1.const指针(指向常量的指针)
一般定义为:
const type *ptr;
示例:
#include <stdio.h>
int main() {
int value = 10;
const int *ptr = &value; // 指向整数的指针,不能通过 ptr 修改 value
// *ptr = 20; // 错误,不能修改指向的值
printf("Value: %d\n", *ptr);
return 0;
}
在这里,ptr
是一个 const int*
类型的指针,意味着你不能通过 ptr
修改 value
的值,只能读取它。
2.const指针(指针本身是常量)
定义:
type *const ptr;
示例:
#include <stdio.h>
int main() {
int value = 10;
int other_value = 20;
int *const ptr = &value; // 指针 ptr 是常量,不能改变指向的位置
*ptr = 15; // 可以修改指向的数据
// ptr = &other_value; // 错误,不能改变 ptr 的指向
printf("Value: %d\n", *ptr);
return 0;
}
在这个示例中,ptr
是一个 int* const
类型的指针,意味着指针的值(即它指向的内存地址)不能改变,但可以通过 ptr
修改指向的值。
3.const指针常量(既是常量指针也指向常量)
定义:
const type *const ptr 或 type const *const ptr
示例:
#include <stdio.h>
int main() {
int value = 10;
const int *const ptr = &value; // 指针和指向的数据都是常量
// *ptr = 15; // 错误,不能修改指向的数据
// ptr = &other_value; // 错误,不能改变 ptr 的指向
printf("Value: %d\n", *ptr);
return 0;
}
在这个示例中,ptr
是一个 const int* const
类型的指针,意味着 ptr
既不能改变指向的位置,也不能修改指向的数据。
通过 const
关键字,可以确保函数不会意外修改传入的参数。通过合理使用 const
和指针,可以提高程序的安全性、可读性和维护性,避免意外的数据修改和指针错误。
六、结语
以上就是我对指针模块基础知识点的一些总结,欢迎小伙伴们在评论区发布讨论,一起交流。