Short answer: Because C and C++ are different languages with different rules.
Long answer: In both cases the reason is that the array is too small for the string literal. The literal consists of the five visible characters, with a zero terminator on the end, so the total size is 6.
In C, you’re allowed to initialise an array with a string that’s too long; extra characters are simply ignored:
C99 6.7.8/14: An array of character type may be initialized by a character string literal, optionally enclosed in braces. Successive characters of the character string literal (including the terminating null character if there is room or if the array is of unknown size) initialize the elements of the array.
The compiler helpfully warns that the string is too large, since it almost certainly indicates an error; but it can’t reject the code unless you tell it to treat warnings as errors.
In C++, the initialiser isn’t allowed to be larger than the array:
C++11 8.5.2/2: There shall not be more initializers than there are array elements.
so, for that language, the compiler should give an error.
In both languages, when you want a character array to be the right size for a string literal initialiser, you can leave the size out and the compiler will do the right thing.
char a[] = "hello"; // size deduced to be 6
Aside from the missing commas in your set of initializers, why not just skip making it a 2-dimensional array of char? You’re only passing the array data to strstr(), and all it wants are null terminated strings. Why not use the more flexible:
char* array[] = {
"Becky Warre, 555-1223",
"Joe Looney, 555-0097",
"Geri Palmer, 555-8787",
"Lynn Presnell, 555-1212",
"Holly Gaddis, 555-8878",
"Sam Wiggins, 555-0998",
"Bob Kain, 555-8712",
"Tim Haynes, 555-7676",
"Warren Gaddis, 555-9037",
"Jean James, 555-4939",
"Ron Palmer, 555-2893",
NULL
};
Now that the array is just a set of pointers (and it’s terminated with a NULL pointer), the loop can look something like:
char *ptr = NULL;
int i;
for (i = 0; array[i] != NULL; i++)
{
ptr = strstr(array[i], search);
if (ptr != NULL)
cout << array[i];
}
One advantage of this is that you can add new strings to the data without having to update any corresponding ‘size’ numbers. And you’ll never have to worry about the compiler complaining that the array is too small in one dimension or another — it’ll just make the array the size it needs to be.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <ctime> using namespace std; const int n = 5; typedef struct { int day; int month; int year; } DATE; typedef struct { char naimenuv[20]; int cina; DATE datavir; int termin; int kilkist; char virobnyk[20]; }shop; bool repair(shop *pShop) //проверка на просроченность { time_t seconds; struct tm currdate = { 0 }; seconds = time(NULL); //время в секундах с 1.1.1970 года //сформируем структуру currdate с датой изготовления товара. Остальные поля = 0 (0 год соответствует 1900, месяц - с 0) currdate.tm_year = pShop->datavir.year-1900; currdate.tm_mon = pShop->datavir.month-1; currdate.tm_mday = pShop->datavir.day; //проверим время (в секундах): разность текущей и даты товара со сроком годности return (seconds - mktime(&currdate) > pShop->termin * 24 * 60 * 60); //true - товар просрочен } int main () { setlocale(0, "rus"); shop tovar[n] = { "Сосиски", 50, {20,10,2020}, 15, 130, "Завод Петровский", "Хлеб", 14, {19,10,2020}, 5, 150, "Хлебозавод", "Вафли", 25, {19,10,2020}, 30, 98, "Вафлезавод", "Растишка", 40, {21,10,2020}, 45, 73, "Молокозавод", "Баунти", 35, {20,10,2020}, 35, 31, "Завод кондитерский" }; cout << "***** Список товаров, срок годности которых менее 20-ти дней:" << endl; for (int i = 0; i < n; ++i) { if (tovar[i].termin < 20) { cout << tovar[i].naimenuv << " " << tovar[i].termin << endl; } } cout << endl << "***** Просроченные товары:" << endl; int count = 0; for (int i = 0; i < n; ++i) { if (repair(&tovar[i])) { count++; cout << tovar[i].naimenuv << " " << tovar[i].datavir.day << "." << tovar[i].datavir.month << " " << tovar[i].termin << endl; } } cout << "Всего: " << count << endl; |
Следующая программа прекрасно компилируется в C с предупреждениями, но не компилируется в C ++. Зачем? Какова причина?
#include <stdio.h>
int main(void)
{
char a[5]="Hello";
a[0]='y';
puts(a);
for(int i=0;i<5;i++)
printf("%c",a[i]);
return 0;
}
Предупреждение:
Warning:[Error] initializer-string for array of chars is too long [-fpermissive] enabled by default
Но если программа скомпилирована как программа C ++, то компилятор C ++ выдает следующую ошибку:
[Error] initializer-string for array of chars is too long [-fpermissive]
Я использую компилятор GCC 4.8.1.
5
Решение
Краткий ответ: потому что C и C ++ — это разные языки с разными правилами.
Длинный ответ: в обоих случаях причина в том, что массив слишком мал для строкового литерала. Литерал состоит из пяти видимых символов с нулевым терминатором на конце, поэтому общий размер равен 6.
В C вы можете инициализировать массив слишком длинной строкой; лишние символы просто игнорируются:
C99 6.7.8 / 14: Массив символьного типа может быть инициализирован литералом символьной строки, необязательно заключенным в фигурные скобки. Последовательные символы литерала символьной строки (включая завершающий нулевой символ, если есть место или если массив имеет неизвестный размер) инициализируют элементы массива.
Компилятор предупреждает, что строка слишком велика, поскольку почти наверняка указывает на ошибку; но он не может отклонить код, если вы не скажете, чтобы он воспринимал предупреждения как ошибки.
В C ++ инициализатору не разрешено быть больше, чем массив:
C ++ 11 8.5.2 / 2: Инициализаторов не должно быть больше, чем элементов массива.
поэтому для этого языка компилятор должен выдать ошибку.
В обоих языках, когда вы хотите, чтобы символьный массив соответствовал размеру инициализатора строкового литерала, вы можете не указывать размер, и компилятор сделает все правильно.
char a[] = "hello"; // size deduced to be 6
22
Другие решения
Проблема в нижней строке
char a[5]="Hello";
Нет места для хранения завершающего нуля.
По умолчанию, gcc не выдает никаких ошибок для этого случая с -fpermissive опция включена. Так что компилирует нормально в C,
Согласно требованию от языковых стандартов,
- За
C99стандарт, глава 6.7.9, пункт 14,
Массив символьного типа может быть инициализирован символьным строковым литералом или строковым литералом UTF-8, необязательно заключенным в фигурные скобки. Последовательные байты строкового литерала (включая завершающий нулевой символ, если есть место или массив имеет неизвестный размер) инициализируют элементы массива.
- За
C++11, глава 8.5.2, пункт 2
Инициализаторов не должно быть больше, чем элементов массива.
Итак, код компилируется (с предупреждениями) с gcc, выдает ошибку с g++,
10
Допустимая строка в C и C ++ должна иметь терминатор и в
char a[5]="Hello";
Там нет места для нулевого терминатора и не является допустимой строкой.
Так что это может не быть ошибкой в C, но вы обязательно столкнетесь с проблемами при использовании встроенных строковых функций, таких как strlen() и семья.
5
Потому что «Привет» 6 charс долго и г ++ без -fpermissive не будет инициализировать 5 char массив с ним, но GCC будет.
3
- Forum
- Beginners
- Initializer string is too long
Initializer string is too long
Whenever I attempt to compile this code I get the error «initializer-string for array of chars is too long». Any help is greatly appreciated.
|
|
Each string literal has additional character — terminating zero. So your array shall be declared as
char Map[10][11];
because it is initialized by string literal «##########» that has in total 11 characters (including the terminating zero).
Last edited on
"##########" doesn’t contain 10 characters. It contains 11.
There’s a hidden
»
character added to the end, so it actually looks like this:
{'#', '#', '#', '#', '#', '#', '#', '#', '#', '#', ''}
»
is called the NUL character, and it’s used as a hint where the array ends.
Thank you for the help vlad and Catfish
Last edited on
Topic archived. No new replies allowed.
Короткий ответ: потому что C и С++ — разные языки с разными правилами.
Длинный ответ: В обоих случаях причина заключается в том, что массив слишком мал для строкового литерала. Литерал состоит из пяти видимых символов с нулевым терминатором на конце, поэтому общий размер равен 6.
В C вы можете инициализировать массив со слишком длинной строкой; дополнительные символы просто игнорируются:
C99 6.7.8/14: массив типа символа может быть инициализирован литералом строковой буквы, необязательно заключенным в фигурные скобки. Последовательные символы символьного строкового литерала (включая завершающий нулевой символ, если есть место или массив неизвестного размера) инициализируют элементы массива.
Компилятор предупреждает, что строка слишком велика, поскольку она почти наверняка указывает на ошибку; но он не может отклонить код, если вы не сообщите ему, чтобы он рассматривал предупреждения как ошибки.
В С++ инициализатор не может быть больше, чем массив:
С++ 11 8.5.2/2: не должно быть больше инициализаторов, чем элементов массива.
поэтому для этого языка компилятор должен сообщить об ошибке.
В обоих языках, когда вы хотите, чтобы массив символов был правильным размером для инициализатора строкового литерала, вы можете оставить размер, и компилятор выполнит правильные действия.
char a[] = "hello"; // size deduced to be 6
Неужели Вы думаете, что сообщения об этих ошибках очевидны для всех, так же как и для Вас.
Мне абсолютно неясно, почему gnu99 для си valid, я для с++ «инвалид». Или в 99-ом плюсы не придумали? Конечно же, придумали.
Как уже было сказано, С и С++ это разные языки.
С++ постарался поддержать как можно больше из С, но вот как раз плюсы придумали ДО 99 года и особенности С стандарта 99-го года в него не вошли.
В любом случае, указывать для С++ ключ совместимости со стандартом языка С — странно. То же самое (предупредение option … is valid for … but not for …) будет при попытке указать ключ -std=c++98 -std=gnu++98 для файла .c
И если сообщение error: initializer-string for array of chars is too long непонятно, то таки надо что-то почитать по языкам. Можно, конечно, и в разделе дл начинающих поспрашивать, как уже советовали, но лучше почитать.
cc1plus.exe: warning: command line option «-std=gnu99» is valid for C/ObjC but not for C++
При использовании makefile из примера операционки, как поправить его?
makfile из примера (если имеется ввиду порт AVR/GCC) вроде как не содержит -std=gnu99. Сам ключ -std там, кажется, не используется, идёт применение стандарта по умолчанию.
Глянул я по диагонали то меню, там используются так называемые incomplete типы, С++ этого не любит. К моменту определения объекта тип должен быть дополнен(доопределён), что просто невозможно для заданной структуры.
Так что переделывать menu item так, чтобы в хвосте был не неполный тип, а указатель на строку, но там кроме увеличения расхода памяти на два байта на каждый элемент меню вылезут свои проблемы уже с AVR/gcc-шным PROGMEM (строку не удастся так просто задавать прямо в инициализаторе, надо будет поменять макрос).
Или разносить C и C++ копиляцию с тем вот extern «C» {, причём в С++-части те фокусы вокруг неполных типов должны быть не видны, наружу должны торчать только С-функции. Насколько это возможно в данном случае — я не стал разбираться.
Примеры scmRTOS для AVR/GCC не рассчитывались на компиляцию смешанных проектов с разными ключами стандарта, там единая переменная для ключей CFLAGS,
Если без указания стандарта через -std пример не собирается, то нужны разные ключи компиляции для С и С++ файлов, надо разводить на CFLAGS и CPPFLAGS.
(надо будет к следующему релизу scmRTOS это место подправить, сделать две переменные ключей)