C 中指针比较如何工作?可以比较不指向同一数组的指针吗?

2024-02-29

在 K&R(C 编程语言第二版)第 5 章中,我读到了以下内容:

首先,在某些情况下可以比较指针。 如果p and q指向同一个数组的成员,那么关系如下==, !=, <, >=等工作正常。

这似乎意味着只能比较指向同一数组的指针。

但是当我尝试这段代码时

    char t = 't';
    char *pt = &t;
    char x = 'x';
    char *px = &x;

    printf("%d\n", pt > px);

1被打印到屏幕上。

首先,我认为我会得到未定义或某种类型或错误,因为pt and px不指向同一个数组(至少在我看来)。

Also is pt > px因为两个指针都指向存储在栈上的变量,而栈是向下增长的,所以t大于x?这就是为什么pt > px是真的?

当引入 malloc 时,我会更加困惑。在第 8.7 章的 K&R 中,还写了以下内容:

然而,仍然有一个假设,即指向由sbrk可以进行有意义的比较。标准不保证这一点,该标准仅允许在数组内进行指针比较。因此这个版本的malloc仅在一般指针比较有意义的机器之间可移植。

将指向堆上分配的空间的指针与指向堆栈变量的指针进行比较没有任何问题。

例如,以下代码运行良好,使用1正在打印:

    char t = 't';
    char *pt = &t;
    char *px = malloc(10);
    strcpy(px, pt);
    printf("%d\n", pt > px);

根据我对编译器的实验,我认为任何指针都可以与任何其他指针进行比较,无论它们单独指向哪里。此外,我认为两个指针之间的指针算术很好,无论它们分别指向哪里,因为算术只是使用指针存储的内存地址。

尽管如此,我对在 K&R 上读到的内容感到困惑。

我问的原因是因为我的教授。实际上把它变成了一个考试题。他给出了以下代码:

struct A {
    char *p0;
    char *p1;
};

int main(int argc, char **argv) {
    char a = 0;
    char *b = "W";
    char c[] = [ 'L', 'O', 'L', 0 ];

   struct A p[3];
    p[0].p0 = &a;
    p[1].p0 = b;
    p[2].p0 = c;

   for(int i = 0; i < 3; i++) {
        p[i].p1 = malloc(10);
        strcpy(p[i].p1, p[i].p0);
    }
}

这些评估的目的是什么:

  1. p[0].p0 < p[0].p1
  2. p[1].p0 < p[1].p1
  3. p[2].p0 < p[2].p1

答案是0, 1, and 0.

(我的教授确实在考试中包含了免责声明,即问题适用于 Ubuntu Linux 16.04、64 位版本编程环境)

(编者注:如果允许更多标签,最后一部分将保证x86-64 /questions/tagged/x86-64, linux /questions/tagged/linux, 有可能assembly /questions/tagged/assembly。如果问题/类的重点是特定的低级操作系统实现细节,而不是可移植的 C。)


根据C11标准 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf, 关系运算符<, <=, >, and >=只能用于指向同一数组或结构对象的元素的指针。第 6.5.8p5 节对此进行了详细说明:

当两个指针进行比较时,结果取决于 所指向的对象在地址空间中的相对位置。 如果两个指向对象类型的指针都指向同一个对象,或者 两者都指向同一数组的最后一个元素 对象,它们比较相等。如果指向的对象是 同一聚合对象的成员、结构体指针 后来声明的成员比较大于指向的指针 结构中前面声明的成员,以及指向的指针 下标值较大的数组元素比较大于 指向同一数组中具有较低下标值的元素的指针。 指向同一联合对象的所有成员的指针进行比较 平等的。如果表达式 P 指向数组的一个元素 对象和表达式 Q 指向最后一个元素 同一个数组对象,指针表达式 Q+1 比较大于 P。 在所有其他情况下,行为是未定义的。

请注意,任何不满足此要求的比较都会调用未定义的行为 https://en.wikipedia.org/wiki/Undefined_behavior,这意味着(除其他外)您不能依赖结果是可重复的。

在您的特定情况下,对于两个局部变量的地址之间以及本地地址和动态地址之间的比较,该操作似乎“有效”,但是通过对代码进行看似不相关的更改,结果可能会发生变化甚至使用不同的优化设置编译相同的代码。具有未定义的行为,只是因为代码could崩溃或产生错误并不意味着它will.

