несколько вопросов для начинающих C - PullRequest
1 голос
/ 11 сентября 2009

Я как бы изучаю C, я не новичок в программировании, хотя я "знаю" Java и python, и, кстати, я на Mac (леопард). Во-первых,

1 : может ли кто-нибудь объяснить , когда использовать указатель, а когда нет?

2

char *fun = malloc(sizeof(char) * 4);

или

char fun[4];

или

char *fun = "fun";

И тогда все, кроме последнего, установят индексы 0, 1, 2 и 3 равными 'f', 'u', 'n' и '\ 0' соответственно. Мой вопрос: почему второй не указатель? Почему char fun [4], а не char * fun [4]? И почему получается, что указатель на struct или int всегда является массивом?

3 : Я понимаю это:

typedef struct car
{
    ...
};

является ярлыком для

struct car
{
    ...
};
typedef struct car car;

Правильно? Но что-то, что меня действительно смущает:

typedef struct A
{
    ...
}B;

В чем разница между A и B? А это «имя тега», но что это? Когда я использую что? То же самое для перечислений.

4 . Я понимаю, что делают указатели, но я не понимаю, в чем их смысл (не каламбур). И когда что-то выделяется в стеке по сравнению с кучей? Как я узнаю, где он выделен? Указатели как-то с этим связаны?

5 . И, наконец, знаете какой-нибудь хороший учебник по программированию на C (простой)? А для mac / OS X, а не для windows?

PS. Есть ли какое-либо другое имя, которое люди используют для обозначения только C, а не C ++? Я ненавижу, что все они названы почти одинаково, так что трудно пытаться гуглить специально C, а не просто получать C ++ и C #.

Спасибо !!

Трудно было выбрать лучший ответ, все они были великолепны, но тот, который я выбрал, был единственным, который заставил меня понять мой 3-й вопрос, который был единственным, который я первоначально собирался задать. Еще раз спасибо!

Ответы [ 6 ]

4 голосов
/ 11 сентября 2009

Мой вопрос: почему второй не указатель?

Потому что он объявляет массив. В двух других случаях у вас есть указатель, который ссылается на данные, которые живут где-то еще. Однако объявление вашего массива объявляет массив данных, который живет там, где он объявлен. Если вы объявили это внутри функции, то данные умрут, когда вы вернетесь из этой функции. Наконец, char *fun[4] будет массивом 4 указателей - это не будет указатель на символ. В случае, если вы просто хотите указать на блок из 4 символов, то вполне достаточно char*, не нужно указывать, что есть ровно 4 символов, на которые нужно указать.

Первый способ, который создает объект в куче, используется, если вам нужны данные, чтобы жить от них до соответствующего вызова free. Данные переживут возврат из функции.

Последний способ просто создает данные, которые не предназначены для записи. Это указатель, который ссылается на строковый литерал - он часто хранится в постоянной памяти. Если вы напишите в него, то поведение не определено.

Я понимаю, что делают указатели, но я не понимаю, в чем их смысл (не каламбур).

Указатели используются, чтобы указывать на что-то (конечно, не каламбур). Посмотрите на это так: если у вас есть ряд предметов на столе, и ваш друг говорит «выберите второй предмет», то этот предмет волшебным образом не дойдет до вас. Вы должны схватить это. Ваша рука действует как указатель, и когда вы возвращаете руку обратно к вам, вы разыменовываете этот указатель и получаете предмет. Ряд элементов можно рассматривать как массив элементов:

И почему получается, что указатель на struct или int всегда является массивом?

item row[5];

Когда вы делаете item i = row[1];, вы сначала указываете рукой на первый предмет (получаете указатель на первый), а затем продвигаетесь, пока не окажетесь у второго предмета. Затем вы берете руку с элементом обратно к себе :) Итак, синтаксис row[1] не является чем-то особенным для массивов, а скорее является особенным для указателей - он эквивалентен *(row + 1), и при использовании создается временный указатель такой массив.

В чем разница между A и B? А это «имя тега», но что это? Когда я использую что? То же самое для перечислений.

typedef struct car
{
    ...
};

Это недействительный код. Вы в основном сказали: «определите тип struct car { ... } для ссылки по следующему обычному идентификатору», но вы упустили возможность сообщить ему идентификатор. Два следующих фрагмента эквивалентны, насколько я могу видеть

