C中的2D阵列段故障 [英] 2D array seg fault in C

查看:76
本文介绍了C中的2D阵列段故障的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试取消引用函数 islandPerimeter 中的2D数组.
但是我不明白为什么我为此而遭受段错误.
有人可以指出我到底在做什么错吗?

更新:所以这是我尝试解决的leetcode问题的一部分,我现在知道它不是2D数组而是指针.我仍然对int **感到困惑.有人可以解释吗?

  #include< stdio.h>int islandPerimeter(int **网格,int gridSize,int gridColSize){int周长= 0,点= 4,i = 0;for(int row = 0; row< gridSize; ++ row){for(int col = 0; col< gridColSize; ++ col){printf(%d",grid [row] [col]);}}返回周长;}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);返回0;} 

解决方案

指向数组的指针

数组在C语言中是一种独特的类型.它是给定类型的元素的顺序集合.在C中,2D数组实际上是1D数组的数组.在您的情况下,您有一个[code] int [5] 的数组[4](例如,通常将 int 的4-5个元素数组称为 int的2D数组)

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

就一维数组而言,这很简单,该数组将转换为指向数组第一个元素的指针(该指针只是 int * ).对于2D数组,情况相同,该数组将转换为指向第一个元素的指针-但该第一个元素是一个5整数的1D数组.(指针是 int [5] pointer-to-array ,正式是 int(*)[5] ))

您可以根据需要将2D数组作为 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] ( int(* grid)[5] )的 pointer-to-array 而不是指针数组 int (其中5个)和 int * grid [5] .

指向指针的指针

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

然后为每个可用的 pointers 分配一个块(例如,要容纳5个 int ,然后将该内存块的起始地址分配给您的第一个可用指针).您继续分配列(或字符串或结构),并按顺序将开始地址分配给每个可用的指针.完成后,您可以使用与2D数组相同的索引访问分配的集合中的各个元素.这样的集合与2D数组数组之间的区别-每个指针指向的内存不必在内存中是顺序的.

与众不同

知道使用哪个的关键是问"我的指针指向什么?".它指向指针吗?还是指向数组?如果它指向另一个指针,则您有一个 pointer-to-pointer .如果指向的对象是数组,则您有一个 pointer-to-array .这样,您就知道需要什么作为参数.

为什么带有int **的SegFault

类型控制指针算术.回想一下, int ** pointer-to-pointer ,那么指针有多大?( sizeof(a_pointer)-通常在x86_64上为8字节,在x86上为4字节).因此, grid [1] [0] grid [0] [0] 仅有一个指针(8字节).那指向数组的指针呢?第一个索引中的每个增量与第一个索引之间是 sizeof(int [5]).因此,对于4x5数组,除 grid [0]以外, grid [1] [0] 5 * sizeof(int)(20字节)[0] .

因此,当尝试访问数组数组时,请使用 int ** ,以 grid [1] [3] (或 grid [1])开头[4] (在32位框中),您正在读取的是第一行值的末尾.(您的偏移量为8字节(单指针8字节-跳过2整数),将您放在第一行的第3个整数的前面,然后再偏移3个整数,将您放置在 grid [0] [5] 在第一行 grid [0] [4] 的最后一个值之后.

当您传递适当的 pointer-to-array 时,行索引的每个增量都会偏移20个字节,从而将您置于下一个1D值数组的开头,从而对每个值进行迭代列仍在该1D数组的范围内.

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

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中的2D阵列段故障的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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