【C语言】数组和指针一样吗?

目录

为什么可以像指针一样使用数组?

为什么可以像数组一样使用指针?

数组和指针的区别总结


对于一些初学的小伙伴来说,最大的误区就是认为指针和数组是一样的。这是很严重的一个问题,数组本身是来顺序存储一系列同类型数据的,而指针是指向内存地址的,虽然两者在某些地方可以相互替代使用,但这并不代表两者相同。

为什么可以像指针一样使用数组?

举一个非常常见的例子,大家可能在定义函数的时候会写到如下:

cpp 复制代码
void func(int arr[]); 

这里写入的参数是一个 int 类型的数据,但是我们在实际使用的时候可能会既可以传入一个数组,又可以传入一个指针,如下:

cpp 复制代码
int main() {
    // 情况1:传递数组
    int my_array[3] = {1, 2, 3};
    func(my_array); 
    
    // 情况2:直接传递指针
    int *ptr = my_array;
    func(ptr); 
    
    return 0;
}

为什么会这样呢?难道数组和指针的类型相同,是同一个东西吗?

并不是这样的, 这是因为数组在表达式中使用的时候,编译器会有一个隐式的类型转换 ,将他转换成一个指针常量 ,这个指针常量指向的就是数组首元素的地址。要注意,转换的是一个指针常量,我们是不能去修改指针的指针指向的(如a、b都是一个数组,是不可以让 a = b 的)。

但是又两种特殊情况,数组名是不能用指针常量来代替的。当对一个数组名进行 sizeof 或 & 操作的时候,并不会转换成一个指针常量。sizeof() 返回的是一个数组的长度,而不是数组的指针的长度;& 取数组名返回的是一个指向数组的指针,而不是一个指向指针常量的指针。

下面举一个例子:

cpp 复制代码
int a[10];
int b[10];
int *c;

c = &a[0];

表达式 &a[0] 是一个指向数组首元素的一个指针,但那正好是数组本身的值,所以使用 c = a 和上面那条语句执行的任务是完全一样的。

b = a 和 c = a 这两个操作都是非法的。都是去尝试修改指针常量的指向了。

为什么可以像数组一样使用指针?

下面举一个例子,定义一个指针,我们去用数组的方式对其进行访问。

cpp 复制代码
int main()
{
    char *p = "abcdefgh";
    char arr[] = "abcdefgh";
    char a = p[5];
    char b = arr[5];
    printf("a = %c,b = %c",a,b);
    return 0;
}
/* 打印:a = f,b = f */

在上述两种情况下,我们都可以通过下标5拿到 'f',但是两者的途径完全不一样。

使用指针 p[5] 去引用元素的时候,编译器实际会进行以下操作:

  1. 取得符号表中 p 的地址,提取存储于此处的指针。
  2. 把下标所表示的偏移量与指针的值相加,产生一个地址。
  3. 访问上面这个地址,取得字符。

也就是说 p[5] 本质上就是进行了 *(p+5) 这样一个操作,也就是向后便宜了5个 char 的地址,去拿到了 'f' 。

我们使用数组 arr[5] 去引用元素的时候,我们上面已经说过了,数组在表达式中会转化成一个指针常量,所以同样是进行了 *(arr + 5) 这样一个操作。

虽然二者最终结果是一样的,但是两者的最终获取偏移量的一个过程并不相同。

数组和指针的区别总结

上述只是帮助大家分清了为什么有时候数组和指针可以相互替换。下面总结一下本质上的区别:

特性 数组 指针
类型 连续内存块的别名 存储地址的变量
存储内容 直接存储数据元素 存储其他变量的地址
内存分配 编译期静态分配(栈/全局区) 运行时动态分配(可指向堆/栈/全局区)
大小(sizeof) 返回整个数组的字节大小(如char[9]→9) 返回指针本身的字节大小(4或8字节)
地址性质 常量 变量

指针和数组都可以在定义中用字符串常量进行初始化,虽然看着一样,但底层的逻辑并不相同。

cpp 复制代码
char *p = "abcdefgh";

定义指针的时候,编译器并不为指针所指向的对象分配空间,他只是分配指针本身的空间,除非我们在定义的时候就赋值给指针一个字符串常量进行初始化。初始化的时候,字符串常量会被设置为只读。如果通过指针修改这个字符串,就会造成未定义的行为。在有些编译器中,字符串常量存在于只允许读取的数据段中,防止被修改。

cpp 复制代码
char [] = "abcdefg";

但是使用字符串常量初始化的数组是可以通过下标去修改字符串的。

相关推荐
嵌入小生00731 分钟前
双向链表、双向循环链表之间的异同---嵌入式入门---Linux
linux·c语言·数据结构·链表·嵌入式·小白
BoJerry7771 小时前
数据结构——单链表(不带头)【C】
c语言·开发语言·数据结构
进击的小头1 小时前
设计模式组合应用:智能硬件控制系统
c语言·设计模式
EmbedLinX1 小时前
FreeRTOS 学习笔记
c语言·笔记·学习
学嵌入式的小杨同学2 小时前
【Linux 封神之路】文件操作 + 时间编程实战:从缓冲区到时间格式化全解析
linux·c语言·开发语言·前端·数据库·算法·ux
浪客灿心2 小时前
Linux的Ext系列文件系统
linux·运维·服务器·c语言
日拱一卒——功不唐捐3 小时前
交换排序:冒泡排序和快速排序(C语言)
c语言·数据结构·算法
敲皮裤的代码3 小时前
《C语言》操作符详解
c语言
小魏每天都学习12 小时前
【算法——c/c++]
c语言·c++·算法
智码未来学堂13 小时前
探秘 C 语言算法之枚举:解锁解题新思路
c语言·数据结构·算法