Здрасти, всем.Решил собирать проект с опцией -std=gnu99.
Все хорошо, но есть один warning и я не понимаю его смысл.---<Код proba.c>----------------------------------------
#include <stdio.h>
typedef unsigned char TMac[6];
void f(const TMac* mac)
{
puts(mac?"mac":"no mac");
}
int main()
{
/*const*/ TMac mac;
f(&mac);
return 0;
}
---</Код proba.c>----------------------------------------gcc -std=gnu99 -o q proba.c
proba.c: In function ‘main’:
proba.c:10: warning: passing argument 1 of ‘f’ from incompatible pointer type
proba.c:3: note: expected ‘const unsigned char (*)[6]’ but argument is of type ‘unsigned char (*)[6]’Соответственно, если раскоментировать /*const*/ то варнинга не будет.
Так что же не нравится копилятору и как написать коректно?
Например, если функция ждет аргумент "const int*", а передается "int*", то все ОК и внутри функции аргумент будет указателем на константный инт.
Вроде, у меня ситуация аналогична.Ещё раз, что же не нравится копилятору и как написать коректно?
Заверни массив в структуру - проще жизнь станет.
> Заверни массив в структуру - проще жизнь станет.Не вариант - это уже устоявшаяся конструкция (проект большой, работают несколько человек).
К тому же, интересно разобраться, как с точки зрения компилятора написать корректно в этом случае.
>[оверквотинг удален]
> void f(const TMac* mac)
> {
> puts(mac?"mac":"no mac");
> }
> int main()
> {
> /*const*/ TMac mac;
> f(&mac);
> return 0;
> }Для С подойдет приведение типов при вызове:
f((const TMac *)&mac);
> Для С подойдет приведение типов при вызове:
> f((const TMac *)&mac);Да, тогда варнинга не будет. Еще можно убрать const в функции. Да и на варнинг можно забить.
Но, эти варианты не подходитят.Задача - разобраться что не нравится компилятору.
На мой взгляд все понятно - ф-ии передается указатель на неконстантный массив, но функция ждет указатель на константный массив и поэтому внутри ф-ии массив будет константным и его изменять нельзя.
Если вместо TMac подставить int, то компилятор считает именно так, а вот с массивом он не понимает.
>[оверквотинг удален]
>> f((const TMac *)&mac);
> Да, тогда варнинга не будет. Еще можно убрать const в функции. Да
> и на варнинг можно забить.
> Но, эти варианты не подходитят.
> Задача - разобраться что не нравится компилятору.
> На мой взгляд все понятно - ф-ии передается указатель на неконстантный массив,
> но функция ждет указатель на константный массив и поэтому внутри ф-ии
> массив будет константным и его изменять нельзя.
> Если вместо TMac подставить int, то компилятор считает именно так, а вот
> с массивом он не понимает.Очевидно компилятор обрабатывает встроенные типы (int,double) и пользовательские по разному
> Очевидно компилятор обрабатывает встроенные типы (int,double) и пользовательские по разномуЗвучит логично, но это не так.
Если в примере выше TMac определить как структуру, то все ОК.
Проблема именно с массивами (любыми массивами - интов, структур, указателей).Есть ещё у кого-нибудь варианты :-)
>> Очевидно компилятор обрабатывает встроенные типы (int,double) и пользовательские по разному
> Звучит логично, но это не так.
> Если в примере выше TMac определить как структуру, то все ОК.
> Проблема именно с массивами (любыми массивами - интов, структур, указателей).
> Есть ещё у кого-нибудь варианты :-)Т.к. массивы передаются по указателю, очевидно, что компилятор требует чтобы значения были константными. (В отличие от встроенных типов которые итак копируются на стек, т.е. изменению не подлежат)
>> Очевидно компилятор обрабатывает встроенные типы (int,double) и пользовательские по разному
> Звучит логично, но это не так.
> Если в примере выше TMac определить как структуру, то все ОК.
> Проблема именно с массивами (любыми массивами - интов, структур, указателей).
> Есть ещё у кого-нибудь варианты :-)Вот так?
(Или я не понял что вы спрашиваете)#include <stdio.h>
typedef unsigned char TMac[6];
void f(const TMac* mac)
{
puts(mac?"mac":"no mac");
}
int main()
{
/*const*/ TMac mac;
f((const TMac *) &mac);
return 0;
}
> Вот так?
> (Или я не понял что вы спрашиваете)Не подходит. Вариант с приведением типов уже предлагали выше.
Проблема решилась.
Оказывается:
1) Mac mac; //mac равен &mac (ну и, конечно всё равно указателя на первый элемент)
(операция & к имени массива не действует)2) Компилятором трактуются по-разному "указатель на массив" и "указатель на указатель на элемент массива"
void f(const TMac* mac); НЕ то же самое что
void f(const unsigned char** mac);Т.о. в моем случае надо писать
void f(const TMac mac);
вместо
void f(const TMac* mac);На мой взгляд, такое поведение компилятора не очевидно, но логика в этом есть.
Здесь можно найти интересную информацию по теме.
http://stackoverflow.com/questions/1810083/c-pointers-pointi...Здесь комментарий на эту тему от разработчика компилятора gcc.
http://gcc.gnu.org/ml/gcc-help/2009-12/msg00026.htmlВсем спасибо ;-)
>> Вот так?
>> (Или я не понял что вы спрашиваете)
> Не подходит. Вариант с приведением типов уже предлагали выше.
> Проблема решилась.
> Оказывается:1.
Большая часть путаницы вокруг указателей в С происходит от
непонимания этого утверждения. "Эквивалентность" указателей и
массивов не позволяет говорить не только об идентичности, но и
взаимозаменяемости."Эквивалентность" относится к следующему ключевому определению:
значение типа массив-Т, которое появляется
в выражении, превращается (за исключением трех случаев) в
указатель на первый элемент массива; тип результирующего
указателя - указатель-на-Т.(Исключение составляют случаи, когда массив оказывается операндом
sizeof, оператора & или инициализатором символьной строки для
массива литер.)Вследствие этого определения нет заметной разницы в поведении
оператора индексирования [], если его применять к массивам и
указателям. Согласно правилу, приведенному выше, в выражении типа
а[i] ссылка на массив "a" превращается в указатель и дальнейшая
индексация происходит так, как будто существует выражение с
указателем p[i] (хотя доступ к памяти будет различным.
В любом случае выражение x[i], где х - массив или указатель)
равно по определению *((x)+(i)).Смотри: K&R I Разд.5.3 c.93-6; K&R II Разд.5.3 c. 99; H&S
Разд.5.4.1 c. 93; ANSI Разд.3.2.2.1, Разд.3.3.2.1,
Разд.3.3.6 .2: Как объявить указатель на массив?
О: Обычно этого делать не нужно. Когда случайно говорят об указателе на
массив, обычно имеют в виду указатель на первый элемент массива.Вместо указателя на массив рассмотрим использование указателя на один
из элементов массива. Массивы типа T превращаются в указатели типа Т
(см. 1), что удобно; индексация или увеличение указателя
позволяет иметь доступ к отдельным элементам массива. Истинные
указатели на массивы при увеличении или индексации указывают на
следующий массив и в общем случае если и полезны, то лишь при
операциях с массивами массивов.Если действительно нужно объявить указатель на целый массив,
используйте что-то вроде "int (*ap)[N];" где N - размер массива.
Если размер массива неизвестен, параметр N может быть опущен, но
получившийся в результате тип " указатель на массив неизвестного
размера" - бесполезен.3. Исходя из того, что ссылки на массив превращаются в указатели,
скажите в чем разница для массиваint array[NROWS][NCOLUMNS];
между array и &array?
O: Согласно ANSI/ISO стандарту C, &array дает указатель типа
"указатель-на-массив-Т", на весь массив
В языке C до выхода стандарта ANSI оператор & в &array игнорировался,
порождая предупреждение компилятора. Все компиляторы C, встречая
просто имя массива, порождают указатель типа указатель-на-Т, т.е. на
первый элемент массива. (Cм. 1)
> Ещё раз, что же не нравится копилятору и как написать коректно?
#include <stdio.h>typedef unsigned char (*TMac)[6]; /* ахтунг */
void f(const TMac * mac)
{
puts(mac ? "mac" : "no mac");
}int main()
{
TMac mac;
f(&mac);
return 0;
}