1)

struct car
{
    ...
};
typedef struct car car;

2)

typedef struct car
{
    ...
} car;

В чем разница между А и В? А это «имя тега», но что это? Когда я использую что? То же самое для перечислений.

В нашем случае идентификатор car был объявлен два раза в одной и той же области видимости. Но объявления не будут конфликтовать, потому что каждый из идентификаторов находится в различном пространстве имен . Два вовлеченных пространства имен - это обычное пространство имен 1060 * и пространство имен тегов . Идентификатор тега необходимо использовать после ключевого слова struct , union или enum , в то время как обычному идентификатору ничего вокруг него не нужно. Возможно, вы слышали о функции POSIX stat, интерфейс которой выглядит следующим образом

struct stat {
  ...
};

int stat(const char *path, struct stat *buf);

В этом фрагменте кода stat также регистрируется в двух вышеупомянутых пространствах имен. struct stat будет ссылаться на структуру, и просто stat будет ссылаться на функцию. Некоторым людям не нравится всегда начинать идентификаторы с struct, union или enum. Они используют typedef для введения обычного идентификатора, который также будет ссылаться на структуру. Конечно, идентификатор может быть одинаковым (оба раза car) или отличаться (один раз A, другой раз B). Это не важно

2 голосов
/ 11 сентября 2009
  1. Используйте их, когда вам нужно. Прочитайте еще несколько примеров и учебных пособий, пока не поймете, какие указатели являются , и это должно быть намного понятнее:)
  2. Во втором случае создается массив в памяти с пространством для четырех байтов. Когда вы используете имя этого массива, вы волшебным образом получаете указатель на первый (индекс 0) элемент. И тогда оператор [] фактически работает с указателем, а не с массивом - x[y] эквивалентно *(x + y). И да, это означает, что x[y] совпадает с y[x]. К сожалению.
    Также обратите внимание, что когда вы добавляете целое число к указателю, оно умножается на размер указанных элементов, поэтому, если вы сделаете someIntArray[1], вы получите второй (index 1) элемент, а не где-то между ними, начиная с первого байта .
    Кроме того, в качестве окончательного варианта - типы массивов в списках аргументов функции - например, void foo(int bar[4]) - тайно превращаются в типы указателей - то есть void foo(int *bar). Это только в случае аргументов функции.
  3. Ваш третий пример объявляет тип структуры с двумя именами - struct A и B. В чистом C struct является обязательным для A - в C ++ вы можете просто ссылаться на него как A или B. Помимо изменения имени, эти два типа полностью эквивалентны, и вы можете заменить один на другой в любом месте, в любое время без каких-либо изменений в поведении.
  4. C имеет три места, где можно хранить вещи:

    • Стек - локальные переменные в функциях идут сюда. Например:

      void foo() {
          int x; // on the stack
      }
      
    • Куча - все происходит здесь, когда вы явно выделяете их с помощью malloc, calloc или realloc.

      void foo() {
          int *x; // on the stack
          x = malloc(sizeof(*x)); // the value pointed to by x is on the heap
      }
      
    • Статическое хранилище - глобальные переменные и static переменные, выделяемые один раз при запуске программы.

      int x; // static
      void foo() {
          static int y; // essentially a global that can only be used in foo()
      }
      
  5. Понятия не имею. Хотелось бы, чтобы мне не нужно было отвечать на все вопросы сразу - вот почему вы должны разделить их:)

Примечание: форматирование выглядит некрасиво из-за какой-то ошибки уценки, если кто-то знает об обходном пути, пожалуйста, не стесняйтесь редактировать (и удалить эту заметку!)

2 голосов
/ 11 сентября 2009

3) Неправильно использовать два разных имени A и B:

typedef struct A
{
    ...
} B;

С этим определением вы можете сказать

struct A a;
B b;
b.field = 42;
a.field = b.field;

потому что переменные a и b имеют одинаковый тип. Программисты на C обычно говорят

typedef struct A
{
  ...
} A;

так что вы можете использовать «A» в качестве имени типа, эквивалентного «struct A», но это сэкономит вам много печатания.

0 голосов
/ 11 сентября 2009