例如,在 8086 实模式下运行的 x86 处理器具有分段内存模型,使用 16 位段和 16 位偏移量来构建 20 位地址。因此在这种情况下,地址不会精确地转换为整数。

相等运算符== and !=但没有这个限制。它们可以在任意两个指向兼容类型的指针或 NULL 指针之间使用。所以使用== or !=在你的两个例子中都会产生有效的 C 代码。

然而,即使有== and !=你可能会得到一些意想不到但仍然明确的结果。看不相关指针的相等比较可以计算为 true 吗? https://stackoverflow.com/q/45966762/1687119有关此的更多详细信息。

关于你的教授给出的考试问题,它做出了一些有缺陷的假设:

  • 存在平面内存模型,其中地址和整数值之间存在一一对应关系。
  • 转换后的指针值适合整数类型。
  • 该实现在执行比较时只是将指针视为整数,而不利用未定义行为所提供的自由。
  • 使用堆栈并且局部变量存储在那里。
  • 堆用于从中提取分配的内存。
  • 堆栈(以及局部变量)出现在比堆(以及分配的对象)更高的地址。
  • 该字符串常量出现在堆的较低地址处。

如果您要在不满足这些假设的体系结构和/或编译器上运行此代码,那么您可能会得到非常不同的结果。

此外,这两个示例在调用时也表现出未定义的行为strcpy,因为右操作数(在某些情况下)指向单个字符而不是空终止字符串,导致函数读取超出给定变量的边界。

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

