RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1248342
Accepted
Reiji Akkerman
Reiji Akkerman
Asked:2022-02-25 06:23:01 +0000 UTC2022-02-25 06:23:01 +0000 UTC 2022-02-25 06:23:01 +0000 UTC

为什么当使用条目“a”将数组的地址增加 11 时,它增加了 44?而不是写“a[0]”和“a[0][0]”时所需的11?

  • 772

我最近开始学习C,课程代码如下

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
int a[3][4] = { {1, 2, 3, 4} , {5, 6, 7, 8}, {9, 10, 11, 12}};
int n = sizeof(a)/sizeof(a[0]);         // число строк
int m = sizeof(a[0])/sizeof(a[0][0]);   // число столбцов
 
int *final = a[0] + n*m - 1;    // указатель на самый последний элемент
for(int *ptr=a[0], i=1; ptr<=final; ptr++, i++)
{
    printf("%d \t", *ptr);
    // если остаток от целочисленного деления равен 0,
    // переходим на новую строку
    if(i%m==0)
    {
        printf("\n");
    }
}   
return 0;
}

它在这里说

int *final = a[0] + n*m - 1;

这意味着最终指针必须获得二维数组的最后一个元素的地址,所以它发生了。但是我决定检查它是否适用于在指针中指定数组地址的所有方式——嗯,就是这样。

  • 一个;
  • 一个[0];
  • &a[0][0];

最后两个问题没有问题,它们根据下面的代码将地址增加了 11 - 应该是这样,但在第一个版本中,地址全部增加了 44,这在理论上不应该。问题是为什么会发生这种情况,更有趣的是如何?如果它们都以相同的地址开头。

#include <stdio.h>

