指针
指针定义
- 指针就是地址,分配一块内存,存储其他数据的地址信息;
- 定义的变量来存储数据,而指针存储地址,是一个十六进制数据;
- 定义指针
// 声明指针,分配内存,准备存储地址(此时未初始化,不能直接使用)
int* p1; // 分配4bytes or 8bytes内存
// int 表示指向的数据类型
// * 代表是指针
// p1是指针变量(内存的名称),用于存储地址数据
int a = 10;
p1 = &a; //初始化指针,指向a的地址(栈内存),才可以安全地使用;
float *p2; // * 可以跟类型,也可以跟变量
double* p3;
char* p4;
int a = 10; // 变量是内存地址的名称
p1 = &a; // & 取地址运算符 与 *指针运算符 (优先级最高,从右到左结合)
// 整型变量占用四个 字节,第一个字节的首地址存入p1;
printf("地址%d\n", p1);
printf("地址中的值%d\n", *p1); // * 解引用地址的值,相当于拿到了变量,可以赋值或读取值
- 指针 p1++; --p1则移动一个数据类型的长度,如int则移动四个字节;
- p1++先取地址,再加; ++p1 先加再取地址;
- *p1 解引用,相当于变量,&(*p1)获取变量地址;*(&a) 获取变量
- 一般初始化指针为NULL;
在指针未存储任何地址时,初始化为NULL(0x0的地址)
int *p = NULL;
if(p){// 存在
printf("指针p有指向");
}
if(!p){
printf("指针p无指向");
}
- 一维数组与指针
- int arr[] = {1,2,3}, *p;
- arr数组名称,即为数组的首地址,相当于&arr[0];
- arr[i] 通过索引获取元素值,&arr[i] 获取元素地址
- p = arr 赋值首地址,p++ 实现地址偏移,*p 获取元素值,*p++ 先获取值,在移动地址;也可以p[i]访问元素值;
#include <stdio.h>
// const定义一个常量
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *ptr;
/* 指针赋值 数组首地址 */
ptr = var;
// ptr = var + 1 // 等价 &var[1]
/* 数组中最后一个元素的地址 */
// ptr = &var[len-1];
for ( i = 0; i < len; i++)
{
printf("地址:var[%d] = %p\n", i, ptr );
printf("值:var[%d] = %d\n", i, *ptr );
/* 指向地址 移动到下一个元素 */
ptr++;
}
return 0;
}
输出如下,
地址:var[0] = e4a298cc
值:var[0] = 10
地址:var[1] = e4a298d0
值:var[1] = 100
地址:var[2] = e4a298d4
值:var[2] = 200
指针是十六进制的整数地址,可以比较大小;
指针还可与数组元素的地址进行比较,如 ptr <= &var[len-1] 就可以一直遍历。
- 二维数组与指针
- int arr[2][3] = {{1,2,3}, {5,7,11}}
- *(arr+0)、&arr[0]、&arr[0][0] 获取第一个元素1的地址;
- 获取5的地址->*(arr+1);
- 获取11的地址->*(arr+1) + 2,相当于arr[1] + 2;
- 获取元素地址
- arr[i] + j;
- *(arr+i)+j
- 获取元素值
- 索引获取 arr[i][j];
- 地址解引用 *(*(arr+i)+j)
// 为一个二维int 数组 连续赋值,然后依次输出所有元素
#include <stdio.h>
int main() {
int arr[2][3] = { 0 }; // 仅初始化一个0,数组长度必须为常量值
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
scanf("%d", *(arr + i) + j);
}
}
printf("\n索引访问数组:\n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
// 索引访问
printf("arr[%d][%d]=%d ", i, j, arr[i][j]);
}
printf("\n");
}
printf("\n地址访问数组:\n");
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
// 地址访问
printf("arr[%d][%d]=%d ", i, j, *(*(arr+i)+j));
}
printf("\n");
}
return 0;
}
非线程安全问题:

解决:
项目上右键->属性->C/C++ /命令行 输入:/D xxxx ,然后应用即可;

指针(地址)解析
指针的定义:
int a; // 定义一个整形变量,分配4bytes内存(32位系统),变量即为该地址空间的名称
a = 5; // 存入5
int* p;
p = &a; //赋值 a变量的首地址
指针的原理:

字符类型的指针,可表示字符串:
char* name;
name = "jack";
printf("姓名: %s\n", name); //指针指向首个字符的地址
指针的指针
定义一个指针,来存储其他指针的地址,这个指针就是指向指针的指针。
//定义在main函数
int arr[3]; // 定义整型数组,并分配内存
arr[0] = 3;
arr[1] = 20;
arr[2] = 30;
//定义指针
int* p;
p = arr; //数组的名字就是首地址(指针)
// 定义指针的指针
int** pp = &p;
printf("arr[%d]:%d\n", 0, **pp); //引用地址的最终数值
// *pp 解引用p内存中的地址
// *(*pp) 再次解引用p内地址 存储的值
指针的指针原理:

