Я разработал с Java с нескольких лет, теперь я хотел изучить C, и я заметил несколько отличий.
В Java, когда я хочу что-то вернуть из функции (например, читать пользовательский ввод, я бы написал
String s;
s = new Scanner(System.in).nextLine();
В CI будет писать
char s[20];
scanf("%d", name);
Разница в том, что в Java функция возвращает значение, которое может быть назначено непосредственно переменной, в то время как в C функция принимает имя переменной в качестве аргумента (соответственно указатель на переменную).
Я заметил это со многими функциями C. Когда я пишу свои собственные функции, должен ли я делать это так, как будто я привык к Java, или должен использовать стиль C для назначения/возврата значений из функций?
Наибольшее внимание при выполнении вещей на C только с возвращаемыми значениями - отсутствие исключений. В отличие от Java, где условия ошибки могут быть сообщены вне обычного процесса вызова/возврата, C не имеет таких возможностей. Другими словами, у Scanner
есть возможность выбросить исключение, когда нет следующей строки; scanf
не имеет такой опции.
Если вы хотите вернуть статус ошибки из своей функции, вы ограничены (1) возвратом ее в статической переменной (небезопасной для параллелизма), (2) возвращает ее как возвращаемое значение или (3) возвращает ее в отдельный объект "ошибка". Иногда варианты (2) и (3) объединяются.
В результате вы часто увидите API C, которые могут выходить из строя как scanf
, когда статус возвращается как возвращаемое значение, а остальные значения изменяются с помощью указателей.
if (scanf("%d%d", &i, &j) == 2) {
... // Got 2 numbers
} else {
fprintf(stderr, "Wrong input! Expected two numbers.");
}
API, которые не терпят неудачу (скажем, isalpha(ch)
или tolower(ch)
) возвращают свое значение напрямую.
scanf
. Все функции ввода / вывода находятся в этой категории. Когда вы пишете свои собственные функции, которые не работают, потому что они каким-то образом обрабатывают все входные значения, используйте стиль tolower
.
@DasBlinkenLight имеет отличную точку: иногда вам нужно вернуть значение функции, чтобы передать целое число, указывающее на возникшую ошибку и какую ошибку.
Еще одна причина, как @chmike указывает, что если вы хотите, чтобы вернуть строку в этом случае вы должны использовать malloc()
(C эквивалент Java new
). Но C не имеет сбор мусора, поэтому его нужно явно освободить. Из-за этого часто "плохой этикет" передает другим внешним функциям указатель, который должен быть освобожден. Кто скажет, что программист даже хочет, чтобы память была выделена в кучу? Использование кучи дорого, и часто вся причина, по которой программист использует C, состоит в том, что данная программа должна быть быстрой.
Если программист, который вызывает вашу функцию, хотел использовать кучу, они сделали бы это в случае scanf
, вызвав сами malloc()
, а затем давая scanf
указатель на новую ячейку памяти, которую вы могли бы заполнить своей функцией. Таким образом, вызывающий scanf
может отслеживать всю выделенную память, а какая - нет.
Когда API, действительно возвращают выделенные указатели, как правило, потому что нельзя избежать, и они явно задокументировать и, как правило, обеспечивают функцию, чтобы освободить память должным образом, как и в случае с POSIX регулярных выражений regfree
или Глоб globfree
. Таким образом, если библиотека выделяет память, библиотека также освобождает ее.
Если в Java есть такая вещь, которая называется сборщиком мусора, то в C вы отвечаете за управление памятью.
Java "дайте мне".
String s = MyFunction(); // Give it to me.
В Java, когда вы вызываете функцию, которая извлекает данные откуда-то, она просто распределяет ее прозрачно и возвращает ее, сборщик мусора освободит ее позже.
C "там".
В C вы должны управлять памятью. Обычная практика заключается в том, чтобы сначала сделать какое-то место, чтобы поместить данные, а затем попытаться их восстановить.
Также ваш код должен быть более чем таким:
char s[80]; // Static memory allocation, we hope it is enough.
scanf("%79s", s); // Now we retrieve data. (Put it there).
Или что-то вроде этого:
char* s = malloc(80 * sizeof(char)); // Dynamic memory allocation, we hope it is enough.
scanf("%79s", s); // Now we retrieve data. (Put it there).
// Do stuff with data
free(s); // Be responsible.
Итак, да, вы должны использовать стиль C для назначения/возвращения значений (это имеет смысл, ведь вы все делаете C).
Еще один момент, если вы закодируете его как Java здесь, что у вас будет:
char* MyTest() {
char* a = "pipo";
return a; // Big fail, 'a' is local and will not exist anymore after return.
}
char* MyOtherTest() {
char a[]= "pipo";
char* b = malloc(80 * sizeof(char));
strncpy(b, a, sizeof(a));
return b; // My bet this one will never be freed.
}
Этот последний пример нарушает хорошо известное правило правильной практики, вызывающий должен нести ответственность за выделение/освобождение памяти.
Единственное исключение, которое я вижу, это тип значения, типа простого типа или структуры.
// Nice and simple.
int MyAdd(int a, int b) { return a+b; }
// Why ? just Why ?
void MyAdd(int a, int b, int* r) { *r = a+b; }
getline
(POSIX.1-2008) и fgets
и fgets
, из-за чего программа может глючить на неожиданно длинных строках (или должна иметь сложную логику для их правильной обработки, обычно не реализуемой ...).
ну, вы должны знать основное различие между C и Java,
Java
- это Object Oriented programming
а C
- нет.
в Java
почти все Object
(кроме primitives
).
То, как вы называете Scanner class
, очень плохо подходит, вы должны сделать это как:
Scanner scanner= new Scanner(System.in);
затем вызовите его, чтобы получить строку:
String s = scanner.nextLine();
Причина в том, что каждый раз, когда вы вызываете new Scanner(System.in)
вы назначаете свой сканер на тот же Object
чтобы он начинался с самого начала значения System.in
, вы продолжаете получать один и тот же ввод снова и снова.
однако в C
это не так. так как там нет объекта, который вы вызываете.
Так что коротко просто помните, что в java
все reference
(кроме примитивных значений cource) на что-то еще, а в C это не так
EDIT: с точки зрения отправки параметра в метод java может быть сложно, потому что это зависит от того, отправляете ли вы примитивные данные или объект.
по этой причине обычно лучше заставить метод возвращать что-то, а не отправлять данные, которые необходимо установить как параметр
например, посмотрите на сценарий:
1. Example 1
public void addOne(int i){
i++;
}
public static void main(String... args){
int i=0;
addOne(i);
System.out.println(i);
}
output= 0;
2. Example 2
public int addOne(int i){
return i++;
}
//output = 1
public static void main(String... args){
int i=0;
i = addOne(i);
System.out.println(i);
}
value=function()
(функция возвращает значение, и я могу присвоить это значение, как в Java) или с function(value)
(функции записывает свои значения непосредственно в аргумент, конечно, аргумент должен быть указателем, как в с)
Java объектно ориентирована и возвращает объект, который является указателем на объект.
В C вы также можете вернуть указатель, но не массив. Код C, который вы показываете, неверен, потому что s, объявленный как указатель на символ, не инициализируется. Scanf ожидает указатель на предварительно выделенный буфер в качестве второго аргумента. Он будет записывать входную строку в буфер. Наверное, вы имели в виду scanf("%d", s);
,
Код C, который вы написали, не будет работать, потому что s не указывает на выделенный буфер для хранения символов. Вы можете определить s как char s[1024];
в этом случае s эквивалентно указателю на буфер размером 1024 символа.
Вы также можете определить sa char *s = malloc(1024*sizeof(char));
который будет динамически распределять буфер для s. Затем вам нужно явно вызывать бесплатное освобождение буфера, когда он вам больше не нужен. Это связано с тем, что C не имеет сборщика мусора, как в Java.
char s[20]
Чтобы прочитать массив символов в C, сначала нужно инициализировать фактический массив с ограничением того, как долго это может быть. Что-то вроде этого:
char str[20];
scanf("%s", &str);
Обратите внимание на строку формата% s, в которой scanf сканирует строку,% d - для целых чисел.
c
код пытается использовать неинициализированную переменную. попробуйтеchar c
иscanf("%d", &c)