LINUX.ORG.RU

Присвоение массивов в Си

 


0

1

Всегда принимал это как должное, но недавно задумался, а почему собственно нельзя в Си присваивать один массив другому?

То есть почему нельзя сделать так:

int a[10];
int b[10];
a = b;

Однако, если обернуть всё в структуру, то можно так:

struct {int array[10]; } a, b;
a = b;

Сломалось ли бы что-то в языке, если бы присвоение массивов a = b работало как условный memcpy(a, b, sizeof(b)) ?

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

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

mxfm ★★
()

Потому что в Си массивов нет. Что-то похожее на массив можно сделать только в C++.

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

Потому что в Си массивов нет.

Опять ШУЕ выпустили…

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

Не очень понятно, какой код сломался бы

Может такой код имелся ввиду: https://godbolt.org/z/1enPnGWqh

Как то, что разрешат присваивать массивы, сломает то, что в объявлениях функции int a[10] преобразуется к int* a?

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

Вам был приведён

Фигвам был привидён, говоришь? Ну-ну…

anonymous
()

Потому что вместо решения проблемы ублюдочного синтаксиса описаний типов K&R решили подпереть костылем проблему и не решать ее никогда. Так она до наших дней дожила без изменений.

На самом деле, когда ты делаешь int b[10];, ты как бы пишешь int (*b)[10]; — ублюдочность такой записи уже самоочевидна. Если тебе не очевидны следствия явной записи операций с массивами, то вот конкретный законченный пример:

#include <stdio.h>

int a[10];
int (*b)[10] = (int (*)[10])a;

int main()
{
    a[2] = 2;
    (*b)[1] = 1;
    printf("%d, %d, %d\n", (*b)[0], (*b)[1], (*b)[2]);
    return 0;
}

Удобно?

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

Не совсем. Первое определение выделяет память, второе - нет. Проблема не в синтаксисе, а в том что нельзя присваивать массиву значения.

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

не все процессоры поддерживают вычисления с плавающей точкой, но конпеляторы Си для них есть, и код вида

a + b
для типов double и float успешно собирается и работает, как же так? :)

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

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

Можно. memset и прочее. Другое дело, что в Си любая переменная-массив является rvalue указателем, по итогу теряя как часть операций над указателями, так и часть операций над массивом, в том числе присвоение. Если ты попытаешься сделать &a = &a, то получишь ту же ошибку.

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

вот конкретный законченный пример:

Можно же упростить:

#include <stdio.h>

int a[10];
int (*b)[10] = (int (*)[10])a;

int main()
{
    a[2] = 2;
    1[*b] = 1;
    printf("%d, %d, %d\n", 0[*b], 1[*b], 2[*b]);
    return 0;
}
anonymous
()
Ответ на: комментарий от byko3y

Memset это не языковая конструкция, а вызов функции. Тред о том, что массиву нельзя присвоить значение, а не про синтаксис описаний типов.

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

В смысле что в таких процессорах a+b переписывается в вызов функции? Не в курсе особенностей реализации, но такое может быть.

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

то вот конкретный законченный пример

В котором UB. Так что можно отправить его на мусорку.

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

Memset это не языковая конструкция, а вызов функции. Тред о том, что массиву нельзя присвоить значение, а не про синтаксис описаний типов

А как тогда по-твоему компилятор присваивает массив в структуре? Точно так же.

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

Довай я сначала сам задам вопрос. Не то, чтобы наводящий. Но тем не менее.

Почему пришлось написать (int (*)[10])a вместо a?

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

Почему пришлось написать (int (*)[10])a вместо a?

Потому что сишка умеет только в расширяющие автокасты, то есть, массив в указатель, но не указатель в массив.

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

Потому что вместо решения проблемы ублюдочного синтаксиса описаний типов

Ну насчет описаний типов, typeof() из GCC это решает

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
  typeof(int [3]) a = {1,2,3};
  printf("%d %d %d\n", a[0], a[1], a[2]);
  return EXIT_SUCCESS;
}

и его предлагают в новый стандарт Си добавить: https://thephd.dev/_vendor/future_cxx/papers/C - typeof.html

А в крестах вот так можно:

#include <iostream>
#include <cstdlib>

int main()
{
  std::type_identity<int[3]>::type a = {1,2,3};
  std::cout << a[0] << " " << a[1] << " "<< a[2] << std::endl;
}
SZT ★★★★★
()
Последнее исправление: SZT (всего исправлений: 1)
Ответ на: комментарий от SZT

Ну насчет описаний типов, typeof() из GCC это решает

Оно ничего не решает — оно заменяет одну ублюдочную запись другой ублюдочной.

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

А как должна выглядеть «неублюдочная запись»?

let a: [10]int;
let b: *[10]int = &a;

В этом же духе массив указателей будет let c: [10]*int. Никаких скобочек, читаем строго слева-направо.

Или как более известно некоторым присутствующим здесь:

let a: [int; 10];
let b: *[int; 10] = &a;
byko3y ★★★★
()
Ответ на: комментарий от byko3y

Уже обсуждалось выше, зависит от размера структурыи настроек оптимизации.

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

Возвращаясь к вопросу, после некоторых размышлений думаю так. В принципе, копирование массивов могло было быть реализовано также, как и структур, с теми же ограничениями на implementation limits и details, что и к структурам. Я не вижу технических причин для разрешения копирования структур, но ограничения для массивов постоянного размера.

Ты думаешь плохо. Подумай ещё раз.

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

