设为首页 加入收藏

TOP

C语言进阶指南(2)丨数组和指针、打桩(一)
2019-04-06 22:08:33 】 浏览:286
Tags:语言 进阶 指南 指针 打桩

三、指针和数组

尽管在某些上下文中数组和指针可相互替换,但在编译器看来二者完全不同,并且在运行时所表达的含义也不同。

当我们说对象或表达式有类型的时候,我们通常想的是定位器值的类型,也叫做左值。当左值有完全non-const类型时,此类型不是数组类型(因为数组本质是内存的一部分,是个只读常量,译者注),我们称此左值为可修改左值,并且此变量是个值,当表达式放到赋值运算符左边的时候,它被赋值。若表达式在赋值运算符的右边,此变量不必被修改,变量成为了修改左值的的内容。若表达式有数组类型,则此表达式的值是个指向数组第一个元素的指针。

上文描述了大多数场景下数组如何转为指针。在两种情形下,数组的值类型不被转换:当用在一元运算符&(取地址)或sizeof 时。参见C99/C11标准 6.3.2.1小节:

(Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type “array of type” is converted to an expression with type “pointer to type” that points to the initial element of the array object and is not an lvalue.)

除非它是sizeof或一元运算符&的操作数,再或者它是用于初始化数组的字符文本,否则有着“类型数组”类型的表达式被转换为“指向类型”类型的指针,此指针指向数组对象的首个元素且指针不是左值。

由于数组没有可修改的左值,并且在绝大多数情况下,数组类型的表达式的值被转为指针,因此不可能用赋值运算符给数组变量赋值(即int a[10]; a = 1;是错的,译者注)。下面是一个小示例:

short a[] = {1,2,3};

short *pa;

short (*px)[];

void init(){

    pa = a;

    px = &a;

    printf("a:%p; pa:%p; px:%p\n", a, pa, px);

    printf("a[1]:%i; pa[1]:%i (*px)[1]:%i\n", a[1], pa[1], (*px)[1]);

}

(译者注:%i能识别输入的八进制和十六进制)

a是 int 型数组,pa 是指向 int 的指针,px 是个未完成的、指向数组的指针。a 赋值给 pa前,它的值被转为一个指向数组开头的指针。右值表达式 &a并非意味着指向 int,而是一个指针,指向 int 型数组因为当使用一元符号&时右值不被转换为指针。

表达式 a[1] 中下标的使用等价于 *(a+1),且服从如同 pa[1] 的指针算术规则。但二者有一个重要区别。对于 a 是数组的情况,a 变量的实际内存地址用于获取指向第一个元素的指针。当对于 pa 是指针的情况,pa 的实际值并不用于定位。编译器必须注意到 a 和 pa见的类型区别,因此声明外部变量时,指明正确的类型很重要。

int a[];

int *pa;

但在另外的编译单元使用下述声明是不正确的,将毁坏代码:

extern int *a;

extern int pa[];

3.1 数组作为函数形数

某些类型数组变为指针的另一个场合在函数声明中。下述三个函数声明是等价的:

void sum(int data[10]) {}

void sum(int data[]) {}

void sum(int *data) {}

编译器应报告函数 sum 重定义相关错误,因为在编译器看来上述三个例子中的参数都是 int型的。.

多维数组是有点棘手的话题。首先,虽然用了“多维”这个词,C并不完全支持多维数组。数组的数组可能是更准确的描述。

typedef int[4] vector;

vector m[2] = {{1,2,3,4}, {4,5,6,7}};

int n[2][4] = {{1,2,3,4}, {4,5,6,7}};

变量 m 是长度为2的 vector 类型,vector 是长为4的 int 型数组。除了存储的内存位置不同外,数组 n 与 m 是相同的。从内存的角度讲,两个数组都如同括号内展示的内容那样,排布在连续的内存区域。访问到的和声明的完全一致。

int *p = n[1];

int y = p[2];

通过使用下标符号 n[1],我们获取到了每个元素大小为4字节的整型数组。因为我们要定位数组的第二个元素, 其位置在多维数组中是数组开始偏移四倍的整型大小。我们知道,在这个表达式中整型数组被转为指向 int 的指针,然后存为 p。然后 p[2] 将访问之前表达式产生的数组中的第三个元素。上面代码中的 y 等价于下面代码中的 z:

int z = *(*(n+1)+2);

也等价于我们初学C时写的表达式:

int x = n[1][2];

当把上文中的二维数组作为参数传输时,第一“维”数组会转为指针,指向再次阵列的数组的第一个元素。因此不需要指明第一维。剩余的维度需要明确指出其长度。否则下标将不能正确工作。当我们能够随心所欲地使用下述表格中的任一形式来定义函数接受数组时,我们总是被强制显式地定义最里面的(即维度最低的)数组的维度。

void sum(int data[2][4]) {}

void sum(int data[][4]) {}

void sum(int (*data)[4]) {}

为绕过这一限制,可以转换数组为指针,然后计算所需元素的偏移。

void list(int *arr, int max_i, int max_j){

    int i,j;

    for(i=0; i<max_i; i++){

        for(j=0; j<max_j; j++){

            int x = arr[max_i*i+j];

            printf("%i, ", x);

        }

        printf("\n");

    }

}

另一种方法是main函数用以传输参数列表的方式。main函数接收二级指针而非二维数组。这种方法的缺陷是,必须建立不同的数据,或者转换为二级指针的形式。不过,好在它运行我们像以前一样使用下标符号,因为我们现在有了每个子数组的首地址。

int main(int argc, char **argv){

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

    int arr2[4] = {5,6,7,8};

    int *arr[] = {arr1, ar
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C语言进阶指南(3)丨显式内联、.. 下一篇数据结构——线性表的顺序存储结构

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目