int main(void)
{

int a[3][4] = { {1, 2, 3, 4} , {5, 6, 7, 8}, {9, 10, 11, 12} };
int n = sizeof(a) / sizeof(a[0]);         
int m = sizeof(a[0]) / sizeof(a[0][0]);

printf("size of a = %d\n", sizeof(a));
printf("size of a[0] = %d\n", sizeof(a[0]));
printf("size of a[0][0] = %d\n", sizeof(a[0][0]));

printf("n = %d m = %d\n\n\n", n, m);

printf("address a = %p\n", a);                      //адрес первого элемента массива
printf("address a[0] = %p\n", a[0]);                //адрес первого элемента массива
printf("address a[0][0] = %p\n\n\n", &a[0][0]);     //адрес первого элемента массива

int* fin = a + n * m - 1;                           //(увеличивается не на 11 адресов а на 44 хотя начальный адрес у всех одинаковый)
int* fin1 = a[0] + n * m - 1;                       //адрес последнего элемента массива
int* fin2 = &a[0][0] + n * m - 1;                   //адрес последнего элемента массива

printf("fin: address %p \t value %d\n", fin, *fin);             //(увеличивается не на 11 адресов а на 44 хотя начальный адрес у всех одинаковый)
printf("fin1: address %p \t value %d\n", fin1, *fin1);          //адрес последнего элемента массива
printf("fin2: address %p \t value %d\n\n\n", fin2, *fin2);      //адрес последнего элемента массива

int* ptrd = a;
int diff = fin - ptrd;
printf("fin - ptrd = %d\n\n\n", diff);

for (int* ptr = a[0], i = 1; ptr <= fin2; ptr++, i++)
{
    printf("i = %d   ptr = %p   %d   ", i, ptr, *ptr);
    
    if (i % m == 0)
    {
        printf("\n");
    }
}

printf("\n\n");

for (int* ptr = a[0], i = 1; ptr <= fin; ptr++, i++)
{
    printf("i = %d   ptr = %p   %d   ", i,  ptr, *ptr);

    if (i % m == 0)
    {
        printf("\n");
    }
}

printf("\n\n\n");
return 0;
}
c
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. Best Answer
    Harry
    2022-02-25T12:25:31Z2022-02-25T12:25:31Z

    指针算法是基于这样一个事实,对于

    type * p
    

    增加p1 意味着移动到下一个元素,即 将值增加sizeof(type).

    Bina a[3][4]类型a是指向 的指针int[4],所以在

     int* fin = a + n * m - 1;
    

    a添加到地址11*sizeof(int[4])=11*16=176?那些。以字节为单位 - 偏移 176 个字节,以int'ax ( fin- 指向int) 的指针 - 到176/4=44.

    • 2
  2. wololo
    2022-02-25T19:58:31Z2022-02-25T19:58:31Z

    指针算法

    可以对指针进行整数值的加减运算。此类操作的含义如下。

    如果P是指向i元素数组的第-个n元素(或紧跟数组的最后一个元素的假设元素)的指针,并且j是某个整数值,则指针P + j指向i+j数组的第-个元素(或紧跟最后一个元素数组的假设元素),前提是0 <= i+j <= n,否则程序的行为是未定义的。

    此外,指针P - j指向数组的i-j第 - 个元素(或紧跟数组最后一个元素的假设元素),前提是0 <= i-j <= n,否则程序的行为是未定义的。

    在给定的代码中

    int arr[2];
    int* p0 = &arr[0];
    int* p1 = p0 + 1;
    

    指针指向索引p0处的数组元素,指针指向索引处的数组元素。arr0p1arr1

    每个数组元素arr占用一个sizeof(int)字节(通常是四个字节)。虽然我们给指针加了一个,但是以字节为单位,它的值增加了一个sizeof(int)字节。

    这是指针运算的一个特点。如果指针p类型为T*,则表达式的结果是指向字节p + j的p - j“修改后的”指针。j * sizeof(T)


    同样,考虑到类型的大小,计算两个指针的差值。

    如果指针P和Q指向同一数组的第iij个元素(或紧跟数组最后一个元素的假设元素),则表达式的结果P - Q是一个有符号整数值,等于i - j,并且类型为ptrdiff_t(定义在头文件<stddef.h>)。

    如果P并且Q不指向同一数组的元素(或紧随数组最后一个元素的假设元素),或者如果该值i-j不能用 type 表示ptrdiff_t,则程序的行为是未定义的。

    在给定的代码中

    int arr[2];
    int* p0 = &arr[0];
    int* p1 = &arr[1];
    ptrdiff_t diff = p1 - p0; //1
    

    值为。diff_ 但是,指针和字节1之间的差异是(通常是四个)。这是指针运算的一个特点。p1p0sizeof(int)


    如果某个对象不是数组,那么出于指针算术的目的,它被认为是由一个元素组成的数组。

    int obj;
    int* p = &obj;
    //OK, obj считается массивом из одного элемента. 
    //Указывать на гипотетический элемент за последним можно.
    p = p + 1;
    //А вот получить значение гипотетического элемента нельзя.
    obj = *p; //Поведение не определено.
    

    数组和指针

    数组是一个独立的实体。数组不等同于指向其第一个元素的指针。

    sizeof应用于数组的运算符返回数组的大小(以字节为单位),但不返回指向数组第一个元素的指针的大小。

    int arr[7];
    printf("%zu\n", sizeof(arr));      //7 * sizeof(int)
    printf("%zu\n", sizeof(&arr[0]));  //sizeof(int*)
    

    假设有一个n类型为 的元素数组T。

    &应用于此类数组的地址获取运算符返回指向n类型元素T数组的指针,即 指针是类型T (*)[n]但是应用于数组第一个元素的地址运算符返回一个指向 的指针T,即 指针是类型T*。

    指向整个数组的指针和指向数组第一个元素的指针表示相同的地址但具有不同的类型。这直接影响指针算术,因为 它取决于指针指向的类型的大小。

    int arr[7];
    int (*p_arr)[7] = &arr;
    int* p_int      = &arr[0];
    
    p_arr = p_arr + 1; //Добавили sizeof(arr) == 7 * sizeof(int) байт.
    p_int = p_int + 1; //Добавили sizeof(int) байт.
    

    在某些情况下,数组可以隐式转换为指向其第一个元素的指针。

    例如,如果一个数组参与了指针运算,那么它会被隐式转换为指向其第一个元素的指针。

    int arr[2];
    int* p1 = arr + 1;
    ptrdiff_t diff1 = p1 - arr;  //1
    ptrdiff_t diff0 = arr - arr; //0
    

    int* p1 = arr + 1- 数组被arr隐式转换为指向其第一个元素(索引为零)的指针,因此arr + 1- 这是指向数组第二个元素(索引为一)的指针arr。
    ptrdiff_t diff1 = p1 - arr- 数组arr再次隐式转换为指向其第一个元素的指针,因此不同p1 - arr之处在于指向同一数组中具有索引的元素的指针之间的差异1和0,因此表达式的结果p1 - arr是1。
    ptrdiff_t diff0 = arr - arr- 同样,数组被arr转换为指向其第一个元素的指针。

    多维数组

    k维数组k > 1是一个一维数组,其元素是k-1维数组。

    int arr[7][4][3];
    

    在上面的代码arr中,是семи类型元素的一维数组,T1其中是类型元素的一维数组,其中是类型元素的一维数组,其中是。T1четырёхT2T2трёхT3T3int

    arr有类型int [7][4][3]。
    arr[0]有类型int [4][3]。
    arr[0][0]有类型int [3]。
    arr[0][0][0]有类型int。


    有了上面写的所有内容,您的代码

    int a[3][4] = { {1, 2, 3, 4} , {5, 6, 7, 8}, {9, 10, 11, 12}};
    
    &a[0][0] + 11;
    a[0]     + 11;
    a        + 11;
    

    工作如下。

    表达式&a[0][0]的类型是int*。该表达式&a[0][0] + 11意味着向int*指向对象的指针arr[0][0]添加一个字节одиннадцать。sizeof(int)

    表达式a[0]的类型为int [4],即 这是一个数组。由于将整数值添加到数组中,因此数组被隐式转换为指向其第一个元素的指针,即 到指针int*。因此,表达式a[0] + 11意味着在int*指向 object的指针上arr[0][0]增加одиннадцать一个sizeof(int)字节。

    表达式a的类型为int [3][4],即 这是一个数组。由于将整数值添加到数组中,因此数组被隐式转换为指向其第一个元素的指针,即 到指针int (*)[4]。(是的,数组元素a是数组,而不是int's)。因此,表达式a + 11意味着在int (*)[4]指向 object的指针上arr[0]增加одиннадцать一个sizeof(int[4]) == 4 * sizeof(int)字节。

    关于数组溢出

    在答案的开头,它说如果表达式P + j,其中P是指针并且j是整数,不指向数组元素(或数组最后一个元素之后的假设元素),那么程序的行为未定义。

    上面的代码包含未定义的行为:

    int a[3][4] = { {1, 2, 3, 4} , {5, 6, 7, 8}, {9, 10, 11, 12}};
    a        + 11; //Поведение не определено.
    

    数组a正好包含триtype的元素int [4],所以表达式a + 11不指向数组的元素a,也不引用数组之外的假设元素,因此程序的行为是未定义的。

    此外,下面的代码还包含未定义的行为:

    int a[3][4] = { {1, 2, 3, 4} , {5, 6, 7, 8}, {9, 10, 11, 12}};
    &a[0][0] + 11; //Поведение не определено.
    a[0]     + 11; //Поведение не определено.
    

    表达式&a[0][0]指向由type 元素arr[0]组成的数组的第一个元素,因此表达式不指向数组的元素,也不指向数组之外的假设元素,因此程序的行为是未定义的。четырёхint&a[0][0] + 11arr[0]

    是的,我们知道一个数组后面紧跟着一个arr[0]数组arr[1],然后一个数组后面紧跟着一个数组arr[2],但是,语言标准并没有对这种情况做出任何让步。

    在该站点上,关于允许超出多维数组的子数组的讨论已经破坏了相当多的副本。没有单一的答案。主要有两个职位:

    1. 标准很苛刻,但它就是标准。在指针算术周期中,不可能超出多维数组的子数组的边界。
    2. 因为我们知道多维数组实际上是一个大的一维数组,并且所有现有的编译器都能正确处理超出多维数组的子数组,我们可以这样做。如果明天发布的编译器错误地处理越界的子数组,那么必须立即删除它,编译器的作者应该被枪杀。

    相关问题:可以将多维数组视为一维吗?

    Proprintf和转换说明符

    使用转换说明符,您承诺将printf某种类型的参数传递给函数。你必须信守诺言。如果您不这样做,那么语言标准就其本身而言,对您没有任何承诺 - 程序的行为没有定义。

    printf("size of a = %d\n", sizeof(a));
    

    转换说明符%d表示必须传递类型参数int,但运算符sizeof返回无符号整数类型的值size_t。程序的行为是未定义的。使用说明符%zu输出 type 的值size_t。


    printf("address a = %p\n", a);
    printf("address a[0] = %p\n", a[0]);
    printf("address a[0][0] = %p\n\n\n", &a[0][0]);
    

    转换说明符%p表示应该传递一个类型的参数void*,但是传递的参数(既不是a、也不是a[0]、也不是&a[0][0])都不是指定的类型,并且在这种特殊情况下不会隐式转换为指定的类型。程序的行为是未定义的。使用显式强制转换:(void*)a, (void*)a[0], (void*)&a[0][0].

    相关问题:printf 和 pointers。

    cppreference.comprintf上关于转换说明符的大表函数的文章:https ://en.cppreference.com/w/c/io/fprintf 。

    • 2

相关问题

  • free 出于某种原因不会从内存中删除数组

  • 请帮助代码

  • 为什么 masm 对字符串或文本文字太长发誓,为什么在结构中设置 db 或 dw?

  • 如何将数字拆分为位并将其写入 C 中的数组?

  • 如何以给定的角度移动物体?

  • 解决“子集和问题”的时效算法

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    表格填充不起作用

    • 2 个回答
  • Marko Smith

    提示 50/50,有两个,其中一个是正确的

    • 1 个回答
  • Marko Smith

    在 PyQt5 中停止进程

    • 1 个回答
  • Marko Smith

    我的脚本不工作

    • 1 个回答
  • Marko Smith

    在文本文件中写入和读取列表

    • 2 个回答
  • Marko Smith

    如何像屏幕截图中那样并排排列这些块?

    • 1 个回答
  • Marko Smith

    确定文本文件中每一行的字符数

    • 2 个回答
  • Marko Smith

    将接口对象传递给 JAVA 构造函数

    • 1 个回答
  • Marko Smith

    正确更新数据库中的数据

    • 1 个回答
  • Marko Smith

    Python解析不是css

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5