Я мнение ламеров не спрашиваю.

Сам себя игноришь?

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

После «в си b и a трактуются как указатели» можешь уже ничего не писать про «божественную», питонист.

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

Или как более известно некоторым присутствующим здесь

Как известно, они все латентные питономакаки, и пердолинг с указателями им не интересен, поэтому в треды про Си они не ходят.

Virtuos86 ★★★★★
()

Глобально - потому что ты не знаешь количества элементов a[10] и b[10] когда делаешь «=» с массивами; когда ты так делаешь «=» со структурами - размер данных известен.

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

Глобально - потому что ты не знаешь количества элементов a[10] и b[10] когда делаешь «=» с массивами; когда ты так делаешь «=» со структурами - размер данных известен.

Известен.

#include <stdio.h>

int main(){
    int a[10];
    int b[5];
    printf("%ld\n%ld\n", sizeof(a), sizeof(b));
}
$ gcc test.c && ./a.out 
40
20
Waterlaz ★★★★★
() автор топика
Ответ на: комментарий от Psilocybe

Потому, что в си b и a трактуются как указатели,

нет

Не надо превращать божественную сишечку в очередной питон.

Видимо, вы не знаете ни сишечки, ни питона, раз считаете, что это сделает си хоть чуть ближе к питону

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

Шутка

Я вот тута с колгоспу «БлиныКомом».
Шо це за Си.
Свынья якась?
Понял с вашего вопроса тока ОДНО

Раз сказав A, то говоры и B ...
anonymous
()
Ответ на: комментарий от anonymous

Вот кто не знает сишечки.

Ну ладно, ладно, уели. Proves my point, anyway...

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

Любитель обмазываться UB детектед.

Я бы не ожидал, что там прям UB. Скорее implementation defined? Впрочем, если меня ткнут носом в стандарт, буду даже рад.

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

K&R

Уже давно выяснилось, что Кернинган там был только в роли писателя - к языку Си он отношения не имеет. Так что грамотно говорить Ричи, а не K&R.

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

http://port70.net/~nsz/c/c11/n1570.html#7.21.6.1p8:

The conversion specifiers and their meanings are:

d, i — The int argument is converted to …

o, u, x, X — The unsigned int argument is converted to …

http://port70.net/~nsz/c/c11/n1570.html#7.21.6.1p9:

If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

Так што совать беззнаковый аргумент спецификатору d это UB. Даже если это беззнаковый аргумент соответствующего модификаторам длины (l, ll …) размера.

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

Уже давно выяснилось, что Кернинган там был только в роли писателя - к языку Си он отношения не имеет. Так что грамотно говорить Ричи, а не K&R

А разве весь Си — это не наколенный компилятор для сборки одной программы? Если уж говорить про идею и «архитектуру» (которой там нет), то тогда пора бы вспомнить Ричардса, который написал прародителя C, но с архитектурой.

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

Ты про bcpl? Томпсон сделал и B и UNIX, а Ричи только систему типов подогнал. Я о том, что Кернинган тут никаким местом, так что K&R это книга, а не деятели ;-)

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

Почему треды про массивы в C всегда собирают кучу идиотов, шизиков (с утверждениями типа «в C нет массивов») и просто долбоёбов?

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

не известен

Етить-колотить. Очень смешно. До усёру. «Кто о чём, а вшивый о бане». Хде кто и в каком окружении?

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

Почему пришлось написать (int (*)[10])a вместо a?

Потому что сишка умеет только в расширяющие автокасты, то есть, массив в указатель, но не указатель в массив.

Я вообще ожидал чего более внятного. Типа что выражение a, т.к. оно не в одном из перечисленных кое-где контекстов, преобразуется к выражению типа int* и указывающему на первый элемент массива. А что ты написал это как-то слишком размыто.

Ну лан. Дальше это кастуется к (int (*)[10]) (сохраняем в объект и т.д.) и… нигде не сказано, что после такого каста у нас получился указатель на массив. Стандарт говорит, что скастовав к типу T* можно кастовать обратно к типу исходного выражения. Для некоторых T есть ещё доп. разрешения. Вроде: если это char, то указатель теперь указывает на первый байт объекта и инкрементами можно получить указатели на последующие байты.

А вот для T == int[10] нет гарантий вроде «если исходный указатель указывал на первый элемент массива с типом U[N], то каст к U(*)[N] выдаст указатель на соответствующий массив». Ну и получается что использовать такой указатель можно только как описано выше: кастовать обратно к int* (ну может ещё к void* скастовать). Как известно, что явно не определено стандартом, является неопределённым поведением. Использовать такой скастованный указатель как указатель на массив это неопределённое поведение.

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

Ну лан. Дальше это кастуется к (int (*)[10]) (сохраняем в объект и т.д.) и… нигде не сказано, что после такого каста у нас получился указатель на массив. Стандарт говорит, что скастовав к типу T* можно кастовать обратно к типу исходного выражения. Для некоторых T есть ещё доп. разрешения. Вроде: если это char, то указатель теперь указывает на первый байт объекта и инкрементами можно получить указатели на последующие байты

Да, согласен с аргументацией. Потому я компилирую свой код с "-fno-strict-aliasing" и сижу довольный с полностью определенным поведением. К моей величайшей радости точно так же делает ядро линукса, модули для которого я ковыряю на своем нынешнем рабстве.

Как известно, что явно не определено стандартом, является неопределённым поведением

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

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

в Си вообще хоть что-то доделали?

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