指针数组
- 指针组成的数组;
- 数组中的每个元素都是一个指针。
// 定义在main函数
char* arr[3]; // {"jack", "lucy"}这种只能定义变量的同时 赋值
arr[0] = "jack"; //定义数组后,再赋值只能根据索引逐一赋值
arr[1] = "tom";
arr[2] = "lucy";
// 打印
for(int i=0; i < 3; i++){
printf("arr[%d]: %s\n", i, arr[i]);
}
函数的指针
指向函数的指针。
// 定义函数
int testP(int a, int b){
printf("testP running...");
return a + b;
}
//指向函数的指针 定义在main函数
int (*p)(int, int) = &testP; // & 可以省略
//调用
p(3, 5);
- 交换两个变量的值,变量传参,未符合预期
// 利用函数的指针 实现a、b变量值的互换
int swapVal(int a, int b) {
int temp;
temp = a;
a = b;
b = temp;
printf("交换后,函数内部a: %d\n", a);
printf("交换后,函数内部b: %d\n", b);
return 0;
}
int main() {
// 定义两个变量
int a, b;
a = 11;
b = 22;
// 定义函数的指针,即指向函数的指针
int (*ptr)(int, int) = &swapVal;
// 指针执行函数
ptr(a, b); // (整型、浮点型、字符、枚举)基本类型 通过变量传入数值
printf("原始a:%d\n", a);
printf("原始b:%d\n", b); // 可以发现 函数swapVal并没有实现a、b数值的交换
return 0;
}
- 交换两个变量的值,指针传参,符合预期
// 利用函数的指针 实现a、b变量值的互换
int swapVal(int* a, int *b) {
int temp;
temp = *a;
*a = *b;
*b = temp;
printf("交换后,函数内部a: %d\n", *a);
printf("交换后,函数内部b: %d\n", *b);
return 0;
}
int main() {
// 定义两个变量
int a, b;
a = 11;
b = 22;
// 定义函数的指针,即指向函数的指针
int (*ptr)(int*, int*) = &swapVal;
// 指针执行函数
ptr(&a, &b); // (整型、浮点型、字符、枚举)基本类型 通过变量传入数值
printf("原始a:%d\n", a);
printf("原始b:%d\n", b); // 可以发现 函数swapVal并没有实现a、b数值的交换
return 0;
}
- 基于指针实现冒泡排序
void bubbleSort(int *arr, int size) { // 除了main函数外,其他函数中不能使用sizeof
int temp;
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - 1 - i; j++) {
if (*(arr+j) > *(arr + j + 1)) {
temp = *(arr + j);
*(arr + j) = *(arr + j + 1);
*(arr + j + 1) = temp;
}
}
}
}
int main() {
int arr[] = { 4,3,2,1,5 };
bubbleSort(arr, 5);
for (int i = 0; i < 5; i++) {
printf("arr[%d]=%d\n", i, arr[i]);
}
return 0;
}
- 基于指针实现:求整型二维数组中每行的最大值,并对其累加求和;
int cumSum(int arr[][3], int row, int col) { // 二维数组必须指定列
int result = 0; // 局部变量必须初始化
for (int i = 0; i < row; i++) {
int curMax = **(arr + i); // 获取每行的第一个值,假设为最大
int curVal;
for (int j = 0; j < col; j++) {
curVal = *(*(arr + i) + j);
if (curVal > curMax) {
curMax = curVal;
}
}
// 当前行的最大值,累加
result += curMax;
}
return result; //返回数值, 不能在函数内部创建数组并返回首地址,局部变量内存空间会释放,导致首地址无效
}
int main() {
int arr[][3] = {4,3,2,1,5,7};
int result = cumSum(arr, 2, 3);
printf("%d\n", result);
return 0;
}
函数返回指针
- 不能在函数内部创建数组,并返回其首地址(弹栈、释放内存、首地址失效);
- 可以将外部变量的地址传给函数,并在函数内部返回该地址;
// 传递数组给函数,形参定义必须为数组或者指针
char* testArr(char arr[], int arrLen){
for(int i=0; i < arrLen; i++){
printf("arr[%d]: %c\n", i, arr[i]);
}
return arr; // 返回数组(即指针)
}
//main中测试
char arr[3];
arr[0] = 'a';
arr[1] = 'g';
arr[2] = 't';
testArr(arr, 3);

