4-指针和数组

本文深入解析C语言中的指针概念,包括一维数组名作为指针常量、下标与指针的关系、动态内存分配原理及其实现。通过实例演示如何使用malloc和free函数进行动态内存管理,探讨静态与动态内存的区别,以及多级指针的应用。
1- 指针和一维数组
  • 一维数组名:一维数组名是个指针常量,他存放的是一维数组第一个元素的地址。
#include <stdio.h>
/*

总结:
    一维数组名
    一维数组名是个指针常量
    它存放的是一维数组第一个元素的地址
*/
int main(void)
{
    int a[5]; //a是数组名  5是数组元素的个数 元素就是变量  a[0]  -- a[4]
//  int a[3][4]; //3行4列 a[0][0]是第一个元素 a[i][j]第i+1行j+1列
    int b[5];
    
    //a = b;//error a是常量
    printf("%#X\n", &a[0]);
    printf("%#X\n", a);

    return 0;
}

常量是不能被改变的,也就是说,一维数组名是不能被改变的。
数组名a存放的是一维数组第一个元素的地址,也就是a = &a。

2-下标和指针的关系

**如果p是个指针变量,则p[i]永远等价于*(p+i)**

# include <stdio.h>

int main(void)
{
    int a[5] = {1,2,3,4,5};
    int i;

    for (i=0; i<5; ++i)
        printf("%d\n", a[i]);  //a[i] == *(a+i) 

    return 0;
}
3- 确定一个一维数组需要几个参数
# include <stdio.h>
/*
 a是个指针变量,所以上面局部函数f的pArr则要定义成指针函数才可以,而len则是int类型。代表接收的是整型的数字。
*/
//f函数可以输出任何一个一维数组的内容
void f(int * pArr, int len)
{
    int i;

    for (i=0; i<len; ++i)
        printf("%d  ", *(pArr+i) );  //*pArr *(pArr+1) *(pArr+2) 
    printf("\n");
}

int main(void)
{
    int a[5] = {1,2,3,4,5};
    int b[6] = {-1,-2,-3,4,5,-6};
    int c[100] = {1, 99, 22, 33};

    f(a, 5);  //a是 int *
    f(b, 6);
    f(c, 100);

    return 0;
}

/*
1、一定要明白 10行的pArr[3] 和17行 19行的a[3] 是同一个变量因为数组a的名称代表的是a的第一个元素的地址,所以在函数f中所定义的指针变量pArr和a是相同的,因为a也是指针类型。

2、也就是说pArr=a=a[0],pArr[1]=a[1]=*(pArr+1)=*(a+1),pArr[2]=a[2]=*(pArr+2) =*(a+2).所以在f函数中pArr[3]=a[3],所以第二个printf输出的结果是88.总结:pArr[i] = a[i] = *(pArr+i) = *(a+i)

3、在没有学习指针时,可将a[3]认为是数组中第4个元素,这里下标也当成指针了,从首元素开始向后移动3个,即指向第4个元素。
*/

# include <stdio.h>

void f(int * pArr, int len)
{
    pArr[3] = 88;  //10行
}

int main(void)
{
    int a[6] = {1,2,3,4,5,6};
    
    printf("%d\n", a[3]);  //17行
    f(a, 6);
    printf("%d\n", a[3]);  //  19行

    return 0;
}
/*
因为数组a的名称代表的是a的第一个元素的地址,所以在函数f中所定义的指针变量pArr和a是相同的,因为a也是指针变量类型。也就是说pArr=a=a[0],pArr[1]=a[1]=*(pArr+1)=*(a+1),pArr[2]=a[2]=*(pArr+2) =*(a+2).我们在f函数中修改数组的值,相当于修改主函数中相对应的值。
*/
# include <stdio.h>

void f(int * pArr, int len)
{
    int i;
    for (i=0; i<len; ++i)
        printf("%d  ",  pArr[i]);  //*(pArr+i) 等价于 pArr[i]  也等价于 b[i] 也等价于 *(b+i) 

    printf("\n");
}

