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

#include 
int main(){

int total_ch=0;
int total_courses=2;
float sum=0.0;
float gpa=0.0;


for(int i=0;i < total_courses; i++){
    printf("%d \n",total_courses);
char course;
int marks=0;
int ch=0;
float cgpa=0.0;
printf("Enter the course name: \n");
scanf(" %s",course);
printf("Enter the credit hours of the course\n");
scanf(" %d",&ch);
printf("Enter the marks of the course\n");
scanf(" %d",&marks);
total_ch += ch;
if (marks > 85){
 printf("Your Cgpa of %s is 4.00 and your grade is A\n",&course);
 sum+=(ch*4.00);
}else if (marks > 79 && marks < 86 ){
 printf("Your Cgpa of %s is 3.67 and your grade is A-\n",&course);
 sum+=(ch*3.67);
}else if (marks > 75 && marks < 80){
 printf("Your Cgpa of %s is 3.33 and your grade is B+\n",&course);
 sum+=(ch*3.33);
}else if (marks > 71 && marks < 76){
 printf("Your Cgpa of %s is 3.00 and your grade is B\n",&course);
 sum+=(ch*3.00);
}else if (marks > 67 && marks < 72){
 printf("Your Cgpa of %s is 2.67 and your grade is B-\n",&course);
 sum+=(ch*2.67);
}else if (marks > 63 && marks < 68){
 printf("Your Cgpa of %s is 2.50 and your grade is C+\n",&course);
 sum+=(ch*2.50);
}else if (marks > 59 && marks < 64){
 printf("Your Cgpa of %s is 2.00 and your grade is C\n",&course);
 sum+=(ch*2.00);
}else if (marks > 56 && marks < 60){
 printf("Your Cgpa of %s is 1.67 and your grade is C-\n",&course);
 sum+=(ch*1.67);
}else if (marks > 53 && marks < 57){
 printf("Your Cgpa of %s is 1.33 and your grade is D+\n",&course);
 sum+=(ch*1.33);
}else if (marks > 49 && marks < 54){
 printf("Your Cgpa of %s is 1.00 and your grade is D\n",&course);
 sum+=(ch*1.00);
}else if (marks < 50){
 printf("Your Cgpa of %s is 0.00 and your grade is F\n",&course);
 sum+=(ch*0.0);
}


}
gpa=sum/total_ch;
printf("Your total GPA of this semester is %f",gpa);


}

user61855

Ответов: 3

Ответы (3)

Так почему же компилятор вообще позволяет это делать?

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

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

https://en.wikipedia.org/wiki/Undefined_behavior

В статье далее говорится, что

Для C и C++ компилятору разрешено выдавать диагностику во время компиляции в таких случаях, но не обязательно: реализация будет считаться корректной, что бы она ни делала в таких случаях, аналогично условиям don't-care в цифровой логике. Ответственность за написание кода, который никогда не вызывает неопределенного поведения, лежит на программисте, хотя реализации компилятора разрешено выдавать диагностику, когда это происходит.

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

Причина, по которой это не работает, заключается в том, что строки хранятся как указатели char, но в метод scanf вы передаете просто переменную char. Это не приводит к ошибке компиляции, но предупреждение должно было быть выдано. Убедитесь, что вы передаете корректный массив char с размером, достаточным для хранения названия курса. Например: char course[100];

Переменная, объявленная в программе

char course;

- это переменная, в которой хранится всего один символ. Это не массив char и не обычная строка (если только вас не устраивает строка из 1 символа). Поэтому инициализированного массива нет, потому что нет массива.

Чтобы иметь строку в стиле C, то есть массив char, вы должны объявить что-то вроде

char course[100];

Это объявит массив из 100 символов, разумеется, неинициализированный.

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

main.cpp:17:10: warning: format '%s' expects argument of type 'char*', but argument 2 has type 'int' [-Wformat=]

   17 | scanf(" %s",course);

У вас две проблемы: сначала вы должны передать указатель на char, где хранится строка. Передавая course, компилятор преобразует его в указатель и записывает прочитанную строку кто-знает-где, что приводит к UB.

.

Во-вторых, если вы передаете указатель на char, вы должны убедиться, что он указывает на выделенную область памяти, достаточно длинную для хранения результата. Например:

char course[100];
scanf("%s", course);

Но в этом есть и проблема: что произойдет, если результат scanf превысит выделенный размер 100? Тогда вы столкнетесь с переполнением буфера, опять же UB. На самом деле, это основная проблема при использовании scanf для чтения строки. См. этот вопрос и принятый ответ для поиска лучших альтернатив.

2022 WebDevInsider