C中的二维数组段错误 [英] 2D array seg fault in C

查看:30
本文介绍了C中的二维数组段错误的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试取消引用函数 islandPerimeter 中的二维数组.
但我不明白为什么我会因此出现段错误.
有人能指出我到底做错了什么吗?

更新:所以这是我试图解决的 leetcode 问题的一部分.我现在明白它不是二维数组而是一个指针.我仍然对 int** 感到困惑.有人可以解释一下吗?

#include int islandPerimeter(int** grid,int gridSize,int gridColSize){int 周长=0,点数=4,i=0;for(int row=0;row

解决方案

数组指针

数组是 C 中的一种不同类型.它是给定类型元素的顺序集合.在 C 中,二维数组实际上是一维数组的数组.在您的情况下,您有一个 int [5] 的数组 [4](例如,int 的 4 - 5 个元素数组通常称为 int 的二维数组)

新程序员通常感到困惑的地方是在访问时如何处理数组.当访问数组时,它被转换为指向第一个元素的指针.C11 标准 - 6.3.2.1 其他操作数 - 左值、数组和函数指示符(p3)(注意4个例外)

在一维数组的情况下,也就是简单,将数组转换为指向数组第一个元素的指针(指针就是int*).在二维数组的情况下,同样适用,数组被转换为指向第一个元素的指针——但第一个元素是一个 5-int 的一维数组.(指针是int [5]指针到数组,形式上是int (*)[5])

您可以将二维数组(在您的情况下)作为 int grid[4][5]int grid[][5]、或者反映数组被转换为指向第一个元素的指针,int (*grid)[5].关键是您必须始终为数组提供最终维度中的元素数量(在此处不相关的情况下允许额外的 '*') 5(或数字元素的数量)必须是一个整数常量,除非使用可变长度数组(VLA),这在编译时是已知的,这是单独讨论的主题.>

在访问数组时将其转换为指向其第一个元素的指针的相同规则适用于数组中的每个维度,无论是 2D 数组还是 6D 数组.C11 标准 - 6.5.2.1 数组下标(p3)

此外,了解指针到数组(例如int (*grid)[5])和指针数组之间的区别(例如 int *grid[5]).由于 C 运算符优先级[..] 在这种情况下比 '*' 具有更高的优先级,因此要求 *grid (在 int *grid[5]) 被评估为一个 指针(而不是一个 array grid[5]) 你把它括起来是括号 (*网格).从而导致 int [5]pointer-to-array, (int (*grid)[5]) 而不是 指针数组int(其中 5 个)和 int *grid[5].

指向指针的指针

将其与 pointer-to-pointer(例如 int **,通常称为双指针)进行对比.您有两个 ** 表示的两级间接.指针本身是一个单指针——指向什么?(另一个指针,而不是数组).您通常会通过首先分配一块内存来保存一定数量的指针来使用双指针,例如当您动态分配未知数量的已分配对象时.这可以是 int 的未知列数的未知行数,也可以是未知数的字符串,或未知数的结构等.关键是您的第一个级别间接指向包含指针的内存.

然后对于每个可用的指针,您可以分配一个块(例如,在您的情况下,要保存 5 个 int,然后将该内存块的起始地址分配给您的第一个可用指针).您继续为您的列(或字符串或结构)分配并按顺序将起始地址分配给您的每个可用指针.完成后,您可以使用与 2D 数组相同的索引来访问已分配集合中的各个元素.这种集合与二维数组的区别在于每个指针指向的内存在内存中不需要是连续的.

区分它们

知道使用哪个的关键是问我的指针指向什么?"它是否指向一个指针?或者,它是否指向一个数组?如果它指向另一个指针,那么你就有了一个指针到指针.如果指向的东西是一个数组,那么你就有了一个指向数组的指针.有了它,您就知道需要什么作为参数.

为什么会出现带有 int** 的 SegFault

类型控制指针运算.回想一下上面的内容,int** 是一个指针到指针,那么指针有多大呢?(sizeof (a_pointer) - 通常在 x86_64 上为 8 字节,在 x86 上为 4 字节).所以 grid[1][0] 距离 grid[0][0] 仅一个指针(8 字节).指向数组的指针怎么样?第一个索引中的每个增量与第一个相距一个 sizeof (int[5]).所以在 4x5 数组的情况下,grid[1][0]5 * sizeof(int)(20 字节)和 grid[0][0].

