深入理解C指针之四:指针和数组

原文:深入理解C指针之四:指针和数组

  数组是C内建的基本数据结构,数组表示法和指针表示法紧密关联。一种常见的错误认识是数组和指针完全可以互换,尽管数组名字有时可以当做指针来用,但数组的名字不是指针。数组和指针的区别之一就是,尽管数组的名字可以返回数组地址,但是名字不能作为赋值操作的目标。

  概述

  数组是能用索引访问的同质元素连续集合。数组的元素在内存中是相邻的,而且元素都是同一类型的。数组的长度是固定的,realloc函数和变长数组提供了应对长度需要变化的数组的技术。

  一维数组是线性结构,用一个索引访问成员。取决于不同的内存模型,数组的长度可能会有不同。

int vector[5];

     数组的名字只是引用了一块内存,对数组使用sizeof操作符会得到为该数组分配的字节数。要知道数组的元素数,只要对其除以元素长度即可。

int vector[5];
printf("vector is %p\nsize is %d\n", &vector, sizeof(vector));
//vector is 0xbffb8c08
//size is 20

   可以用一个块语句初始化一维数组。

int vector[5] = {1,2,3,4,5};

     声明一个二维数组。可以将二维数组当做数组的数组,只使用一个下标访问二维数组得到的是对应行的指针。

int matrix[2][3] = {{1,2,3},{4,5,6}};
printf("0 is %p\n1 is %p\n", &matrix[0], &matrix[1]);
//0 is 0xbf984044
//1 is 0xbf984050

    可以看到,两个地址刚好差12个字节,也就是matrix数组一行的长度。

  把数组地址赋给指针。

int* pv = vector;

     注意这种写法和&vector[0]是等价的,不同于&vector是返回的整个数组的指针。一个是数组指针,一个是整数指针,指针类型不同。pv实际代表的是一个地址,*(pv+i)的写法等价于pv[i],前者称作指针表示法,后者称作数组表示法

printf("pv[0] is %d\n*(pv+1) is %d\n ",pv[0],*pv);
//pv[0] is 1
//*(pv+1) is 1

    pv指针包含一个内存块的地址,方括号表示法会取出pv中包含的地址,用指针算数运算符把索引i加上,然后接引新地址返回其内容。给指针加上一个整数实际是给它加了整数与数据类型长度的乘积,这一点对数组名字也同样适用,*(pv+1)和*(vector+1)是等价的。因此数组表示法可以理解为“偏移并解引”操作。vector[i]和*(vector+i)两种方法结果相同,仅仅在实现上略有差别,可以忽略。注意指针可以作为左值,但是数组名字不可以作为左值使用,vector=vector+1是错误的写法。

  一维数组

  如果从堆上分配内存并把地址赋给一个指针,那就肯定可以对指针使用下标并把这块内存当做一个数组。用malloc创建的已有数组的长度可以通过realloc函数来调整。C99支持变长数组,但是变长数组只能在函数内部声明。所以如果数组的生命周期需要比函数长,或者你没有使用C99,那就只能使用realloc。

   下面这个函数接受用户的输入,并且使用realloc函数动态的申请所需的内存,按回车结束输入。

char* getLine(void)
{
const size_t sizeIncrement = 10;
char* buffer = malloc(sizeIncrement);
char* currentPosition = buffer;
size_t maximumLength = sizeIncrement;
size_t length = 0;
int character;
if(currentPosition == NULL){return NULL;}
while(1)
{
character = fgetc(stdin);
if(character == '\n'){break;}
if(++length >= maximumLength)
{
maximumLength += sizeIncrement;
char* newBuffer = realloc(buffer,maximumLength);
if(newBuffer == NULL){free(buffer); return NULL;}
currentPosition = newBuffer + (currentPosition - buffer);
buffer = newBuffer;
}
*currentPosition++ = character;
}
*currentPosition = '\0';
printf("buffer is %s\n", buffer);
return buffer;
}
getLine();

   将一维数组作为参数传递给函数实际是通过值来传递数组的地址,我们需要告诉函数数组的长度,否则函数只有一个地址,不知道数组到底有多长。对于字符串来说,可以依靠NUL字符来判断其长度。对于有些数组则无法判断。

  声明一个指向数组的指针和指针数组、数组指针是不同的。指向数组的指针是一个指针指向数组下标为0的元素,指针数组是一个元素为指针的数组,数组指针是数组类型的指针。