综合应用
技术点:
- 指针数组
- 字符串及其比较
- 指针的指针
- 指针的算术运算
遍历数组中的所有的用户名,将值为‘jack’的用户名输出,并停止循环。
char* getUser() {
char* users[] = { "tom\a", "lucy\n", "jack\t", "jack" };
char* tt = "jack";
char** ptr = users; // 数组的名称,即数组指针
while (ptr <= &users[3]) {
if (strcmp(*ptr, tt) == 0) {
printf("目标用户:%s\n", *ptr);
//break;
return *ptr;
}
ptr++; // 指向下一个元素
}
return "null";
}
#include <string.h> //预处理器,编译时文本替换的步骤
int main() {
char* result = getUser();
printf("获取的结果:%s\n", result);
system("pause");
return 0;
}
再探数组
数组指针,
数组的名称,即首元素地址,即指向数组的指针;
另外数组的声明及初始化尽量放函数内部。
动态数组与静态数组
- 静态数组
- 声明时需要指定长度;
- 编译时分配内存,大小固定;
- 生命周期与作用域有关;
- 动态数组
- 运行时手动分配内存,大小可变;
- 生命周期由程序员控制。
- 使用动态数组时,需要分配和释放内存,以避免内存泄漏和访问无效的内存
- 使用malloc/calloc分配内存;
静态数组:
int staticArray[5]; // 静态数组声明
int staticArray[] = {1, 2, 3, 4, 5}; // 静态数组声明并初始化
// 静态数组,可以使用 sizeof 运算符来获取数组长度,例如:
int length = sizeof(staticArray) / sizeof(staticArray[0]);
//以上代码中 sizeof(array) 返回整个数组所占用的字节数,而 sizeof(array[0]) 返回数组中单个元素的字节数,将两者相除,就得到了数组的长度。
动态数组:
分配一个长度为10的整数数组空间,分别按照动态数组、指针的形式赋值并输出;
#include <stdio.h>
#include <stdlib.h>
int main() {
int size = 10; //指定要分配的长度
int *dynamicArray = (int*)malloc(size * sizeof(int)); // 动态数组内存分配
// 分配失败
if (dynamicArray == NULL) {
printf("Memory allocation failed.\n");
return 1;
}
for (int i = 0; i < size; i++) {
scanf("%d", &dynamicArray[i]); // 动态数组
}
for (int i = 0; i < size; i++) {
printf("%d ", dynamicArray[i]);
}
// 动态数组内存释放
free(dynamicArray);
return 0;
}
// 指针形式
//int main() {
//
// int* dynamicArr = (int*)malloc(10 * sizeof(int));
// if (dynamicArr == NULL) {
// printf("分配地址失败");
// }
// else {
// for (int i = 0; i < 10; i++) {
// *dynamicArr = pow(i+1, 2); // 取幂计算
// dynamicArr++;
// }
//
// dynamicArr -= 10;
// for (int i = 0; i < 10; i++) {
// printf("%d\n", *dynamicArr);
// dynamicArr++;
// }
// }
// dynamicArr -= 10;
// free(dynamicArr);
// return 0;
//}
分配内存后,需要检查分配是否成功(即 dynamicArray== NULL),以避免在内存分配失败时发生错误。
函数指针
- 指向函数的指针,该指针可以直接调用(像函数一样);
- 回调函数,将一个函数传递给另一个函数,在其内部调用。python里属于高阶函数范畴。
定义函数指针:
// 定义求和函数
int sum(int arr[], int size){
int result = 0;
for(int i = 0; i < size; i++){
result += arr[i];
}
return result;
}
// 定义main
int main() {
// 定义函数指针
int (*ptr)(int[], int) = ∑ // 获取函数的地址
int arr[] = { 1, 3, 10 };
int result = ptr(arr, 3);
printf("函数指针: %d\n", result);
system("pause"); // 系统调用
return 0;
}
回调函数:
将一个函数A以实参形式,传给另一个函数B,在其内部调用。
此时,B函数的形参必须定义为函数指针形式。
// 定义一个函数,计算数组的均值,同时将以上的sum函数,传递进去,用于回调。
double calcAvg(int arr[], int size, int(*ptr)(int[], int)){
int calcSum = ptr(arr, size);
return (double) calcSum / size;
}
int main() {
int arr[] = { 1, 3, 10 };
double avg = calcAvg(arr, 3, sum); // 实参 传入sum函数
printf("函数指针: %f\n", avg);
system("pause"); // 系统调用 (调cmd命令)
return 0;
}
[上一篇]:C语言编程—函数与头文件
[下一篇]:C语言编程—字符串与结构体
本文详细介绍了C语言中的指针概念,包括指针的定义、指针的指针、指针数组、函数指针以及动态数组的使用。通过示例代码展示了如何通过指针操作数组,以及如何使用函数指针进行回调。文章还探讨了静态和动态数组的区别,并给出了使用malloc分配和释放内存的例子。

628

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



