数组与指针_指针与数组 - CSDN
  • 数组指针的区别

    2019-08-23 17:36:16
    指针与数组是C语言中很重要的两个概念,它们之间有着密切的关系,利用这种关系,可以增强处理数组的灵活性,加快运行速度,本文着重讨论指针与数组之间的联系及在编程中的应用。 1.指针与数组的关系  当一个指针...

    一.指针与数组的联系:

    指针与数组是C语言中很重要的两个概念,它们之间有着密切的关系,利用这种关系,可以增强处理数组的灵活性,加快运行速度,本文着重讨论指针与数组之间的联系及在编程中的应用。

    1.指针与数组的关系

      当一个指针变量被初始化成数组名时,就说该指针变量指向了数组。如:

    char str[20], *ptr;
    ptr=str;

      ptr被置为数组str的第一个元素的地址,因为数组名就是该数组的首地址,也是数组第一个元素的地址。此时可以认为指针ptr就是数组str(反之不成立),这样原来对数组的处理都可以用指针来实现。如对数组元素的访问,既可以用下标变量访问,也可以用指针访问。

    2.指向数组元素的指针

      若有如下定义:

     int a[10], *pa;
     pa=a;

      则p=&a[0]是将数组第1个元素的地址赋给了指针变量p。

      实际上,C语言中数组名就是数组的首地址,所以第一个元素的地址可以用两种方法获得:p=&a[0]或p=a。

      这两种方法在形式上相像,其区别在于:pa是指针变量,a是数组名。值得注意的是:pa是一个可以变化的指针变量,而a是一个常数。因为数组一经被说明,数组的地址也就是固定的,因此a是不能变化的,不允许使用a++、++a或语句a+=10,而pa++、++pa、pa+=10则是正确的。由此可见,此时指针与数组融为一体。

    3.指针与一维数组

      理解指针与一维数组的关系,首先要了解在编译系统中,一维数组的存储组织形式和对数组元素的访问方法。

      一维数组是一个线形表,它被存放在一片连续的内存单元中。C语言对数组的访问是通过数组名(数组的起始地址)加上相对于起始地址的相对量(由下标变量给出),得到要访问的数组元素的单元地址,然后再对计算出的单元地址的内容进行访问。通常把数据类型所占单元的字节个数称为扩大因子。

      实际上编译系统将数组元素的形式a[i]转换成*(a+i),然后才进行运算。对于一般数组元素的形式:<数组名>[<下标表达式>],编译程序将其转换成:*(<数组名>+<下标表达式>),其中下标表达式为:下标表达式*扩大因子。整个式子计算结果是一个内存地址,最后的结果为:*<地址>=<地址所对应单元的地址的内容>。由此可见,C语言对数组的处理,实际上是转换成指针地址的运算。

      数组与指针暗中结合在一起。因此,任何能由下标完成的操作,都可以用指针来实现,一个不带下标的数组名就是一个指向该数组的指针。

      2)指向由j个整数组成的一维数组的指针变量

      当指针变量p不是指向整型变量,而是指向一个包含j个元素的一维数组。如果p=a[0],则p++不是指向a[0][1],而是指向a[1]。这时p的增值以一维数组的长度为单位。

     

    编译器为了简化对数组的支持,实际上是利用指针实现了对数组的支持。具体来说,就是将表达式中的数组元素引用转化为指针加偏移量的引用。这么说大家可能不理解,首先什么是引用呢?引用其实就是使用,从编译器的角度来看,就是从一个符号找到对应内存并进行读写的过程。为了理解这一个过程,我们先来看看对于一个普通变量,编译器是怎么做的。

    比如我们用C语言写了这样的语句

    int a;
    a = 3;
    编译器为了完成这两句代码,首先在编译过程中要创建一个符号表,样子大概如下图:

    然后在运行过程中,编译器发现a=3这句代码时,会在符号表里找a对应的地址,然后把3放入对应的地址,即这里的0x1000。那如果是一个指针呢?即如果是*p=3会怎么做呢?首先,符号表变成了这个样子

    在运行过程中,编译器遇到*p=3时,首先要从符号表中找到p的地址0x1004,然后取出0x1004中的内容,这里假设为0x2000,最后把3放到0x2000内存地址中,即*(0x2000)=3。

    相信大家已经看明白了,想比于利用普通变量,利用指针存取数据的过程中多了一部取地址的的过程。这也就是指针变量于普通变量最大的不同。

    下面再来看一下指针加偏移量的引用方式,还以上面的指针p为例,让我们来看一下*(p+2)=3的实现过程。首先,编译器从符号表中找到p然后,取出里面的内容0x2000,再根据其类型(int*),做一个运算,0x2000+2×sizeof(int)=0x2008。所以编译器会把3放入0x2008这个内存地址。整个过程可表示为*(*(0x1004)+2)=3。从这里也可以看出为什么指针必须有类型,因为在引用过程中要用到指针所指类型的长度。

    最后来看一下,数组元素的引用是如何实现的,假设我们定义了一个数组,并对其元素进行了引用

    int b[10];
    b[4] = 3;
    对应的符号表变成了这个样子

    那b[4]=3如何完成呢?首先,找到符号b,然后发现其类型为int[](假想表达方式,C语言中不支持这样写),所以计算式变成了0x1008+4×sizeof(int)=0x1018,然后把3放入0x1018就可以了。用一个式子表达就是*(0x1008+4)=3。

    从上面的寻址式子可以看出,普通变量、指针、数组三者对于编译器的区别。具体到数组,它即具有普通变量的直接性,即不用取两次地址里的内容而是取一次,同时又具有和指针相同的偏移量引用方式,即下标的实现实际是由指针加偏移量实现的

    为了表明上述事实(或者是为了提高C语言入门门槛),C语言对指针与数组的引用方式做了可以“交叉”使用的语法规定。就上面的例子来说,如果p指针指向数组b时,b[i]、*(b+i)、p[i]、*(p+i)都是对数组第i个元素的正确引用方式,这也给很多C语言学习者制造了“指针和数组一样”的错觉。

    4.指针与字符数组

      C语言中许多字符串操作都是由指向字符数组的指针及指针的运算来实现的。因为对于字符串来说,一般都是严格的顺序存取方式,使用指针可以打破这种存取方式,更为灵活地处理字符串。

      另外由于字符串以′\0′作为结束符,而′\0′的ASCII码是0,它正好是C语言的逻辑假值,所以可以直接用它作为判断字符串结束的条件,而不需要用字符串的长度来判断。C语言中类似的字符串处理函数都是用指针来完成,使程序运行速度更快、效率更高,而且更易于理解。

    二.指针与数组的区别:

    1.把数组作为参数传递的时候,会退化为指针

    在作为函数传递参数的时候,数组是等同于指针的,为什么呢?因为,数组在作为参数传递或者调用数组的时候,通常都是把数组首元素的地址作为参数来传递或者调用,这个时候数组名就相当于数组首元素的地址,也就是指针了。就是说void test (int array[]); void test (int * array),这两种函数声明其实都是一样的,形参这都是&array[0];只是表达的方式不一样

    数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅只是一个指针;很遗憾,在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。

    所以,数组名作为函数形参时,其沦落为一个普通指针!它的贵族身份被剥夺,成了一个地地道道的只拥有4个字节的平民。

    典型的情况是

     void func(int A[])
     {
           //sizeof(A)得到的是4bytes
     }
     
     int main()
     {
         int a[10]; //sizeof(a) 得到的结果是40bytes
         funct(a);
     }

    2、数组名可作为指针常量

      根据结论2,数组名可以转换为指向其指代实体的指针,所以程序1中的第5行数组名直接赋值给指针,程序2第7行直接将数组名作为指针形参都可成立。

     下面的程序成立吗?

    int intArray[10];
    intArray++;
    

      读者可以编译之,发现编译出错。原因在于,虽然数组名可以转换为指向其指代实体的指针,但是它只能被看作一个指针常量,不能被修改。

      而指针,不管是指向结构体、数组还是基本数据类型的指针,都不包含原始数据结构的内涵,在WIN32平台下,sizeof操作的结果都是4。

    顺便纠正一下许多程序员的另一个误解。许多程序员以为sizeof是一个函数,而实际上,它是一个操作符,不过其使用方式看起来的确太像一个函数了。语句sizeof(int)就可以说明sizeof的确不是一个函数,因为函数接纳形参(一个变量),世界上没有一个C/C++函数接纳一个数据类型(如int)为"形参"。

    3.对于问题

    char a[3] = "abc";
    strcpy(a,"end");//it's ok
    
    char *a = "abc";
    strcpy(a,"end"); //it's wrong

    解释如下:

    char *a = "abc";  abc是一个字符串常量,有它自己的存储空间,因为分配在只读数据块,我们无法直接访问。这样赋值后,a只能读,不能写

    所以strcpy(a, "end")不行,只有当你为a分配非常量的存储空间后才行

    如:

    char *a = new char[4];
    strcpy(a, "end");  
    printf("%s", a);
    delete []a

    4.数组和指针的分配

    数组是开辟一块连续的内存空间,数组本身的标示符代表整个数组,可以用sizeof取得真实的大小;指针则是只分配一个指针大小的内存,并可把它的值指向某个有效的内存空间

    [全局的和静态的]

    char *p= "hello ";//一个指针,指向只读数据块(section)里的 "hello ",可被编译器放入字符串池(也就是说, 你在写一个char *q= "hello ",可能和p共享数据)
    char a[]= "hello ";//一个数组,分配在可写数据块(section),不会被放到字符串池中

     

    其他:

    ??1.编译时不会为指针分配存储空间, 但是指针在结构体中, 是会默认分配的,

    2.数组的内部实现也是依靠指针, 只是数组名是一个指针常量, 数组一次寻址(即通过数组名直接找到数组元素), 指针需要两次寻址(先确定指针变量的己值,然后定位到指针的他值)

    3.指针变量+1, 不是加一个地址量(即一个字节), 而是加一个内存单元, 具体的长度由指针类型决定

    4.指针指向数组的时候, 只是指向数组的首地址, 他不代表数组类型, 可以通过 指针++的方式依次获取数组内容

    5.数组名在两种情况下不被视为指向起始元素的指针。

        a.作为sizeof运算符的操作数出现时

        b.作为取址运算符&的操作数出现时

    6.两个指针不能做加法运算,但是可以做减法运算

    7.数组名是指针常量, 所以没有办法再次对一个数组名重新分配内存地址; 换言之, 可以对指针变量赋值, 但是不能对数组名赋值

    8.二者在结构体中占用内存大小不同

    void test12(){
        struct test{
            int *p;
        };
    
        size_t tmp1 = sizeof(struct test); //输出8
        printf("%zu\n",tmp1);
    
        struct test1{
            int p[10];
        };
    
        size_t tmp2 = sizeof(struct test1); //输出40
        printf("%zu\n",tmp2);
    }

    9. 定义了一个指针, 是可以按数组的方式使用的

    10. 数组是比指针更高一层级的定义, 数组作为形参, 将退化为指针

    如 int *p; ... p[5]=2;

    11.数组存数据,指针存地址

    12.指针对比数组的一个优势: 可以扩容, 因为指针的内存可以通过malloc分配, 所以也可以通过realloc扩容

    13. C语言中结构体数组:每一个元素都是一个结构体类型的变量,都包含结构体中所有的成员项

           而JAVA中的数组, 类似与C中的指针数组, 存放的是对象的地址引用

    展开全文
  • 本文是对《C语言深度剖析》一书内容的拓展,在看这本书的时候解了很多我之前的一些困惑,故在此记录。对数组的引用总是可以写成对指针的引用,而且...而数组就是数组,其大小元素的类型和个数有关,定义数组时必须...

    本文是对《C语言深度剖析》一书内容的拓展,在看这本书的时候解了很多我之前的一些困惑,故在此记录。对数组的引用总是可以写成对指针的引用,而且确实存在一种指针和数组的定义完全相同的上下文环境。

    但是指针和数组还是在本质上是不一样的。指针就是指针,指针变量在32位的系统下面是4Byte,而在64位系统下面是8Byte,其值为某一个内存的地址。而数组就是数组,其大小与元素的类型和个数有关,定义数组时必须制定其元素的类型和个数,数组可以存放任何类型的数据,但是不能存放函数。

     

    1、从变量大小来看指针与数组的区别

    #include<stdio.h>
    
    int main(){
    	char *p = "abcdef";
    	char a[] = "abcdef";
    	int b[] = {1,2,3,4,5};
    	int *ptr = (int*)(&b+1);
    	printf("sizeof(p) = %d;\n",sizeof(p));
    	printf("sizeof(a) = %d;\n",sizeof(a));
    	printf("*(b+1) = %d,*(ptr - 1) = %d;\n",*(b+1),*(ptr-1));	
    	return 0;
    }

    上面的演示程序中,对于指针p的求值是p这个指针的大小,由于测试的操作系统是64位的,所以是8;而对于数组a,它的值就是这个数组的大小了。

    后面对于b这个数组,*(b+1)=2非常好理解,其实就相当于b[1];而后者为什么是这样的?(&b+1)其实是取b的首地址,该地址的值加上sizeof(b)的值,即&b+5*sizeof(int),也就是下一个数组的首地址的,显然现在的指针已经越界了。将前者的值赋给ptr,则此时ptr指向的是b[5],在此基础上减一,就是b[4]了,即5。

     

    2、指针数组与数组指针

    指针数组:首先它是一个数组,这个数组全是指针,数组占用多少个字节有数组本身决定。它是“储存指针的数组”的简称。

    数组指针:首先它是一个指针,它指向一个数组。在32位系统下永远占用4个字节,至于它指向的数组占多少个字节,不知道。它是“指向数组的指针”的简称。

    如下所示,前者的指针数组,后者是数组指针。

    Int *p1[10];

    Int (*p2)[10];

    如何区分,前者的话[]比*号的优先级要高,所以其一定先是数组,然后才是指针,合起来就是指针数组了。后者也是一个符号优先级的问题,看到(),毫无疑问了。它们的图示如下。

                                 

    展开全文
  • 数组与指针

    2020-03-06 16:16:13
    #include<stdio.h> #include<stdlib.h> //struct Test //{ // int Num; // char pcName; // short sDate; // char cha[2]; // short sBa[4]; //}; //此结构体占内存为20个字节 ...a得到是一个int...

    #include<stdio.h>
    #include<stdlib.h>
    //struct Test
    //{
    // int Num;
    // char pcName;
    // short sDate;
    // char cha[2];
    // short sBa[4];
    //};
    //此结构体占内存为20个字节
    int main()
    {
    //问题1
    //&a得到是一个int(
    )[5],指针数组
    //&a+1得到的是5后边的那个元素
    //所以ptr指向的是5后边的那个元素
    //但是ptr被强转为int*
    //所以指针-1向前跳过一个元素指向的是5这个元素的地址
    //在对其*得到5这个元素
    // (a+1)可以看成a[1]
    // 即打印出的就是 2 5
    //int a[5] = { 1,2,3,4,5 };
    //int ptr = (int )(&a + 1);
    //printf("%d%d\n", (a + 1), (ptr - 1));
    //问题2
    // struct Test
    p=(struct Test
    )0x100000;
    //给P赋值为0x100000
    //%p为打印地址
    //因为p占内存为20
    //所以输出0x100020
    // printf("%p\n",p+0x1);
    //下面的强制类型转换.导致p就变成了一个unsigned long 类型的,在去+1,就只是简单的整数+1,所以输出结果为0x100001
    // printf("%p\n",(unsigned long)p+0x1);
    //此时强转为一个指针类型,指针类型在32位系统下占4个字节所以 输出结果为0x100004
    //指针+1时,需要查看前边类型在内存中需要占几个字节,
    // printf("%p\n",(unsigned int
    )p+0x1);
    //问题3
    //int a[4] = { 1,2,3,4 };
    //// &a可以看成int(
    )[4],&a+1即跳过整个数组,此时指向4元素后的一位元素
    //int
    ptr1 = (int *)(&a + 1);
    ////(int)a将a强转为int类型,此时a+1即a[0]+1,即为整数+1
    ////a[0]为首元素的地址,但是它现在执行的是整数+1,即首元素的地址+1
    ////即假设a[0]的首地址为0x100,但是整个首元素地址为4个字节
    ////即0x100-0x103,此时+1位0x101,ptr2指向的就是0x101这个地址
    ////*ptr2的含义就是从ptr2存的地址来时往后读取四个字节,即当成一个整数来理解
    ////ptr2它存的就是这四个字节中的整数
    //此电脑为小端字节序
    ////a[0]为01 00 00 00a[1]为02 00 00 00
    ////即从01后的00开始到02结束,又因为是小端字节序,所以结果为0x02000000
    //int
    ptr2 = (int *)((int)a + 1);
    //printf("%x,%x", ptr1[-1], ptr2); //输出4 200000
    //问题四
    //注意这里的(),(,)这个表达式为逗号表达式,它只取,最后一位
    //即二维数组的真正的值为{(1,3),{5,0},{0,0}}
    //int a[3][2] = { (0,1),(2,3),(4,5) };
    //int p;
    ////a[0]取到的是一个长度为2的一维数组
    ////p是当前这个数组首元素的地址
    //p = a[0];
    ////所以输出的应该是1``
    //printf("%d", a[0][0]);
    //return 0;
    //问题5
    //int a[5][5];//二维数组
    //int(p)[4];//数组指针
    //p = a;
    //printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]);
    //return 0;在这里插入图结题思路片描述
    //问题6
    // int aa[2][5]=
    // {
    // {1,2,3,4,5},
    // {6,7,8,9,10}
    // };
    // //&aa可以看成将a[2][5]隐式转为int(
    )[2][5]
    // //此时+1可以看成跳过整个数组
    // //此时ptr1中存的地址应该是10后边元素的地址
    // //在对ptr1-1在解引用正好是10
    // int ptr1 = (int )(&aa + 1);
    // //aa[1]可以看成
    (aa + 1);
    // //aa是数组名再+1隐式转为int(
    )[5]
    // //执行+1操作,跳过一个一维数组,即指向5后边的元素
    // int ptr2 = (int )(aa[1]);
    // //10 5
    // printf("%d,%d", (ptr1 - 1), (ptr2 - 1));
    ////问题7
    //char
    a[] = { “work”,“at”,“alibaba” };
    //charpa = a;
    //pa++;
    //printf("%s\n", pa);
    在这里插入图片描述
    //问题8
    char
    c[] = { “ENTER”,“NEW”,“POINT”,“FIRST” };
    char
    cp[] = { c + 3,c + 2,c + 1,c };
    char
    cpp = cp;
    printf("%s\n", **++cpp);
    printf("%s\n",
    ++cpp+3);
    printf("%s\n", *cpp[-2]+3);
    printf("%s\n", cpp[-1][-1]+1);
    }

    展开全文
  • 2.存储方式、初始化(指针数组、数组指针)、存储内容(求sizeof) 3.赋值 6.传参(函数指针、函数指针数组、函数指针数组的指针、野指针) 一、概念 数组:数组是用于储存多个相同类型数据的集合。 指针:指针...

    一、概念

    • 数组:数组是用于储存多个相同类型数据的集合。
    • 指针:指针相当于一个变量,但是它和不同变量不一样,它存放的是其它变量在内存中的地址。

    二、赋值、存储方式、求sizeof、初始化等

    1.赋值

    同类型指针变量可以相互赋值,数组不行,只能一个一个元素的赋值或拷贝

    2.存储方式
    • 数组:数组在内存中是连续存放的,开辟一块连续的内存空间。数组是根据数组的下进行访问的,多维数组在内存中是按照一维数组存储的,只是在逻辑上是多维的。

    数组的存储空间,不是在静态区就是在栈上。

    • 指针:指针很灵活,它可以指向任意类型的数据。指针的类型说明了它所指向地址空间的内存。

    指针:由于指针本身就是一个变量,再加上它所存放的也是变量,所以指针的存储空间不能确定。

    3.求sizeof
    • 数组

    数组所占存储空间的内存:sizeof(数组名)
    数组的大小:sizeof(数组名)/sizeof(数据类型)

    • 指针

    在32位平台下,无论指针的类型是什么,sizeof(指针名)都是4,在64位平台下,无论指针的类型是什么,sizeof(指针名)都是8。

    关于指针和数组求sizeof,我在之前的博客中写过,现将连接贴上:
    https://blog.csdn.net/cherrydreamsover/article/details/81589838

    4.初始化
    • 数组
    1char a[]={"Hello"};//按字符串初始化,大小为6.2char b[]={'H','e','l','l'};//按字符初始化(错误,输出时将会乱码,没有结束符)3char c[]={'H','e','l','l','o','\0'};//按字符初始化
    

    这里补充一个大家的误区,就是关于数组的创建和销毁,尤其是多维数组的创建与销毁。
    (1)一维数组:
    int* arr = new int[n];//创建一维数组
    delete[] arr;//销毁

    (2)二维数组:
    int** arr = new int*[row];//这样相当于创建了数组有多少行
    for(int i=0;i<row;i++)
    {
    arr[i] = new int[col];//到这里才算创建好了
    }
    //释放
    for(int i=0;i<row;i++)
    {
    delete[] arr[i];
    }
    delete[] arr;

    • 指针
    //(1)指向对象的指针:(()里面的值是初始化值)
    int *p=new int(0) ;    delete p;
    //(2)指向数组的指针:(n表示数组的大小,值不必再编译时确定,可以在运行时确定)
    int *p=new int[n];    delete[] p;
    //(3)指向类的指针:(若构造函数有参数,则new Class后面有参数,否则调用默认构造函数,delete调用析构函数)
    Class *p=new Class;  delete p;
    //(4)指针的指针:(二级指针)
    int **pp=new (int*)[1]; 
    pp[0]=new int[6];
    delete[] pp[0];

    这里我们区分两个重要的概念:指针数组、数组指针。

    (1)指针数组:它实际上是一个数组,数组的每个元素存放的是一个指针类型的元素。

    int* arr[8];
    //优先级问题:[]的优先级比*高
    //说明arr是一个数组,而int*是数组里面的内容
    //这句话的意思就是:arr是一个含有8和int*的数组

    这里写图片描述

    (2)数组指针:它实际上是一个指针,该指针指向一个数组。

    int (*arr)[8];
    //由于[]的优先级比*高,因此在写数组指针的时候必须将*arr用括号括起来
    //arr先和*结合,说明p是一个指针变量
    //这句话的意思就是:指针arr指向一个大小为8个整型的数组。

    这里写图片描述

    三、传参

    • 数组

    数组传参时,会退化为指针,所以我们先来看看什么是退化!
    (1)退化的意义:C语言只会以值拷贝的方式传递参数,参数传递时,如果只拷贝整个数组,效率会大大降低,并且在参数位于栈上,太大的数组拷贝将会导致栈溢出。
    (2)因此,C语言将数组的传参进行了退化。将整个数组拷贝一份传入函数时,将数组名看做常量指针,传数组首元素的地址。

    1.一维数组的传参
    #include <stdio.h>
    //传参方式正确
    //用数组的形式传递参数,不需要指定参数的大小,因为在一维数组传参时,形参不会真实的创建数组,传的只是数组首元素的地址。(如果是变量的值传递,那么形参就是实参的一份拷贝)
    void test(int arr[])
    {}
    
    //传参方式正确
    //不传参数可以,传递参数当然也可以
    void test(int arr[10])
    {}
    
    //传参方式正确
    //一维数组传参退化,用指针进行接收,传的是数组首元素的地址
    void test(int *arr)
    {}
    
    //传参方式正确
    //*arr[20]是指针数组,传过去的是数组名
    void test2(int *arr[20])
    {}
    
    //传参方式正确
    //传过去是指针数组的数组名,代表首元素地址,首元素是个指针向数组的指针,再取地址,就表示二级指针,用二级指针接收
    void test2(int **arr)
    {}
    int main()
    {
    int arr[10] = {0};
    int *arr2[20] = {0};
    test(arr);
    test2(arr2);
    }
    2.二维数组的传参
    //传参正确
    //表明二维数组的大小,三行五列
    void test(int arr[3][5])
    {}
    
    //传参不正确
    //二维数组的两个方括号,不能全部为空,也不能第二个为空,只能第一个为空
    void test(int arr[][])
    {}
    
    //传参正确
    //可以写成如下这样传参形式,但是不能写int arr[3][]
    void test(int arr[][5])
    {}
    
    //传参不正确
    //arr是一级指针,可以传给二维数组,但是不能正确读取
    void test(int *arr)
    {}
    
    //传参不正确
    //这里的形参是指针数组,是一维的,可以传参,但是读取的数据不正确
    void test(int* arr[5])
    {}
    
    //传参正确
    //传过去的是二维数组的数组名,即数组首元素的地址,也就是第一行的地址,第一行也是个数组,用一个数组指针接收
    void test(int (*arr)[5])
    {}
    
    //传参不正确
    //可以传参,但是在读取的时候会有级别不同的问题
    void test(int **arr)
    {}
    int main()
    {
    int arr[3][5] = {0};
    test(arr);
    }
    • 指针
    1.一级指针传参

    当函数参数部分是一级指针时,可以接受什么参数例如:test(int*p)

    (1)可以是一个整形指针
    (2)可以是整型变量地址
    (3)可以是一维整型数组数组名

    #include <stdio.h>
    void print(int *p, int sz)
    {
    int i = 0;
    for(i=0; i<sz; i++)
    {
    printf("%d\n", *(p+i));
    }
    }
    int main()
    {
    int arr[10] = {1,2,3,4,5,6,7,8,9};
    int *p = arr;
    int sz = sizeof(arr)/sizeof(arr[0]);
    //一级指针p,传给函数
    print(p, sz);
    return 0;
    }
    2.二级指针传参

    即当函数参数部分是二级指针时,可以接受什么参数例如:test(int**p)

    (1)二级指针变量
    (2)一级指针变量地址
    (3)一维指针数组的数组名

    #include <stdio.h>
    void test(int** ptr)
    {
    printf("num = %d\n", **ptr);
    }
    int main()
    {
    int num = 10;
    int*p = &num;
    int **pp = &p;
    test(pp);
    test(&p);
    return 0;
    }

    四、函数指针、函数指针数组、函数指针数组的指针

    1.函数指针
    void test()
    {
    printf("hehe\n");
    }
    //pfun能存放test函数的地址
    void (*pfun)();

    函数指针的形式:类型(*)( ),例如:int (*p)( ).它可以存放函数的地址,在平时的应用中也很常见。

    2.函数指针数组

    形式:例如int (*p[10])( );
    因为p先和[ ]结合,说明p是数组,数组的内容是一个int (*)( )类型的指针
    函数指针数组在转换表中应用广泛

    3.函数指针数组的指针

    指向函数指针数组的一个指针,也就是说,指针指向一个数组,数组的元素都是函数指针

    void test(const char* str)
    {
    printf("%s\n", str);
    }
    int main()
    {
    //函数指针pfun
    void (*pfun)(const char*) = test;
    //函数指针的数组pfunArr
    void (*pfunArr[5])(const char* str);
    pfunArr[0] = test;
    //指向函数指针数组pfunArr的指针ppfunArr
    void (*(*ppfunArr)[10])(const char*) = &pfunArr;
    return 0;
    }
    展开全文
  • c语言多维数组与指针

    2019-01-12 13:28:12
    而一维数组相对于多维数组来说很简单的,然而数组a就表示的是整个数组内存段的首地址也就是0X01,那可可想而知变量a存得值就是一个地址,到此不难看出a就是一个指针。。。(不理解的可以看看我之前呢写的有关指...

    一.前言:

    对于数组想必大家都不陌生首先得要知道的是对于数组元素在内存存储是连续性的

    例如:

    char a[10]    //那么数组a的各个原数在内存中的存储地址为:0X01~0X10

    而一维数组相对于多维数组来说很简单的,然而数组a就表示的是整个数组内存段的首地址也就是0X01,那可可想而知变量a存得值就是一个地址,到此不难看出a就是一个指针。。。(不理解的可以看看我之前呢写的有关指针的介绍就清楚了此处不再重复解释)。

    对于a存放的地址和&a的地址有啥区别了?

        int c[10];
        int(*p)[10];  //定义一个指向10个int宽度的指针
        p = &c;
        printf("c的地址:%p\n",c);
        printf("&c的地址:%p\n", &c);
        printf("p存的地址:%p\n", p);

    结果:

    重结果可以看出a存的地址等于&a的地址(指针a的地址),可是他们真的完全一样吗?打那是不完全一样。例如:


        char c[10];
        char(*p)[10];
        p = &c;
        printf("c的地址:%p,c+1的地址为:%p\n",c,c+1);
        printf("&c的地址:%p,&c+1的地址为:%p\n", &c,&c+1);
        printf("p存的地址:%p,P+1存的地址为:%p\n",p, p+1);

    结果:

    这里说明了虽然&a的地址和a存的地址相同都为首地址但是两者意义不一样&a表示整个数组地址a表示首元素地址,同理如果要接收整个数组地址要用指向10数据宽度的指针即 char(*p)[10];

    注意:数组字母虽然表示指针但是他是不能修改的左值即不能执行++运算等。

    二.二维数组和多维数组。

    存储方式:

    一维存储方式:

    表示每个元素占据一个内存单元。

    二维数组:char a[3][6]

    从存储单眼我们可以看出:二维数组中存有3个一维数组并且这三个一维数组都是含有6个char型的数组。

    由此可以看出多维数组实际上就是由多个一维数组构成。

    例如:char a[2][2][2]:表示数组a中有2个元素,这两个元素又由2个元素构成,这三个元素又包含了2个char型元素表示为:

    {{{{},{}},{{},{}}},{{{},{}},{{},{}}}}

    二维数组和指针:

    同理二维数组名指向的是数组中每一个一维数组。例如  char a[2][2]  a指向第一个元素地址即是:&a[0][0]地址

    总之数组名表示的是整个数组中最外层的数据元素如果是二维数组那么他表示的就是第一层的表示一维数组的元素项,若要继续项内层访问就要*a解引用。

    注意:a[1]等价于*(a+1)是一个指针的解引用可能是地址也可能是数。

              a[1][1]等价于*(*(a+1)+1)而&a[1]代表二维数组第二个人元素项的地址实际上a[1]可以用一个一个一级指针接收。

    对于该用什么指针接收实参具体得看传进来的内容是什么。。。。

    展开全文
  • 数组指针

    2018-04-24 16:48:15
    数组数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。 所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高...
  • 数组与指针的关系

    2018-07-28 13:26:26
    数组与指针的关系   在刚刚接触到数组与指针时,我想最常苦恼的问题就是数组与指针之间有什么关系了吧。但是他们两者之间真的有什么数不清道不明的关系吗?答案无疑是否定的!接下来就让我们来一探究竟吧! ...
  • 数组指针与指针数组

    2018-12-04 18:10:27
    数组指针(也称行指针) 定义 int (*p)[n]; ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。 如要将二维...
  • 指针数组与数组指针详解1.什么是指针数组和数组指针? 指针数组:指针数组可以说成是”指针的数组”,首先这个变量是一个数组,其次,”指针”修饰这个数组,意思是说这个数组的所有元素都是指针类型,在32位系统中...
  • 二维数组指针⑴ 用指针表示二维数组元素。要用指针处理二维数组,首先要解决从存储的角度对二维数组的认识问题。我们知道,一个二维数组在计算机中存储时,是按照先行后列的顺序依次存储的,当把每一行看作一个...
  • 二维数组与指针数组的区别 在功能的实现上二维数组指针数组十分相似,都可以实现对多个字符串的存储,然而两者在本质上是有很大差别的,下面简单介绍一下 二维数组 二维数组的一般定义和赋值方式为 char a[3]...
  • 二维数组与指针 要用指针处理二维数组,首先要解决从存储的角度对二维数组的认识问题。我们知道,一个二维数组在计算机中存储时,是按照先行后列的顺序依次存储的,当把每一行看作一个整体,即视为一个大的数组元素...
  • 1.简单数组与指针的关系(int型数组为例): 解释: 第二句相当于int型指针cp指向了数组的第一个元素,所以,打印出的cp是第一个元素的地址;&cp[0]cp等价;&cp也仍然是个地址;*cp通过解引用得到了数组的...
  • 数组与指针的关系颇有点像诗和词的关系:他们都是文学形式之一,有不少共同之处,但在实际的表现手法上又有各自的特色。---Peter Van Der Linden 在笔记(4)中我们讨论了数组指针并不一致的情况(在一个文件中定义...
  • 一、简述一维数组与指针 (1)在C语言中,数组是占用一串连续的存储单元。数组中每个元素都可以看作一个变量,因此每个数组元素都有一个地址。 那么:int *p,a[5]={1,2,3,4,5}; 指针p的基类型是int,数组a的各个...
  • 指针数组与数组指针  指针数组 指针数组是一个数组,数组的元素保存的是指针; 数组指针 数组指针是一个指针,该指针指向的是一个数组; #include &lt;stdio.h&gt; #define SIZE 5 int main(int ...
  • 数组与指针(二)   数组与指针的纠葛 以指针的形式访问数组: 下标表达式: 后缀表达式[表达式] 在C语言中,根据定义,表达式e1[e2]准确地对应于表达式*((e1)+(e2))。因此,要求表达式e1[e2]的其中一个操作...
  • 如何正确的理解数组与指针?它们之间有何联系? 指针(Pointer)就是内存的地址,C语言允许用一个变量来存放指针,这种变量称为指针变量。指针变量可以存放基本类型数据的地址,也可以存放数组、函数以及其他指针...
  • c++ 一维数组与指针

    2017-09-23 09:48:06
    本文主要讲述c++中数组与指针的转换关系。在某些情况下,数组名会转化为指针。如有不当之处,请指正,谢谢! (本文代码平台vs2013,以int类型说明,文中仅列出了关键的代码)。 // 1、当我们定义一个数组时,数组名...
  • 一维数组指针: 1、一维数组名: 对于这样的一维数组:int a[4]; a作为数组名就是我们数组的首地址, a是一个地址常量 .  首先说说常量和变量的关系, 对于变量来说, 用箱子去比喻再好不过了, 声明一个变量就声明一...
1 2 3 4 5 ... 20
收藏数 690,357
精华内容 276,142
关键字:

数组与指针