int vector[2] = {1,2};
int* pv1 = vector;//指向数组的指针
int* pv2[2];//指针数组,可以用一个循环来为每个指针分配内存
int (*pv3)[2];//数组类型的指针

    现在再来区分一下数组表示法和指针表示法在指针数组的应用。

int* array[5];
array[0] = (int*) malloc (sizeof(int));
*array[0] = 100;
*(array+1) = (int*) malloc (sizeof(int));
**(array+1) = 200;

    在这里array[1]和*(array+1)是等价的,实际都是指针类型,使用malloc函数为指针在堆上分配内存,解引指针来为数据赋值,因此**(array+1)其实不难理解,一个*解引得到一个指针,对指针再解引才得到数据,而[]括号前面已经 解释过,就是相当于一个取地址和加索引的操作。当然,还可以使用array[0][0]来代替*array[0]。

  指针和多维数组

  可以将多维数组的一部分看做子数组,比如二维数组的每一行当做一个一维数组。数组按行-列顺序存储,第二行的第一个元素的内存地址紧跟在第一行最后一个元素后面。

int matrix[2][3] = {{1,2,3},{4,5,6}};
int (*pmat)[3] = matrix;//3是二维数组的列数
printf("size of pmat[0] is %d\n", sizeof(pmat[0]));
//size of pmat[0] is 12

   可以看到,该数组指针的第一个元素的长度是12个字节,也就是说是第一行的长度。如果要访问第一行第一个元素,需要用pmat[0][0]来访问。array[i][j]等于 array+i*sizeof(row) + j* sizeof(element)。sizeof(row)是一行的总大小,sizeof(element)是单个元素的大小,array是array[0][0]的地址。

   给函数传递数组参数时,要考虑如何传递数组的维数以及每一维度的大小。下面是两种传递二维数组的方法:

void display2DArray(int arr[][3], int rows){}
void display2DArray(int (*arr)[3], int rows){}

    两种方法都指定了行数。如果传递的数组维度超过了二维,除了一维的部分,需要指定其它维度的长度。如果传递一个array3D[3][2][4]的数组:

void display3DArray(int (*arr)[2][4], int rows){}

    当使用malloc分别为二维数组的不同子数组分配内存时,可能导致内存分配不连续的问题。使用块语句一次性初始化不会有这个问题。使用malloc来为二维数组分配连续的内存有两种策略。假设二维数组有三行四列。第一种一次性分配所有内存3*4*sizeof(element),然后使用的时候通过前面提到的如何访问array[i][j]的方法手动计算要访问的内存地址。第二种方法分为两步。第一步先使用malloc分配一块内存用来存放指向二维数组每一行的指针的指针。第二步为array[0][0]的指针分配所有内存,然后计算array[1][0]和array[2][0]的位置,分别给这两个关键指针赋值,这样就可以根据每一行的指针来使用下标访问了。

int rows = 3;
int columns = 5;
//第一种方法
int* matrixx = (int*) malloc (rows * columns * sizeof(int));
//第二种方法
int **matrixy = (int**) malloc (rows * sizeof(int*));
matrixy[0] = (int*) malloc (rows * columns * sizeof(int));
int i = 1;
while(i<rows)
{
i++;
matrixy[i] = matrix[0] + i * columns;
}

   不规则数组是每一行的列数不一样的二维数组。可以使用复合字面量来初始化不规则数组。

(const int) {100}
(int [3]) {10, 20, 30}

    不规则数组的访问和维护都比较麻烦,使用前应慎重考虑。

时间: 2024-10-02 04:18:47

深入理解C指针之四:指针和数组的相关文章

深入理解数组指针与指针数组的区别_C 语言