int main(void)
{
    int b[6] = {-1,-2,-3,4,5,-6};

    f(b, 6);

    b[i]

    return 0;
}
4- 何谓变量地址 / 一个指针变量占几个字节
  1. Sizeof(变量名/数据类型) 其返回值就是该变量或数据类型所占字节数
  2. ==一个指针变量无论其指向变量占几个字节,其本身所占大小都是4字节。==
  3. *p具体指向几个字节,要靠前面类型确定,如果为int则为4字节,如果double则占8字节。
  4. CPU 与 内存 交互时 有32根线,每根线只能是1或0两个状态,所有总共有232个状态。1 个状态 对应 一个单元。如全为0 全为1 等。内存中第一个单元,即32根线状态全为0。0000 0000 0000 0000 0000 0000 0000 0000 其大小为4字节
  5. 所有每个地址(硬件所能访问)的用4个字节保存(而不是一 位bit)
    一个变量的地址—用该变量首字节的地址表示。这也就是为什么指针变量始终只占4字节的原因。
# include <stdio.h>

int main(void)
{
    char ch = 'A';
    int i = 99;
    double x = 66.6;
    char * p = &ch;
    int * q = &i;
    double * r = &x;

    printf("%d %d %d\n", sizeof(p), sizeof(q), sizeof(r));

    return 0;
}
5-动态内存分配
  1. 传统数组的缺点
  • 数组长度必须事先指定,而且只能是常整数,不能是变量

  • 例子 int a[5]; //必须事先指定,而且只能是常整数int len = 5; int a[len];//error

2.传统形式定义的数组,该数组的内存程序员无法手动释放数组一旦定义.

系统为数组分配的内存空间就会一直存在,除非数组所在的函数运行终止。在一个函数运行期间,系统为该函数中的数组分配的空间会一直存在。直到该函数运行完毕时,数组的空间才会被系统自动释放(不是清零)。

例子:void f(void){ int a[5]={1,2,3,4,5};....}

//数组a 占20个字节的内存空间,程序员无法手动编程释放它,数组a只能在f()函数结束被系统释放

3、数组的长度一旦定义,数组长度就不能再更改。数组的长度不能在函数运行的过程中动态的扩充或缩小.

4、传统方式定义的数组不能跨函数使用

A函数定义的数组,只有在A函数运行期间才可以被其他函数使用,

但A函数运行完毕后,A函数中的数组将无法在被其他函数使用。

# include <stdio.h>
/*
代码实现(传统数组的缺陷)
*/
void g(int * pArr, int len)
{
    pArr[2] = 88;  //pArr[2] == a[2]
}

void f(void)
{
    int a[5] = {1,2,3,4,5};  //20个字节的存储空间程序员无法手动编程释放它,
                             //它只能在本函数运行完毕时由系统自动释放
    g(a, 5);            
    printf("%d\n", a[2]);
}

int main(void)
{   
    f();

    return 0;
}

为什么需要动态分配内存

很好的解决的了传统数组的4个缺陷

6-动态内存分配举例_动态数组的构造
/*
    2009年11月17日10:21:31
    malloc 是 memory(内存) allocate(分配)的缩写

*/
# include <stdio.h>
# include <malloc.h>  //不能省

int main(void)
{
    int i = 5; //分配了4个字节 静态分配   11 行
    int * p = (int *)malloc(4); //12行
    /*
    1. 要使用malloc函数,必须添加malloc.h这个头文件
    2. malloc函数只有一个形参,并且形参是整型
    3. 4表示请求系统为本程序分配4个字节
    4. malloc函数只能返回第一个字节的地址
    5. 12行分配了8个字节, p变量占4个字节, p所指向的内存也占4个字节
    6. p本身所占的内存是静态分配的, p所指向的内存是动态分配的    
    */  

    *p = 5; //*p 代表的就是一个int变量, 只不过*p这个整型变量的内存分配方式和11行的i变量的分配方式不同
    free(p); //freep(p)表示把p所指向的内存给释放掉  p本身的内存是静态的,不能由程序员手动释放,p本身的内存只能在p变量所在的函数运行终止时由系统自动释放 
    printf("同志们好!\n");

    return 0;
}
7-malloc 实现图解

1665530-20190625120108932-1025196564.png

# include <stdio.h>
# include <malloc.h>

void f(int * q)
{
    //*p = 200; //error
    //q = 200;  //200不是整型变量的地址,q只能存放整型变量的地址
    //**q = 200;  //error
    *q = 200;
    //free(q);  //把q所指向的内存释放掉  本语句必须的注释掉,否则会导致第20行的代码出错
}

int main(void)
{
    int * p = (int *)malloc(sizeof(int)); //sizeof(int)返回值是int所占的字节数
    *p = 10;

    printf("%d\n", *p);  //10
    f(p);  //p是int *类型
    printf("%d\n", *p);  //200    第20行

    return 0;
}
8-动态一维数组示例

图解
1665530-20190625120121539-740876524.jpg

