Cách tạo mảng hai chiều động trong c. Mảng hai chiều động với con trỏ tới con trỏ

// khai báo mảng động hai chiều gồm 10 phần tử:

float **ptraarray = float mới* ; // hai dòng trong mảng

for (int count = 0; đếm< 2; count++)

ptrarray = phao mới; // và năm cột

// trong đó ptraarray là một mảng các con trỏ tới vùng bộ nhớ được phân bổ cho một mảng các số thực kiểu float

Đầu tiên, một con trỏ bậc hai float **ptrarray được khai báo, tham chiếu đến một mảng các con trỏ float*, trong đó kích thước của mảng là hai . Sau đó, trong vòng lặp for, mỗi dòng của mảng được khai báo trong dòng 2 bộ nhớ được phân bổ cho năm phần tử. Kết quả là một mảng động hai chiều ptrarray. Hãy xem xét một ví dụ về giải phóng bộ nhớ được phân bổ cho mảng động hai chiều.

// giải phóng bộ nhớ được phân bổ cho mảng động hai chiều:

for (int count = 0; đếm< 2; count++)

xóa ptrarray;

// trong đó 2 là số dòng trong mảng

#bao gồm
#bao gồm
#bao gồm
khoảng trống chính()
{

int *a; // con trỏ tới mảng

hệ thống ("chcp 1251");

scanf("%d", &n);

scanf("%d", &m);

// Cấp phát bộ nhớ

a = (int*) malloc(n*m*sizeof(int));

// Nhập các phần tử mảng

vì(i=0; tôi

vì(j=0; j

printf("a[%d][%d] = ", i, j);

scanf("%d", (a+i*m+j));

// Xuất ra các phần tử mảng

vì(i=0; tôi

vì(j=0; j

printf("%5d ", *(a+i*m+j)); // 5 người quen cho một phần tử mảng

getchar(); getchar();
}

Kết quả thực hiện

Nhập số dòng: 3

Nhập số cột: 4

Cũng có thể thực hiện một cách khác để cấp phát bộ nhớ động cho mảng hai chiều - sử dụng mảng con trỏ. Để làm điều này bạn cần:
- phân bổ một khối RAM cho một mảng con trỏ;
- phân bổ các khối RAM cho mảng một chiều, là các hàng của ma trận được yêu cầu;
- Viết địa chỉ các dòng vào mảng con trỏ.

Hàm malloc() trả về một con trỏ tới byte đầu tiên của vùng bộ nhớ có kích thước kích thước được phân bổ từ vùng bộ nhớ được phân bổ động. Nếu không có đủ bộ nhớ trong vùng bộ nhớ động, con trỏ null sẽ được trả về.

#bao gồm
#bao gồm
#bao gồm
khoảng trống chính()
{

int**a; // con trỏ tới con trỏ tới chuỗi

hệ thống ("chcp 1251");

printf("Nhập số dòng: ");

scanf("%d", &n);

printf("Nhập số cột: ");

scanf("%d", &m);

// Cấp phát bộ nhớ cho con trỏ tới chuỗi

a = (int**)malloc(n*sizeof(int*));

// Nhập các phần tử mảng

vì(i=0; tôi

// Cấp phát bộ nhớ để lưu trữ chuỗi

a[i] = (int*)malloc(m*sizeof(int));

vì(j=0; j

printf("a[%d][%d] = ", i, j);

scanf("%d", &a[i][j]);

// Xuất ra các phần tử mảng

vì(i=0; tôi

vì(j=0; j

printf("%5d ", a[i][j]); // 5 người quen cho một phần tử mảng

miễn phí(a[i]); // giải phóng bộ nhớ cho chuỗi

getchar(); getchar();
}

Kết quả thực hiện chương trình tương tự như trường hợp trước.

Bằng cách sử dụng cấp phát bộ nhớ động cho con trỏ hàng, bạn có thể cấp phát các mảng trống. Miễn phí là một mảng hai chiều (ma trận), kích thước của các hàng có thể khác nhau. Ưu điểm của việc sử dụng mảng trống là bạn không phải phân bổ quá nhiều bộ nhớ máy tính để chứa một chuỗi có độ dài tối đa có thể. Trong thực tế, mảng tự do là mảng một chiều chứa các con trỏ tới mảng dữ liệu một chiều.

Con trỏ.

Con trỏ là một biến có giá trị là địa chỉ chứa dữ liệu. Địa chỉ là số ô nhớ chứa dữ liệu trong hoặc từ đó.

Theo kiểu dữ liệu trong SI, con trỏ được chia thành:

Con trỏ được gõ là con trỏ chứa địa chỉ dữ liệu thuộc một loại nhất định (hệ thống hoặc người dùng).

Một con trỏ chưa được gõ là một con trỏ chứa địa chỉ dữ liệu thuộc loại không xác định (chỉ là một địa chỉ).

Khai báo con trỏ;

Đặt con trỏ;

truy cập một giá trị nằm ở một con trỏ. Việc khai báo (mô tả) một con trỏ trong ngôn ngữ SI có dạng sau:

Nhập *tên [=giá trị];

Khi được khai báo, một con trỏ trong SI có thể được khởi tạo bằng cách chỉ ra giá trị tương ứng thông qua dấu gán. Giá trị này phải là một địa chỉ, được viết bằng một trong các dạng sau:

Giá trị Null (mã định danh NULL);

Một con trỏ khác;

Địa chỉ biến (thông qua thao tác lấy địa chỉ);

Một biểu thức biểu diễn số học con trỏ;

Địa chỉ là kết quả của việc cấp phát bộ nhớ động.

#bao gồm

int var; // biến số nguyên thông thường

int *ptrVar; // con trỏ số nguyên (ptrVar phải thuộc kiểu int, vì nó sẽ tham chiếu đến một biến có kiểu int)

ptrVar = // gán cho con trỏ địa chỉ của ô nhớ nơi chứa giá trị của biến var

scanf("%d", &var); // biến var chứa giá trị nhập từ bàn phím

printf("%d\n", *ptrVar); // giá trị đầu ra thông qua con trỏ

Kết quả thực hiện: 6 6

Bài giảng số 3.

Chức năng.

Hàm là một mô-đun chương trình được đặt tên theo cú pháp để thực hiện một hành động hoặc nhóm hành động cụ thể. Mỗi chức năng có giao diện và cách thực hiện riêng. Giao diện hàm – tiêu đề hàm, cho biết tên của hàm, danh sách các tham số của hàm và loại giá trị trả về.

Việc mô tả một hàm trong ngôn ngữ SI được thực hiện ở bất kỳ đâu trong chương trình bên ngoài mô tả các hàm khác và bao gồm ba phần tử:

1. nguyên mẫu chức năng;

2. tiêu đề chức năng;

3. Thân chức năng.

Nguyên mẫu hàm là một phần tùy chọn của mô tả hàm, nhằm khai báo một hàm có giao diện tương ứng với một nguyên mẫu nhất định. Khai báo nguyên mẫu có dạng sau:

Tên loại (danh sách các loại tham số chính thức);

Tham số của hàm là các giá trị được truyền cho hàm khi nó được gọi.

Tiêu đề hàm – mô tả phần giao diện của hàm, bao gồm: loại giá trị trả về, tên hàm và danh sách các tham số hình thức của hàm. Cú pháp khai báo tiêu đề hàm là:

Tên loại (danh sách các tham số chính thức)

Ví dụ về tiêu đề hàm:

Int func(int i, double x, double y)

Void func(int ind, char *string)

Chức năng kép (void)

Phần thân của hàm là phần triển khai chứa mã chương trình được thực thi khi hàm được gọi. Phần thân hàm luôn đứng ngay sau phần đầu hàm (chúng không thể tách rời) và được đặt trong dấu ngoặc nhọn.

Thực hiện hàm trong SI để tính giai thừa của một số.

Giai thừa kép (không dấu);

Giai thừa kép(số không dấu)

Thực tế kép = 1,0;

For(unsigned i=1;i<=num;i++)

Sự thật *= (gấp đôi)i;

Trả lại thực tế;

Cấu trúc.

Cấu trúc là một kiểu dữ liệu phức tạp đại diện cho một tập hợp các phần tử thuộc nhiều loại khác nhau được sắp xếp trong bộ nhớ. Mỗi phần tử trong cấu trúc có tên riêng và được gọi là trường.

Khai báo trong SI của một cấu trúc trông như sau:

Cấu trúc [tên loại]

Trường_1;

Trường_2;

Trường_N;

) [danh sách các biến];

Chỉ có thể khai báo các trường cấu trúc mà không cần khởi tạo. Nếu một số trường nối tiếp nhau trong mô tả cấu trúc có cùng kiểu, thì để mô tả chúng, bạn có thể sử dụng cú pháp khai báo một số biến cùng kiểu.

Các tập tin.

Tệp là một vùng dữ liệu được đặt tên trên một số phương tiện lưu trữ. Các loại tệp (liên quan đến ngôn ngữ SI):
chữ;
nhị phân.
Các thao tác cơ bản được thực hiện trên tập tin:
1.Mở tập tin.
2.Đọc và ghi dữ liệu.
3.Đóng tập tin.

Các hoạt động bổ sung:
1.Điều hướng tập tin.
2. Xử lý lỗi khi làm việc với file.
3.Xóa và đổi tên tập tin.
4.Mô tả biến

Chế độ mở tập tin có SI

Chuyển hướng luồng
FILE * freopen(const char *tên tệp, const char *mode, FILE *stream);

Hàm trả về:
Con trỏ tới tập tin - mọi thứ đều ổn,
NULL - lỗi ghi đè.

Đóng một tập tin
int fclose(FILE *stream);

Hàm trả về:
0 – đóng tập tin thành công.
1 – xảy ra lỗi khi đóng tệp.

Kiểm tra kết thúc tập tin
int feof(FILE *stream);
luồng - con trỏ tới một tập tin đang mở.

Hàm trả về:
0 – nếu chưa đến cuối tập tin.
!0 – đạt đến cuối tập tin.

Mở tập tin văn bản
Tham số thứ hai chỉ định thêm ký tự t (tùy chọn):
rt, wt, tại, rt+, wt+, at+

Đọc từ một tập tin văn bản

Đọc có định dạng
int fscanf(FILE *stream, định dạng const char *, ...);

Hàm trả về:
>0 – số lượng biến được đọc thành công,
0 – không có biến nào được đọc thành công,
EOF – lỗi hoặc đến cuối tập tin.
Đọc một dòng

Hàm trả về:
đệm - mọi thứ đều ổn,
Đọc một dòng
char * fgets(char * buffer, int maxlen, FILE *stream);

Hàm trả về:
đệm - mọi thứ đều ổn,
NULL – lỗi hoặc đến cuối tập tin.
Đọc một biểu tượng
int fgetc(FILE *stream);
Hàm trả về:
mã ký hiệu - nếu mọi thứ đều ổn,
EOF – nếu có lỗi hoặc đã đến cuối tệp.
Đưa một ký tự trở lại luồng
int ungetc(int c, FILE *stream);
Hàm trả về:
mã ký hiệu – nếu mọi thứ thành công,
EOF – đã xảy ra lỗi.

Viết thành văn bản tập tin SI

Đầu ra được định dạng
int fprintf(FILE *stream, const char *format, ...);
Hàm trả về:
số ký tự được viết - nếu mọi thứ đều bình thường,
giá trị âm - nếu có lỗi.
Viết một chuỗi
int fputs(const char *string, FILE *stream);
Hàm trả về:
số lượng ký tự được viết - mọi thứ đều ổn,
EOF – đã xảy ra lỗi.
Viết một biểu tượng
int fputc(int c, FILE *stream);
Hàm trả về:
mã của ký hiệu được ghi - mọi thứ đều ổn,
EOF – đã xảy ra lỗi.
Mở tập tin nhị phân
Tham số thứ hai chỉ định thêm ký hiệu b (bắt buộc): rb, wb, ab, rb+, wb+, ab+
Đọc từ tập tin nhị phân
size_t fread(void *buffer, size_t size, size_t num,FILE *stream);
Hàm trả về số khối đã đọc. Nếu nó nhỏ hơn num thì đã xảy ra lỗi hoặc đã đạt đến
phần cuối của tập tin.

Ghi vào tệp nhị phân
size_t fwrite(const void *buffer, size_t size, size_t num, FILE *stream);
Hàm trả về số khối đã ghi. Nếu nó nhỏ hơn num thì đã xảy ra lỗi.

Điều hướng tệp

Đọc phần bù hiện tại trong một tệp:
long int ftell(FILE *stream);
Thay đổi phần bù hiện tại trong một tệp:
int fseek(FILE *stream, int offset dài, int Origin);

SEEK_SET (0) – từ đầu tập tin.
SEEK_CUR (1) – từ vị trí hiện tại.
SEEK_END (2) – từ cuối tệp.
Hàm trả về:
0 – mọi thứ đều ổn,
!0 – đã xảy ra lỗi.
Di chuyển đến đầu tập tin:
void tua lại (FILE *stream);
Đọc vị trí hiện tại trong một tập tin:
int fgetpos(FILE *stream, fpos_t *pos);
Đặt vị trí hiện tại trong tệp:
int fsetpos(FILE *stream, const fpos_t *pos);
Các hàm trả về:
0 – mọi thứ đều thành công,
!0 – đã xảy ra lỗi.
cấu trúc fpos_t:
typedef cấu trúc fpos_t (
lâu rồi;
mbstate_t wstate;
) fpos_t;

Nhận được một dấu hiệu lỗi:
int ferror(FILE *stream);
Hàm trả về giá trị khác 0 nếu xảy ra lỗi.
Chức năng reset lỗi:
void clearr(FILE *stream);
Chức năng thông báo lỗi:
void perror(const char *string);

Đang đệm

Chức năng xóa bộ đệm:
int fflush(FILE *stream);
Hàm trả về:
0 – mọi thứ đều ổn.
EOF – đã xảy ra lỗi.
Chức năng quản lý bộ đệm:
void setbuf(FILE *stream, char * buffer);

Tạo bộ đệm có kích thước BUFSIZ. Được sử dụng trước đầu vào hoặc đầu ra của luồng.

Hồ sơ tạm thời

Chức năng tạo tập tin tạm thời:
TẬP TIN * tmpfile(void);
Tạo một tập tin tạm thời ở chế độ wb+. Sau khi đóng tập tin, tập tin sau sẽ tự động bị xóa.
Chức năng tạo tên tệp tạm thời:
char * tmpnam(char *buffer);

Xóa và đổi tên

Chức năng xóa tập tin:
int loại bỏ (const char *tên tệp);
Chức năng đổi tên tập tin:
int đổi tên(const char *fname, const char *nname);
Các hàm trả về:
0 – nếu thành công,
!0 – ngược lại.

Bài giảng số 4.

Cây rơm.

Ngăn xếp ngược lại với hàng đợi vì nó hoạt động trên cơ sở vào sau, ra trước (LIFO). Để hình dung một chồng đĩa, hãy nghĩ đến một chồng đĩa. Đĩa đầu tiên đặt trên bàn sẽ được sử dụng sau cùng, đĩa cuối cùng đặt lên trên sẽ được sử dụng trước. Ngăn xếp thường được sử dụng trong phần mềm hệ thống, bao gồm cả trình biên dịch và trình thông dịch.

Khi làm việc với ngăn xếp, các thao tác chèn và truy xuất một phần tử là những thao tác chính. Các hoạt động này theo truyền thống được gọi là “push” và “pop”. Do đó, để triển khai một ngăn xếp, bạn cần viết hai hàm: push(), “đẩy” một giá trị vào ngăn xếp và pop(), “bật” một giá trị từ ngăn xếp. Bạn cũng cần phân bổ một vùng bộ nhớ sẽ được sử dụng làm ngăn xếp. Với mục đích này, bạn có thể cấp phát một mảng hoặc cấp phát động một phần bộ nhớ bằng cách sử dụng các hàm ngôn ngữ C được cung cấp để cấp phát bộ nhớ động. Giống như hàng đợi, hàm tìm nạp lấy một phần tử từ danh sách và xóa nó nếu nó chưa được lưu trữ ở nơi khác. Dưới đây là dạng tổng quát của hàm push() và pop() hoạt động trên một mảng số nguyên. Các loại ngăn xếp dữ liệu khác có thể được sắp xếp bằng cách thay đổi kiểu dữ liệu cơ bản của mảng.

int tos=0; /*đỉnh ngăn xếp*/

/* Đẩy một phần tử vào ngăn xếp. */

đẩy vô hiệu (int i)

if(tos >= MAX) (

printf("Ngăn xếp đã đầy\n");

/* Lấy phần tử trên cùng của ngăn xếp. */

nếu(tos< 0) {

printf("Ngăn xếp trống\n");

trả lại ngăn xếp;

Biến tos ("đỉnh ngăn xếp") chứa chỉ mục của đỉnh ngăn xếp. Khi thực hiện các chức năng này, cần tính đến các trường hợp ngăn xếp đầy hoặc trống. Trong trường hợp của chúng ta, dấu hiệu của ngăn xếp trống là tos bằng 0 và dấu hiệu của tràn ngăn xếp là tos tăng nhiều đến mức giá trị của nó trỏ đến đâu đó ngoài ô cuối cùng của mảng.

Một ví dụ về làm việc với ngăn xếp.

Ngăn xếp sẽ được đặt trong bộ nhớ được cấp phát động chứ không phải trong một mảng có kích thước cố định. Mặc dù việc sử dụng cấp phát bộ nhớ động là không cần thiết trong ví dụ đơn giản như vậy nhưng chúng ta sẽ xem cách sử dụng bộ nhớ động để lưu trữ dữ liệu ngăn xếp.

/* Một máy tính đơn giản có bốn bước. */

#bao gồm

#bao gồm

int *p; /*con trỏ tới vùng nhớ trống */

int *tos; /*con trỏ lên đỉnh ngăn xếp */

int *bos; /*con trỏ xuống cuối ngăn xếp */

đẩy vô hiệu (int i);

p = (int *) malloc(MAX*sizeof(int)); /*lấy bộ nhớ cho ngăn xếp */

printf("Lỗi khi cấp phát bộ nhớ\n");

bos = p + MAX-1;

printf("Tính 4 bước\n");

printf("Nhấn "q" để thoát\n");

printf("%d\n", a+b);

printf("%d\n", b-a);

printf("%d\n", b*a);

printf("Chia cho 0.\n");

printf("%d\n", b/a);

case ".": /* hiển thị nội dung ở đỉnh ngăn xếp */

printf("Giá trị hiện tại ở đầu ngăn xếp: %d\n", a);

) while(*s != "q");

/* Đẩy một phần tử vào ngăn xếp. */

đẩy vô hiệu (int i)

if(p > bos) (

printf("Ngăn xếp đã đầy\n");

/* Lấy phần tử trên cùng của ngăn xếp. */

Nếu p< tos) {

printf("Ngăn xếp trống\n");

Xếp hàng.

Hàng đợi là một danh sách thông tin tuyến tính được xử lý trên cơ sở nhập trước, xuất trước; nguyên tắc này (và hàng đợi dưới dạng cấu trúc dữ liệu) đôi khi còn được gọi là FIFO. Điều này có nghĩa là phần tử đầu tiên được đặt trong hàng đợi sẽ được nhận từ nó trước, phần tử thứ hai được đặt ở vị trí thứ hai sẽ bị xóa, v.v. Đây là cách duy nhất để làm việc với hàng đợi; không được phép truy cập ngẫu nhiên vào các phần tử riêng lẻ.

Để hình dung hàng đợi hoạt động như thế nào, hãy giới thiệu hai hàm: qstore() và qretrieve() (từ "store" - "save", "retrieve" - ​​​​"receive"). Hàm qstore() đặt một phần tử ở cuối hàng đợi và hàm qretrieve() loại bỏ một phần tử khỏi đầu hàng đợi và trả về giá trị của nó. Bảng này cho thấy trình tự của các hoạt động như vậy.

Hoạt động Nội dung xếp hàng
qstore(A) MỘT
qstore(B) A B
qstore(C) A B C
qretrieve() trả về A B C
qstore(D) B C D
qretrieve() trả về B ĐĨA CD
qretrieve() trả về C D

Hãy nhớ rằng thao tác tìm nạp sẽ loại bỏ một phần tử khỏi hàng đợi và hủy phần tử đó trừ khi nó được lưu trữ ở một nơi khác. Vì vậy, sau khi lấy tất cả các phần tử, hàng đợi sẽ trống.

Trong lập trình, hàng đợi được sử dụng để giải quyết nhiều vấn đề. Một trong những loại nhiệm vụ phổ biến nhất là mô phỏng. Hàng đợi cũng được sử dụng trong bộ lập lịch tác vụ của hệ điều hành và trong bộ đệm I/O.

/* Lập kế hoạch sự kiện nhỏ */

#bao gồm

#bao gồm

#bao gồm

#bao gồm

char *p, *qretrieve(void);

void enter(void), qstore(char *q), review(void), delete_ap(void);

vì(t=0; t< MAX; ++t) p[t] = NULL; /* иницилизировать массив

con trỏ trống */

printf("Nhập (E), Danh sách (L), Xóa (R), Thoát (Q): ");

*s = toupper(*s);

/* Chèn một cuộc hẹn mới vào hàng đợi. */

khoảng trống nhập(void)

printf("Nhập cuộc hẹn %d: ", spos+1);

if(*s==0) ​​​​nghỉ; /*không có bản ghi nào được thực hiện*/

p = (char *) malloc(strlen(s)+1);

printf("Không đủ bộ nhớ.\n");

if(*s) qstore(p);

/* Xem nội dung của hàng đợi. */

đánh giá vô hiệu(void)

for(t=rpos; t< spos; ++t)

printf("%d. %s\n", t+1, p[t]);

/* Xóa cuộc hẹn khỏi hàng đợi. */

void delete_ap(void)

if((p=qretrieve())==NULL) trả về;

printf("%s\n", p);

/* Chèn một cuộc hẹn. */

void qstore(char *q)

printf("Danh sách đầy đủ\n");

/* Lấy lại cuộc hẹn. */

char *qretrieve(void)

if(rpos==spos) (

printf("Không họp nữa.\n");

trả lại p;

Danh sách.

Danh sách tuần hoàn được liên kết đơn là một khai báo đệ quy của các cấu trúc, hay đúng hơn là một con trỏ tới nó trong chính cấu trúc kiểu đó:

dữ liệu int;//trường dữ liệu

s *next;//phần tử tiếp theo

) *first,*curr;//phần tử đầu tiên và hiện tại

Khởi tạo:

đầu tiên->tiếp theo=curr;

để lấy phần tử đầu tiên, hãy sử dụng first->data

để thêm phần tử mới: Curr->next=new s;

curr=curr->next;//đi tới cái cuối cùng

và để lấy phần tử thứ 50 chẳng hạn, hãy lặp qua danh sách:

Curr=đầu tiên;//đi tới đầu tiên

for(int i=0;i<50;i++)

if(curr->next!=NULL)

Curr=curr->next;


Thông tin liên quan.


Đây là bộ đếm thời gian đầu tiên trên trang web này, vì vậy nó sẽ xuất hiện ở đây.

Tôi mới làm quen với C++ và hiện tôi đang viết cuốn sách "Cấu trúc dữ liệu sử dụng C++ ấn bản thứ 2, D.S. Malik".

Trong cuốn sách, Malik đưa ra hai cách để tạo mảng hai chiều động. Trong phương thức đầu tiên, bạn khai báo một biến dưới dạng một mảng các con trỏ, trong đó mỗi con trỏ có kiểu số nguyên. ví dụ

Bảng Int *;

Và sau đó sử dụng vòng lặp for để tạo "cột" trong khi sử dụng một mảng con trỏ làm "hàng".

Phương pháp thứ hai, bạn sử dụng con trỏ để trỏ tới một con trỏ.

Bảng Int **; bảng = int mới* ;

Câu hỏi của tôi là: phương pháp nào tốt hơn? Tôi dễ hình dung phương thức ** hơn, nhưng phương pháp đầu tiên có thể được sử dụng theo cách tương tự. Cả hai phương pháp đều có thể được sử dụng để tạo mảng 2 chiều động.

Chỉnh sửa: Không đủ rõ ràng như đã nêu ở trên. Đây là mã tôi đã thử:

Hàng int, col; cout<< "Enter row size:"; cin >> hàng; cout<< "\ncol:"; cin >>col; int *p_board; vì (int i=0; tôi< row; i++) p_board[i] = new int; for (int i=0; i < row; i++) { for (int j=0; j < col; j++) { p_board[i][j] = j; cout << p_board[i][j] << " "; } cout << endl; } cout << endl << endl; int **p_p_board; p_p_board = new int* ; for (int i=0; i < row; i++) p_p_board[i] = new int; for (int i=0; i < row; i++) { for (int j=0; j < col; j++) { p_p_board[i][j] = j; cout << p_p_board[i][j] << " "; } cout << endl; }

4 câu trả lời

Phương pháp đầu tiên không thể được sử dụng để tạo năng động Mảng 2D vì:

Bảng Int *;

về cơ bản bạn đã phân bổ một mảng gồm 4 con trỏ tới int mỗi ngăn xếp. Vì vậy, nếu bây giờ bạn điền vào mỗi con trỏ trong số 4 con trỏ này bằng một mảng động:

Với (int i = 0; tôi< 4; ++i) { board[i] = new int; }

những gì bạn kết thúc là một mảng 2D với tĩnh số dòng (trong trường hợp này là 4) và năng động số cột (trong trường hợp này là 10). Vì vậy, động lực học không đầy đủ, vì khi cấp phát một mảng trên ngăn xếp bạn phải biểu thị kích thước không đổi, I E. Được biết đến ở thời gian. Năng động mảng được gọi là năng động, bởi vì kích thước của nó không nhất thiết phải được biết trong thời gian biên dịch, mà đúng hơn là có thể được xác định bởi một số biến trong trong quá trình thực hiện.

Một lần nữa khi bạn làm:

Bảng Int *;

Hằng int x = 4; //<--- `const` qualifier is absolutely needed in this case! int *board[x];

bạn cung cấp một hằng số được biết đến trong thời gian biên dịch(trong trường hợp này là 4 hoặc x) để trình biên dịch có thể bây giờ chọn trước bộ nhớ này cho mảng của bạn và khi chương trình của bạn được tải vào bộ nhớ, nó sẽ có sẵn lượng bộ nhớ này cho mảng bảng, đó là lý do tại sao nó được gọi là tĩnh, I E. bởi vì kích thước mã hóa cứngkhông thể thay đổi linh hoạt(trong thời gian chạy).

Mặt khác, khi bạn làm:

Bảng Int **; bảng = int mới*;

Int x = 10; //<--- Notice that it does not have to be `const` anymore! int **board; board = new int*[x];

trình biên dịch không biết mảng bảng sẽ cần bao nhiêu bộ nhớ, vì vậy nó không biết phân bổ trước Tất cả. Nhưng khi bạn chạy chương trình, kích thước của mảng sẽ được xác định bởi giá trị của biến x (trong thời gian chạy) và không gian tương ứng cho mảng bảng sẽ được phân bổ cho cái gọi là một bó- vùng bộ nhớ trong đó tất cả các chương trình đang chạy trên máy tính của bạn có thể phân bổ không biết trước(tại thời điểm biên dịch) tóm tắt bộ nhớ để sử dụng cá nhân.

Kết quả là, để thực sự tạo một mảng 2D động, bạn cần thực hiện theo phương pháp thứ hai:

Bảng Int **; bảng = int mới*; // mảng động (kích thước 10) của các con trỏ tới int for (int i = 0; i< 10; ++i) { board[i] = new int; // each i-th pointer is now pointing to dynamic array (size 10) of actual int values }

Chúng ta vừa tạo một mảng 2D vuông có kích thước 10 x 10. Để xem qua nó và điền vào nó các giá trị thực, chẳng hạn như 1, chúng ta có thể sử dụng các vòng lặp lồng nhau:

Với (int i = 0; tôi< 10; ++i) { // for each row for (int j = 0; j < 10; ++j) { // for each column board[i][j] = 1; } }

Những gì bạn mô tả cho phương thức thứ hai chỉ tạo ra mảng 1D:

Int *board = int mới;

Điều này chỉ đơn giản là phân bổ một mảng có 10 phần tử. Có lẽ bạn có ý gì đó như thế này:

Int **bảng = int mới*; vì (int i = 0; tôi< 4; i++) { board[i] = new int; }

Trong trường hợp này, chúng tôi phân bổ 4 int* s và sau đó mỗi điểm tới một mảng được phân bổ động gồm 10 int s.

Vì vậy bây giờ chúng ta so sánh cái này với bảng int*; . Sự khác biệt chính là khi sử dụng một mảng như vậy, số lượng "hàng" phải được biết tại thời điểm biên dịch. Điều này là do mảng phải có kích thước thời gian biên dịch cố định. Bạn cũng có thể gặp vấn đề nếu muốn trả về mảng này từ int* s, vì mảng sẽ bị hủy ở cuối phạm vi của nó.

Phương pháp phân bổ động cả hàng và cột yêu cầu các biện pháp phức tạp hơn để tránh rò rỉ bộ nhớ. Bạn nên giải phóng bộ nhớ như thế này:

Với (int i = 0; tôi< 4; i++) { delete board[i]; } delete board;

Tôi phải khuyên bạn nên sử dụng một thùng chứa tiêu chuẩn để thay thế. Bạn có thể sử dụng std::array 4> hoặc có lẽ std::vector > mà bạn khởi tạo với kích thước phù hợp.

Trong cả hai trường hợp, thứ nguyên bên trong của bạn có thể được đặt động (tức là được lấy từ một biến), nhưng sự khác biệt là ở thứ nguyên bên ngoài.

Câu hỏi này về cơ bản tương đương với những điều sau đây:

Có phải int* x = int mới; "tốt hơn" int x?

Câu trả lời là "không, trừ khi bạn cần chọn kích thước mảng đó một cách linh hoạt."

Mã này hoạt động tốt với rất ít yêu cầu về thư viện bên ngoài và cho thấy cách sử dụng cơ bản của int **array .

Câu trả lời này cho thấy rằng mảng mỗi có kích thước động và cả cách gán mảng tuyến tính có kích thước động cho mảng nhánh có kích thước động.

Chương trình này lấy các đối số từ STDIN theo định dạng sau:

2 2 3 1 5 4 5 1 2 8 9 3 0 1 1 3

Mã của chương trình dưới đây...

#bao gồm int main() ( int **array_of_arrays; int num_arrays, num_queries; num_arrays = num_queries = 0; std::cin >> num_arrays >> num_queries; //std::cout<< num_arrays << " " << num_queries; //Process the Arrays array_of_arrays = new int*; int size_current_array = 0; for (int i = 0; i < num_arrays; i++) { std::cin >> size_current_array; int *tmp_array = int mới; vì (int j = 0; j< size_current_array; j++) { int tmp = 0; std::cin >>tmp; tmp_array[j] = tmp; ) mảng_of_arrays[i] = tmp_array; ) //Xử lý truy vấn int x, y; x = y = 0; vì (int q = 0; q< num_queries; q++) { std::cin >> x >> y; //std::cout<< "Current x & y: " << x << ", " << y << "\n"; std::cout << array_of_arrays[x][y] << "\n"; } return 0; }

Đây là cách triển khai int main rất đơn giản và chỉ phụ thuộc vào std::cin và std::cout . Đơn giản nhưng đủ tốt để chỉ ra cách làm việc với các mảng đa chiều đơn giản.

Thông thường, lượng bộ nhớ cần thiết cho một biến cụ thể được chỉ định trước quá trình biên dịch bằng cách khai báo biến đó. Nếu có nhu cầu tạo một biến có kích thước không xác định trước thì bộ nhớ động sẽ được sử dụng. Sự đặt chỗ giải phóng vấn đề về bộ nhớ trong chương trình C++ có thể xảy ra bất cứ lúc nào. Các hoạt động được thực hiện phân bổ bộ nhớ theo hai cách:

  • sử dụng chức năng malloc, calloc, phân bổ lạimiễn phí;
  • thông qua nhà điều hành mớixóa bỏ.

Chức năng malloc dự trữ một khối ô nhớ liền kề để lưu trữ đối tượng đã chỉ định và trả về một con trỏ tới ô đầu tiên của khối đó. Cuộc gọi đến hàm trông giống như:

void *malloc(size);

Đây kích cỡ- một giá trị số nguyên không dấu xác định kích thước của vùng bộ nhớ được phân bổ tính bằng byte. Nếu việc đặt trước bộ nhớ thành công, hàm sẽ trả về một biến có kiểu trống *, có thể được chuyển sang bất kỳ loại con trỏ mong muốn nào.

Chức năng - calloc cũng dành cho việc cấp phát bộ nhớ. Mục bên dưới có nghĩa là nó sẽ được đánh dấu số các yếu tố bởi kích cỡ byte.

void *calloc(nime, size);

Hàm này trả về một con trỏ tới vùng đã chọn hoặc VÔ GIÁ TRỊ khi không thể cấp phát bộ nhớ. Tính năng đặc biệt của hàm là đặt lại tất cả các phần tử đã chọn về 0.

Chức năng phân bổ lạithay đổi kích thước bộ nhớ được phân bổ trước đó. Họ xưng hô với cô ấy như thế này:

char *realloc (void *p, kích thước);

Đây P- con trỏ tới vùng nhớ cần thay đổi kích thước thành kích cỡ. Nếu địa chỉ của vùng bộ nhớ thay đổi do hàm này thì kết quả là địa chỉ mới sẽ được trả về. Nếu giá trị thực của tham số đầu tiên VÔ GIÁ TRỊ, thì hàm phân bổ lại hoạt động tương tự như chức năng malloc, tức là nó cấp phát một vùng nhớ có kích thước kích cỡ byte.

Để giải phóng bộ nhớ được phân bổ, hãy sử dụng chức năng miễn phí. Họ xưng hô với cô ấy như thế này:

void free(void *p size);

Đây P- con trỏ tới vị trí bộ nhớ được cấp phát trước đó bởi các hàm malloc, calloc hoặc phân bổ lại.

Toán tử mớixóa bỏ chức năng tương tự mallocmiễn phí. Mới phân bổ bộ nhớ và đối số duy nhất của nó là một biểu thức chỉ định số byte được dành riêng. Toán tử trả về một con trỏ về đầu khối bộ nhớ được phân bổ. Nhà điều hành xóa bỏ giải phóng bộ nhớ, đối số của nó là địa chỉ của ô đầu tiên của khối cần được giải phóng.

Mảng động- một mảng có độ dài thay đổi, bộ nhớ được phân bổ trong quá trình thực hiện chương trình. Việc cấp phát bộ nhớ được thực hiện bởi các hàm calloc, malloc hoặc toán tử mới. Địa chỉ của phần tử đầu tiên của vị trí bộ nhớ được cấp phát được lưu trữ trong một biến được khai báo dưới dạng con trỏ. Ví dụ: câu lệnh sau có nghĩa là một con trỏ được mô tả mẹ và nó được gán địa chỉ bắt đầu của vùng bộ nhớ động liền kề được phân bổ bằng toán tử mới:

int *mas=int mới;

Lượng bộ nhớ được phân bổ đủ để lưu trữ 10 giá trị int.

Trên thực tế, trong biến mẹĐịa chỉ của phần tử 0 của mảng động được lưu trữ. Do đó, địa chỉ của phần tử tiếp theo, phần tử đầu tiên trong vùng bộ nhớ được cấp phát là mẹ+1, một mẹ+i là địa chỉ của phần tử thứ i. Phần tử thứ i của mảng động có thể được truy cập như bình thường bằng mas[i] hoặc theo cách khác *(mẹ + tôi) . Điều quan trọng là phải đảm bảo rằng bạn không vượt quá giới hạn của vùng bộ nhớ được phân bổ.

Khi mảng động (bất kỳ lúc nào trong quá trình vận hành chương trình) không còn cần thiết nữa, bộ nhớ có thể được giải phóng bằng hàm miễn phí hoặc toán tử xóa bỏ.

Tôi đề nghị xem xét một số nhiệm vụ củng cố bài học này:

Vấn đề 1

Tìm tổng các phần tử thực của mảng động.

// Ví dụ về cách sử dụng hàm malloc và hàm miễn phí #include "stdafx.h" #include sử dụng không gian tên std; int main() ( setlocale(LC_ALL,"Rus"); int i, n; float *a; // con trỏ tới float float s; cout<<"\n"; cin>>n; // nhập kích thước mảng // cấp phát bộ nhớ cho mảng gồm n phần tử thực a=(float *)malloc(n*sizeof(float)); cout<<"Введите массив A \n"; //ввод элементов массива for (i=0; i>*(a+i); ) // tính tổng các phần tử mảng cho (s=0, i=0; i

// Ví dụ sử dụng hàm malloc và hàm free

#include "stdafx.h"

#bao gồm

sử dụng không gian tên std ;

int chính()

int tôi, n;

nổi * a ; // con trỏ để nổi

thả nổi ;

cout<< "\n" ; cin >> n ; //nhập kích thước mảng

// cấp phát bộ nhớ cho mảng gồm n phần tử thực

a = (float * ) malloc (n * sizeof (float ) ) ;

cout<< "Nhập mảng A\n";

// nhập các phần tử mảng

với (i = 0 ; tôi< n ; i ++ )

cin >> * (a + i) ;

// tích lũy tổng các phần tử mảng

với (s = 0 , i = 0 ; i< n ; i ++ )

s += * (a + i) ;

//xuất giá trị số tiền

cout<< "S=" << s << "\n" ;

//giải phóng bộ nhớ

Miễn phí một);

hệ thống ("tạm dừng");

trả về 0;

Vấn đề 2

Sửa đổi một mảng số nguyên động để các phần tử dương của nó trở thành âm và ngược lại. Để giải bài toán, chúng ta sẽ nhân từng phần tử với -1.

//Ví dụ về sử dụng toán tử mới và xóa #include "stdafx.h" #include sử dụng không gian tên std; int main() ( setlocale(LC_ALL,"Rus"); int i, n; //nhập số phần tử mảng cout<<"n="; cin>>n; // cấp phát bộ nhớ int *a=new int[n]; cout<<"Введите элементы массива:\n"; //ввод массива for (i=0; i>a[i]; //xuất mảng đã chỉ định cho (i=0; i

//Ví dụ về sử dụng toán tử mới và xóa

#include "stdafx.h"

#bao gồm

sử dụng không gian tên std ;

int chính()

setlocale(LC_ALL, "Rus");

int tôi, n;

//nhập số phần tử mảng

cout<< "n=" ; cin >> n ;

//cấp phát bộ nhớ

int * a = int mới [ n ] ;

cout<< "Nhập phần tử mảng:\n";

// mảng đầu vào

Trong khi thu thập thông tin để viết bài này, tôi nhớ lại lần đầu tiên làm quen với con trỏ - tôi rất buồn... Vì vậy, sau khi đọc một số phần về chủ đề này từ nhiều cuốn sách khác nhau về lập trình C++, tôi đã quyết định đi một con đường khác và trình bày chủ đề về Con trỏ C++ theo thứ tự mà tôi thấy cần thiết. Tôi sẽ cung cấp cho bạn một định nghĩa ngắn gọn ngay lập tức và chúng ta sẽ xem xét hoạt động của con trỏ bằng các ví dụ. Bài tiếp theo() sẽ nêu các sắc thái, cách sử dụng con trỏ với chuỗi kiểu C (mảng ký tự) và những điều chính cần nhớ.

Con trỏ trong C++ là một biến lưu trữ địa chỉ của dữ liệu (giá trị) trong bộ nhớ chứ không phải chính dữ liệu đó.

Sau khi xem các ví dụ sau, bạn sẽ hiểu điều chính - tại sao chúng ta cần con trỏ trong lập trình, cách khai báo và sử dụng chúng.

Giả sử trong một chương trình, chúng ta cần tạo một mảng số nguyên, kích thước chính xác mà chúng ta không biết trước khi chương trình bắt đầu. Tức là chúng ta không biết người dùng sẽ cần nhập bao nhiêu số vào mảng này. Tất nhiên, chúng ta có thể chơi an toàn và khai báo một mảng gồm vài nghìn phần tử (ví dụ: 5.000). Điều này (theo ý kiến ​​chủ quan của chúng tôi) là đủ để người dùng làm việc. Vâng – thực sự – điều này có thể là đủ. Nhưng đừng quên rằng mảng này sẽ chiếm nhiều dung lượng trong RAM (5.000 * 4 (kiểu int) = 20.000 byte). Chúng tôi đã tự bảo mật và người dùng sẽ chỉ điền 10 phần tử vào mảng của chúng tôi. Hóa ra thực tế có 40 byte đang được sử dụng và 19.960 byte đang gây lãng phí bộ nhớ.

sử dụng RAM không hợp lý

#bao gồm sử dụng không gian tên std; int main() ( setlocale(LC_ALL, "rus"); const int SizeOfArray = 5000; int arrWithDigits = (); cout<< "Массив занял в памяти " << sizeof(arrWithDigits) << " байт" << endl; int amount = 0; cout << "Сколько чисел вы введёте в массив? "; cin >> số tiền; cout<< "Реально необходимо " << amount * sizeof(int) << " байт" << endl; for (int i = 0; i < amount; i++) { cout << i + 1 << "-е число: "; cin >> ArrayWithDigits[i]; ) cout<< endl; for (int i = 0; i < amount; i++) { cout << arrWithDigits[i] << " "; } cout << endl; return 0; }

#bao gồm

sử dụng không gian tên std ;

int chính()

const int SizeOfArray = 5000;

int ArrayWithDigits [ SizeOfArray ] = ( ) ;

cout<< "Mảng chiếm trong bộ nhớ"<< sizeof (arrWithDigits ) << " байт" << endl ;

số tiền int = 0;

cout<< "Bạn sẽ nhập bao nhiêu số vào mảng?";

cin >> số tiền ;

cout<< "Vô cùng cần thiết"<< amount * sizeof (int ) << " байт" << endl ;

vì (int i = 0; tôi< amount ; i ++ )

cout<< i + 1 << "-е число: " ;

cin >> arrWithDigits [ i ] ;

cout<< endl ;

vì (int i = 0; tôi< amount ; i ++ )

cout<< arrWithDigits [ i ] << " " ;

cout<< endl ;

trả về 0;

Đến một chức năng thư viện tiêu chuẩn kích thước() truyền mảng đã khai báo mảngWithDigits dòng 10. Nó sẽ quay trở lại vị trí cuộc gọi với kích thước tính bằng byte mà mảng này chiếm trong bộ nhớ. Đối với câu hỏi “Bạn sẽ nhập vào mảng bao nhiêu số?” câu trả lời là 10. Ở dòng 15, biểu thức số tiền * sizeof(int) sẽ trở thành tương đương với 10 * 4, vì hàm kích thước(int) sẽ trả về 4 (kích thước tính bằng byte kiểu int). Tiếp theo, nhập số từ bàn phím và chương trình sẽ hiển thị chúng trên màn hình. Hóa ra 4990 phần tử còn lại sẽ lưu trữ số không. Vì vậy, không có ích gì khi cho họ xem.

Thông tin chính trên màn hình: mảng chiếm 20.000 byte, nhưng thực tế nó cần 40 byte. Làm thế nào để thoát khỏi tình trạng này? Ai đó có thể muốn viết lại chương trình để người dùng nhập kích thước của mảng từ bàn phím và sau khi nhập giá trị, khai báo một mảng có số phần tử theo yêu cầu. Nhưng điều này không thể thực hiện được nếu không có con trỏ. Như bạn nhớ, kích thước của mảng phải là một hằng số. Nghĩa là, một hằng số nguyên phải được khởi tạo trước khi khai báo mảng và chúng ta không thể nhắc nhập dữ liệu từ bàn phím. Hãy thử nghiệm và kiểm tra.


Ở đây người điều hành bật đèn đỏ cho chúng tôi >> vì giá trị không đổi không thể thay đổi được.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~


Ở đây chúng tôi được cảnh báo rằng kích thước của mảng không thể là giá trị của một biến thông thường. Cần có giá trị không đổi!

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Trong đoạn mã sau, chúng tôi sẽ sử dụng một con trỏ và các toán tử mới đối với bạn mới(cấp phát bộ nhớ) và xóa bỏ(giải phóng bộ nhớ).

sử dụng RAM thông minh bằng con trỏ

#bao gồm #bao gồm sử dụng không gian tên std; int main() ( setlocale(LC_ALL, "rus"); int sizeOfArray = 0; // kích thước mảng (do người dùng nhập) cout<< "Чтобы создать массив чисел, введите его размер: "; cin >> sizeOfArray; // CHÚ Ý! int* arrWithDigits - khai báo một con trỏ // tới một phần bộ nhớ sẽ được phân bổ new int* arrWithDigits = new int ; vì (int i = 0; tôi< sizeOfArray; i++) { arrWithDigits[i] = i + 1; cout << arrWithDigits[i] << " "; } cout << endl; delete arrWithDigits; // освобождение памяти return 0; }

#bao gồm

#bao gồm

sử dụng không gian tên std ;

int chính()

setlocale(LC_ALL, "rus");

int sizeOfArray = 0; // kích thước mảng (do người dùng nhập)

cout<< "Để tạo một dãy số, hãy nhập kích thước của nó: ";

cin >> sizeOfArray ;

// CHÚ Ý! int* ArrayWithDigits - khai báo con trỏ

// tới một phần bộ nhớ sẽ được cấp phát bởi new

int * ArrayWithDigits = int mới [ sizeOfArray ] ;

vì (int i = 0; tôi< sizeOfArray ; i ++ )

ArrayWithDigits[i] = i+1;

cout<< arrWithDigits [ i ] << " " ;

cout<< endl ;

xóa ArrayWithDigits; // giải phóng bộ nhớ

trả về 0;

Người dùng nhập một giá trị từ bàn phím - dòng 12. Con trỏ được xác định bên dưới: int * ArrayWithDigits Mục này có nghĩa là mảngWithDigits là một con trỏ. Nó được tạo để lưu trữ địa chỉ của ô chứa số nguyên. Trong trường hợp của chúng ta mảngWithDigits sẽ trỏ đến ô mảng có chỉ số 0. Dấu hiệu * - giống như được sử dụng để nhân. Dựa vào ngữ cảnh, trình biên dịch sẽ “hiểu” rằng đây là khai báo con trỏ chứ không phải phép nhân. Tiếp theo là dấu hiệu = và toán tử mới, phân bổ một phần bộ nhớ. Chúng tôi nhớ rằng bộ nhớ của chúng tôi phải được phân bổ cho một mảng chứ không phải cho một số duy nhất. Ghi int mới [sizeOfArray] có thể được giải mã như thế này: mới(cấp phát bộ nhớ) int(để lưu trữ số nguyên) (về số lượng kích thướcOfArray ).

Như vậy, ở dòng 16 nó đã được định nghĩa mảng động. Điều này có nghĩa là bộ nhớ dành cho nó sẽ được cấp phát (hoặc không được cấp phát) trong khi chương trình đang chạy chứ không phải trong quá trình biên dịch, như xảy ra với mảng thông thường. Nghĩa là, việc phân bổ bộ nhớ phụ thuộc vào sự phát triển của chương trình và các quyết định được đưa ra trực tiếp trong hoạt động của chương trình. Trong trường hợp của chúng tôi, nó phụ thuộc vào những gì người dùng nhập vào biến kích thướcOfArray

Dòng 25 sử dụng toán tử xóa bỏ. Nó giải phóng toán tử được phân bổ mới ký ức. Bởi vì mới bộ nhớ được phân bổ để lưu trữ mảng, sau đó khi giải phóng nó, bạn cần nói rõ với trình biên dịch rằng cần phải giải phóng bộ nhớ của mảng chứ không chỉ ô 0 của nó, được trỏ đến mảngWithDigits. Vì vậy, giữa xóa bỏ và tên con trỏ được đặt trong dấu ngoặc vuông xóa ArrayWithDigits; Hãy nhớ rằng mỗi khi bộ nhớ được cấp phát bằng cách sử dụng mới, bạn cần giải phóng bộ nhớ này bằng cách sử dụng xóa bỏ. Tất nhiên, khi chương trình kết thúc, bộ nhớ mà nó chiếm giữ sẽ tự động được giải phóng. Nhưng hãy tạo thói quen tốt khi sử dụng các toán tử mớixóa bỏ kết hợp với. Rốt cuộc, một chương trình có thể chứa 5-6 mảng chẳng hạn. Và nếu bạn giải phóng bộ nhớ mỗi khi nó không còn cần thiết trong tương lai trong một chương trình đang chạy, bộ nhớ sẽ được sử dụng một cách khôn ngoan hơn.

Giả sử trong chương trình của chúng ta, chúng ta đã điền vào một mảng có mười giá trị. Tiếp theo, chúng tôi tính tổng của chúng và ghi nó vào một biến nào đó. Và thế là xong – chúng ta sẽ không làm việc với mảng này nữa. Chương trình tiếp tục hoạt động và các mảng động mới được tạo trong đó cho một số mục đích. Trong trường hợp này, nên giải phóng bộ nhớ do mảng đầu tiên chiếm giữ. Sau đó, khi cấp phát bộ nhớ cho các mảng khác, bộ nhớ này có thể được tái sử dụng trong chương trình.

Hãy xem việc sử dụng con trỏ làm tham số của hàm. Để bắt đầu, hãy nhập và biên dịch đoạn mã sau. Trong đó, hàm nhận hai biến và đề nghị thay đổi giá trị của chúng.

cố gắng thay đổi các biến được truyền cho một hàm

#bao gồm #bao gồm sử dụng không gian tên std; void ChangeData(int varForCh1, int varForCh2); int main() ( setlocale(LC_ALL, "rus"); int biếnForChange_1 = 0; int biếnForChange_2 = 0; cout<< "variableForChange_1 = " << variableForChange_1 << endl; cout << "variableForChange_2 = " << variableForChange_2 << endl; cout << endl; changeData(variableForChange_1, variableForChange_2); cout << endl; cout << "variableForChange_1 = " << variableForChange_1 << endl; cout << "variableForChange_2 = " << variableForChange_2 << endl; return 0; } void changeData(int varForCh1, int varForCh2) { cout << "Введите новое значение первой переменной: "; cin >> varForCh1; cout<< "Введите новое значение второй переменной: "; cin >> varForCh2; )

#bao gồm

#bao gồm

sử dụng không gian tên std ;

void ChangeData(int varForCh1, int varForCh2);

int chính()

setlocale(LC_ALL, "rus");

int biếnForChange_1 = 0 ;

int biếnForChange_2 = 0 ;

cout<< "variableForChange_1 = " << variableForChange_1 << endl ;

cout<< "variableForChange_2 = " << variableForChange_2 << endl ;

cout<< endl ;

ChangeData(variableForChange_1, VariableForChange_2);

cout<< endl ;

cout<< "variableForChange_1 = " << variableForChange_1 << endl ;

cout<< "variableForChange_2 = " << variableForChange_2 << endl ;

trả về 0;

void ChangeData(int varForCh1, int varForCh2)

cout<< "Nhập giá trị mới cho biến đầu tiên: ";

cin >> varForCh1 ;

cout<< "Nhập giá trị mới cho biến thứ hai: ";

cin >> varForCh2 ;

Chạy chương trình và nhập các giá trị biến mới. Kết quả là bạn sẽ thấy rằng sau khi hoàn thành hàm, các biến không thay đổi và bằng 0.

Như bạn nhớ, hàm này không hoạt động trực tiếp với các biến mà tạo ra các bản sao chính xác của chúng. Những bản sao này sẽ bị hủy sau khi thoát khỏi chức năng. Nghĩa là, hàm nhận một số biến làm tham số, tạo một bản sao của biến đó, làm việc với biến đó và hủy biến đó. Bản thân biến sẽ không thay đổi.

Sử dụng con trỏ, chúng ta có thể truyền địa chỉ biến cho hàm. Khi đó hàm sẽ có thể hoạt động trực tiếp với dữ liệu biến tại địa chỉ. Hãy thực hiện những thay đổi đối với chương trình trước đó.

thay đổi giá trị biến bằng con trỏ

#bao gồm #bao gồm sử dụng không gian tên std; void ChangeData(int* varForCh1, int* varForCh2); int main() ( setlocale(LC_ALL, "rus"); int biếnForChange_1 = 0; int biếnForChange_2 = 0; cout<< "variableForChange_1 = " << variableForChange_1 << endl; cout << "variableForChange_2 = " << variableForChange_2 << endl; cout << endl; changeData(&variableForChange_1, &variableForChange_2); cout << endl; cout << "variableForChange_1 = " << variableForChange_1 << endl; cout << "variableForChange_2 = " << variableForChange_2 << endl; return 0; } void changeData(int* varForCh1, int* varForCh2) { cout << "Введите новое значение первой переменной: "; cin >> *varForCh1; cout<< "Введите новое значение второй переменной: "; cin >> *varForCh2; )

Mục đích của bài giảng: nghiên cứu khai báo, cấp phát và giải phóng bộ nhớ cho mảng động một chiều, truy cập các phần tử, học cách giải các bài toán sử dụng mảng động một chiều trong ngôn ngữ C++.

Khi sử dụng nhiều cấu trúc dữ liệu, điều thường xảy ra là chúng phải có kích thước thay đổi tùy theo kích thước của cấu trúc dữ liệu. thời gian dẫn các chương trình. Trong những trường hợp này cần phải sử dụng cấp phát bộ nhớ động. Một trong những cấu trúc dữ liệu phổ biến nhất là mảng, trong đó kích thước ban đầu không được xác định hoặc cố định.

Dựa theo chuẩn ngôn ngữ Mảng là một tập hợp các phần tử, mỗi phần tử có cùng thuộc tính. Tất cả các phần tử này được đặt ở các vị trí bộ nhớ liền kề trong một hàng, bắt đầu từ địa chỉ tương ứng với phần đầu của mảng. Nghĩa là, tổng số phần tử của mảng và kích thước của bộ nhớ được phân bổ cho nó được xác định đầy đủ và duy nhất bởi định nghĩa của mảng. Nhưng điều này không phải lúc nào cũng thuận tiện. Đôi khi, yêu cầu bộ nhớ được phân bổ cho một mảng phải có kích thước để giải quyết một vấn đề cụ thể và kích thước của nó không được biết trước và không thể sửa được. Việc hình thành các mảng có kích thước thay đổi (mảng động) có thể được tổ chức bằng cách sử dụng con trỏ và công cụ cấp phát bộ nhớ động.

Mảng động là một mảng có kích thước không cố định trước và có thể thay đổi trong quá trình thực hiện chương trình. Để thay đổi kích thước mảng động ngôn ngữ lập trình C++, hỗ trợ các mảng như vậy, cung cấp các tính năng đặc biệt Chức năng tích hợp sẵn hoặc các hoạt động. Mảng động mang đến cơ hội làm việc linh hoạt hơn với dữ liệu, vì chúng cho phép bạn không dự đoán khối lượng dữ liệu được lưu trữ mà có thể điều chỉnh kích thước của mảng phù hợp với khối lượng thực sự cần thiết.

Khai báo mảng động một chiều

Theo khai báo một chiều mảng động hiểu cách khai báo một con trỏ tới một biến có kiểu cho trước để biến này có thể được sử dụng như mảng động.

Cú pháp:

Nhập *Tên mảng;

Type – loại phần tử được khai báo mảng động. Yếu tố mảng động Các hàm và phần tử không được có kiểu void.

Ví dụ:

int *a; gấp đôi *d;

Trong các ví dụ này, a và d là các con trỏ tới phần đầu của vị trí bộ nhớ được phân bổ. Con trỏ lấy giá trị địa chỉ của vùng nhớ được cấp phát cho các giá trị kiểu int và kiểu double tương ứng.

Vì vậy, khi cấp phát động bộ nhớ cho mảng động, bạn nên mô tả con trỏ tương ứng sẽ được gán giá trị địa chỉ đầu của vùng bộ nhớ được cấp phát.

Phân bổ bộ nhớ cho mảng động một chiều

Để cấp phát bộ nhớ cho một chiều mảng động Trong C++ có 2 cách.

1) bằng hoạt động new , phân bổ một phần bộ nhớ động có kích thước phù hợp để lưu trữ mảng và không cho phép khởi tạo các phần tử mảng.

Cú pháp:

ArrayName = Loại mới [ConstantTypeExpression];

ArrayName – mã định danh mảng, nghĩa là tên của con trỏ cho khối bộ nhớ được phân bổ.

KiểuBiểu thứcHằng số– thiết lập số phần tử ( chiều) của mảng. Một biểu thức có kiểu không đổi được đánh giá tại thời điểm biên dịch.

Ví dụ:

int *mas; mas = int mới; /*phân bổ bộ nhớ động 100*sizeof(int) byte*/ double *m = new double [n]; /*phân bổ bộ nhớ động n*sizeof(double) byte*/ long (*lm); lm = mới dài; /*phân bổ bộ nhớ động 2*4*sizeof(dài) byte*/

Khi cấp phát bộ nhớ động, kích thước của mảng phải được xác định đầy đủ.

2) sử dụng chức năng thư viện malloc (calloc) , được sử dụng để phân bổ bộ nhớ động.

Cú pháp:

ArrayName = (Loại *) malloc(N*sizeof(Type));

ArrayName = (Loại *) calloc(N, sizeof(Type));

ArrayName – mã định danh mảng, nghĩa là tên của con trỏ cho khối bộ nhớ được phân bổ.

Loại – loại con trỏ tới một mảng.

N – số phần tử mảng.

Ví dụ:

nổi *a; a=(float *)malloc(10*sizeof(float)); // hoặc a=(float *)calloc(10,sizeof(float)); /*phân bổ bộ nhớ động 10*sizeof(float) byte*/

Vì hàm malloc(calloc) trả về con trỏ chưa được gõ void * , thì cần phải chuyển đổi kết quả