数组指针与指针数组的区别在于:数组指针p是一个指针,而指针数组p是一个存放N个指针变量的数组. 一.数组指针int (*p)[n]重点:()优先级高([].()的优先级是一样的,但它们的方向是从左至右的,所以先运行括号里的*p),首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长.也就是说执行p+1时,p要跨过n个整型数据的长度(n*sizeof(int)).如要将二维数组赋给一指针,应这样赋值:       int a[3][4];       int (

C/C++中字符指针数组及指向指针的指针的含义

就指向指针的指针,很早以前在说指针的时候说过,但后来发现很多人还是比较难以理解,这一次我们再次仔细说一说指向指针的指针. 先看下面的代码,注意看代码中的注解: #include <iostream>#include <string>using namespace std; void print_char(char* array[],int len);//函数原形声明void main(void){//-----------------------------段1----------

C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com

原文:C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表 | IT宅.com C语言语法笔记 – 高级用法 指针数组 指针的指针 二维数组指针 结构体指针 链表本文由 arthinking 发表于315 天前 ⁄ itzhai.com原创文章 ⁄ C语言 ⁄ 评论数 3 ⁄ 被围观 1,775 views+   指针数组: 在一个数组中,如果它的元素全部都是指针

c++的问题-指针和二维数组的用法

问题描述 指针和二维数组的用法 int *s for(i=0;i<5;i++) for(j=0;j<5;j++) cout<<s[i][j];//error想请教各位程序员哥哥这个要怎么改啊? 解决方案 楼上的就行了,int (*s)[5][5] 也可以吧. 解决方案二: 虽然是二维的数组,其实指针的指的是第一行第一列,所以*s[i]就成了一个一维数组的指针,在c++里面你还可以试试引用,同时理解指针. 解决方案三: 首先,s在此处定义为一维,二维引用会导致错误.其次,对于s,没有

深入理解c++指针的指针和指针的引用_C 语言

展示一下使用指针的指针和指针的引用修改传递给方法的指针,以便更好的使用它.(这里说的指针的指针不是一个二维数组) 为什么需要使用它们 当我们把一个指针做为参数传一个方法时,其实是把指针的复本传递给了方法,也可以说传递指针是指针的值传递. 如果我们在方法内部修改指针会出现问题,在方法里做修改只是修改的指针的copy而不是指针本身,原来的指针还保留着原来 的值.我们用下边的代码说明一下问题: int m_value = 1; void func(int *p) { p = &m_value; } i

《指针的编程艺术(第二版)》一3.2 指针与二维数组

3.2 指针与二维数组 指针的编程艺术(第二版) 一维数组与指针的关系前面已经介绍过.一维数组的元素值,可使用[]或一个*得到,假设有一个数组如下: int i[7] = {0, 1, 2, 3, 4, 5, 6}; int *ptr=i; 则(i+2) 或i[2] 或(ptr+2) 或ptr[2],都表示数组某一个元素的值.而二维数组(two dimension array){XE "二維陣列(two dimension)"},则需要两个,或一个与一个[ ],或两个[ ],才能得到数

求大神讲解一下指针指向二维数组时该怎么用?

问题描述 求大神讲解一下指针指向二维数组时该怎么用? 指针指向一维数组时只用*(p+n)即可,但是二维数组时候改怎么用呢? 解决方案 个人认为其实二维数组还是一维数组,没有什么区别,例如int a[3][4],就相当于int a[12].要想访问a[2][3],就用*(a+(4*2+3)*(sizeof(int))),应该是这样,不过我一般用a[4*2+3] 来访问对应的元素 本人也是菜鸟,如果有错还请大神们指正~ 解决方案二: 由于指针与数组之间可以若转化,所以在一定程度上可以理解为数组就是指

数组指针、指针数组以及二位数组的深入解析_C 语言

int *p[3]与int (*p)[3]的区别*p[3]这个是一个指针数组,它所代表的意思是数组中的每一个元素都是一个指针变量,而(*p)[3],p是一个指针变量,表示指向一个含有3个整型元素的一维数组. 复制代码 代码如下: int i,j;    int a[2][3]={3,4,5,6,7,8}; //    int *p[3] ;  //表示一个数组,数组中的元素是指针类型,一共有三个元素    int (*q)[3]; //是一个指针,指向一个含有三个int型的数组(q+1)会跳三个

图文详解c/c++中的多级指针与多维数组_C 语言

前言 首先先声明一些常识,如果你对这些常识还不理解,请先去弥补一下基础知识:      1.实际上并不存在多维数组,所谓的多维数组本质上是用一维数组模拟的.      2.数组名是一个常量(意味着不允许对其进行赋值操作),其代表数组首元素的首地址.      3.数组与指针的关系是因为数组下标操作符[],比如,int a[3][2]相当于*(*(a+3)+2) .      4.指针是一种变量,也具有类型,其占用内存空间大小和系统有关,一般32位系统下,sizeof(指针变量)=4.