Выделение памяти в C malloc()

#c #memory #segmentation-fault #malloc

#c #память #ошибка сегментации #malloc

Вопрос:

я немного смущен поведением этого кода,

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

char* foo(){
   char* arr = (char*)malloc(sizeof(char)*100);
   return arr;
}

void foo1(char* myArr){
 char* arr = (char*)malloc(sizeof(char)*100);
 myArr = arr;
}

int main(){
  char* myArr;
  foo1(myArr);
  myArr[23] = 'a';
  printf("%c", myArr[23]);
  return (0);
  

}

Когда я вызываю myArr = foo() , это работает просто отлично, и значение адреса выделенной памяти будет присвоено myArr , и у меня есть выделенный сегмент памяти без ошибок. С другой стороны, когда я вызываю функцию, foo1(myArr) она должна передать myArr функции, это указатель на сегмент, поэтому любые изменения с этими указателями повлияют на сегмент памяти (например, если я хочу поменять местами два строковых значения, я могу просто поменять указатели местами), но я получу segmentation fault error , почему? потому что myArr не является myArr в main функции? это правда?!!

Комментарии:

1. Прочитайте современный C и документацию вашего компилятора C (возможно, GCC …) и отладчика (возможно, GDB )…. Изучите для вдохновения исходный код существующих проектов с открытым исходным кодом на github или gitlab . Вы многому научитесь, внеся свой вклад в некоторые из них. Wirth GCC, скомпилируйте со всеми предупреждениями и отладочной информацией, так что gcc -Wall -Wextra -g

2. Если вы используете реальный компилятор, у него есть функция статической проверки кода. clang -analyze.. Но вы могли бы также просто заметить, что вашему аргументу не хватает одного уровня косвенности, чтобы ваши материалы работали.

3. Конечно, помните о стеке вызовов , но помните, что в некоторых случаях оптимизирующие компиляторы могут помещать автоматическую переменную в некоторый регистр процессора. Для получения дополнительной информации прочитайте этот черновик отчета

4. @Basile Starynkevitch, Прочитав некоторые части этой книги, которые вы упомянули (Современный C), я нашел это действительно интересным, и это очень помогло моим знаниям о языке программирования C. Я хочу по-настоящему поблагодарить вас за представление этой книги.

5. @AliNaderiParizi: найдите время, чтобы прочитать этот черновик , и, если вас это заинтересует, свяжитесь со мной по электронной почте, чтобы basile@starynkevitch.net

Ответ №1:

Вы вызываете две разные переменные myArr , что, вероятно, сбивает вас с толку. Изменения в myArr in foo1 не влияют на myArr in main только потому, что они имеют одинаковое имя. Вы передаете значение из myArr in main в foo1 , но ничего не возвращается.

 void foo1(char* myArr){
 char* arr = (char*)malloc(sizeof(char)*100);
 myArr = arr;
}
  

Концептуально это ничем не отличается от:

 void foo1(int j) {
    int q = 2;
    j = q;
}
  

Так же, как foo1(3) это каким-то образом не превратило бы это 3 в 2 , foo1(myArr) не изменяет значение myArr . Это myArr указатель, не изменяющий его семантику — C передается по значению, точка. Таким образом, функции передается только значение myArr .

   char* myArr;
  foo1(myArr);
  

Обратите внимание, что здесь myArr не присваивается значение, поэтому вы передаете foo1 значение указателя ни на что конкретное. С этим значением не foo1 можно сделать ничего полезного.

Вы, вероятно, хотите:

 void foo1(char** myArrPtr){
 char* arr = (char*)malloc(sizeof(char)*100);
 *myArrPtr = arr;
}
  

и

  char* myArr;
 foo1(amp;myArr);
  

Таким образом, функция получает указатель на myArr , а затем изменяет объект, на который указывает указатель, который является myArr in main .

Комментарии:

1. итак, если я хочу исправить это, я должен изменить void foo1(char* myArr) на void foo1(char** myArr) и в функции main, я должен вызвать foo1 вот так foo1(amp;myArr) , и это решило бы проблему. Спасибо.

2. @AliNaderiParizi Это правильно. Таким образом, вы передаете указатель на myArr , а не на его (неопределенное и бессмысленное) значение.

3. И память, выделенная в функции, остается там и никогда не будет освобождена. это memory lack ?

4. @AliNaderiParizi В этом случае вызывающий foo1 обычно несет ответственность за free использование памяти после завершения работы с ней. Но этот код предназначен только для того, чтобы показать, как правильно использовать указатель, это нереалистичный код.

Ответ №2:

Аргументы в C всегда вызываются по значению — аргумент получает копию того, что передает вызывающий объект, и является новой переменной, поэтому, если функция изменяет значение аргумента, это не влияет на значение того, что было передано. Итак, когда вы вызываете

 foo1(myArr);
  

значение myArr не изменится, даже если foo1 присвоит ему другое значение.