C 中指针比较如何工作?可以比较不指向同一数组的指针吗? 的相关文章

  • 如何在 Caliburn.Micro 中使用 Conductor 的依赖注入

    我有时用Caliburn Micro http caliburnmicro com创建应用程序 使用最简单的 BootStrapper 我可以像这样使用 IoC 容器 SimpleContainer private SimpleContai
  • 为什么我应该使用内联代码? [复制]

    这个问题在这里已经有答案了 我是一名 C C 开发人员 这里有几个始终困扰我的问题 常规 代码和内联代码之间有很大区别吗 主要区别是什么 内联代码只是宏的一种 形式 吗 选择内联代码时必须进行什么样的权衡 Thanks 表现 正如之前的答案
  • 线程独占数据:如何存储和访问?

    NET 中是否有可能将对象实例绑定到线程的当前执行上下文 这样在代码的任何部分我都可以做类似的事情CurrentThread MyObjectData DoOperation 并确保我访问特定于线程的数据 谢谢 你可以看一下线程静态属性 h
  • 如何创建语法突出显示文本框[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 如何使用 C Net 创建语法突出显示文本框 Take 闪烁网 http scintillanet codeplex com 并采取其
  • 为什么使用数组索引循环数组比指针访问慢?

    我正在读Kochan的书 Programming in C 在第 14 页的 指针和数组 部分中 264 他说 一般来说 索引数组的过程比执行索引过程花费更多的时间 访问指针内容的过程 其实这也是主要原因之一 为什么使用指针来访问数组的元素
  • 提取单花括号内的值

    我想要一个收藏 value 一个字符串使用正则表达式 例如 lorem ipsum field1 lorem ipsum field2 lorem ipsum field1 lorem ipsum field2 field3 我会得到 fi
  • 为什么 fgets 接受 int 而不是 size_t?

    功能如strcpy malloc strlen 和其他各种接受他们的参数或返回值作为size t代替int or an unsigned int出于显而易见的原因 一些文件功能 例如fread and fwrite use size t以及
  • 析构函数与成员函数竞赛

    当我在析构函数内时 其他线程是否可能开始执行对象的成员函数 遇到这种情况该如何处理呢 C 没有内在的保护来防止在删除对象后使用它 忘记竞争条件 另一个线程可以在完全删除你的对象后使用你的对象 Either 确保只有一个位置 代码拥有该对象
  • 为什么 ObservableCollection 有两个集合构造函数?

    The 可观察集合 T https msdn microsoft com en us library ms668604类有两个构造函数 可以在其中传递项目集合 一个构造函数接受一个IEnumerable T 另一个List T 鉴于List
  • 微软怎么能说WinAPI中一个字的大小是16位呢?

    我刚刚开始学习WinAPI 在MSDN中 对WORD数据类型提供了以下解释 WORD16 位无符号整数 范围是十进制 0 到 65535 该类型在 WinDef h 中声明如下 typedef 无符号短 WORD 很简单 而且它与我一直在使
  • 有没有办法找到dll公开的所有函数

    我一直在寻找一种方法来获取映射到 dll 中函数名称的所有字符串 我的意思是您可以调用 GetProcAddress 的所有字符串 如果你对 dll 进行十六进制转储 符号 字符串 就在那里 但我认为必须有一个系统调用来获取这些名称 如果您
  • 在 Linq 查询中使用动态列名称

    foreach Dimension dimensions in Enum GetValues typeof Dimension var r new ReferenceTable dimensions referenceItems List
  • 使用信号和槽更新指针

    我对 Qt 很陌生 请帮我解决这个问题 我正在使用线程在后台执行密集操作 同时我想更新 UI 所以我使用 SIGNALS 和 SLOTS 为了更新 UI 我发出一个信号并更新 UI 让我们考虑下面的示例代码 struct sample QS
  • 解析连接字符串

    是否有标准库或代码片段可以使用这样的连接字符串获取值 string connstr DataServiceUrl http localhost foo RemoteServerConnection server http localhost
  • “DeploymentItem”属性是什么意思?

    假设我们有一个简短的程序 namespace ConsoleTryIt static class Program static void Main string args var sum Add 1 2 private static int
  • 如何将 Boost Spirit 自动规则与 AST 结合使用?

    编辑 当我想在另一个规则上使用它时 我扩展了 sehe 的示例以显示问题 http liveworkspace org code 22lxL7 http liveworkspace org code 22lxL7 17 我正在尝试提高 Bo
  • WCF 服务中的缓冲区大小

    我们有一个 WCF 服务 它执行某些存储过程并将结果返回给 silverlight 客户端 某些存储过程最多返回 80K 行 下面给出的是 web config 中服务的设置
  • C++ 模板参数数量错误(2,应该是 1)

    我使用 C 并行快速排序程序进行了测试 如下所示 首先使用列表作为容器 然后我转移到通用容器类型 但它报告了标题错误 可以帮忙解决这个问题吗 include
  • 如何在realm-dotnet中存储System.Collections.Generic.Dictionary

    我正在尝试将 Realm NET 集成到我的 uwp 项目中 我想知道是否有任何方法可以在 Realm dotnet 库中存储 System Collections Generic Dictionary 我试过这个 public class
  • NSubstitute - 测试特定的 linq 表达式

    我在当前正在开发的 MVC 3 应用程序中使用存储库模式 我的存储库界面如下所示 public interface IRepository

随机推荐

  • 线性冲突违反了可受理性,让我发疯

    当两个棋子tj和tk发生线性冲突时 如果tj和tk在同一行 则tj和tk的目标位置都在该行上 tj在tk的右侧 tj的目标位置在左侧tk 的目标位置 线性冲突迫使两个冲突图块相互包围 从而使它们的曼哈顿距离至少增加两步 因此 启发式函数将为
  • 创建一个进程并重定向其输入/输出并且不继承套接字句柄

    是否可以创建一个进程并重定向其输入和输出 但设置子进程不继承套接字句柄 也许以某种方式使用 CreateProcess 进行 pinvoke 如果我使用 UseShellExecute true 子级不会继承套接字 但我无法重定向输出 创建
  • 如何更改所有私有 JLabel 的字体大小

    我见过类似的问题 例如this https stackoverflow com questions 1966296 changing default jlabel font 但就我而言 我有 25 个以上的私有 JLabels 我在声明它时
  • 使用pandas写入和读取3D数据

    我有一个使用保存在文本文件中的 3D 数据的项目 我目前使用单个空格来分割第一个维度上的数据 使用一个换行符 n 来分割第二个维度 使用两个换行符 n n 来分割最后一个维度 并使用默认的读取和写入蟒蛇 这些数据的解释是使用字符串分割和列表
  • IndexedDB IDBKeyRange 复合/多重索引不起作用

    谁能解释为什么我的 IDBKeyRange 似乎只在第一列上进行过滤 我有一个索引定义如下 osDrugs createIndex combined name strength form packsize unique false 我的查询
  • eclipse 卡在正在运行的程序上

    这是我结束日食任务后的图片 我的Android程序没有错误 在出现这个问题之前一切都很好 当我在程序中添加一些代码时发生了这种情况 单击运行按钮后它卡住了 当我运行手机调试程序时也会发生这种情况 其他程序都运行良好 只有一个程序卡住了 我卡
  • Java 内置函数 Collections.Frequency(list, element) 的复杂度是多少?

    下面的代码是针对 String 的 ArrayList 的 我想知道这个问题的复杂度是多少Collections frequency 功能 List
  • MessageBox 中的粗体文本

    如何在显示的对话框中以粗体显示文本MessageBox Show 使用 C 有可能 消息框是一个常规窗口 可以像其他窗口一样被弄乱 然而 这样做的代码有点粗糙 将新类添加到您的项目并粘贴以下代码 using System using Sys
  • 如何在java中打开受密码保护的docx文件?

    我想使用 Apache POI 打开受密码保护的 docx 文件 谁能帮我完整的代码吗 我无法使用此代码得到解决方案 线程 main org apache poi poifs filesystem OfficeXmlFileExceptio
  • 深度克隆(复制)可变 Scala 对象的最简单方法是什么?

    深度克隆 复制 可变 Scala 对象的最简单方法是什么 由于您想要以最简单的方式深度复制 Scala 对象 而不是最快的方式 因此您始终可以序列化该对象 前提是它是可序列化的 然后将其反序列化回来 以下代码仅在编译时运行 而不是在 REP
  • 使用 OAuth 2.0 对用户进行身份验证时,重定向 URL 如何知道他们属于哪个用户?

    我是 Web 开发新手 并试图通过构建使用 Google API 的 Web 应用程序来入门 我正在阅读 Google 关于使用 OAuth 2 0 的文档 但重定向 URL 位让我有点困惑 根据例子here https developer
  • 如何防止 Kendo MultiSelect 在网格模板中编辑后丢失值?

    我有一个显示以逗号分隔的值列表的网格 并且它有一个在网格的模板编辑器中使用的数组 在服务器上 我将逗号分隔的列表转换为 Kendo 多选 AngularJS 指令的数组 我几乎已经完成了所有工作 在多选中显示 编辑和添加值 只是发生了一件奇
  • 如何在 dot net core 中获取另一个时区的本地时间

    我正在解决一个问题 我需要获取另一个时区的当前日期和时间 我不知道我的代码将在哪个时区运行 并且它需要在 Windows 和 Linux 机器上运行 我还没有找到任何方法可以做到这一点 有任何想法吗 P S 我特别需要找到瑞典的时间 包括代
  • 如何用CSS显示和隐藏div?

    在我的脚本中有三个 div 我想显示 divclass ab 当我将鼠标悬停在第一行并显示 div 时class abc 当鼠标悬停在第二行时 否则我想显示 divclass a 默认情况下 但它从不显示 divclass a abc ab
  • 如何检查是否已在 元素中选择文件?

    我有多个复选框和一个文件上传输入 如果选中一个或多个复选框并且输入值不为空 我想重新启用按钮 这是一个链接bootply http www bootply com 6YZf8l4QVa 这是我的html div class upload b
  • Django 将 URL 重定向到最新创建的博客文章

    我希望在 urls py 中进行重定向 以便当人们访问博客应用程序索引域时自动加载我的博客应用程序中的最新帖子条目 Blog Post 详细信息通过 blog views post detail request slug 方法提供 博客文章
  • 如何在 Angular 5 中获取上一页 URL

    我正在使用 Angular 5 应用程序 我需要知道如何获取最后一个 URL 将其作为后退按钮的链接 我找到了这个location back 但我需要最后一个 url 作为字符串 我怎样才能得到生成的字符串location back 非常感
  • RStudio 更改 pandoc .latex 模板

    我想使用不同的 Latex 模板来渲染 R markdown 版本 2 我发现了以下建议 a 模板位置 system file rmd latex default tex package rmarkdown b YAML Front Mat
  • Collectstatic 创建空文件

    我正在尝试将应用程序升级到 Django 1 11 但遇到了问题collectstatic 旧版本 django 1 8 17 django storages 1 5 1 新版本 django 1 11 12 django storages
  • C 中指针比较如何工作?可以比较不指向同一数组的指针吗?

    在 K R C 编程语言第二版 第 5 章中 我读到了以下内容 首先 在某些情况下可以比较指针 如果p and q指向同一个数组的成员 那么关系如下 lt gt 等工作正常 这似乎意味着只能比较指向同一数组的指针 但是当我尝试这段代码时 c