Язык С. Что бы это значило? arr[0]; (Нет ясности, с чем имеем дело.)

Модератор: Модераторы разделов

Ответить
dio
Сообщения: 97
ОС: Ubuntu
Контактная информация:

Язык С. Что бы это значило? arr[0];

Сообщение dio »

Код: Выделить всё

#include <stdio.h>
int arr[0];

int main()
{
    printf("%zd %p %i %i \n", sizeof(arr[0]), arr, arr[0], *arr);
    return 0;
}
Вывод у меня:
4 0x55e466a56014 0 0

Суть вопроса. Инициализируем статический массив размером 0(ноль элементов). Потом выводим получившееся и наблюдаем, что:
1. размер массива с нулем элементов равен 4 байтам (как так?);
2. адрес в памяти начала массива (это понятно, указатель задали, значит указывает вроде на что-то, но на что?? Массив же на ноль элементов..);
3. значение нулевого (те как-бы отсутствующего) элемента равно нулю(вроде как static нулем инициализируется, если не задан явно, вроде понятно, но его же нет, массив на НОЛЬ элементов;
4. разыменованный указатель (а на что он указывает??) тоже дает ноль.

У кого есть какие соображения?
Software is like sex: It's better when it's free.
Спасибо сказали:
Аватара пользователя
Red Gremlin
Сообщения: 506
Статус: самоучка
ОС: Rosa 2016 Fresh

Re: Язык С. Что бы это значило? arr[0];

Сообщение Red Gremlin »

Массивы нулевой длины — расширение GNU C.
Declaring zero-length arrays is allowed in GNU C as an extension. A zero-length array can be useful as the last element of a structure that is really a header for a variable-length object

Код: Выделить всё

gcc -pedantic -Werror -o test test.c 
test.c:2:5: error: ISO C forbids zero-size array ‘arr’ [-Werror=pedantic]
    2 | int arr[0];
      |     ^~~
cc1: all warnings being treated as errors
"В мире есть случайность, есть предопределенность и есть то, что ты планируешь совершить."
Спасибо сказали:
dio
Сообщения: 97
ОС: Ubuntu
Контактная информация:

Re: Язык С. Что бы это значило? arr[0];

Сообщение dio »

gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/9/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:hsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu

у меня gcc -Wall -Wextra -std=c18 iso_c_forbid.c -o iso_c_forbid
НЕ вызывает никаких errors и все компилируется.
Ответ такой же 4 0x565319ea8014 0 0
Спасибо за ваш ответ, но я просил прояснить, что конкретно происходит при таком раскладе(дополню, - на системном уровне). Ясности пока нет. Извините, ничего личного. И еще раз спасибо вам.
Software is like sex: It's better when it's free.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5289
ОС: Gentoo

Re: Язык С. Что бы это значило? arr[0];

Сообщение /dev/random »

dio писал:
24.08.2022 08:58
Инициализируем статический массив размером 0(ноль элементов).
Учтите, что стандарт запрещает массивы длиной 0. Некоторые компиляторы, включая gcc, их допускают, но поведение в этом случае зависит от компилятора.
dio писал:
24.08.2022 08:58
1. размер массива с нулем элементов равен 4 байтам (как так?);
В gcc размер массива с нулём элементов равен 0 байтов. Брать его нужно как sizeof(arr). Ваш sizeof(arr[0]) берёт размер типа элемента этого массива, указанного в объявлении (конкретный номер элемента в sizeof игнорируется), который является int'ом и имеет длину 4.
dio писал:
24.08.2022 08:58
это понятно, указатель задали, значит указывает вроде на что-то, но на что?? Массив же на ноль элементов..
На какое-то место в памяти внутри секции, где выделяются неинициализированные глобальные переменные. Этот адрес может совпадать с адресом какой-то другой переменной, т.к. следующая переменная (в неопределённом порядке) обычно имеет в качестве адреса значение "адрес предыдущей плюс размер предыдущей", округлённое в большую сторону до необходимого выравнивания.
dio писал:
24.08.2022 08:58
значение нулевого (те как-бы отсутствующего) элемента равно нулю(вроде как static нулем инициализируется, если не задан явно, вроде понятно, но его же нет, массив на НОЛЬ элементов
Вы вышли за пределы массива. По стандарту это - неопределённое поведение. Компилятор имеет право в такой ситуации сделать что угодно, в том числе вывести из строя совершенно несвязанные с этой части программы. Но в вашем случае, если оптимизатор никакой подлянки не выкинул (а он имеет полное право это сделать), то вы обратились по адресу, который вы вывели на экран в предыдущем пункте, и по которому на самом деле находится какая-то другая переменная (как я описал выше). Вы не знаете, какая.
dio писал:
24.08.2022 08:58
разыменованный указатель (а на что он указывает??)
См. выше.
Спасибо сказали:
Аватара пользователя
Red Gremlin
Сообщения: 506
Статус: самоучка
ОС: Rosa 2016 Fresh

Re: Язык С. Что бы это значило? arr[0];

Сообщение Red Gremlin »

dio писал:
24.08.2022 10:05
НЕ вызывает никаких errors и все компилируется.
std=c18 НЕ отключает все расширения. Нужна опция pedantic
"В мире есть случайность, есть предопределенность и есть то, что ты планируешь совершить."
Спасибо сказали:
dio
Сообщения: 97
ОС: Ubuntu
Контактная информация:

Re: Язык С. Что бы это значило? arr[0];

Сообщение dio »

/dev/random

Не совсем согласен, точнее, совсем не согласен местами.
1. "Учтите, что стандарт запрещает массивы длиной 0. Некоторые компиляторы, включая gcc, их допускают, но поведение в этом случае зависит от компилятора." - стандарт может и запрещает, а gcc разрешает явно https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html и говорит это явно "Declaring zero-length arrays is allowed in GNU C as an extension."
2. "Ваш sizeof(arr[0]) берёт размер..." я нигде не писал, что это размер массива...это вы так решили почему-то. Но это и не размер типа, иначе было бы sizeof(int). Это и выходит размер элемента arr[0], который здесь совпадает с размером массива.
3."На какое-то место в памяти внутри секции, где выделяются неинициализированные глобальные переменные. " Это совсем не так.Эта запись int arr[0]; и есть инициализация (как для static, те вне любой функции инициализируется нулем), так что инициализация здесь есть в section .data , а не в section .bss, как вы говорите.
4. "Вы вышли за пределы массива. По стандарту это - неопределённое поведение." Да не вышел я за пределы массива, - никаких итераций я не делал.
Можете посмотреть примеры такого использования по той же ссылке, что я указал ранее.
Ну, в общем уже и сам разобрался.
Так что спасибо еще раз всем за участие.Red Gremlin особенное - натолкнул на описание на сайте.
Последний раз редактировалось dio 24.08.2022 14:05, всего редактировалось 1 раз.
Software is like sex: It's better when it's free.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5289
ОС: Gentoo

Re: Язык С. Что бы это значило? arr[0];

Сообщение /dev/random »

dio писал:
24.08.2022 13:46
"Ваш sizeof(arr[0]) берёт размер..." я нигде не писал, что это размер массива...это вы так решили почему-то.
Тогда что вам могло быть непонятно?
dio писал:
24.08.2022 13:46
Это совсем не так.Эта запись int arr[0]; и есть инициализация
Вы не поняли. Имелось в виду, что если инициализатор не указан (int arr[10];), то переменные идут в одну секцию в памяти, а если указан (int arr[10] = {};), то в другую.
dio писал:
24.08.2022 13:46
Да не вышел я за пределы массива, - никаких итераций я не делал.
Вышли. Итерации для этого делать не обязательно. int a[10]; a[100] = 3; - это тоже выход за пределы массива.
Спасибо сказали:
dio
Сообщения: 97
ОС: Ubuntu
Контактная информация:

Re: Язык С. Что бы это значило? arr[0];

Сообщение dio »

"Вы не поняли. Имелось в виду, что если инициализатор не указан int arr[10]; " - так это и есть инициализатор(точка с запятой же стоит). Память уже выделяется при этом. Массив задан как статический в глобальной области. Ссылаюсь на то, что было с самого начала. Я не использовал { }.
"Вышли. Итерации для этого делать не обязательно. int a[10]; a[100] = 3; - это тоже выход за пределы массива." - я не только итераций не делал, но и присваиваний не существующим (как в вашем примере) переменным значений. Так что, и снова нет.
"Тогда что вам могло быть непонятно?"
И я написал, что уже в общем разобрался, так что спасибо за попытку мне помочь еще раз.
Software is like sex: It's better when it's free.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5289
ОС: Gentoo

Re: Язык С. Что бы это значило? arr[0];

Сообщение /dev/random »

dio писал:
24.08.2022 14:10
так это и есть инициализатор
Это не инициализатор. Инициализатора здесь нет. Его можно было указать после знака равенства. Глобальные переменные, для которых не указан инициализатор, при запуске программы инициализируются нулями, но всё равно в документации называются неинициализированными и размещаются в памяти отдельно от инициализированных.
dio писал:
24.08.2022 14:10
я не только итераций не делал, но и присваиваний не существующим (как в вашем примере) переменным значений
Вы обратились к arr[0], чтобы передать значение в printf. Поскольку индекс (0) больше либо равен длине массива, это выход за его пределы.
Спасибо сказали:
dio
Сообщения: 97
ОС: Ubuntu
Контактная информация:

Re: Язык С. Что бы это значило? arr[0];

Сообщение dio »

" Глобальные переменные, для которых не указан инициализатор, при запуске программы инициализируются нулями, но всё равно в документации называются неинициализированными и размещаются в памяти отдельно от инициализированных." Буду очень рад ссылке на доку, где это описано. Это возможно?

"Вы обратились к arr[0], чтобы передать значение в printf. Поскольку...." прочтите внимательно документацию по моей ссылке - там как раз развеиваются ваши сомнения по поводу выхода за пределы.
Software is like sex: It's better when it's free.
Спасибо сказали:
Аватара пользователя
/dev/random
Администратор
Сообщения: 5289
ОС: Gentoo

Re: Язык С. Что бы это значило? arr[0];

Сообщение /dev/random »

dio писал:
24.08.2022 14:24
Буду очень рад ссылке на доку, где это описано. Это возможно?
Большая часть документации gcc использует термин "uninitialized global variables" без объяснения его значения. Единственное место, которое я вижу, где этот термин объяснён, это описание опции -fcommon в мане gcc (эта опция управляет тем, в какую секцию в памяти такие переменные будут помещены):
In C code, this option controls the placement of global variables defined without an initializer, known as tentative definitions in the C standard.
...
Т.е. то, что в документации gcc называется "uninitialized global variables", в стандарте C называется "tentative definitions". Смотрим стандарт (C99/C11/C17 6.9.2.2):
A declaration of an identifier for an object that has file scope without an initializer, and
without a storage-class specifier or with the storage-class specifier static, constitutes a
tentative definition. If a translation unit contains one or more tentative definitions for an
identifier, and the translation unit contains no external definition for that identifier, then
the behavior is exactly as if the translation unit contains a file scope declaration of that
identifier, with the composite type as of the end of the translation unit, with an initializer
equal to 0.
----
dio писал:
24.08.2022 14:24
прочтите внимательно документацию по моей ссылке - там как раз развеиваются ваши сомнения по поводу выхода за пределы
Вы тоже прочтите внимательно:
Declaring zero-length arrays in other contexts, including as interior members of structure objects or as non-member objects, is discouraged. Accessing elements of zero-length arrays declared in such contexts is undefined and may be diagnosed.
Т.е. если объявить пустой массив где угодно, кроме хвоста структуры, то обращение к его элементам - неопределённое поведение, как и любой другой выход за пределы массива. Хвост структуры - единственное исключение, где gcc разрешает выход за пределы без неопределённого поведения.
Спасибо сказали:
Аватара пользователя
olecya
Сообщения: 901
ОС: debian, fedora (i3-wm)

Re: Язык С. Что бы это значило? arr[0];

Сообщение olecya »

Да вот лаконично по ссылке описано в секции .bss https://en.wikipedia.org/wiki/.bss Раздел BSS in C
Hence, the BSS segment typically includes all uninitialized objects (both variables and constants) declared at file scope (i.e., outside any function)
Спасибо сказали:
dio
Сообщения: 97
ОС: Ubuntu
Контактная информация:

Re: Язык С. Что бы это значило? arr[0];

Сообщение dio »

И еще для Олеся - имя статического массива это константа и она сразу должна инициализироваться. А разве константы могут быть неинициализированы сразу?
И вы забыли это?
In C, statically allocated objects without an explicit initializer are initialized to zero (for arithmetic types) or a null pointer (for pointer types).
и это:
. An implementation may also assign statically-allocated variables and constants initialized with a value consisting solely of zero-valued bits to the BSS section. Так что совсем не факт.
Добавлено (16:14):
/dev/random
ок, раз нет четкой ссылки на доки, будет копать дальше. Еще раз спасибо. Много выяснили и это хорошо.
Software is like sex: It's better when it's free.
Спасибо сказали:
Аватара пользователя
olecya
Сообщения: 901
ОС: debian, fedora (i3-wm)

Re: Язык С. Что бы это значило? arr[0];

Сообщение olecya »

dio
А что мешает проверить?

Код: Выделить всё

#include <stdio.h>
int arr[0];
const int digit;

int main() {
    printf("%d %d\n", *arr, digit);
    return 0;
}
Даже -pedantic не возмущается.
Добавлено (16:36):
Но она не в BSS секции оказалась
P.S Не видела что вы дописали сообщение.
Спасибо сказали:
dio
Сообщения: 97
ОС: Ubuntu
Контактная информация:

Re: Язык С. Что бы это значило? arr[0];

Сообщение dio »

const int digit;
И где смыл? И что потом с ней делать? Присвоить ничего нельзя ничего...и где вы так кодите? :) И заметьте - все встало в ноль.
Software is like sex: It's better when it's free.
Спасибо сказали:
Аватара пользователя
olecya
Сообщения: 901
ОС: debian, fedora (i3-wm)

Re: Язык С. Что бы это значило? arr[0];

Сообщение olecya »

dio
Мне кажется все логично. Когда глобальная переменная не инициализирована она записывается в секцию .bss и ей присваивается дефолтное значение, согласно типу, а не наоборот - если глобальная переменная не инициализирована ей присваивается дефолтное значение, в результате она становится инициализированной и она помещается в секцию .data
Добавлено (16:52):
dio писал:
24.08.2022 16:39
И что потом с ней делать?
Здесь главное факт, что ей неявно присваивается ноль. Она очень подходит к массиву нулевого размера :)
Последний раз редактировалось olecya 24.08.2022 17:00, всего редактировалось 1 раз.
Спасибо сказали:
dio
Сообщения: 97
ОС: Ubuntu
Контактная информация:

Re: Язык С. Что бы это значило? arr[0];

Сообщение dio »

Хорошо, убедили ))))) Ну и да - спасибо тоже большое.
Software is like sex: It's better when it's free.
Спасибо сказали:
Аватара пользователя
olecya
Сообщения: 901
ОС: debian, fedora (i3-wm)

Re: Язык С. Что бы это значило? arr[0];

Сообщение olecya »

Ладно, неявное можно сделать явным. (жалобы компилятора не в счет):

Код: Выделить всё

#include <stdio.h>
int arr[0] = {0};

int main() {
    printf("%zd %p %i %i \n", sizeof(arr[0]), arr, arr[0], *arr);
    return 0;
}

Shell

./tost1
4 0x404028 0 0
./tost2
4 0x404028 0 0

Shell

diff <(readelf -s tots1) <(readelf -s tost2)
Добавлено (17:28):
Да, чё я туплю.

Shell

cmp -l tost1 tost2
Повторила с выключенной оптимизацией. Результат тот-же
Последний раз редактировалось olecya 24.08.2022 17:33, всего редактировалось 1 раз.
Спасибо сказали:
dio
Сообщения: 97
ОС: Ubuntu
Контактная информация:

Re: Язык С. Что бы это значило? arr[0];

Сообщение dio »

Да где ты там тупишь - ты просто "Софья Ковалевская".
Software is like sex: It's better when it's free.
Спасибо сказали:
Ответить