\ 5. Бурундук

  • Быстрая и легкая 2D библиотека по физике твердого тела в C.

  • Разработан с учетом 2D-видеоигр.

  • Легкая реализация C99 без внешних зависимостей вне Std. Библиотека C.

  • Доступны многие языковые привязки.

  • Просто, прочитайте документацию и посмотрите!

  • Неограниченная лицензия MIT.

  • Делает вас умнее, сильнее и привлекательнее для противоположного пола!

  • ...

0 голосов
/ 11 сентября 2009

На ваш второй вопрос:

char *fun = malloc(sizeof(char) * 4);

против

char fun[4];

против

char *fun = "fun";

Все они включают массив из 4 символов, но на этом сходство заканчивается. Там, где они различаются, это время жизни, модифицируемость и инициализация этих символов.

Первый создает один pointer to char объект с именем fun - эта переменная-указатель будет действовать только с момента запуска этой функции и до ее возврата. Он также вызывает стандартную библиотеку C и просит ее динамически создать блок памяти размером в массив из 4 символов и назначает местоположение первого символа в блоке для fun. Этот блок памяти (который можно рассматривать как массив из 4 символов) имеет гибкое время жизни, которое полностью зависит от программиста - он живет до тех пор, пока вы не передадите эту область памяти в free(). Обратите внимание, что это означает, что блок памяти, созданный malloc, может жить дольше или короче, чем сама переменная-указатель fun. Также обратите внимание, что связь между fun и этим блоком памяти не является фиксированной - вы можете изменить fun, чтобы он указывал на другой блок памяти, или указывать другую точку указателя на этот блок памяти.

Еще одна вещь - массив из 4 символов, созданный malloc не инициализирован - он содержит значения мусора.

Во втором примере создается только один объект - массив из 4 символов, который называется fun. (Чтобы проверить это, измените 4 на 40 и распечатайте sizeof(fun)). Этот массив действует только до тех пор, пока не будет объявлена ​​функция, возвращаемая функцией (кроме случаев, когда он объявлен вне функции, если он длится до тех пор, пока работает вся программа). Этот массив из 4 символов также не инициализирован.

Третий пример создает два объекта. Первая - это переменная указатель на символ, называемая fun, как и в первом примере (и, как обычно, она живет от начала этой функции до ее возврата). Другой объект немного странный - это массив из 4 символов, инициализированный как { 'f', 'u', 'n', 0 }, у которого нет имени , и который живет до тех пор, пока работает вся программа. Кроме того, не гарантированно может быть изменено (хотя то, что происходит, если вы пытаетесь изменить его, остается полностью неопределенным - это может привести к сбою вашей программы или нет). Переменная fun инициализируется с местоположением этого странного безымянного, неизменяемого, долговечного массива (но, как и в первом примере, эта связь не является постоянной - вы можете fun указать на что-то еще). 1039 *

Причина, по которой между массивами и указателями так много запутанных сходств и различий, сводится к двум вещам:

  • «Синтаксис массива» в C (оператор []) фактически работает с указателями , а не с массивами!
  • Попытка определить массив - это как ловить туман - почти во всех случаях массив испаряется и заменяется указателем на его первый элемент.
0 голосов
/ 11 сентября 2009

char * fun = malloc (sizeof (char) * 4);
или же полукокса [4];
или же char * fun = "fun";

Первый может быть установлен на любой размер, который вы хотите во время выполнения, и может быть изменен позже - вы также можете освободить память, когда закончите. Второй - это указатель, действительно «fun» - это то же самое, что char ptr = & fun [0].

Я понимаю, что делают указатели, но я не понимаю, в чем смысл они есть (каламбур не предназначен). И когда что-то распределяется на стек против кучи? Откуда мне знать где он распределяется? Делать указатели как-то с этим связано?

Когда вы определяете что-то в функции, такой как "char fun [4]", оно определяется в стеке, и память не доступна вне функции. Использование malloc (или нового в C ++) резервирует память в куче - вы можете сделать эти данные доступными в любом месте программы, передав ей указатель. Это также позволяет вам определять размер памяти во время выполнения и, наконец, размер стека ограничен (обычно 1 МБ), тогда как в куче вы можете зарезервировать всю доступную память.

edit 5. Не совсем - я бы сказал, что чистый C. C ++ (почти) является надмножеством C, поэтому, если вы не работаете над очень ограниченной встроенной системой, обычно нормально использовать C ++.

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