在C语言中,数组是指针还是用作指针?

2023-12-06

我的理解是,数组只是指向值序列的常量指针,当您在 C 中声明数组时,您就是在声明一个指针并为其指向的序列分配空间。

但这让我很困惑:以下代码:

char y[20];
char *z = y;

printf("y size is %lu\n", sizeof(y));
printf("y is %p\n", y);
printf("z size is %lu\n", sizeof(z));
printf("z is %p\n", z);

使用 Apple GCC 编译时会给出以下结果:

y size is 20
y is 0x7fff5fbff930
z size is 8
z is 0x7fff5fbff930

(我的机器是64位,指针是8字节长)。

如果“y”是常量指针,为什么它的大小为 20,就像它指向的值序列一样?在编译期间,变量名“y”是否在适当的时候被内存地址替换?那么,数组是 C 语言中的某种语法糖,在编译时只是被转换为指针吗?


这是 C 标准中的确切语言(n1256):

6.3.2.1 Lvalues, arrays, and function designators
...
3 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. If the array object has register storage class, the behavior is undefined.

这里要记住的重要一点是,object(在 C 术语中,意味着占用内存的东西)和表达用于指代该对象。

当您声明一个数组时,例如

int a[10];

the object由指定表达 a是一个数组(即,足够大以容纳 10 个连续的内存块int值),以及类型表达a 是“10 元素数组int", or int [10]。如果表达 a出现在除作为操作数之外的上下文中sizeof or &运算符,然后其类型隐式转换为int *,其值为第一个元素的地址。

在这种情况下sizeof运算符,如果操作数是类型的表达式T [N],那么结果是数组对象中的字节数,而不是指向该对象的指针中的字节数:N * sizeof T.

在这种情况下&运算符,其值为数组的地址,与数组第一个元素的地址相同,但是type表达式的不同:给定声明T a[N];, 表达式的类型&a is T (*)[N],或指向 T 的 N 元素数组的指针。value是相同的a or &a[0](数组的地址与数组中第一个元素的地址相同),但类型的差异很重要。例如,给定代码

int a[10];
int *p = a;
int (*ap)[10] = &a;

printf("p = %p, ap = %p\n", (void *) p, (void *) ap);
p++;
ap++;
printf("p = %p, ap = %p\n", (void *) p, (void *) ap);

你会看到输出的顺序

p = 0xbff11e58, ap = 0xbff11e58
p = 0xbff11e5c, ap = 0xbff11e80

IOW,前进p adds sizeof int(4) 恢复原值,同时前进ap adds 10 * sizeof int (40).

更标准的语言:

6.5.2.1 Array subscripting

Constraints

1 One of the expressions shall have type ‘‘pointer to object type’’, the other expression shall have integer type, and the result has type ‘‘type’’.

Semantics

2 A postfix expression followed by an expression in square brackets [] is a subscripted designation of an element of an array object. The definition of the subscript operator [] is that E1[E2] is identical to (*((E1)+(E2))). Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero).

因此,当您为数组表达式添加下标时,幕后发生的事情是计算距数组中第一个元素的地址的偏移量,并取消引用结果。表达方式

a[i] = 10;

相当于

*((a)+(i)) = 10;

这相当于

*((i)+(a)) = 10;

这相当于

 i[a] = 10;

是的,C 中的数组下标是可交换的;看在上帝的份上,永远不要在生产代码中这样做。

由于数组下标是根据指针操作定义的,因此您可以将下标运算符应用于指针类型和数组类型的表达式:

int *p = malloc(sizeof *p * 10);
int i;
for (i = 0; i < 10; i++)
  p[i] = some_initial_value(); 

这是一个方便记住其中一些概念的表格:



Declaration: T a[N];

Expression    Type    Converts to     Value
----------    ----    ------------    -----
         a    T [N]   T *             Address of the first element in a;
                                        identical to writing &a;[0]
        &a;    T (*)[N]                Address of the array; value is the same
                                        as above, but the type is different
  sizeof a    size_t                  Number of bytes contained in the array
                                        object (N * sizeof T)
        *a    T                       Value at a[0]
      a[i]    T                       Value at a[i]
     &a;[i]    T *                     Address of a[i] 

Declaration: T a[N][M];

