0. 为什么是指针和数组?

在C语言中,指针和数组有着非常密切的关系。应该将它们放在一起讨论的原因是,使用数组表示法 (arrayName[index]) 可以实现的功能也可以使用指针实现,通常速度更快。

1. 一维数组

让我们看看当我们写的时候会发生什么int myArray[5];

从到 开始创建五个连续的内存块,其中包含垃圾值。每个块的大小为 4 字节。myArray[0]myArray[4]

因此,如果 myArray[0] 的地址是100(例如),则其余块的地址将为104108112116

现在,看看下面的代码 –

int prime[5] = {2,3,5,7,11};printf("Result using &prime = %d\n",&prime);printf("Result using prime = %d\n",prime);printf("Result using &prime[0] = %d\n",&prime[0]);/* Output */Result using &prime = 6422016Result using prime = 6422016Result using &prime[0] = 6422016

那么 、&primeprime&prime[0]都给出相同的地址,对吗?好吧,等待并阅读,因为你会感到惊讶(和困惑)。让我们尝试将&primeprime&prime[0]分别递增 1。

printf("Result using &prime = %d\n",&prime + 1);printf("Result using prime = %d\n",prime + 1);printf("Result using &prime[0] = %d\n",&prime[0] + 1);/* Output */Result using &prime = 6422036Result using prime = 6422020Result using &prime[0] = 6422020

等等!为什么&prime + 1结果与其他两个不同?为什么prime + 1&prime[0] + 1仍然相等?让我们来回答这些问题。

1.prime&prime[0],都指向数组的第0个元素prime。因此,数组的名称本身就是指向数组第 0 个元素的指针

这里,两者都指向第一个大小为 4 字节的元素。当您向它们添加 1 时,它们现在指向数组中的第一个元素。因此地址增加了 4。

2.&prime另一方面是指向大小为 5 的数组的指针int。它存储数组的基地址prime[5],等于第一个元素的地址。然而,对其加 1 会导致地址增加 5 x 4 = 20 字节。

简而言之,arrayNameand&arrayName[0]指向第 0 个元素,而&arrayName指向整个数组。

我们可以使用下标变量访问数组元素,如下所示 –

int prime[5] = {2,3,5,7,11};for( int i = 0; i < 5; i++){printf("index = %d, address = %d, value = %d\n", i, &prime[i], prime[i]);}

我们可以使用指针做同样的事情,它总是比使用下标更快。

int prime[5] = {2,3,5,7,11};for( int i = 0; i < 5; i++){printf("index = %d, address = %d, value = %d\n", i, prime + i, *(prime + i));}

两种方法都会给出输出 –

index = 0, address = 6422016, value = 2index = 1, address = 6422020, value = 3index = 2, address = 6422024, value = 5index = 3, address = 6422028, value = 7index = 4, address = 6422032, value = 11

因此,&arrayName[i]arrayName[i]分别与arrayName + i*(arrayName + i)相同。

2. 二维数组

二维数组是数组的数组。

int marks[5][3] = { { 98, 76, 89},{ 81, 96, 79},{ 88, 86, 89},{ 97, 94, 99},{ 92, 81, 59}};

这里,marks可以认为是一个包含 5 个元素的数组,每个元素都是包含 3 个整数的一维数组。让我们通过一系列程序来理解不同的下标表达式。

printf("Address of whole 2-D array = %d\n", &marks);printf("Addition of 1 results in %d\n", &marks +1);/* Output */Address of whole 2-D array = 6421984Addition of 1 results in 6422044

与一维数组一样,&marks指向整个二维数组,marks[5][3]。因此,增加 1(= 5 个数组 X 3 个整数,每个数组 X 4 个字节 = 60)会导致增加 60 个字节。

printf("Address of 0th array = %d\n", marks);printf("Addition of 1 results in %d\n", marks +1);printf("Address of 0th array =%d\n", &marks[0]);printf("Addition of 1 results in %d\n", &marks[0] + 1);/* Output */Address of 0th array = 6421984Addition of 1 results in 6421996Address of 0th array = 6421984Addition of 1 results in 6421996

