英文:
when passing a 3d array to a function, the memory address of the array suddenly change after performing an fget
问题
以下是您要翻译的代码部分:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stddef.h> // for macro NULL
int clear_screen() {
int pid = fork();
if (pid == 0) {
char *newargv[] = { NULL };
char *newenviron[] = { "TERM=screen" };
execve("/usr/bin/clear", newargv, newenviron);
}
int wait_time = 1;
int *ptr_wait_time = &wait_time;
wait(ptr_wait_time);
return 0;
}
void init (char char_array[5][5][11]) {
printf("char_array in init is at %p which points to %p\n", &char_array, *char_array);
printf("char_array[0][0] in init is at %p\n", &char_array[0][0]);
printf("char_array[0][0][0] in init is at %p\n", &char_array[0][0][0]);
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) {
for (int single_char = 0; single_char < 11; single_char++) {
char_array[row][col][single_char] = ' ';
}
char_array[row][col][10] = '\0';
}
}
}
void display (char char_array[5][5][11]) {
printf("\t0\t\t\t\t1\t\t\t\t2\t\t\t\t3\t\t\t\t4\n");
for (int row = 0; row < 5; row++) {
printf("%d", row);
for (int col = 0; col < 5; col++) {
printf("\t%p[%s]", char_array[row][col], char_array[row][col]);
}
printf("\n");
}
}
int check_if_int (char *user_maybe_int) {
long result;
char *endptr;
result = strtol(user_maybe_int, &endptr, 10);
if (user_maybe_int[0] != '\n' && *endptr == '\n' && result > -1 && result < 5) {
return result;
}
else {
return -1;
}
}
char *format_user_input (char *user_input) {
int size_of_input;
int padding_size;
char *formated = malloc(11);
size_of_input = strcspn(user_input, "\n") - 1;
padding_size = 11 - size_of_input;
for (int i = 0; i < size_of_input + 1;i ++) {
formated[i] = user_input[i];
}
for (int i = size_of_input + 1; i < 11;i ++) {
formated[i] = ' ';
}
formated[10] = '\0';
return formated;
}
int insert (char char_array[5][5][11]) {
printf("char_array in insert is at %p which points to %p\n", &char_array, *char_array);
printf("char_array[0][0] in insert is at %p and points to %p\n", &char_array[0][0], char_array[0][0]);
printf("char_array[0][0][0] in insert is at %p\n", &char_array[0][0][0]);
char user_input_row[2];
char user_input_col[2];
printf("did char_array[0][0] changed1? %p points to %p (the answer is : not yet)\n", &char_array[0][0], char_array[0][0]);
int row;
printf("did char_array[0][0] changed2? %p points to %p (the answer is : not yet)\n", &char_array[0][0], char_array[0][0]);
int col;
printf("did char_array[0][0] changed3? %p points to %p (the answer is : not yet)\n", &char_array[0][0], char_array[0][0]);
char user_input[11] = "";
printf("did char_array[0][0] changed4? %p points to %p (the answer is : not yet)\n", &char_array[0][0], char_array[0][0]);
printf("insert in row : ");
printf("did char_array[0][0] changed5? %p points to %p (the answer is : not yet)\n", &char_array[0][0], char_array[0][0]);
fgets(user_input_row, 3, stdin);
printf("did char_array[0][0] changed6? %p points to %p (the answer is : yes, most of the time)\n", &char_array[0][0], char_array[0][0]);
row = check_if_int(user_input_row);
if (row == -1) {
return 1;
}
printf("insert in column : ");
fgets(user_input_col, 3, stdin);
col = check_if_int(user_input_col);
if (col == -1) {
return 1;
}
printf("your input : ");
fgets(user_input, 12, stdin);
char *user_input_formated = format_user_input(user_input);
//actual insert
printf("User try to insert at %p via char_array[%d][%d]\n", &char_array[row][col], row, col);
strncpy(char_array[row][col], user_input_formated, 11);
free(user_input_formated);
return 0;
}
void start (char char_array[5][5][11]) {
//clear_screen();
int insert_res;
// this function display a grid (memory address + content)
display(char_array);
insert_res = insert(char_array);
if (insert_res != 1) {
start(char_array);
}
<details>
<summary>英文:</summary>
as the title mention it : when passing a 3d array to a function, the memory address of the array suddenly change after performing an fget
the issue occurs in the insert function right after the fgets
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stddef.h> // for macro NULL
int clear_screen() {
int pid = fork();
if (pid == 0) {
char *newargv[] = { NULL };
char *newenviron[] = { "TERM=screen" };
execve("/usr/bin/clear", newargv, newenviron);
}
int wait_time = 1;
int *ptr_wait_time = &wait_time;
wait(ptr_wait_time);
return 0;
}
void init (char char_array[5][5][11]) {
printf("char_array in init is at %p which points to %p\n", &char_array, *char_array);
printf("char_array[0][0] in init is at %p\n", &char_array[0][0]);
printf("char_array[0][0][0] in init is at %p\n", &char_array[0][0][0]);
for (int row = 0; row < 5; row++) {
for (int col = 0; col < 5; col++) {
for (int single_char = 0; single_char < 11; single_char++) {
char_array[row][col][single_char] = ' ';
}
char_array[row][col][10] = '\0';
}
}
}
void display (char char_array[5][5][11]) {
printf("\t0\t\t\t\t1\t\t\t\t2\t\t\t\t3\t\t\t\t4\n");
for (int row = 0; row < 5; row++) {
printf("%d", row);
for (int col = 0; col < 5; col++) {
printf("\t%p[%s]", char_array[row][col], char_array[row][col]);
}
printf("\n");
}
}
int check_if_int (char *user_maybe_int) {
long result;
char *endptr;
result = strtol(user_maybe_int, &endptr, 10);
if (user_maybe_int[0] != '\n' && *endptr == '\n' && result > -1 && result < 5) {
return result;
}
else {
return -1;
}
}
char *format_user_input (char *user_input) {
int size_of_input;
int padding_size;
char *formated = malloc(11);
size_of_input = strcspn(user_input, "\n") - 1;
padding_size = 11 - size_of_input;
for (int i = 0; i < size_of_input + 1;i ++) {
formated[i] = user_input[i];
}
for (int i = size_of_input + 1; i < 11;i ++) {
formated[i] = ' ';
}
formated[10] = '\0';
return formated;
}
int insert (char char_array[5][5][11]) {
printf("char_array in insert is at %p which points to %p\n", &char_array, *char_array);
printf("char_array[0][0] in insert is at %p and points to %p\n", &char_array[0][0], char_array[0][0]);
printf("char_array[0][0][0] in insert is at %p\n", &char_array[0][0][0]);
char user_input_row[2];
char user_input_col[2];
printf("did char_array[0][0] changed1? %p points to %p (the answer is : not yet)\n", &char_array[0][0], char_array[0][0]);
int row;
printf("did char_array[0][0] changed2? %p points to %p (the answer is : not yet)\n", &char_array[0][0], char_array[0][0]);
int col;
printf("did char_array[0][0] changed3? %p points to %p (the answer is : not yet)\n", &char_array[0][0], char_array[0][0]);
char user_input[11] = "";
printf("did char_array[0][0] changed4? %p points to %p (the answer is : not yet)\n", &char_array[0][0], char_array[0][0]);
printf("insert in row : ");
printf("did char_array[0][0] changed5? %p points to %p (the answer is : not yet)\n", &char_array[0][0], char_array[0][0]);
fgets(user_input_row, 3, stdin);
printf("did char_array[0][0] changed6? %p points to %p (the answer is : yes, most of the time)\n", &char_array[0][0], char_array[0][0]);
row = check_if_int(user_input_row);
if (row == -1) {
return 1;
}
printf("insert in column : ");
fgets(user_input_col, 3, stdin);
col = check_if_int(user_input_col);
if (col == -1) {
return 1;
}
printf("your input : ");
fgets(user_input, 12, stdin);
char *user_input_formated = format_user_input(user_input);
//actual insert
printf("User try to insert at %p via char_array[%d][%d]\n", &char_array[row][col], row, col);
strncpy(char_array[row][col], user_input_formated, 11);
free(user_input_formated);
return 0;
}
void start (char char_array[5][5][11]) {
//clear_screen();
int insert_res;
// this function display a grid (memory address + content)
display(char_array);
insert_res = insert(char_array);
if (insert_res != 1) {
start(char_array);
}
else {
printf("something went wrong on insert!");
exit(1);
}
}
int main () {
// 3d array
// we define 5 x 5 array of arrays of chars (10 chars + null terminator)
// for a visual reprensentation, 2d arrays give columns, 3d arrays add rows
char char_array[5][5][11];
// this function fills each array of chars with "0000000000\0"
init(char_array);
// start the display/insert recursion
start(char_array);
}
as you can see from the multiples printf, I tried to pinpoint the exact moment where the memory address changed, from my understanding, char_array decays to a pointer when passed to this function and the memory address should not change (obviously, i'm missing something because it does change after performing the 1st fgets)
char_array in insert is at 0x7ffc4e60cd70 which points to 0x7ffc4e60cdb0
char_array[0][0] in insert is at 0x7ffc4e60cdb0 and points to 0x7ffc4e60cdb0
char_array[0][0][0] in insert is at 0x7ffc4e60cdb0
did char_array[0][0] changed1? 0x7ffc4e60cdb0 points to 0x7ffc4e60cdb0 (the answer is : not yet)
did char_array[0][0] changed2? 0x7ffc4e60cdb0 points to 0x7ffc4e60cdb0 (the answer is : not yet)
did char_array[0][0] changed3? 0x7ffc4e60cdb0 points to 0x7ffc4e60cdb0 (the answer is : not yet)
did char_array[0][0] changed4? 0x7ffc4e60cdb0 points to 0x7ffc4e60cdb0 (the answer is : not yet)
insert in row : did char_array[0][0] changed5? 0x7ffc4e60cdb0 points to 0x7ffc4e60cdb0 (the answer is : not yet)
0
did char_array[0][0] changed6? 0x7ffc4e60cd00 points to 0x7ffc4e60cd00 (the answer is : yes, most of the time)
did char_array[0][0] changed7? 0x7ffc4e60cd00 points to 0x7ffc4e60cd00 (the answer is : yes, most of the time)
insert in column : did char_array[0][0] changed8? 0x7ffc4e60cd00 points to 0x7ffc4e60cd00 (the answer is : yes, most of the time)
0
did char_array[0][0] changed9? 0x7ffc4e60cd00 points to 0x7ffc4e60cd00 (the answer is : yes, most of the time)
did char_array[0][0] changed10? 0x7ffc4e60cd00 points to 0x7ffc4e60cd00 (the answer is : yes, most of the time)
your input : a
did char_array[0][0] changed11? 0x7ffc4e60cd00 points to 0x7ffc4e60cd00 (the answer is : yes, most of the time)
User try to insert at 0x7ffc4e60cd00 via char_array[0][0]
</details>
# 答案1
**得分**: 0
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main () {
char too_small_buffer[2];
char ok_buffer[3];
printf("在太小的缓冲区中输入一个字符:");
// 如果输入"a"并按回车键:
// 由于fgets没有足够的空间存储'a' '\n' '\0',从这里开始出现未定义的行为
fgets(too_small_buffer, 3, stdin);
printf("存储在太小缓冲区[0]中的十进制数: %d\n", too_small_buffer[0]);
printf("存储在太小缓冲区[1]中的十进制数: %d\n", too_small_buffer[1]);
printf("在ok缓冲区中输入一个字符:");
// 如果上面没有发生缓冲区溢出,这将正常工作
fgets(ok_buffer, 3, stdin);
printf("存储在ok缓冲区[0]中的十进制数: %d\n", ok_buffer[0]);
printf("存储在ok缓冲区[1]中的十进制数: %d\n", ok_buffer[1]);
printf("存储在ok缓冲区[2]中的十进制数: %d\n", ok_buffer[2]);
}
<details>
<summary>英文:</summary>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main () {
char too_small_buffer[2];
char ok_buffer[3];
printf("enter one char in too small buffer : ");
// if you enter "a" and press enter :
// undefined behavior starts here since fgets
// does not have enough room to store 'a' '\n' '\0'
fgets(too_small_buffer, 3, stdin);
printf("decimal stored in too_small_buffer[0] : %d\n", too_small_buffer[0]);
printf("decimal stored in too_small_buffer[1] : %d\n", too_small_buffer[1]);
printf("enter one char in ok buffer : ");
// this would work fine if there was no buffer overflow above
fgets(ok_buffer, 3, stdin);
printf("decimal stored in ok_buffer[0] : %d\n", ok_buffer[0]);
printf("decimal stored in ok_buffer[1] : %d\n", ok_buffer[1]);
printf("decimal stored in ok_buffer[2] : %d\n", ok_buffer[2]);
}
so fgets reads n-1 (`"a\n"`) and try to add `'\0'` but does not have enough space.
credit to @weather-vane in the comments above who have spotted the issue immediately.
It seems to work fine for this program (the sample program in this answer), but it caused an issue in the initial program.
enter one char in too small buffer : a
decimal stored in too_small_buffer[0] : 97
decimal stored in too_small_buffer[1] : 10
enter one char in ok buffer : a
decimal stored in ok_buffer[0] : 97
decimal stored in ok_buffer[1] : 10
decimal stored in ok_buffer[2] : 0
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论