二级指针的问题汇总

二级指针的问题


如何在被调用函数里面新建数据,然后将数据的地址传递出来呢?

一般来说有两种方法,第一种思路是将数据的首地址以返回值的方法返回,第一种方法如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *newBuf(int num)
{
    char *p;
    p = (char *)malloc(num * sizeof(char));
    return p;
}
void deleteBuf(char *p)
{
    if (p != NULL)
    {
        free(p);
        p = NULL; //垃圾代码,执行了也不会有任何用处
    }
}
void main()
{
    char *p = NULL;
    p = newBuf(100);
    printf("p :%x\n", p);
    deleteBuf(p);
    system("pause");
}

第一种方法虽然可用,但是实际上并不好,要是需要新建多个数据然后返回呢?显然返回值不能有多个。第二种方法是利用指针的间接赋值的性质,下面是第二种方法:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//间接赋值是指针存在的最大意义

void modifyP(int *p)
{//用一级指针去修改零级指针(通常是实参)的值
    *p = 20;
}

void main_01()
{
    int a = 0; //定义一个实参变量
    int *p = NULL; //定义一个形参
    p = &a; 

    *p = 10; //间接修改a的值*(a的地址)去间接修改a的值
    modifyP(&a);
    printf("%d\n", a);
    system("pause");
}

void getNewBuf(char **p)
{//用二级指针(通常是形参)去修改一级指针(通常是实参)的值
    //相当于在被调用函数里面分配内存,把结果给传出来了
    //这就是指针做函数参数的精华
    *p = 0x33;//间接修改p的值*(p的地址)去间接修改p的值
    char *tmp = NULL;
    tmp = (char *)malloc(100);
    *p = tmp;//可以甩出被调用函数的结果
}

void deleteBuf(char **p)
{
    if (p == NULL)
    {
        return;
    }
    if (*p != NULL)
    {
        free(*p);
    }
    *p = NULL; //这一句话就不是垃圾代码了
}
void main()
{
    char *p = NULL;
    char **p2 = NULL;

    p = 0x11; //直接修改p的值
    printf("p : %x \n", p);
    p2 = &p;

    //*p2 = 0x99;//间接修改p的值*(p的地址)去间接修改p的值
    getNewBuf(&p);
    printf("p : %x \n", p);
    deleteBuf(&p);
    system("pause");
}

从上面可以看到,第二种方法远远优于第一种,且第二种方法更加说明了使用者对于指针的理解。

野指针问题

什么是野指针?

指针指向的空间释放之后,指针的值并未有改变,这个指针就成为了野指针

如何避免野指针?

1. 指针定义时初始化NULL;

char **myarray = NULL;

2. 释放的时候需要判断是否为NULL;

if (myarray != NULL)
{
    free(myarray);
}

3. 释放完毕之后置成NULL

if (myarray != NULL)
{
    free(myarray);
    myarray = NULL;
}

二维指针做函数参数

如何使用二级数组做函数参数?

在解决这个问题之前,我们必须先了解这么一些知识:

1. 数组做函数参数,会退化为指针。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void printfArray(int a[10])
{
    printf("----------printfArray----------\n");
    printf("a:数组的地址:%x\n", a);
    for (int i = 0; i < 10; ++i)
    {
        printf("%d ", a[i]);
    }
    printf("\n");
}
void main()
{
    int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    printf("----------main----------\n");
    printf("a:数组的地址:%x\n", a);
    for (int i = 0; i < 10; ++i)
    {
        printf("%d ", a[i]);
    }
    printf("\n");
    printfArray(a);
    system("pause");
}

运行上面的代码,我们会发现,在函数printfArray中a的地址和我们传递的a数组的地址是一致的,函数并没有一个一个地拷贝数组的值。因此, 数组做函数参数,会退化为指针。

既然这样,我们可以总结出,下面三种输出函数参数的写法几乎没有区别

第一种:

void printArray(int *a, int size)
{
    for (int i = 0; i < size; ++i)
    {
        printf("%d ", a[i]);
    }
    printf("\n");
}

第二种:

void printArray(int a[], int size)
{
    for (int i = 0; i < size; ++i)
    {
        printf("%d ", a[i]);
    }
    printf("\n");
}

第三种:

void printArray(int a[10])
{
    for (int i = 0; i < 10; ++i)
    {
        printf("%d ", a[i]);
    }
    printf("\n");
}

2. 指针是一种数据类型,是指它指向的内存空间的数据类型

- 指针步长(p++),根据所致内存空间的数据类型来确定

- 指针的步长,根据所指内存空间类型来定。

3. 二维数组也是线性排列的。

看下面的代码:

void printArray(int *a, int size)
{
    int i = 0;
    printf("printArray: %d\n", sizeof(a));
    for(i = 0; i < size; i++)
    {
        printf("%d\n", a[i]);
    }
}