如果marks是一维数组,marks&marks[0]指向第 0 个元素。对于二维数组,元素现在是一维数组。因此,marks&marks[0]指向第 0 个数组(元素),加 1 指向第 1 个数组。

printf("Address of 0th element of 0th array = %d\n", marks[0]);printf("Addition of 1 results in %d\n", marks[0] + 1);printf("Address of 0th element of 1st array = %d\n", marks[1]);printf("Addition of 1 results in %d\n", marks[1] + 1); /* Output */Address of 0th element of 0th array = 6421984Addition of 1 results in 6421988Address of 0th element of 1st array = 6421996Addition of 1 results in 6422000

现在,差异来了。对于一维数组,marks[0]将给出第 0 个元素的值。增量 1 将使值增加 1。

但是,在二维数组中,marks[0]指向第 0 个数组的第 0 个元素。同样,marks[1]指向第一个数组的第 0 个元素。增量 1 将指向第一个数组中的第一个元素。

printf("Value of 0th element of 0th array = %d\n", marks[0][0]);printf("Addition of 1 results in %d", marks[0][0] + 1);/* Output */Value of 0th element of 0th array = 98Addition of 1 results in 99

这是新的部分。marks[i][j]给出第 i 个数组的第 j 个元素的值。对其进行增量会更改存储在 处的值marks[i][j]。现在,让我们尝试marks[i][j]用指针来写。

从我们之前的讨论中我们知道marks[i] + j将指向第 j 个数组的第 i 个元素。取消引用它意味着该地址处的值。因此,marks[i][j]等于*(marks[i] + j)

从我们对一维数组的讨论来看,marks[i]与 相同*(marks + i)。因此,marks[i][j]可以写成*(*(marks + i) + j)指针的形式。

以下是比较一维和二维数组的符号摘要。

表达一维数组二维阵列
&ArrayName指向整个数组的地址,加1使地址增加1 x sizeof(ArrayName)指向整个数组的地址,加1使地址增加1 x sizeof(ArrayName)
ArrayName指向 元素,加1增加元素0th的地址1st指向元素(数组),加1增加元素(数组)0th的地址1st
&ArrayName[i]指向元素,加1增加元素ith的地址(i+1)th指向元素(数组),加1增加元素(数组)ith的地址(i+1)th
ArrayName[i]给出元素的值,加 1 增加元素ith的值ith指向数组0th的元素,加1增加数组元素的ith地址1stith
ArrayName[i][j]没有什么jth给出数组元素的值,加 1 会增加数组元素ith的值jthith
访问元素的指针表达式*(ArrayName + i)*(*(ArrayName + i) + j)

3. Strings

字符串是一个以 结尾的一维字符数组null(\0)。当我们写入时char name[] = "Srijan";,每个字符占用一个字节的内存,最后一个字符始终占据内存\0

与我们见过的数组类似,nameand&name[0]指向0th字符串中的字符,而 while 则&name指向整个字符串。又可name[i]写为*(name + i).

/* String */char champions[] = "Liverpool";printf("Pointer to whole string = %d\n", &champions);printf("Addition of 1 results in %d\n", &champions + 1);/* Output */Address of whole string = 6421974Addition of 1 results in 6421984printf("Pointer to 0th character = %d\n", &champions[0]);printf("Addition of 1 results in %d\n", &champions[0] + 1);/* Output */Address of 0th character = 6421974Addition of 1 results in a pointer to 1st character 6421975printf("Pointer to 0th character = %d\n", champions);printf("Addition of 1 results in a pointer to 1st character %d\n", champions + 1);/* Output */Address of 0th character = 6421974Addition of 1 results in 6421975printf("Value of 4th character = %c\n", champions[4]);printf("Value of 4th character using pointers = %c\n", *(champions + 4));/* Output */Value of 4th character = rValue of 4th character using pointers = r

如前所述,还可以访问和操作二维字符数组或字符串数​​组。

