写入大型4G动态分配阵列时出现段故障

时间:2018-03-24 19:29:15

标签: c

在64位Ubuntu 12.04系统中,一个简单的C程序分配一个大于或等于4GB的动态数组。当程序尝试将值写入数组的每个项目时,会发生段错误。以下是原始代码:

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

#define ROW 1024 * 1024 
#define COL 1024

#define u32 unsigned int
#define u64 unsigned long

int main(int argc, char const *argv[])
{
    u32 count = 0;
    u64* ary = (u64 *)malloc(sizeof(u32) * ROW * COL);
    assert(ary != NULL);
    printf("ary:%p\n", ary);

    for(u32 r = 0; r < ROW; r++) {
        for (u32 c = 0; c < COL; c++) {
            ary[r*ROW + c] = count;
            count++;
        }
    }

    free(ary);
    printf("free array\n");
    return 0;
}

将来源编译为:

gcc -o t_32_64_ary_64 t_32_64_ary.c -g -std=c99 -m64

运行时,出现段故障:

ary:0x7fa13584b010
Segmentation fault (core dumped)

由于该机器有16GB内存,所以我相信它分配4GB内存没有任何问题。

如果我将写入值代码注释为:

    /*
    for(u32 r = 0; r < ROW; r++) {
        for (u32 c = 0; c < COL; c++) {
            ary[r*ROW + c] = count;
            count++;
        }
    }
    */

该程序正常退出:

ary:0x7f136cd85010
free array

这表明它成功分配了4GB内存。

使用valgrind来运行程序:

valgrind ./t_32_64_ary_64
...
==4830== Warning: set address range perms: large range [0x395a5040, 0x1395a5040) (undefined)
ary:0x395a5040
==4830== Invalid write of size 8
==4830==    at 0x4006AE: main (t_32_64_ary.c:21)
==4830==  Address 0x1395a5040 is 0 bytes after a block of size 4,294,967,296 alloc'd
==4830==    at 0x4C2B6CD: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==4830==    by 0x400648: main (t_32_64_ary.c:14)
==4830== 
==4830== 
==4830== Process terminating with default action of signal 11 (SIGSEGV)
==4830==  Access not within mapped region at address 0x1395A6000
==4830==    at 0x4006AE: main (t_32_64_ary.c:21)
==4830==  If you believe this happened as a result of a stack
==4830==  overflow in your program's main thread (unlikely but
==4830==  possible), you can try to increase the size of the
==4830==  main thread stack using the --main-stacksize= flag.
==4830==  The main thread stack size used in this run was 8388608.
==4830== 
==4830== HEAP SUMMARY:
==4830==     in use at exit: 4,294,967,296 bytes in 1 blocks
==4830==   total heap usage: 1 allocs, 0 frees, 4,294,967,296 bytes allocated
==4830== 
==4830== LEAK SUMMARY:
==4830==    definitely lost: 0 bytes in 0 blocks
==4830==    indirectly lost: 0 bytes in 0 blocks
==4830==      possibly lost: 0 bytes in 0 blocks
==4830==    still reachable: 4,294,967,296 bytes in 1 blocks
==4830==         suppressed: 0 bytes in 0 blocks
==4830== Rerun with --leak-check=full to see details of leaked memory
==4830== 
==4830== For counts of detected and suppressed errors, rerun with: -v
==4830== ERROR SUMMARY: 505 errors from 1 contexts (suppressed: 2 from 2)
Segmentation fault (core dumped)

根据这些信息,似乎是行:

        ary[r*ROW + c] = count;

触发段故障,但我不明白为什么。我相信索引:r * RWO + c在数组范围内。

请帮助和谢谢!

4 个答案:

答案 0 :(得分:2)

您的索引错误,而您可能无法正确分配。

您可能希望通过malloc(sizeof(u64) * ROW * COL)进行分配(请注意,我使用的是u64而不是u32)。就目前而言,您只需分配一半您可能想要的内存。

您应该按照ary[r*COL + c](或ary[c*ROW + r]进行索引,具体取决于您的需求)。您应该使用size_t而不是u32作为索引值,以避免the overflow issues C_Elegans mentions