int main()
{
    int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
    printArray(p, 6);
    getchar();
    return 0;
}

我们可以看到,函数轻易地输出了二维数组里面的值,也证明了二维数组里的值是线性排列的

利用sizeof可以判断出指针的步长,指针的移动正是以步长为单位。下面的代码值得细细琢磨。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void printArray(int *a, int size)
{//说明了二维数组里的元素线性排列
//从二维数组指针转换为一维数组指针丢失了步长信息
    int i = 0;
    printf("-----------printArray函数中----------\n");
    printf("sizeof(&a):%d \n", sizeof(&a));
    printf("sizeof(a): %d\n", sizeof(a));
    printf("sizeof(*a): %d\n", sizeof(*a));
    printf("a数组的地址: %d\n", a);
    for (i = 0; i < size; i++)
    {
        printf("%d ", a[i]);
    }
    printf("\n");
}
//其实下面三种函数形参的写法在编译器看来都一样,不信的话可以运行程序
void printArray01(int (*a)[3], int size)
{//推荐的写法
    printf("-----------printArray01函数中----------\n");
    printf("sizeof(&a):%d \n", sizeof(&a));
    printf("sizeof(a): %d\n", sizeof(a));
    printf("sizeof(*a): %d\n", sizeof(*a));
    printf("a数组的地址: %d\n", a);

    //printf("*a: %d\n", *a);
    //printf("*a + 1: %d\n", *a + 1);
    for (int i = 0; i < size; ++i)
    {
        for (int j = 0; j < 3; ++j)
        {
            printf("%d ", (*(a + i))[j]);
            //printf("%x\n", (a + i) + j);
        }

    }
    printf("\n");
}
void printArray02(int a[][3], int size)
{//丢失了一部分的信息,3的存在标志了步长,即*a的步长为12
    printf("-----------printArray02函数中----------\n");
    printf("sizeof(&a):%d \n", sizeof(&a));
    printf("sizeof(a): %d\n", sizeof(a));
    printf("sizeof(*a): %d\n", sizeof(*a));
    printf("a数组的地址: %d\n", a);
    for (int i = 0; i < size; i++)
    {
        for (int j = 0; j < 3; ++j)
        printf("%d ", a[i][j]);
    }
    printf("\n");
}

void printArray03(int a[2][3])
{//虽然能用,但却是最低级的用法,即使这样写,a的步长也只不过是4
//换句话说,2没起到什么作用
    printf("-----------printArray03函数中----------\n");
    printf("sizeof(&a):%d \n", sizeof(&a));
    printf("sizeof(a): %d\n", sizeof(a));
    printf("sizeof(*a): %d\n", sizeof(*a));
    printf("a数组的地址: %d\n", a);
    for (int i = 0; i < 2; i++)
    {
        for (int j = 0; j < 3; ++j)
        printf("%d ", a[i][j]);
    }
    printf("\n");
}

int main()
{
    int a[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } };
    char cc[10][30];

    printf("-----------main函数中----------\n");
    printf("sizeof(&a):%d \n", sizeof(&a));
    printf("sizeof(a):%d \n", sizeof(a));
    printf("sizeof(*a):%d \n", sizeof(*a));
    printf("sizeof(&cc):%d \n", sizeof(&cc));
    printf("sizeof(cc):%d \n", sizeof(cc));
    printf("sizeof(*cc):%d \n", sizeof(*cc));

    int *p = (int *)a;
    printArray(p, 6); //即使是用二级指针,也可以输出正确的结果
    printArray01(a, 2);
    printArray02(a, 2);
    printArray03(a);
    getchar();
    return 0;
}

结论倒是挺简单的:

1.C语言中只会以机械式的值拷贝的方式传递参数(实参把值传给形参)

原因有两个:

* 高效

* C语言处理a[n]的时候,它没有办法知道n是几,它只知道&a[0]是多少,因此把它的值作为参数传递进去了,虽然c语言可以做到直接int fun(char a[20]),然后函数能得到20这个数字,但是,C确实没有这么做。当然,这也提高了效率。

2.二维数组参数同样存在退化的问题。

二维数组可以看做是一维数组–>二维数组中的每个元素是一维数组–>二维数组参数中第一维的参数可以省略.

void f(int a[5]); —> void f(int a[]); —> void f(int* a);

void g(int a[3][3]); —> void g(int a[][3]);—> void g(int (*a)[3]);

3. 等价关系。

数组参数 等效的指针参数
一维数组 char a[30] 指针 char*
指针数组 char *a[30] 指针的指针 char **a
二维数组 char a[10][30] 数组的指针 char(*a)[30]
时间: 2024-08-08 10:54:44

二级指针的问题汇总的相关文章

