LINUX.ORG.RU

Несколько вопросов новичка по Си

 , ,


0

3
  1. Как работает указатель?
  2. Функция sub получает делимое и вычитает из него делитель Она ничего не возвращает и просто меняет значение переменой по указателю. Но когда вызываю функцию, приходится указывать & и имя переменной. Нельзя ли просто передать переменную и почему?
    void sub(int* f, int g)
    {
      *f = *f - g;
    }
    
    int main(void)
    {
      int number = 15, div = 7;
      sub(&number, div);
      printf("%d\n", number);
      return 0;
    }
  3. Насколько этот код говно? Что стоило бы исправить? Спасибо!
★★

1. всё есть число.

1.1 всякое число это последовательность бит(бит это 0 либо 1 )

1.2 последовательность бит имеет местоположение(это метаинформация)

1.3 местоположения(те которые не register) имеют номера своими именами.

1.4 из 1.3 и 1 следует что указатель указыващий на себя «не обладает»(ибо *void есть тип не_тип) типом

1.5 в Си всё передаётся в виде чисел нескольких различных размеров.

qulinxao ★★☆
()
Ответ на: комментарий от Debasher

да нет. просто раньше у меня к нему никакого отношения не было. я просто его нигде не видела и мне было пофигу. но на последней работе я посмотрела на него достаточно близко и внимательно и поняла, что это дико (просто чудовищно) жрущее ресурсы однопоточное УГ с кривой внутренней архитектурой, которое как-то выползает с совсем уж днища только за счёт того, что умеет дёргать сишные библиотеки. единственный его естественный плюс - примитивизм программирования, не требующий никаких мозговых усилий. я соглашусь, что для обучения студентов это сойдёт, ибо там до фени на ресурсы и производительность. но в продакшне его надо запретить раз и навсегда.

Iron_Bug ★★★★★
()

Насколько этот код говно? Что стоило бы исправить?

Смысла в функции div никакого.

Что стоило бы исправить?

int main(void)
{
  int number = 15, div = 7;
  number -= div;
  printf("%d\n", number);
  return 0;
}
theNamelessOne ★★★★★
()
Ответ на: комментарий от theNamelessOne

смысл функции в изучении работы механизма передачи параметров через указатели. собсна, об этом и тред.

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

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

theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 1)
Ответ на: комментарий от theNamelessOne

работа со строками и вызовы функций - это немного разные вещи. здесь вопрос не в динамике, а в возврате значений из функции.

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от theNamelessOne

Я для изучения указателей этот огород горожу, так то понятно, что можно сразу вычесть, просто передав аргумент с оператором printf-у

mystery ★★
() автор топика
Ответ на: комментарий от Iron_Bug

А что по строкам порекомендуете?
Бегло пробежался по докам - ужас ужаснейший, особенно после JS, где наиудобнейшая работа со строками
Может есть библиотеки какие-нибудь для работы со строками, конкатенацией и всякими модификациями

mystery ★★
() автор топика
Ответ на: комментарий от mystery

Я к тому веду, что материал будет усваиваться легче, если изучать его не на абстрактных, а на более приближённых к real-world примерах. Поэтому я и предложил изучать на строках. Там ты легко и с указателями на указатели столкнёшься.

theNamelessOne ★★★★★
()
Последнее исправление: theNamelessOne (всего исправлений: 1)
Ответ на: комментарий от mystery

Может есть библиотеки какие-нибудь для работы со строками, конкатенацией и всякими модификациями

Для обучения, ИМХО, лучше никакими библиотеками не пользоваться.

theNamelessOne ★★★★★
()
Ответ на: комментарий от mystery

со строками надо начинать с простых ascii строк на латинице, без кодировок, без мультибайта и юникода. то есть, для начала никакой кириллицы, «hello, world» и тому подобное. потому что с кодировками сразу выползет много вопросов, связанных не с самими строками в С, а с вопросами их печати в консоль (а эта фигня ещё и разнится местами от платформы к платформе).

вот то, что у K&R по строкам есть - это самое базовое, что надо понимать хорошо, прежде чем лезть в дебри. со строками вместе можно освоить динамическое выделение памяти (malloc и free). оно там понадобится. ну и основные функции работы со строками из стандартной библиотеки тоже нужно изучить хотя бы бегло, чтобы иметь представление, что с ними можно делать.