# include <stdio.h>
# include <malloc.h>

int main(void)
{
    int a[5]; //如果int占4个字节的话,则本数组总共包含有20个字节,每四个字节被当做了一个int变量来使用
    int len;
    int * pArr;
    int i;

    //动态的构造一维数组
    printf("请输入你要存放的元素的个数: ");
    scanf("%d", &len);
    pArr = (int *)malloc(4 * len);  //第12行  本行动态的构造了一个一维数组, 该一维数组的产度是len, 该数组的数组名是pArr, 该数组的每个元素是int类型  类似于 int pArr[len];
    
    //对一维数组进行操作,  如:对动态一维数组进行赋值
    for (i=0; i<len; ++i)
        scanf("%d", &pArr[i]);

    //对位一维数组进行输出
    printf("一维数组的内容是:\n");
    for (i=0; i<len; ++i)
        printf("%d\n", pArr[i]);
    
    free(pArr); //释放掉动态分配的数组
    

    return 0;
}
9-使用动态数组的优点:

1.动态数组长度不需要事先给定;

2.内存空间可以手动释放。

3.在程序运行中,动态内存空间大小可以通过realloc函数手动扩充或缩小

10-静态内存和动态内存的比较

静态内存是由系统自动分配,有系统自动释放
静态内存是在栈分配的

动态内存是由程序员手动分配、手动释放
动态内存是在堆分配的

11-多级指针

1665530-20190625120134003-2064804950.png

#### 多级指针代码实现
# include <stdio.h>

int main(void)
{
    int i = 10;
    int * p = &i;  //p只能存放int类型变量的地址
    int ** q = &p;  //q是int **类型, 所谓int **类型就是指q只能存放int *类型变量的地址,   
    int *** r = &q;  //r是int ***类型, 所谓int ***类型就是指r只能存放int ** 类型变量的地址, 

    //r = &p;  //error 因为r是int *** 类型,r只能存放int **类型变量的地址
    printf("i = %d\n", ***r); //输出结果是10  只有 ***r才表示的是i, *r或 **r或 ****r代表的都不是i

    return 0;
}

多级指针代码实现2

#include <stdio.h>

//多级指针在函数中的应用
void f(int ** q)
{
    **q = 100;  //*q就是p
}

void g()
{
    int i = 10;
    int * p = &i;
    printf("i = %d  *p = %d\n", i, *p);

    f(&p); //p是int *类型  &p就是int ** 类型
    
    printf("i = %d  *p = %d\n", i, *p);
}

       int main(void) 
{
    g();
    return 0;
}
12-静态变量不能跨函数使用
/*
内存越界:程序访问了一个不该被访问的内存,函数内的静态空间,不能被其他函数调用访问。函数中的内存空间,随函数终止而被释放。内存空间释放后的内容不属于其他函数,其他函数无权限访问。但释放后的内存空间的地址是可以被其他函数读取的。但指针变量可以存贮任何函数中静态内存空间的地址,p都能存垃圾,p想存谁存谁。只是它此时已经没有权限读取(访问) i这个地址的数据了,出错。。
*/
# include <stdio.h>

void f(int ** q) //q是个指针变量,无论q是什么类型的指针变量,都只占4个字节
{
    int i = 5;
    //*q等价于p  q和**q都不等价于p
    //*q = i; //error 因为*q = i; 等价于 p = i; 这样写是错误的
    *q = &i;  // p = &i;
}

int main(void)
{
    int *p;  //13行
    
    f(&p);
    printf("%d\n", *p);  //16行  本语句语法没有问题,但逻辑上有问题

    return 0;
}
13- 动态内存可以跨函数使用案例
# include <stdio.h>
# include <malloc.h>

void f(int ** q) //*q等价p 已经声明了q的类型为int **
{
    *q = (int *)malloc(sizeof(int)); //sizeof(数据类型) 返回值是该数据类型所占的字节数
            //等价于 p = (int *)malloc(sizeof(int));
    //q = 5; //error
    //*q = 5; //p = 5;
    **q = 5; //*p = 5;
}

int main(void)
{
    int * p;

    f(&p);    //只有调用变量的地址,才能改变变量的值
    printf("%d\n", *p);  
    
    //f函数中,没有free(q);所以动态空间仍然保留,动态空间中的内容可以被访问
    return 0;
}

转载于:https://www.cnblogs.com/Guard9/p/11082065.html

