C 中的指针 - 数组和字符串

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

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

1. 一维数组

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

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

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

现在,看看下面的代码 -

c 复制代码
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 = 6422016
Result using prime = 6422016
Result using &prime[0] = 6422016

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

c 复制代码
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 = 6422036
Result using prime = 6422020
Result 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指向整个数组。

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

c 复制代码
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]);
}

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

c 复制代码
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));
}

两种方法都会给出输出 -

c 复制代码
index = 0, address = 6422016, value = 2
index = 1, address = 6422020, value = 3
index = 2, address = 6422024, value = 5
index = 3, address = 6422028, value = 7
index = 4, address = 6422032, value = 11

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

2. 二维数组

二维数组是数组的数组。

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

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

c 复制代码
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 = 6421984
Addition of 1 results in 6422044

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

c 复制代码
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 = 6421984
Addition of 1 results in 6421996
Address of 0th array = 6421984
Addition of 1 results in 6421996

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

c 复制代码
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 = 6421984
Addition of 1 results in 6421988
Address of 0th element of 1st array = 6421996
Addition of 1 results in 6422000

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

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

c 复制代码
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 = 98
Addition 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地址1st``ith
ArrayName[i][j] 没有什么 jth给出数组元素的值,加 1 会增加数组元素ith的值jth``ith
访问元素的指针表达式 *(ArrayName + i) *(*(ArrayName + i) + j)

3. Strings

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

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

c 复制代码
/* 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 = 6421974
Addition of 1 results in 6421984

printf("Pointer to 0th character = %d\n", &champions[0]);
printf("Addition of 1 results in %d\n", &champions[0] + 1);

/* Output */
Address of 0th character = 6421974
Addition of 1 results in a pointer to 1st character 6421975

printf("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 = 6421974
Addition of 1 results in 6421975

printf("Value of 4th character = %c\n", champions[4]);
printf("Value of 4th character using pointers = %c\n", *(champions + 4));

/* Output */
Value of 4th character = r
Value of 4th character using pointers = r

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

c 复制代码
/* 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 = 6421952
Addition of 1 results in 6422042

printf("Pointer to 0th string = %d\n", &top[0]);
printf("Addition of 1 results in %d\n", &top[0] + 1);

 /* Output */
Pointer to 0th string = 6421952
Addition of 1 results in 6421967

printf("Pointer to 0th string = %d\n", top);
printf("Addition of 1 results in %d\n", top + 1);

 /* Output */
Pointer to 0th string = 6421952
Addition of 1 results in 6421967

printf("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 = 6422012
Pointer to 1st element of 4th string = 6422013

printf("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 = h
Same using pointers = h

4. 指针数组

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

声明指针数组的语法是 -

c 复制代码
dataType *variableName[size];

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

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

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

c 复制代码
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]

c 复制代码
char *temporary;
temporary = top[3];
top[3] = top[4];
top[4] = temporary;

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

5. 指向数组的指针

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

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

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

c 复制代码
dataType (*variableName)[size];

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

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

c 复制代码
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 6422016
Dereferencing it, we get 6422016

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

c 复制代码
printf("Size of goals[5] = %d, *pointerToGoals);

/* Output */
Size of goals[5] = 20

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

c 复制代码
for(int i = 0; i < 5; i++)
printf("%d ", *(*pointerToGoals + i));

/* Output */
85 102 66 69 67

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

相关推荐
一点媛艺41 分钟前
Kotlin函数由易到难
开发语言·python·kotlin
姑苏风1 小时前
《Kotlin实战》-附录
android·开发语言·kotlin
奋斗的小花生2 小时前
c++ 多态性
开发语言·c++
魔道不误砍柴功2 小时前
Java 中如何巧妙应用 Function 让方法复用性更强
java·开发语言·python
闲晨2 小时前
C++ 继承:代码传承的魔法棒,开启奇幻编程之旅
java·c语言·开发语言·c++·经验分享
老猿讲编程2 小时前
一个例子来说明Ada语言的实时性支持
开发语言·ada
Chrikk3 小时前
Go-性能调优实战案例
开发语言·后端·golang
幼儿园老大*3 小时前
Go的环境搭建以及GoLand安装教程
开发语言·经验分享·后端·golang·go
canyuemanyue3 小时前
go语言连续监控事件并回调处理
开发语言·后端·golang
杜杜的man3 小时前
【go从零单排】go语言中的指针
开发语言·后端·golang