да, самое главное в строках - это то, что они последовательно хранятся в памяти и представляют собой массивы, а «имя» переменной строки есть указатель на начало этого массива.

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 3)
Ответ на: комментарий от Iron_Bug

не всем на пистонах говнокодить

В питоне, кстате, каждый второй пакет на Си написан, чтобы работало быстрее)

menangen ★★★★★
()
Ответ на: комментарий от Iron_Bug

Базу по строкам я знаю, как из создать и т.д.
Но я не умею их модифицировать
Не, можно создать новый массив, по циклу заполнить и т.д., но это рутина
Мне хочется работать с юникодом
Кстати, чем char* str отличается от char str[]?

mystery ★★
() автор топика
Ответ на: комментарий от mystery

http://c-faq.com/aryptr/aryptr2.html
И вообще, на этом сайте найдешь ответы на многие вопросы по Си.
Попробуй реализуй свою библиотеку для работы с pascal или mfc строками. Это всем велосипедам велосипед, но на велосипедах по началу норм учиться.

Deleted
()
Ответ на: комментарий от Iron_Bug

про передачу переменных через стек

Как там в двухтысячном году?

mix_mix ★★★★★
()
Ответ на: комментарий от mystery

expected «;», «,» or «)» before «&» token
Пробовал так уже :(

И этот человек что-то ещё гонит на кресты. И рассуждает о том, что сишка — Ъ, а плюсцы — Thumbs down.

EXL ★★★★★
()
Ответ на: комментарий от menangen

вот потому пистон и не нужен. это плохо сделанная обёртка над С.

Iron_Bug ★★★★★
()

Указатель хранит число, которое интерпретируется компилятором как адрес в памяти. Соответственно если ты в свою функцию передашь просто sub(number, div), то компилчтор(если код скомпилируется) после разыменования *f получит адрес 15 и будет пытаться с ним работать, сначала получить значение и потом попробует туда записать. Это приведет к крэшу скорее всего.

А & - операция получения адреса любой переменной.

invy ★★★★★
()
Последнее исправление: invy (всего исправлений: 2)
Ответ на: комментарий от mystery

char* от char[] не отличается ничем. чисто синтаксис.

что касается юникода - там есть свои тараканы, не сразу очевидные. я думаю, что ты не китаец и собираешься использовать utf-8. вот, для примера собери такую шнягу (сам код должен быть cохранён в utf-8, это важно):

#include <stdio.h>
#include <string.h>

int main()
{
    const char *str = "Hello, world!";
    const char *mbstr = "Привет, мир!";
    printf("\"%s\", %lu\n", str, strlen(str));
    printf("\"%s\", %lu\n", mbstr, strlen(mbstr));
    return 0;
}

скомпили и посмотри на результаты (консоль должна быть в UTF-8, чтобы не было кракозябр).

проблема в том, для кириллицы(как частный случай) юникод использует не один байт. в общем, надо иметь в виду, что там всё усложняется и чаще всего приходится работать со специальными библиотеками, которые умеют работать с юникодом.

Iron_Bug ★★★★★
()
Ответ на: комментарий от mystery

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

Iron_Bug ★★★★★
()
Ответ на: комментарий от Iron_Bug

Это я знаю и поэтому и хочу библиотеку
А про синтаксис - почему так? получается каждый элемент массива хранит указатель на чисьа, которые кодируются?

mystery ★★
() автор топика
Ответ на: комментарий от mystery

в чём хардкор? есть масса инструментов, которые всё позволяют автоматизировать. начиная от аutotools и заканчивая всякими cmake и boost build v2. абсолютно никаких проблем. особенно если сидеть на одном компиляторе и на одной платформе и не заморачиваться переносимостью и всякой совместимостью.

Iron_Bug ★★★★★
()
Ответ на: комментарий от mystery

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

да, тут надо учитывать, что сам код тоже может быть в разных кодировках. и компилятор это просечёт и скомпилирует константные строки в той кодировке, которая у файла использована. можно, например, через iconv сконвертить файл с кодом в ISO-8859-5 и если в консоли поставить эту кодировку, то вывод будет так же на русском языке, но длина строки будет 12.

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

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 2)
Ответ на: комментарий от Iron_Bug

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

mystery ★★
() автор топика
Ответ на: комментарий от mystery

компиляция валится из-за кривости рук, а не из-за плюсов :)