此外,虽然这不会导致这个小程序出现问题,但您应养成用括号括起宏定义的习惯(例如#define ROW (1024 * 1024))。

答案 1 :(得分:1)

这一行是你的主要问题:

u64* ary = (u64 *)malloc(sizeof(u32) * ROW * COL);

您正在创建指向u64的指针,但会根据u32所需的大小计算要分配的大小。

ROW * COL u64unsigned int号内存分配的内存不足。它可能仅仅是该数字的一半(除非使用对unsigned longpackage timelinefx; import static javafx.application.Application.launch; import java.util.Random; import javafx.application.Application; import javafx.scene.Group; import javafx.scene.PerspectiveCamera; import javafx.scene.Scene; import javafx.scene.SceneAntialiasing; import javafx.scene.SubScene; import javafx.scene.paint.Color; import javafx.scene.text.Text; import javafx.scene.transform.Translate; import javafx.stage.Stage; import javafx.scene.CacheHint; public class TextInto3DScene extends Application { static Stage stage; static Scene scene; static Group root; static Group subSceneRoot; static PerspectiveCamera camera; static SubScene subScene; static boolean cached = true; static int width = 1000; static int height = width; @Override public void start(Stage primaryStage) throws Exception { createWindow(); } protected static void createWindow(){ stage = new Stage(); root = new Group(); root.setCache(cached); root.setCacheHint(CacheHint.QUALITY); //doesn't work scene = new Scene(root, 1500, 700, true, SceneAntialiasing.BALANCED); //Initialize the camera camera = new PerspectiveCamera(true); camera.setNearClip(0.1); camera.setFarClip(5000); camera.setTranslateZ(-200); //creates the SubScene and add the camera to it! subSceneRoot = new Group(); subSceneRoot.setCache(cached); //doesn't work subScene = new SubScene(subSceneRoot, 1800, 700, true, SceneAntialiasing.BALANCED); subScene.setCache(cached); root.getChildren().add(subScene); subScene.setFill(Color.WHITE); subScene.setCamera(camera); //Create random texts for( int i = 0;i<=100;++i){ Text text = new Text(Math.random()* width-width/2, Math.random()*height-height/2, randomText() ); text.setFill( Color.BLACK ); text.setTranslateZ( Math.random()*750 ); text.setCache(cached); //doesn't work text.setCacheHint(CacheHint.QUALITY); subSceneRoot.getChildren().add(text); } stage.setScene(scene); setEventHandlers(); stage.show(); } static void setEventHandlers() { scene.setOnMouseDragged((event) -> { //camera.setRotate(event.getX()); double translateX = (event.getSceneX() - 0) / stage.getWidth(); translateX = translateX * width - width/2; double translateY = (event.getSceneY() - 0) / stage.getHeight(); translateY = translateY * height - height/2; double tz; if(!camera.getTransforms().isEmpty()){ tz = camera.getTransforms().get(0).getTz(); } else { tz = camera.getTranslateZ(); } camera.getTransforms().clear(); camera.getTransforms().addAll( new Translate(translateX, translateY, tz) ); }); //KEY PRESSED scene.setOnKeyPressed((event) -> { switch (event.getCode().toString()) { case "F10": double amt = event.isControlDown() ? 100 : 30; camera.setTranslateZ(camera.getTranslateZ() + amt); break; case "F11": amt = event.isControlDown() ? -100 : -30; camera.setTranslateZ(camera.getTranslateZ() + amt); break; } }); } static String randomText(){ String out = ""; Random r = new Random(); for(int i = 0;i<=10;++i){ char c = (char) (r.nextInt(26) + 'a'); out += c; } return out; } public static void main(String[] args) { launch(args); } } 使用相同大小的编译器进行编译)。

编辑:

如其他答案中所述,您的代码存在更多问题。

答案 2 :(得分:1)

使用calloc()可能是计算所需内存的更优雅的解决方案。我认为这也不容易出错...

#define SIZE 0x40000000  // = 1 GiB
uint32_t *array = (uint32_t *)calloc(SIZE, sizeof(uint32_t));

答案 3 :(得分:0)

malloc中要分配的字节数的计算是溢出的,因为ROWCOL的类型为int(它们是文字)。要解决此问题,您需要将它们声明为:

#define ROW 1024LL * 1024LL 
#define COL 1024LL

或者,考虑使用calloc,如下所示:

u64* ary = calloc(sizeof(u64) * COL, ROW); //casting malloc is not recommended in C

calloc在它的参数相乘时检查溢出并始终将它们乘以size_t,因此乘法将不再溢出