Expression     Type        Converts to     Value
----------     ----        ------------    -----
          a    T [N][M]    T (*)[M]        Address of the first subarray (&a;[0])
         &a;    T (*)[N][M]                 Address of the array (same value as
                                             above, but different type)
   sizeof a    size_t                      Number of bytes contained in the
                                             array object (N * M * sizeof T)
         *a    T [M]      T *              Value of a[0], which is the address
                                             of the first element of the first subarray
                                             (same as &a;[0][0])
       a[i]    T [M]      T *              Value of a[i], which is the address
                                             of the first element of the i'th subarray
      &a;[i]    T (*)[M]                    Address of the i-th subarray; same value as
                                             above, but different type
sizeof a[i]    size_t                      Number of bytes contained in the i'th subarray
                                             object (M * sizeof T)
      *a[i]    T                           Value of the first element of the i'th 
                                             subarray (a[i][0])
    a[i][j]    T                           Value at a[i][j]
   &a;[i][j]    T *                         Address of a[i][j]

Declaration: T a[N][M][O];

Expression        Type             Converts to
----------        ----             -----------
         a        T [N][M][O]      T (*)[M][O]
        &a;        T (*)[N][M][O]
        *a        T [M][O]         T (*)[O]
      a[i]        T [M][O]         T (*)[O]
     &a;[i]        T (*)[M][O]
     *a[i]        T [O]            T *
   a[i][j]        T [O]            T *
  &a;[i][j]        T (*)[O]
  *a[i][j]        T 
a[i][j][k]        T
  

从这里开始,高维数组的模式应该很清楚了。

所以,总而言之:数组不是指针。在大多数情况下,数组表达式被转换为指针类型。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在C语言中,数组是指针还是用作指针? 的相关文章

  • for 和 while 循环中没有循环条件

    while cond fine for cond fine 但是当我删除条件部分时 while syntax compilation error for Infinite loop 这些循环内部是如何实现的 或者 编译器 解析器 如何知道中
  • Windows 控制台中的 C++ 按键输入

    我目前正在开发各种consoleWindows 中的游戏无法通过常规输入真正运行cin 我怎样才能 以简单的方式仅使用 MSVC 中提供的标准 Windows 库 让程序等待 特定 按键并返回按键 ID 它必须适用于包括箭头键在内的所有按键
  • 字符串中unicode字符的正则表达式

    我正在使用 C 进行一些 OCR 工作 并提取了我需要使用的文本 现在我需要使用正则表达式解析一行 string checkNum string routingNum string accountNum Regex regEx new Re
  • 无法运行bjam编译boost python教程

    我正在尝试跟随本教程 http www boost org doc libs 1 55 0 libs python doc tutorial doc html python hello html关于为 Windows 的 python 包装
  • 如何获得字符串的所有字谜

    我试图找到一个字符串的所有可能的字谜并仅使用递归将它们存储在数组中 我被困住了 这就是我所拥有的一切 int main const int MAX 10 string a ABCD string arr 10 permute arr a 0
  • 慢速 WPF 文本框

    我正在开发一个简单的串行数据查看器 它将用于观察传输到计算机串行端口之一的数据 我使用 C 和 WPF 编写了一个测试应用程序 它只是将最近读取的行放入文本块中 但是 它会跳过所有其他行 我的理论是 在 WPF 渲染窗口之前 新数据会被放入
  • 在大型数据绑定 ObservableCollection 中添加/删除许多项目,而无需冻结 GUI

    我和我的团队正在开发一个 WPF 应用程序 该应用程序显示多个并发 XamDataChart 控件 由 Infragistics 提供 每个图表都绑定到不同的 ObservableCollection 最多可包含 200 万个点 对于每个图
  • WinForms TreeView - 如何手动“突出显示”节点(就像被单击一样)

    我需要知道如何让以编程方式选择的节点以图形方式处于 选定 状态 就像用户单击它一样 SelectedNode 仅使这一节点在内部被选中 非常感谢 它没有显示为突出显示的原因是由于树视图没有焦点 这是我的测试表单上的按钮单击事件 TreeVi
  • 如何存储将被多个不同类访问的字符串常量? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 关于堆栈溢出有太多不同的答案 声明一个命名空间 并在 hpp 文件中将所有字符串标记为 extern const 并在 cpp 文件中放置它们的
  • STL(标准模板库)中使用的设计模式

    我正在学习STL和设计模式 我想知道是否有任何文档或链接可以解释如何在 STL 中实现设计模式 我做了谷歌但无法获得太多数据 我希望你的意思是 哪些设计模式可以在STL中识别 STL 堆栈是一个容器适配器 适配器是一种设计模式 迭代器也是一
  • 在 C 或 C++ 中使用逗号作为宏名称

    我想做这样的事情 define define MAX 10 000 000 undef 有什么技巧可以做到吗 编辑 我知道 C 14 中的数字分隔符 我正在寻找一种技巧来对不兼容的编译器执行相同的操作 EDIT2 请考虑Variadic M
  • C memcpy 二维数组

    我正在尝试使用将一个二维数组复制到另一个memcpy 我的代码 include
  • 将 KeyUp 作为参数传递 WPF 命令绑定文本框

    我有一个文本框 KeyUp 事件触发器连接到 WPF 中的命令 我需要将按下的实际键作为命令参数传递 该命令执行得很好 但处理它的代码需要知道按下的实际键 记住这可能是一个回车键或不仅仅是一个字母的任何键 所以我无法从 TextBox te
  • 为什么 `boost::any` 比 `void*` 更好?

    有什么先天优势boost any and boost any cast提供超过使用void and dynamic cast 优点是boost any比类型安全得多void E g int i 5 void p i static cast
  • 如何在 C# 中读取 Visio 文档内容

    我的DLL库代码如下 using System using IVisio Microsoft Office Interop Visio namespace Emix public class Visio protected String p
  • 如何将 MouseDown 事件放入样式中?

    这有效 XAML
  • 类型 '' 未映射

    我已经尝试修复这个错误有一段时间了 每当我的应用程序尝试创建数据上下文的实例时 我都会收到此错误 下面是代码 using System using System Collections Generic using System Linq u
  • 提高批量请求的野兽内存使用率

    我运行这个boost beast 客户端 异步 ssl http www boost org doc libs develop libs beast example http client async ssl http client asy
  • win32 内容已更改,但除非移动窗口,否则不会显示更新

    我的 win32 GUI 内容每秒都会更改 但除非手动移动窗口 否则不会显示更新 我尝试每秒弹出一个消息框来触发窗口刷新 它成功了 因此 这证明我的内容确实发生了变化 但窗口没有更新 我希望刷新窗口而不是每次都弹出消息框 有没有这样的窗口功
  • 为什么在嵌套类上调用方法时不调用父类的静态构造函数?

    给出以下代码 为什么在 Main 的第一行之后没有调用 Outer 的静态构造函数 namespace StaticTester class Program static void Main string args Outer Inner