黑马程序员---C基础9【字符串的输入输出】【字符串相关函数】【指针】【指针变量初始】【二级指针】

------Java培训.Android培训.iOS培训..Net培训.期待与您交流! ------- [字符串的输入输出] 1.字符串输出: %s-------从给定的地址开始输出字符直到遇到\0停止: printf("%s\n",&a[3]);  printf("%s\n",a); char a[]="hellowo\0rd!"; 2.字符串输入: 从键盘上接收一个字符串,保存在以a为首地址的字符数组中 scanf("%s&

二级指针**P

首先理解几个概念: 1.对于一个普通变量,进行引用操作,得到的是一级指针.如int a=0;int *p=&a,则&a就是一级指针.因为&a的值就是a的地址,p的值也是a的地址,则&a和p就是一级指针变量(简略为指针),对&a进行解引用操作,int b=*&a;这b等于0. 2.对于普通变量作为形参传递到函数内部,参数的值传递就意味着只是简单的将变量的值copy了一份到临时变量中,然后将临时变量传递给函数,然而临时变量和原始变量是没有任何关系,则函数是无法改

二级指针的三种内存模型

第一种内存模型: /* Module: 二级指针第一种内存模型.cpp Notices: Copyright (c) 2017 Landy Tan */ #include <iostream> using namespace std; ///////////////////////////////////////////////// #define SIZE(a) sizeof(a) / sizeof(a[0]) int SortArray(char **pArray, int nLen);

思维启示之意外的收获(发现自己思维局限和掀开二级指针的虎皮)

潘鹏在CSDN上原创.如其它站点转载请注意排版和写明出处: 今天仍旧是最后一个离开,本来是封装的线程扩展功能来卖票的.但我想将统计是否有漏票的程序封装进去,可是一直纠结的我多个线程就有多个对象,我必需要等全部的线程执行结束才干来统计,可是我不能在接口类和线程类里来写,那我仅仅能写在对用户开放的类里,那这种话,我多个类对象谁去调???调之前我多个对象难道在main里写多次等待线程执行结束??? 由于想要解决第一个问题,我想法去封装线程的类里去接受线程回调函数里面调用的虚函数的返回值,仅仅要有返回值

二级指针与二维数组

最近看<Linux C程序设计大全>这本书,虽然书中有一些错误,但整体来说,书写得还算可以. 当看到网络编程[第23.2.4小节 获得主机信息]时,遇到了一段代码,原文如下: “一台主机有许多和网络相关的信息,例如,主机名称.IP地址.主机提供的服务等.这些信息一般都保存在系统中的某个文件里(例如/etc/hosts等),用户程序可以通过系统提供的函数读取这些文件上的内容.Linux环境下使用gethostent函数读取和主机有关的信息,该函数的原型如下: 1 #include <net

【C/C++学院】0726-cppIDE/一级指针/指针数组/函数指针/函数指针数组/二级指针

[送给在路上的程序员] 对于一个开发者而言,能够胜任系统中任意一个模块的开发是其核心价值的体现. 对于一个架构师而言,掌握各种语言的优势并可以运用到系统中,由此简化系统的开发,是其架构生涯的第一步. 对于一个开发团队而言,能在短期内开发出用户满意的软件系统是起核心竞争力的体现. 每一个程序员都不能固步自封,要多接触新的行业,新的技术领域,突破自我. cppIDE 使用mfc和codeblocks中的mingw编译器.执行system命令中的bat批处理脚本. 一级指针 指针,结构体struct,

二级指针的作用及用途?.xml

pre{ line-height:1; color:#9f1d66; background-color:#e1e1e1; font-size:16px;}.sysFunc{color:#5d57ff;font-style:italic;font-weight:bold;} .selfFuc{color:#8e0ed3;} .bool{color:#008000;} .condition{color:#008000;font-weight:bold;} .key{color:#440080;} .

int?(*p)[4]?p?是二级指针?二维数组?二级指针?.xml

pre{ line-height:1; color:#2f88e4; background-color:#e9ffff; font-size:16px;}.sysFunc{color:#3d7477;font-style:italic;font-weight:bold;} .selfFuc{color:#a0b684;} .bool{color:#86ddd8;} .condition{color:#94e269;font-weight:bold;} .key{color:#ae0bfd;} .

c++中的悬浮指针和野指针 二级指针

(1) c++中的悬浮指针:声明了但没有被付值的指针,它指向内存中的任意一个空间.避免悬浮指针的一个方法是开始就付值为NULL (2)"野指针"不是NULL指针,是指向"垃圾"内存的指针.人们一般不会错用NULL指针,因为用if语句很容易判断.但是"野指针"是很危险的,if语句对它不起作用.野指针的成因主要有两种: 一.指针变量没有被初始化.任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气.所以,指针变量在创建的同