指针的奇技淫巧四_指针和数组

概述

数组是能用索引访问的同质元素的联系集合,这里的连续是指数组元素在内存中是相邻的,中间不存在空隙,而同质是指的相同的数据类型,数组声明用的[],并且可以是多维数组。

#一维数组
一维数组为线性结构,用一个索引访问成员,

1
2
//声明一个5个元素的数组,
int test[5];

注意:

  • 数组索引0开始 n-1结束
  • c语言没有强制规定边界,超出声明范围内的使用会有不可预期的行为
  • 提前声明数组类型是为低层索引偏移做准备
  • 数组size除去单个元素size可以得到元素的个数

#二维数组

二维数组在内存中是以列顺序实现的,先放入第一行接着第二行。。。。直到最后,二维数值也可以理解成数组的数组

1
2
//声明一个二维数值
int test[2][3] {{1,2,3},{4,5,6}};

#多维数组

多维数组具有两个及以上的维度。下面我们定义一个3行2列4阶的三维数组,阶通常用来标识第三维元素

1
2
3
4
5
int arr3d[3][2][4] = {
{{1,2,3,4},{5,6,7,8}},
{{9,10,11,12},{13,14,15,16}},
{{17,18,19,20},{21,22,23,24}}
}

#指针表示和数组

c语言里面指针处理数组是一个高效且节约开销的方法,我们可以用指针指向已有数组,也可以从堆上分配内存然后把这块内存当做一个数组使用。数组表示的方法和指针表示的方法并不完全相同,但是大多数情况下可以互换。

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

我们可以只用数组名称,也可以对数组的第一个元素用取地址符操作一下代码等价

1
2
printf("%p\n",vector);
printf("%p\n",&vector[0]);
1
2
p[i];
*(p+i);

要点:

  • 单独使用数组名返回数组起始内存地址
  • p是指向数组第一个元素而不是指向数组本身的指针
  • 给p赋值是把数组的第一个元素给p,后续通过偏移修改其他元素
  • p[i]表示法其实就是偏移几位然后做解引操作
  • &vector获取数组地址

数组和指针的细微差别

命令 区别
vector[i] vector开始,偏移i个位置取出值
*(vector + i) vector开始,在地址上增加取出值
命令 区别
sizeof(vector) 返回20,数组分配的字节数
sizeof(p) 返回4,指针长度
命令 区别
p 左值可修改
vector 非左值不能修改

要点:

  • *(vector + i) 解引运算符比加优先级更高,所以需要强制加再解引

#用malloc创建一维数组

要点就是我们自己从堆上分配内存,把地址给一个指针然后对它进行地址偏移实现一个数组功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>

int main()
{
int *array = (int*) malloc(sizeof(int)*5);//声明一个5列的数组指针

for (int i = 0; i < 5; ++i) {//给数组赋值
array[i] = i;
//*(array+i) = i; 这2种表示方法最后的结果相同
}

for (int j = 0; j < 5; ++j) {
printf("array[%d]:%d\n",j,array[j]);
}

return 0;
}

用malloc创建一维数组

要点:

  • 用完记住free掉

#用realloc调整数组长度

下面这个代码片段演示了一个动态数组分配的过程,书上所演示的实例。但是有些地方个人感觉有点问题。等有时间我会写一段自己能理解的内存动态分配的代码放这

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
char * getLine(void){
const size_t sizeIncrement = 10; //size_t表示无符号int unsigned int
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) {
char *newBuffer = realloc(buffer,maximumLength+=sizeIncrement);//过载动态添加内存

if(newBuffer == NULL) {//检查指针分配情况
free(buffer);
return NULL;
}

currentPosition = newBuffer + (currentPosition - buffer);//?
buffer = newBuffer;//?
}
*currentPosition = character;//?
}

*currentPosition = '\0';

return buffer;

}

下面这个例子是用c语言实现一个trim函数,动态减少堆分配空间。这个例子就比较理性而且可以正常跑通。trim函数中声明了2个while循环,第一个while是偏移指针找到不是空格的起始位置,第二个while是从空格后的起始位置做偏移,赋值给str的起始位置。相当于整个字符串往前挪动。最后动态的realloc掉不需要的内存空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/**
* 去除开头的空格
* @param str
* @return
*/
char *trim(char* str){

char* oldStr = str;
char* newStr = str;

while (*oldStr == ' '){//不等于空隔开始作为起始位置
oldStr++;
}
while (*oldStr){//偏移一个字符串
*(newStr++) = *(oldStr++);
}
*newStr = 0;//结尾符

return (char*)realloc(str,strlen(newStr)+1);
}

int main(){
char* str = (char*)malloc(strlen(" abc")+1);
strcpy(str," abc");
printf("%s\n",trim(str));
return 0;
}

realloc运行结果
#传递一维数组
待补全
#用数组表示方法
待补全
#用指针表示法
待补全
#使用指针的一维数组
待补全
#指针和多维数组
待补全
#传递多维数组
待补全
#动态分配二维数组
待补全
#分配连续内存
待补全
#不规则数组和指针
待补全

#注意

  • 数组和指针是完全可以互换是错误观点
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

请我喝杯咖啡吧~