一、数据采集层:多源人脸数据获取 该层负责从不同设备 / 渠道采集人脸原始数据,为后续模型训练与识别提供基础样本,核心功能包括: 1. 多设备适配采集 实时摄像头采集: 调用计算机内置摄像头(或外接 USB 摄像头),通过OpenCV的VideoCapture接口实时捕获视频流,支持手动触发 “拍照”(按指定快捷键如Space)或自动定时采集(如每 2 秒采集 1 张),采集时自动框选人脸区域(通过Haar级联分类器初步定位),确保样本聚焦人脸。 支持采集参数配置:可设置采集分辨率(如 64480、1280×720)、图像格式(JPG/PNG)、单用户采集数量(如默认采集 20 张,确保样本多样性),采集过程中实时显示 “已采集数量 / 目标数量”,避免样本不足。 本地图像 / 视频导入: 支持批量导入本地人脸图像文件(支持 JPG、PNG、BMP 格式),自动过滤非图像文件;导入视频文件(MP4、AVI 格式)时,可按 “固定帧间隔”(如每 10 帧提取 1 张图像)或 “手动选择帧” 提取人脸样本,适用于无实时摄像头场景。 数据集对接: 支持接入公开人脸数据集(如 LFW、ORL),通过预设脚本自动读取数据集目录结构(按 “用户 ID - 样本图像” 分类),快速构建训练样本库,无需手动采集,降低系统开发与测试成本。 2. 采集过程辅助功能 人脸有效性校验:采集时通过OpenCV的Haar级联分类器(或MTCNN轻量级模型)实时检测图像中是否包含人脸,若未检测到人脸(如遮挡、侧脸角度过大),则弹窗提示 “未识别到人脸,请调整姿态”,避免无效样本存入。 样本标签管理:采集时需为每个样本绑定 “用户标签”(如姓名、ID 号),支持手动输入标签或从 Excel 名单批量导入标签(按 “标签 - 采集数量” 对应),采集完成后自动按 “标签 - 序号” 命名文件(如 “张三
### 指针数组数组指针的区别及使用场景 #### 区别 指针数组数组指针是两种不同的数据结构,尽管它们的名字听起来相似。以下是它们的核心区别: 1. **指针数组** 指针数组一个数组,其元素都是指针。每个元素存储的是一个地址值,指向其他数据类型(如整数、字符等)。在32位系统中,每个指针4个字节,因此整个指针数组的大小由数组的长度决定[^2]。 示例代码如下: ```c int *pArray[3]; // 定义了一个包含3个int指针指针数组 ``` 2. **数组指针** 数组指针一个指针,指向一个数组。它本身只占用指针大小的空间(例如,在32位系统中为4字节),但它可以指向一个更大的数组[^4]。 示例代码如下: ```c int (*pArray)[3]; // 定义了一个指向包含3个int元素的数组指针 ``` #### 使用场景 1. **指针数组的使用场景** - 当需要存储多个动态分配的对象时,指针数组非常有用。例如,如果需要管理多个字符串或动态分配的数组,可以使用指针数组- 示例:存储多个字符串。 ```c char *strArray[3] = {"Hello", "World", "C++"}; // 指针数组,每个元素指向一个字符串常量 ``` 2. **数组指针的使用场景** - 当需要操作二维或多维数组时,数组指针特别有用。通过数组指针,可以方便地访问二维数组的行或列。 - 示例:访问二维数组的行。 ```c int arr[2][3] = {{1, 2, 3}, {4, 5, 6}}; int (*p)[3] = arr; // p指向包含3个int元素的数组 printf("%d\n", (*p)[1]); // 输出2 ``` #### 关键点总结 - 指针数组一个数组,其元素是指针;而数组指针一个指针,指向一个数组- 在定义时需要注意括号的优先级问题。例如,`int *pArray[3]` 是指针数组,而 `int (*pArray)[3]` 是数组指针[^4]。 - 指针数组的大小由数组长度决定,而数组指针的大小仅由指针本身决定。 ```c // 示例:指针数组数组指针的区别 #include <stdio.h> int main() { // 指针数组 int a = 10, b = 20, c = 30; int *pArray[3]; pArray[0] = &a; pArray[1] = &b; pArray[2] = &c; printf("Pointer Array: %d, %d, %d\n", *pArray[0], *pArray[1], *pArray[2]); // 数组指针 int arr[2][3] = {{1, 2, 3}, {4, 5, 6}}; int (*p)[3] = arr; printf("Array Pointer: %d, %d, %d\n", (*p)[0], (*p)[1], (*p)[2]); return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值