/* Array of Strings */char top[6][15] = {"Liverpool","Man City","Man United","Chelsea","Leicester","Tottenham"};printf("Pointer to 2-D array = %d\n", &top);printf("Addition of 1 results in %d\n", &top + 1); /* Output */Pointer to 2-D array = 6421952Addition of 1 results in 6422042printf("Pointer to 0th string = %d\n", &top[0]);printf("Addition of 1 results in %d\n", &top[0] + 1); /* Output */Pointer to 0th string = 6421952Addition of 1 results in 6421967printf("Pointer to 0th string = %d\n", top);printf("Addition of 1 results in %d\n", top + 1); /* Output */Pointer to 0th string = 6421952Addition of 1 results in 6421967printf("Pointer to 0th element of 4th string = %d\n", top[4]);printf("Pointer to 1st element of 4th string = %c\n", top[4] + 1); /* Output */Pointer to 0th element of 4th string = 6422012Pointer to 1st element of 4th string = 6422013printf("Value of 1st character in 3rd string = %c\n", top[3][1]);printf("Same using pointers = %c\n", *(*(top + 3) + 1)); /* Output */Value of 1st character in 3rd string = hSame using pointers = h

4. 指针数组

与 s 数组int和 s 数组一样char,也存在指针数组。这样的数组只是地址的集合。这些地址也可以指向单个变量或另一个数组。

声明指针数组的语法是 –

dataType *variableName[size];/* Examples */int *example1[5];char *example2[8];

按照运算符优先级,第一个示例可以理解为 –example1是一个包含 5 个指向 的指针的 array([])int。同样,example2是一个由 8 个指针组成的数组char

我们可以使用指针数组将二维数组存储为字符串top,这样也可以节省内存。

char *top[] = {"Liverpool","Man City","Man United","Chelsea","Leicester","Tottenham"};

top将包含所有相应名称的基地址。的基地址"Liverpool"将存储在top[0]"Man City"intop[1]等中。

在之前的声明中,我们需要 90 个字节来存储名称。在这里,我们只需要 ( 58 (名称字节总和) + 12 (在数组中存储地址所需的字节) ) 70 个字节。

使用指针数组时,字符串或整数的操作变得更加容易。

如果我们尝试放在"Leicester"之前,我们只需要切换和"Chelsea"的值,如下所示 –top[3]top[4]

char *temporary;temporary = top[3];top[3] = top[4];top[4] = temporary;

如果没有指针,我们将需要交换字符串的每个字符,这将花费更多时间。这就是为什么字符串通常使用指针声明。

5. 指向数组的指针

就像“指向int”或“指向char”的指针一样,我们也有指向数组的指针。该指针指向整个数组而不是其元素。

还记得我们讨论过如何&arrayName指向整个数组吗?嗯,它是一个指向数组的指针。

指向数组的指针可以这样声明 –

dataType (*variableName)[size];/* Examples */int (*ptr1)[5];char (*ptr2)[15];

注意括号。如果没有它们,这些将是一个指针数组。第一个示例可以理解为 -是一个指向 5(整数)ptr1数组的指针int

int goals[] = { 85,102,66,69,67};int (*pointerToGoals)[5] = &goals;printf("Address stored in pointerToGoals %d\n", pointerToGoals);printf("Dereferncing it, we get %d\n",*pointerToGoals);/* Output */Address stored in pointerToGoals 6422016Dereferencing it, we get 6422016

当我们取消引用指针时,它会给出该地址处的值。类似地,通过取消引用指向数组的指针,我们得到该数组,并且该数组的名称指向基地址。如果我们找到数组的大小,我们就可以确认*pointerToGoals给出了数组。goals

printf("Size of goals[5] = %d, *pointerToGoals);/* Output */Size of goals[5] = 20

如果我们再次取消引用它,我们将获得存储在该地址中的值。我们可以使用 打印所有元素pointerToGoals

for(int i = 0; i < 5; i++)printf("%d ", *(*pointerToGoals + i));/* Output */85 102 66 69 67

指针和数组指针与函数配合使用时非常有用。我们将在下一篇中讨论它们。