2011-10-08 38 views
7

Tôi hơi mới đối với C và tôi đang gặp sự cố khi hiểu cách hoạt động của bộ nhớ, đặc biệt là các chức năng được tích hợp sẵn như memcpy.C - Malloc và memcpy (quản lý bộ nhớ)

Dưới đây là một struct Tôi đang sử dụng

struct data_t { 
    int datasize; 
    void *data; 
}; 

Và đây là một chức năng phụ trợ mà tôi đang sử dụng nó với:

struct data_t *data_create(int size) 
{ 
    struct data_t *dt=malloc(sizeof(struct data_t)+size); 
    dt->datasize=size; 
    dt->data="1234567890a"; 
    return dt; 
} 

Bây giờ trong main chức năng tôi không có vấn đề làm điều này:

struct data_t *data = data_create(1024); 
data->data="123456a";//just an example 

Nhưng điều này sẽ ném ra lỗi Seg:

memcpy(data->data,"123456a",strlen("1234567890a")+1); 

Câu hỏi của tôi là lý do tại sao? Và làm thế nào để tránh nó? Xin lưu ý rằng tôi là người mới đối với C, vì vậy cách thức giao dịch với bộ nhớ là một chút mới đối với tôi

Cảm ơn bạn.

Chỉnh sửa: Nó hoạt động! Cảm ơn nhiều. Hoàn toàn bị mất con trỏ dữ liệu. Bây giờ mọi thứ đều hoạt động tốt theo valgrind.

Trả lời

6

memcpy(data->data,"123456a",strlen("1234567890a")+1);