因此,当尝试访问数组时,使用 int**,以 grid[1][3](或 grid[1][4] 在 32 位盒子上)您正在阅读第一行值的末尾.(您偏移了 8 字节(一个指针 8 字节 - 跳过 2-int),将您放在第一行中的第 3 个整数之前,然后再偏移 3 个整数,将您置于 grid[0][5] 超过第一行中的最后一个值grid[0][4].(这与每行增量复合)结果未定义,任何事情都可能发生.

当您传递适当的指向数组的指针时,行索引的每个增量偏移 20 字节,将您置于下一个一维值数组的开头,以便迭代每个列保持在该一维数组的范围内.

仔细考虑,如果您还有其他问题,请告诉我,我很乐意为您提供进一步帮助.

I am trying to de-reference the 2D array inside the function islandPerimeter.
But I cannot understand why I am getting segfault for this.
Can someone point out what exactly I am doing wrong?

update: So this was a part of a problem from leetcode I was trying to solve.I now understand it is not 2D array but a pointer. I am still confused over the int**. can someone explain it?

#include <stdio.h>

int islandPerimeter(int** grid, int gridSize, int gridColSize)
{
    int perimeter=0,points=4,i=0;

    for(int row=0;row<gridSize;++row)
    {
        for(int col=0;col<gridColSize;++col)
        {
            printf("%d ",grid[row][col]);
        }
    }
    return perimeter;
}


int main()
{
    int arr[4][5] = {{8,1,0,0,0},
                     {1,1,1,0,0},
                     {0,1,0,0,0},
                     {1,1,0,0,0}};

    islandPerimeter(arr,4,5);

    return 0;
}

解决方案

A Pointer to Array

An array is a distinct type in C. It is a sequential collections of elements of a given type. In C a 2D array is actually an array of 1D arrays. In your case, you have an array [4] of int [5] (e.g. 4 - 5-elements arrays of int commonly called a 2D array of int)

Where new programmers normally get confused is how an array is treated on access. When an array is accessed, it is converted to a pointer to the first element. C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3) (pay attention to the 4 exceptions)

In the case of a 1D array, that is simple, the array is converted to a pointer to the first element of the array (the pointer is simply int*). In the case of a 2D array, the same holds true, the array is converted to a pointer to the first element -- but that first element is a 1D array of 5-int. (the pointer is a pointer-to-array of int [5], formally int (*)[5])

You can pass the 2D array (in your case) as a parameter of either int grid[4][5], int grid[][5], or to reflect that the array is converted to a pointer to the first element, int (*grid)[5]. The key is you must always provide the number of elements in the final dimension for your array (with additional '*' allowed for circumstances not relevant here) The 5 (or number of elements) must be an integer constant which is known at compile-time unless using a Variable Length Array (VLA), which are the topic for a separate discussion.

The same rule that on access an array is converted to a pointer to its first element applies to each dimension in your array, be it a 2D array or a 6D array. C11 Standard - 6.5.2.1 Array subscripting(p3)

Additionally, know the difference between a pointer-to-array (e.g. int (*grid)[5]) and an array-of-pointers (e.g. int *grid[5]). The parenthesis are required due to C Operator Precedence, the [..] has higher precedence than '*' in this case, so to require that *grid (in int *grid[5]) be evaluated as a pointer (instead of as an array grid[5]) you enclose it is parenthesis (*grid). Thus resulting in a pointer-to-array of int [5], (int (*grid)[5]) instead of an array-of-pointers to int (5 of them) with int *grid[5].

A Pointer to Pointer

Contrast that with a pointer-to-pointer (e.g. int **, commonly called a double-pointer). You have two-levels of indirection represented by the two **. The pointer itself is a single-pointer -- to what? (another pointer, not to an array). You will generally use a double-pointer by first allocating a block of memory to hold some number of pointers, such as when you are dynamically allocating for an unknown number of allocated objects. This can be an unknown number of rows of an unknown number of columns of int or it can be an unknown number of strings, or a unknown number of structs, etc.. The key is your first level of indirection points to memory containing pointers.

Then for each of the available pointers you can allocate a block (e.g. in your case to hold 5 int and then assign the starting address for that block of memory to your first available pointer). You continue allocating for your columns (or strings or structs) and assigning the beginning address to each of your available pointers in sequence. When done, you can access the individual elements in your allocated collection using the same indexing you would for a 2D array. The difference between such a collection and a 2D array of arrays -- is the memory pointed to by each pointer need not be sequential in memory.

Telling Them Apart

The key to knowing which to use is to ask "What does my pointer point to?" Does it point to a pointer? Or, does it point to an array? If it points to another pointer, then you have a pointer-to-pointer. If the thing pointed to is an array, then you have a pointer-to-array. With that, you know what you need as a parameter.

Why the SegFault with int**

Type controls pointer arithmetic. Recall above, int** is a pointer-to-pointer, so how big is a pointer? (sizeof (a_pointer) - usually 8-bytes on x86_64, or 4-bytes on x86). So grid[1][0] is only one-pointer (8-bytes) away from grid[0][0]. What about the pointer-to-array? Each increment in the first index is a sizeof (int[5]) apart from the first. So in the case of a 4x5 array grid[1][0] is 5 * sizeof(int) (20-bytes) apart from grid[0][0].

So when attempting to access your array of arrays, using int**, beginning with grid[1][3] (or grid[1][4] on a 32-bit box) you are reading one-past the end of the 1st row of values. (you have offset by 8-bytes (one-pointer 8-bytes - skipping 2-int), placing you just before the 3rd integer in the 1st row, then offset 3 more integers placing you at what would be grid[0][5] one past the last value in the 1st row grid[0][4]. (this compounds with each row increment) The result is undefined and anything can happen.

When you pass the appropriate pointer-to-array, each increment of the row-index offsets by 20-bytes, placing you at the beginning of the next 1D array of values so iterating over each column remains within the bounds of that 1D array.

Think through it, and if you have further questions, just let me know and I'm happy to help further.

这篇关于C中的二维数组段错误的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