что касается «долго» - я не могу сказать, что прямо намного дольше, чем С. у меня были большие проекты и на С и на плюсах. существенной разницы в скорости компиляции не ощущалось. большую часть времени съедает тщательная оптимизация. в работе бинарники, полученные из С кода, могут быть чуть-чуть быстрее. но в большинстве случаев разница несущественна. всё зависит от грамотного использования возможностей языков.

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

Iron_Bug ★★★★★
()
Последнее исправление: Iron_Bug (всего исправлений: 2)
Ответ на: комментарий от mystery

ты имеешь в виду С11? смотря с чем сравнивать. в С99 появились массивы переменной длины. в С11 добавили типы char16_t и char32_t, но это, по сути, бантики.

Iron_Bug ★★★★★
()
Ответ на: комментарий от mystery

Бегло пробежался по докам - ужас ужаснейший, особенно после JS

для понимания прелести С следует не после JS(которое то ещё поделие) а после асма т.е когда учишься С сравнивай чему служит замещением способы в С в асме твоей машины.

а так-то в С «строк нет» - есть чисто лаконичный способ задачи буквенно-цифровой последоваттельности куска памяти и работы с ними.

qulinxao ★★☆
()
Ответ на: комментарий от mystery

причина появления си++ - ооп

это тезис жертвы пропаганды и маркетинга конца 80-ых

причина появления С++ - необходимость быстрой Симулы в окружении где всё пишут на Сях.

Поэтому я считаю плюсы говном

даааа . ты крут.

qulinxao ★★☆
()
Ответ на: комментарий от Iron_Bug

char* от char[]

в части объявления аргумента функции - ага.

но вот когда объявление переменной локальной/глобальной бывает небольшое отличие - char* это выделение места под указатель и значение адрес места хранения адреса символа.

а char[] выделение непосредственно памяти под байты-символы и значение один и тот же адрес т.е char[] и есть место по которому хранится символ(символы)

т.е опятже для понимания стоит помнить что Си - это ассемблер но со структурированным(в сравнении с обычными один-один простынями асма оператор мнемоника - маш код команда с аргументами) синтаксисом.

qulinxao ★★☆
()
Ответ на: комментарий от Iron_Bug

char* от char[] не отличается ничем. чисто синтаксис.

Не верно.

Там выше есть попытка объяснить, но опять же неудачная.

В си есть два типа объектов копируемые и не копируемые. Копируемые - это структуры/юнионы, а не копируемые - это функции, массивы.

Не копируемые объекты при попытки их присваивания преобразуются к указателю, т.е. в случае в случае с функцией - это указатель на функцию, а в случае с массивом - в указатель на тип елемента.

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

Поэтому char * и char[] - отличаются, но такого типа как char[] как аргумент существовать не может, поэтому он сводится к char *.

return 0;

Это такой привет из 80-х?

AnonCxx
()
Ответ на: комментарий от qulinxao

но вот когда объявление переменной локальной/глобальной бывает небольшое отличие

Не вот. struct {char * ptr;} - это что?

а char[] выделение непосредственно памяти под байты-символы и значение один и тот же адрес т.е char[] и есть место по которому хранится символ(символы)

Слабо как-то. Какое отношение выделение памяти имеет к массиву? При определении той же: struct {char[];}; - будет выделена память под структуру, а не под массив - массив просто проекция на пространство в структере. А в юнионе? Одна из проекций.

А каст char[] к char * - следствие того, что операций над не-копируемыми типами в си нет. Всё невалидное просто сводится к указателю на данный объект. &char[], (*(*f))() и прочие приколюхи.

AnonCxx
()
Ответ на: комментарий от AnonCxx

struct {char *ptr} - это вообщет месага компилятору что для любого

char a,*b

ты сможешь printf(«%c»,*((&(b=&a))->ptr))

и результат будет тот же что и printf(«%c»,a)

когда char[]Namr вообще(только тогда когда) выделяет память тогда Namr - есть адрес(который средствами языка(если не начитать патчить в этом же сырце на ходу бинарь котого результата чей сырец мы сейчас обсуждаем) непосредственно не заменишь - т.е ) который(адресс) не имеет отдельного имени для хранения и возможности изменения в отличии от char* pN;

Всё невалидное просто сводится к указателю на данный объект

слишком сильное утверждение что бы быть верным по всему Си.

