提示:文章
文章目录
前言
前期疑问:
本文目标:
一、背景
最近
二、
2.1 使用二维数组指针接受函数返回的静态二维数组
/*
* 来源于考试遇到的split分割字符串的问题。当时对于release.24没有分割成功
* 今天复盘一下。一开始以为是strtok用的问题。但是实际好像是我传参的问题
* 下面的就是一系列验证的代码
*/
#include "securec.h"
#include "FurtherUnderstanding.h"
#define SPLIT_CHARACTER "."
#define VERSION_LEN 20
char** GetVersionSplit(char* version, char strArray[][VERSION_LEN + 1], int* arrayLen)
{
char* split = strtok(version, SPLIT_CHARACTER);
while(split)
{
memcpy_s(strArray[*arrayLen], VERSION_LEN * sizeof(char), split, VERSION_LEN * sizeof(char));
(*arrayLen)++;
split = strtok(NULL, SPLIT_CHARACTER);
}
for (int i = 0; i < *arrayLen; i++) {
printf("%s\n", strArray[i]);
printf("%p\n", strArray[i]);
}
printf("\n");
return strArray;
}
//char[][VERSION_LEN + 1] GetVersionSplit4(char* version, char strArray[][VERSION_LEN + 1], int* arrayLen)
//{
// char* split = strtok(version, SPLIT_CHARACTER);
// while(split)
// {
// memcpy_s(strArray[*arrayLen], VERSION_LEN * sizeof(char), split, VERSION_LEN * sizeof(char));
// (*arrayLen)++;
// split = strtok(NULL, SPLIT_CHARACTER);
// }
//
// for (int i = 0; i < *arrayLen; i++) {
// printf("%s\n", strArray[i]);
// printf("%p\n", strArray[i]);
// }
// printf("\n");
// return strArray[*arrayLen][VERSION_LEN + 1];
//}
//(char*)[3] GetVersionSplit5(char* version, char strArray[][VERSION_LEN + 1], int* arrayLen)
//{
// char* split = strtok(version, SPLIT_CHARACTER);
// while(split)
// {
// memcpy_s(strArray[*arrayLen], VERSION_LEN * sizeof(char), split, VERSION_LEN * sizeof(char));
// (*arrayLen)++;
// split = strtok(NULL, SPLIT_CHARACTER);
// }
//
// for (int i = 0; i < *arrayLen; i++) {
// printf("%s\n", strArray[i]);
// printf("%p\n", strArray[i]);
// }
// printf("\n");
// return strArray[*arrayLen][VERSION_LEN + 1];
//}
int main()
{
char version[] = "release.24";
char** spiltArrayMalloc = (char**) malloc(sizeof(char*) * 3);
for (int i = 0; i < 3; i++) {
spiltArrayMalloc[i] = (char*) malloc(sizeof(char*) * (VERSION_LEN + 1));
}
char spiltArray[3][21] = {'\0'};
int arrayLen = 0;
char (*retArray)[3] = GetVersionSplit(version, spiltArray, &arrayLen);
// 这个写法很奇怪,将二维数组转成二级指针,再用二维数组接收,结果不对吧
// 确实结果不对
for (int i = 0; i < arrayLen; i++) {
printf("%p\n", retArray[i]);
printf("%s\n", retArray[i]);
}
printf("22\n");
for (int i = 0; i < arrayLen; i++) {
printf("%p\n", retArray + i);
printf("%s\n", retArray + i);
}
printf("\n");
for (int i = 0; i < arrayLen; i++) {
printf("%p\n", &retArray + i);
printf("%s\n", &retArray + i);
}
printf("\n");
for (int i = 0; i < arrayLen; i++) {
printf("%p\n", (*retArray) + i);
printf("%s\n", (*retArray) + i);
}
printf("\n");
for (int i = 0; i < arrayLen; i++) {
printf("%p\n", (retArray) + i);
printf("%s\n", (*retArray) + i);
}
printf("\n");
// char retArray2[3][21]= {'0'};
// retArray2 = GetVersionSplit(version, spiltArray, &arrayLen);
printf("下面是用二级指针接受返回的数组打印结果:\n");
char version3[] = "release.24";
char spiltArray3[3][21] = {'\0'};
int arrayLen3 = 0;
char** retArray3;
retArray3 = GetVersionSplit(version3, spiltArray3, &arrayLen3);
for (int i = 0; i < arrayLen3; i++) {
printf("%p\n", retArray3 + i);
printf("%s\n", retArray3 + i);
}
printf("\n");
// 这样可以成功。目前的理解是二位数组传参后弱化为指针了。又用二级指针返回。就用二级指针接受。再用指针偏移解析。
// 但是感觉还是有个问题。就是二级指针不是不能转成二级指针吗,这样会有风险吗
// 基于此我准备更新写个函数。就是以char[][VERSION_LEN + 1] GetVersionSplit4(char* version, char strArray[][VERSION_LEN + 1], int* arrayLen)方式返回,但是报错
// 为什么这样能成功?我的理解是静态二维数组传参后数组名变成了指针。在函数内部编译器可以通过形参strArray[][VERSION_LEN + 1]写法,确定偏移长度
// 最后返回数组名指针的地址。使用二级指针接收。但是为什么函数外部能知道偏移地址?离谱。。。
// 需要验证一下
// 尝试使用retArray3[i]找地址
for (int i = 0; i < arrayLen3; i++) {
printf("%p\n", retArray3[i]);
printf("%s\n", retArray3[i]);
}
// 上述不能成功,会报错
//最后的最后我突然又发现了一个问题,那就是对于指针数组的理解。感觉自己要崩塌了,数组指针和指针数组又分不清了
// 出去打了水,我又豁然开朗了,其实我就是没有搞懂一个问题。那就是,怎么定义指向二位数组的指针?然后就是查到下面的知识点
// 那么对于上述的问题又感觉很好解决了
// 我再重新创建一个文件
// FurtherUnderstanding();
return 0;
}
2.2 指向二位数组的指针怎么定义
指向二维数组的指针怎么定义
指向二维数组的指针可以通过定义一个指向二维数组首元素的指针来实现。这种指针通常被称为二维数组指针,它允许直接访问二维数组的元素,而不需要通过索引进行间接访问。以下是关于二维数组指针的定义、访问和应用的详细解释:
定义二维数组指针
二维数组指针的定义通常采用以下形式:
Copy Code
int (*p)[n];
其中,p 是一个指向包含 n 个整型元素的数组的指针。这个定义表明 p 可以指向一个二维数组的第一行,从而允许访问整个二维数组的元素。例如,对于一个 2x3 的二维数组 arr,可以这样定义一个指向该数组的指针:
Copy Code
int arr[][3] = {{1, 2, 3}, {4, 5, 6}}; // 2x3 二维数组:ml-citation{ref="1,2" data="citationList"}
int (*ptr)[3] = arr; // ptr 是一个指向 arr 第一行的指针,即二维数组指针:ml-citation{ref="2" data="citationList"}
通过 ptr 可以访问 arr 中的所有元素,例如 ptr 就是 2。3
访问二维数组元素
使用二维数组指针访问元素时,可以直接使用指针算术来获取特定位置的元素。例如,对于上述定义的 ptr,可以通过 ptr[i][j] 来访问第 i 行第 j 列的元素。这种直接访问的方式比通过索引间接访问更加高效。
指针数组与二维数组的关系
指针数组是一组有序的指针的集合,每个元素都是一个指针。指针数组可以用于指向二维数组的每一行,从而实现一种特殊的数据结构处理方式。例如,一个指针数组可以用于指向一个二维数组的每一行首地址,从而实现对二维数组的灵活操作。
实际应用示例
在实际应用中,二维数组指针和指针数组常用于处理大型数据集、图像处理、矩阵运算等场景。它们提供了对数据的直接访问能力,减少了间接访问的开销,提高了程序的执行效率。此外,这些数据结构也使得程序更加灵活,能够适应不同的数据处理需求。
通过上述解释和示例,我们可以看到二维数组指针和指针数组在计算机编程中的重要作用和应用场景。这些概念不仅在C语言中常见,也在其他支持指针操作的编程语言中出现,是编程中处理复杂数据结构的重要工具。
三、
3.1 下面是FurtherUnderstanding();
// FurtherUnderstanding.c文件
#include "FurtherUnderstanding.h"
#include "securec.h"
#include <string.h>
#include <stdio.h>
#include <malloc.h>
// 下面重新新建一个文件来对二维数组传参进行梳理
// 首先二维数组传参目前就两种方式:传值和传地址
// 这边我又愣住了。因为对于静态数组来说,确实是有传值和传址两种方式
// 那么对于动态数组呢,是个二级指针指向二维数组,可以传值吗?我理解传值的话要进行内存拷贝,内存拷贝又意味着地址是要连续的。
// 那么就存在一个问题,malloc出的地址是连续的吗?未知,后面查一下
// 所以这边只对静态二维数组进行整理
#define VERSION_LEN 20
#define SPLIT_CHARACTER "."
// 静态二维数组传值
// 返回数组指针会报错
// 这个写法是会报错的
static char (*)[VERSION_LEN + 1] GetVersionSplit(char* version, char strArray[][VERSION_LEN + 1], int* arrayLen)
{
char* split = strtok(version, SPLIT_CHARACTER);
while(split)
{
memcpy_s(strArray[*arrayLen], VERSION_LEN * sizeof(char), split, VERSION_LEN * sizeof(char));
(*arrayLen)++;
split = strtok(NULL, SPLIT_CHARACTER);
}
for (int i = 0; i < *arrayLen; i++) {
printf("%s\n", strArray[i]);
printf("%p\n", strArray[i]);
}
printf("\n");
return strArray;
}
// 静态二维数组传地址,返回二维数组指针还是报错,无语,先不搞了
// 这个尝试是没有用的
static (*)[20] GetVersionSplit(char* version, char (strArray*)[20], int* arrayLen)
{
char* split = strtok(version, SPLIT_CHARACTER);
while(split)
{
memcpy_s(strArray[*arrayLen], 20 * sizeof(char), split, 20 * sizeof(char));
(*arrayLen)++;
split = strtok(NULL, SPLIT_CHARACTER);
}
for (int i = 0; i < *arrayLen; i++) {
printf("%s\n", strArray[i]);
printf("%p\n", strArray[i]);
}
printf("\n");
return p;
}
// 这个尝试是没有用的
char (*)[20] GetVersionSplit()
{
char array[3][4] = {"hello", "world"};
return array;
}
// 这个尝试是没有用的
int[][5] GetArray()
{
static int array[2][5] = {{1,2,3,4,5},{2,3,4,5,6}};
return array[2][5];
}
//这个函数是参考了文章https://zhuanlan.zhihu.com/p/444782245,返回二级指针前先将二维数组成员首地址一次赋值给指针数组,再返回二级指针
//第二个参数想写成数组指针也会报错,不知道为啥
static char **GetVersionSplit(char* version, char strArray[][20], int* arrayLen)
{
char* split = strtok(version, SPLIT_CHARACTER);
while(split)
{
memcpy_s(strArray[*arrayLen], 20 * sizeof(char), split, 20 * sizeof(char));
(*arrayLen)++;
split = strtok(NULL, SPLIT_CHARACTER);
}
for (int i = 0; i < *arrayLen; i++) {
printf("%s\n", strArray[i]);
printf("%p\n", strArray[i]);
}
printf("\n");
static char* retArray[3];
for(int i = 0; i < *arrayLen; i++)
{
retArray[i] = strArray[i];
}
return retArray;
}
// 会报错
//static void GetVersionSplit2(char* version, char (strArray*)[20], int* arrayLen)
//{
// char* split = strtok(version, SPLIT_CHARACTER);
// while(split)
// {
// memcpy_s(strArray[*arrayLen], 20 * sizeof(char), split, 20 * sizeof(char));
// (*arrayLen)++;
// split = strtok(NULL, SPLIT_CHARACTER);
// }
//
// for (int i = 0; i < *arrayLen; i++) {
// printf("%s\n", strArray[i]);
// printf("%p\n", strArray[i]);
// }
// printf("\n");
//}
//这边其实是创建指针数组,数组中存储二维数组的首地址
void secondPointerTest()
{
// malloc创建二维数组,打印地址值
int **ptr = (int**) malloc(sizeof(int*) * 5);
for(int i = 0; i < 5; i++)
{
ptr[i] = (int*)malloc(sizeof(int) * 6);
memset_s(ptr[i], sizeof(int) * 6, 0, sizeof(int) * 6);
}
for(int i = 0; i < 5; i++)
{
for(int j = 0; j < 6; j++)
{
ptr[i][j] = i * j;
}
}
for(int i = 0; i < 5; i++)
{
for(int j = 0; j < 6; j++)
{
printf("%d ", ptr[i][j]);
}
printf("\n");
}
// 打印指针数组元素地址
printf("打印指针数组元素地址\n");
for(int i = 0; i < 5; i++)
{
printf("addr:%p ", ptr[i]);
}
printf("\n");
printf("打印指针数组所有元素地址\n");
for(int i = 0; i < 5; i++)
{
for(int j = 0; j < 6; j++)
{
printf("addr:%p ", &ptr[i][j]);
}
printf("\n");
}
// 验证静态数组地址
printf("打印静态数组地址\n");
int array[5][6] = {0};
for(int i = 0; i < 5; i++)
{
for(int j = 0; j < 6; j++)
{
array[i][j] = i * j;
}
}
for(int i = 0; i < 5; i++)
{
for(int j = 0; j < 6; j++)
{
printf("%d ", array[i][j]);
}
printf("\n");
}
for(int i = 0; i < 5; i++)
{
for(int j = 0; j < 6; j++)
{
printf("addr:%p ", &array[i][j]);
}
printf("\n");
}
#ifndef PRINT_SECOND_POINTER //这边将array强转成二级指针,后面用数组指针指向array会报错。。
// 将二维数组强转成二级指针,打印地址
printf("将二维数组强转成二级指针,打印地址\n");
int **p = (int**)array;
//强转不能索引访问
for(int i = 0; i < 5; i++)
{
printf("%p ", p[i]);
}
printf("\n");
for(int i = 0; i < 5; i++)
{
printf("%p ", p + i);
}
#else
//二维数组指针指向二位数组,验证地址是否正确
int (*p)[6] = array; //二位数组指针写法就是这样。后面中括号为指向数组的一维成员的长度
for(int i = 0; i < 5; i++)
{
printf("addr:%p ", p[i]);
}
printf("\n");
#endif
}
int FurtherUnderstanding()
{
char version[] = "release.24";
char spiltArray[3][21] = {'\0'};
int arrayLen = 0;
char **ret = GetVersionSplit(version, spiltArray, &arrayLen);
for(int i = 0; i < arrayLen; i++)
{
printf("%s\n", ret[i]);
}
secondPointerTest();
return 0;
}
// 其中还看到这篇文章,和我要做的事情一样,但是没看懂
// https://blog.nowcoder.net/n/685d5d3a280841019b8487a30f49825b?from=nowcoder_improve
// FurtherUnderstanding.h文件
#ifndef TESTPROJ_LOCAL_WITHOUT_LIBRARY_FURTHERUNDERSTANDING_H
#define TESTPROJ_LOCAL_WITHOUT_LIBRARY_FURTHERUNDERSTANDING_H
int FurtherUnderstanding();
#endif //TESTPROJ_LOCAL_WITHOUT_LIBRARY_FURTHERUNDERSTANDING_H
总结
未完待续