随机推荐

  • 集群内无法访问 Kubernetes POD

    我尝试安装Kubernetes with kubeadm在 3 个虚拟机上Debian我的笔记本电脑上的操作系统 一个作为主节点 另外两个作为工作节点 我完全按照教程做的kubernetes io建议 我用命令初始化了集群kubeadm i
  • NSUserDefaults 无法在带有 Watch OS2 的 Xcode beta 上工作

    我刚刚安装了最新的 Xcode 测试版来尝试Swift 2以及对 Apple Watch 开发部分所做的改进 我实际上很难弄清楚为什么这个基本NSUserDefaults之间共享信息的方法iOS and 观看 OS2不工作 我跟着this
  • Python - 查找第二小的数字

    我在这个网站上找到了这段代码来查找第二大数字 def second largest numbers m1 m2 None None for x in numbers if x gt m1 m1 m2 x m1 elif x gt m2 m2
  • 在测试类中模拟私有静态最终变量

    我想测试的类中有一些私有静态最终字段 就像下面这样 public class ClassToTest private static final Myclass myclass MyClassFactory getMyClass type f
  • ffmpeg 在 iOS5.1 中不会 ./configure

    我试图在 iOS5 1 armv7 上构建 ffmpeg 当我尝试像这样运行 configure 时 configure disable doc disable ffmpeg disable ffplay disable ffserver
  • Pyodbc - “未找到数据源名称,并且未指定默认驱动程序”

    我很难得到pyodbc工作 我有unixodbc unixodbc dev odbc postgresql pyodbc安装在我的 Linux Mint 14 上的软件包 我对自己找到解决方案失去了希望 感谢任何帮助 详情请参阅下面 Run
  • 如何在还使用 keycloak 提供网页的客户端上使用计划任务?

    我在用着Spring Boot and Keycloak开发一个网络应用程序 然后我写了一个计划任务 我正在使用KeycloakRestTemplate向另一个应用程序询问一些数据 如下所示 Override Scheduled cron
  • 增加 Java.sql.date/时间的最佳方法是什么?

    我有一个问题 我有一个 MySQL 数据库将日期和时间存储在单独的列中 但是 在 Java 代码中 我需要将数据库中日期和时间的结果时间戳增加分钟 小时或天 然后更新数据库中的相应列 我目前在 Java 中使用 Java sql date
  • 将旧内容(.html/.php 等)重新路由到 Ruby on Rails

    我已切换到 Ruby on Rails 当前的问题是重新路由 旧内容如XXX dummy html or XXX dummy php in RoR 到底什么是最好的解决方案 孤立的内容 XXX onlyinstance html 具有如下内
  • Visual Studio 代码。为什么窗口控制按钮位于左侧菜单栏上方?

    我正在开发视觉工作室应用程序 突然 我的窗口控制按钮跳到主菜单导航栏的左侧 隐藏按钮 文件 和 编辑 有谁知道如何将 Windows 控制按钮重新放回导航栏的右侧 在 vscode v1 71 2 中window experimental
  • 在 Kivy 画布上显示 PIL 图像

    我找不到任何有关如何在 Kivy Canvas 上显示 PIL 图像的文档 Rectangle source image give TypeError Image object has no attribute getitem 由于其他图像
  • 在Windows应用程序中调用Web服务

    我有一个 Windows 应用程序 我要求用户通过网站中提供给用户的用户名和密码登录应用程序 为此 我首先检查用户是否连接到互联网 如果存在连接 则继续检查凭据是否有效 因此 我需要在Windows应用程序中调用Web服务 我该如何进行 欢
  • 使用 firebase.com 作为数据库的应用程序中的分页

    前端应用程序将使用 firebase com 作为数据库 应用程序应该像博客一样工作 管理员添加 删除帖子 有 主页 页面 每页显示多篇文章 带有 下一页 和 上一页 按钮 以及显示单篇文章的页面 我还需要深层链接 IE 当用户输入 URL
  • Backbone.Collection.Create 未在视图中触发“添加”

    希望这是一个简单的问题 我正在努力学习骨干 但我坚持做一件非常简单的事情 当我使用 create 方法更新集合时 视图上的渲染永远不会被调用 我认为这应该在不显式调用 render 的情况下发生 我没有加载任何动态的东西 在这个脚本触发之前
  • 在android中启动2个模拟器

    我想知道是否有任何简单的方法可以在 android 中启动 2 个模拟器 我已经查看了开发人员指南 但没有足够的信息来帮助我 请有人解释一下这是如何完成的 谢谢 据我所知 您不能两次启动同一个模拟器 但通过创建两个单独的模拟器 您可以同时启
  • 休息最佳实践:何时返回 404 not found

    如果我有以下休息电话 GET items id subitems 在这些情况下我们应该返回以下内容吗 If id 没有找到 我们应该返回吗404 Not Found If id 已找到但未找到子项 我们是否应该返回200 Ok和一个空数组
  • 更新约会时时区更改为 UTC

    我正在使用 EWS 1 2 发送预约 创建新约会时 时区在通知邮件上正确显示 但在更新同一约会时 时区会重置为 UTC 有人能帮我解决这个问题吗 以下是复制该问题的示例代码 ExchangeService service new Excha
  • 使用mysql、php和ajax(使用jquery)创建表

    对于我的新项目 我想要一种不需要在每个数据库请求上重新加载页面的现代方法 我希望脚本查询数据库并使用查询信息创建一个表 我尝试了在互联网上找到的不同脚本 下面这个最接近我的需求 索引 php
  • 重定向回来时 LightOpenID 被禁止

    我正在尝试使用 lightOpenID 它应该很简单 并且上传文件然后测试它是否有效 当我使用 example google php 时 我第一次点击登录按钮 它要求我登录 Google 并允许 记住我正在构建的网站 然后它重定向回 exa
  • 在C语言中,数组是指针还是用作指针?

    我的理解是 数组只是指向值序列的常量指针 当您在 C 中声明数组时 您就是在声明一个指针并为其指向的序列分配空间 但这让我很困惑 以下代码 char y 20 char z y printf y size is lu n sizeof y