qulinxao ★★☆
()
Последнее исправление: qulinxao (всего исправлений: 2)
Ответ на: комментарий от qulinxao

struct {char *ptr} - это вообщет месага компилятору что для любого

Ничего не понял из этих потуг. Попытайся сформулировать что-то внятное и осмысленное.

Структура - это строктура ни к каким месагам для конпелятора отношения не имеет.

когда char[]Namr вообще(только тогда когда) выделяет память тогда Namr

Я уже объяснил почему это не так. char[] - есть ни что иное как некая память в которую проецируется набор значений типа char.

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

По поводу потуг с адресом - я уже объяснил. Все не-копируемые типы преобразуются к указателю на этот не-копируемый объект.

слишком сильное утверждение что бы быть верным по всему Си.

Есть контрпримеры - выкатывай, а если нет - зачем споришь?

AnonCxx
()
Ответ на: комментарий от AnonCxx

char[] - есть ни что иное как некая память в которую проецируется набор значений типа char.

это просто не верно для случая когда char[] фигурирует как описание аргумента функции.

Ничего не понял из этих потуг.

прескриптум: то что потуги понял - ок спец по потугам.

соболезную и поясню: объявление структуры ест декларация имён_полей как «более лучших» мнемоник и какого -размера туда/отуда чем численное смещение и тоскания по всюду сколько байтное это. смещений для произвольного адреса-базы.

qulinxao ★★☆
()
Ответ на: комментарий от Iron_Bug

пистон и не нужен. это плохо сделанная обёртка над С.

+100500

char* от char[] не отличается ничем

Неправильно. Это разные вещи. char* — указатель, т.е. для того, чтобы прочитать строку, его надо разыменовать сначала и выяснить адрес, где хранится эта строка. char[] — уже адрес, его разыменовывать не нужно. Для ТСа: в случае char* компилятор отводит под переменную дополнительные 4-8 байт, где хранится адрес строки; в случае char[] это место не отводится, и в функции вроде sprintf(str, ...) фактически на место str сразу подставляется адрес — константа.

Но вот в функции аргументы передаются через указатели, т.е. там действительно пофиг, как писать: char* или char[] — все равно разыменовывать.

ТСу: ты можешь сразу выделить указатель через malloc:

int *num = malloc(sizeof(int));
*num = 15;
sub(num, div);
free(num);
но это менее удобно. Вот в случае, если бы твой num был динамически выделяемой структурой, списком или чем-то вроде того, было бы совсем иначе!

anonymous
()
Ответ на: комментарий от AnonCxx

return 0;

Это такой привет из 80-х?

что за бред? Функция main должна возвращать 0, иначе башевские проверки вида

prog1 args && echo "Done" || echo "Error"
коту под хвост!

Садись, неуч, два!

anonymous
()
Ответ на: комментарий от Iron_Bug

char* от char[] не отличается ничем. чисто синтаксис.

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

anonymous
()
Ответ на: комментарий от anonymous

сча он скажет, что нужно писать exit(EXIT_SUCCESS) (или там _exit(EXIT_SUCCESS)), а ещё оформлять main() правильно. Ты всерьёз думаешь, что ему интересно?

anonymous
()
Ответ на: комментарий от qulinxao

это просто не верно для случая когда char[] фигурирует как описание аргумента функции.

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

Точно так же «типа-функции» не может быть в аргументе функции, даже если ты его там напишешь.

соболезную и поясню: объявление структуры ест декларация имён_полей как «более лучших» мнемоник и какого -размера туда/отуда чем численное смещение и тоскания по всюду сколько байтное это. смещений для произвольного адреса-базы.

Опять какие-то потуги несвязные и игорирование моих вопросов.

Структура есть объект-память, какие в ней поля есть - никого не волнует. Поля её - есть лишь проекции чего-то в память этой структуры - заполнители её пространства.

Причём тут база, при чём тут остальные потуги и что из них следует - так же не ясно. Тебя попросили объяснить - какая-такая память выделяется для char[] при определении внутри структуры. Ты либо отвечаешь, либо не отвечаешь - всё просто.

Далее, почему ты игнорируешь юнион? Он не вписывается в твою кулл"логику"?

слишком сильное утверждение что бы быть верным по всему Си.

Ответа на этот вопрос так и не последовало.

AnonCxx
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.