Странное поведение в коде C под gcc 4.4.3 - PullRequest
2 голосов
/ 03 декабря 2010

Я наткнулся на этот фрагмент кода сегодня, когда обучал некоторых студентов курсам языка Си.В упражнении предлагается реализовать две функции.Первый сканирует ввод от пользователя, а второй отображает то, что было отсканировано ранее.Код, с которым я столкнулся, выглядит следующим образом:


#include <stdio.h>

void myInput(int i,int n)
{
  int cpt;
  int tab[n];   

  for ( cpt=0; cpt<n; cpt++)
  {
    printf("Enter a number :");
    scanf("%d",&i); 
    tab[cpt]=i;
   }
 }



void myDisp (int n)
{
  int tab[n];      
  int cpt;

  for ( cpt=0; cpt <n; cpt++)
  {
    printf("%d ", tab[cpt]); 
  } 
}

int main()
{
  int n; int i;
  printf(" Entrer the numbers of elements you want: \n");
  scanf("%d \n",&n);
  int tab[n];
  myInput(i,n);         
  myDisp(n);
}

Хотя этот код полон несоответствий, он фактически работает в gcc 4.4.3: он отображает числа, которые были введены!!!!!!Кто-нибудь понимает, как получается, что этот код работает?

Большое спасибо

Ответы [ 5 ]

8 голосов
/ 03 декабря 2010

Если это сработает, это просто тупая удача. То, что напечатано в myDisp, является неинициализированным стеком, который может содержать или не содержать данные, которые были помещены в переменные с одинаковыми именами в myInput. Связанное чтение

Вот простой способ разбить его с помощью кода, ничего не делающего:

void myInput(int i,int n)
{
  // Add some variables to mess up the stack positioning.
  int breaker;
  int cpt;
  int stomper;
  int tab[n];
  int smasher;

  for ( cpt=0; cpt<n; cpt++)
  {
    printf("Enter a number :");
    scanf("%d",&i); 
    tab[cpt]=i;
   }

  // Trick the compiler into thinking these variables do something.
  breaker = 1;
  smasher = 3 * breaker;
  stomper = smasher + breaker;
  breaker = stomper * smasher;
 }

Еще один способ прервать его - поместить вызов функции (скажем, на printf) между вызовами myInput и myDisp.

1 голос
/ 03 декабря 2010

Это не работает, по крайней мере, не всегда.Конечно, у меня есть gcc 4.4.4, а не 4.4.3

$ ./a.out
 Entrer the numbers of elements you want:
5
2
Enter a number :Enter a number :4
Enter a number :1
Enter a number :2
Enter a number :3
2 4 1 134514562 3

Мораль этой истории - когда вы обращаетесь к неинициализированной памяти, может произойти все что угодно, включая внешний вид работы.

0 голосов
/ 03 декабря 2010

Похоже, что программа обращается к одной и той же ячейке памяти для каждого объявленного вами массива int tab[n], но, как уже упоминалось, она не должна работать.

Но я думаю, что здесь происходит что-то вроде: вы размещаете tab [] внутри main (), скажем, по адресу 0x00000001 (возьмем это только в качестве примера). Массив имеет n целых чисел, но не имеет значений вообще.

Затем вы заходите в myInput (), снова объявляете массив (того же размера), в другом адресе, например 0x001F0000, и затем устанавливаете значения одно за другим.

Итак, когда функция завершается, она освобождает выделенную память от своих переменных, поэтому ваш массив больше не существует.

Но подождите, это C, поэтому, когда вы освобождаете память, вы только сообщаете куче (или распределителю памяти, в общем), что адреса могут использоваться снова. Вы НЕ точно удаляете значения из памяти.

Затем вы вызываете myDisp () и снова объявляете ваш массив. Похоже, что память, которую вы только что запросили, имеет более высокий приоритет, а затем она снова отдается вашей программе. Таким образом, ваш массив снова создается и по тому же адресу.

Таким образом, даже если вы не заполнили его значениями, память читается (как это всегда допустимо в C), и значения все еще там.

О, а массив объявлен внутри main ()? С этим ничего не происходит. Попробуйте напечатать его значения, и я уверен, что у вас не будет правильных значений.

Это мое предположение.

РЕДАКТИРОВАТЬ: просто чтобы увидеть, что происходит: попробуйте объявить другой массив после табуляции (не переименовывать табуляцию), скажем, tab2, той же длины, и используйте его, чтобы поместить ваши значения вместо табуляции, затем позвольте программе снова работать :)

0 голосов
/ 03 декабря 2010

Вероятно, это работает, потому что область памяти локального tab до myInput e myDisp оказывается (почти?) Одинаковой.

Мне это не кажется странным: myInput и myDisp имеют почти одну и ту же сигнатуру (они отличаются только одним параметром int); даже в худшем случае местоположения в стеке, обозначенные tab в двух функциях, все равно будут правильно выровнены и смещены максимум на два int s (i и cpt в myInput).

0 голосов
/ 03 декабря 2010

Поскольку два массива полностью разделены, он действительно не должен работать. Если это так, то только потому, что они оказались в одном месте в памяти.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...