thất bại vì data->data một void * điểm loại một số địa chỉ rác/không hợp lệ mà không được phân bổ. data có địa chỉ của chuỗi ký tự được lưu trữ trong phần chỉ đọc (như trong số .rodata của tệp thực thi và được tải trong bộ nhớ, không thể ghi được. Ngoài ra, nếu bạn không chỉ định địa chỉ chuỗi đó cho con trỏ biến, sau đó nó sẽ giữ một số giá trị địa chỉ không hợp lệ/rác mà không được phân bổ hoặc khởi tạo với một số vị trí được phép hợp lệ. Vì vậy, đầu tiên phân bổ bộ đệm.

data->data = malloc (sizeof (char) * size); 

các malloc sẽ trả về địa chỉ vị trí đầu tiên của một khối địa chỉ của atleast size * sizeof (char) bytes Bây giờ bạn có thể sao chép size byte vào vị trí bộ nhớ này được chỉ bởi data->data.

Hãy nhớ giải phóng khối bộ nhớ được cấp phát khi bạn đã kết thúc thao tác với bộ nhớ đó bằng lệnh gọi free (addr).


Tôi thấy bạn đã cố gắng phân bổ data đệm với một cách rất lạ (?):

struct data_t *dt=malloc(sizeof(struct data_t)+size); 

mà thêm phân bổ size byte cùng với struct data_t. Nhưng trường hợp nào, thành phần data vẫn trỏ đến một số nơi không thể thay đổi. Vui lòng sử dụng:

struct data_t *dt = malloc(sizeof(struct data_t)); 
dt->data = malloc (sizeof (char) * size); 
memcpy (data->data, "whatever", sizeof ("whatever")+1); 
return dt; 

để giải phóng đầu tiên làm:

free (dt->data); 

sau đó

free (dt); 
+0

Space đã được phân bổ trong việc phân bổ ban đầu; con trỏ chỉ đơn giản là không được thiết lập để trỏ đến không gian được phân bổ. –

+0

@JonathanLeffler: vâng, người hỏi đã phân bổ một số khoảng trống bổ sung khi ban đầu phân bổ, anh ta nên đã khởi tạo cho vấn đề. Tôi vẫn sẽ thích cuộc gọi rõ ràng hơn, nếu không có nhu cầu cụ thể để phân bổ tất cả cùng một lúc. – phoxis

+0

điều này sẽ đưa ra lỗi: "chuyển đổi không hợp lệ từ‘ void * ’thành‘ data_t * ’[-fpermissive]" để giải quyết "bạn phải rõ ràng truyền con trỏ được trả về bởi malloc". – VasaraBharat

1

Thông báo void *data là một con trỏ, trong data_create, bạn đã không phân bổ không gian cho nó, bạn chỉ cần làm cho nó trỏ đến một hằng số chuỗi "1234567890a" chỉ đọc.

Trong main, bạn tạo một hằng số chuỗi "123456a", sau đó bạn thực hiện void *data trỏ đến hằng số chuỗi, chỉ đọc.

Vì vậy, khi bạn gọi memcpy để ghi vào địa chỉ bộ nhớ không thể ghi (hoặc không được khởi tạo), bạn gặp lỗi.

0

dữ liệu-> dữ liệu là con trỏ. Và con trỏ này trỏ đến hư không. Trước khi thực hiện memcpy, bạn nên phân bổ không gian và làm cho dữ liệu-> dữ liệu trỏ vào không gian này.

data->data = malloc(strlen("1234567890a")+1); 

và sau đó memcpy sẽ không thất bại chừng nào đĩa dữ liệu> Xóa dữ liệu! = NULL

làm

data->data = "123"

là ok, vì "123" được phân bổ tại thời gian biên dịch, Vì vậy, dữ liệu-> điểm dữ liệu để bắt đầu chuỗi "123", nhưng gọi memcpy(data->data,"123",4) sẽ thất bại, bởi vì con trỏ đến dữ liệu dữ liệu là uninitialised và trỏ đến một số vị trí ngẫu nhiên trong bộ nhớ mà thậm chí không thể đọc được.

+0

'memcpy' vẫn không hợp lệ nếu cố gắng sao chép nhiều byte hơn từ nguồn so với nguồn có, đây là trường hợp ở đây. – Mat

+0

Có hai lỗi trong mã của anh ấy –

3

sai lầm đầu tiên của bạn là thế này:

struct data_t *dt=malloc(sizeof(struct data_t)+size); 

này sẽ tạo ra một đoạn bộ nhớ kích thước struct data_t + kích thước. Tôi nghĩ rằng những gì bạn mong đợi là trường dữ liệu của bạn bên trong data_t có thể sử dụng bộ nhớ này nhưng nó không thể vì dữ liệu không chứa một địa chỉ cho bộ nhớ này.

sai lầm thứ hai của bạn là để giả định rằng bạn nơi sao chép các giá trị của chuỗi sau vào "dữ liệu":

data->data="123456a"; 

gì trên thực tế đã xảy ra ở đây là có một chuỗi trong bộ nhớ "123456a" tồn tại cho toàn bộ cuộc đời của chương trình của bạn. Khi bạn gán "123456a" cho dữ liệu-> dữ liệu, điều thực sự xảy ra là bạn đang lấy địa chỉ của chuỗi này "123456a" và đặt nó vào dữ liệu-> dữ liệu bạn không sao chép giá trị ("123456a") nhưng vị trí hoặc địa chỉ (0x23822 ...) của "123456a".

sai lầm cuối cùng của bạn là thế này:

memcpy(data->data,"123456a",strlen("1234567890a")+1); 

Bạn đã cố gắng để sao chép các giá trị "123456a" vào bộ nhớ được trỏ đến bởi dữ liệu. Dữ liệu trỏ đến là gì? Nó trỏ đến vùng chỉ đọc của bộ nhớ chứa chuỗi đã gán trước đó của bạn là "123456a". Nói cách khác, bạn đã nói với chương trình của bạn để ghi vào địa chỉ của "123456a".

Dưới đây là một chương trình gì sẽ làm những gì bạn mong đợi:

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

typedef struct { 
    size_t datasize; 
    char *data; 
} data_t; 

data_t *data_create(size_t size) 
{ 
    data_t *dt; 

    dt = malloc(sizeof(data_t)); 
    assert(dt != NULL); 
    dt->data = malloc(size); 
    assert(dt->data != NULL); 
    dt->datasize = size; 

    /* need to decide what to do when this happens */ 
    assert((strlen("1234567890a") + 1) < size); 
    strcpy(dt->data, "1234567890a"); 

    return dt; 
} 

void data_destroy(data_t *dt) 
{ 
    free(dt->data); 
    free(dt); 
} 

int main(void) 
{ 
    data_t *data = data_create(1024); 
    /* data->data="123456a"; DONT DO THIS YOU WILL CAUSE A MEMORY LEAK */ 

    assert(data->datasize >= (strlen("123456a")+1)); 
    memcpy(data->data, "123456a", strlen("123456a")+1); 

    printf("%s\n", data->data); 

    data_destroy(data); 

    return EXIT_SUCCESS; 
}