Изменение строки, жестко закодированной в программе, встречается относительно редко;char *foo = "literal";
, вероятно, более распространено, и неизменность строки может быть одной из причин.
По крайней мере, я никогда не видел "производственную" программу, которая делает этос символьными строками.(И мне трудно думать о программе, которая использует арифметику указателей, а не подписку на массивы для массивов других типов.)
3) Книга говорит, что использование static
прекрасно в этомконкретная функция:
/* month_name: return name of n-th month */
char *month_name(int n)
{
static char *name[] = {
"Illegal month",
"January", "February", "March",
"April", "May", "June",
"July", "August", "September",
"October", "November", "December"
};
return (n < 1 || n > 12) ? name[0] : name[n];
}
Я не понимаю, почему именно это хорошее использование static
.Это потому, что char *name[]
будет удалено после возврата функции, если это не static
(потому что это локальная переменная)?Значит ли это, что в c вы не можете делать такие вещи, как:
void testFunction(){
int x = 1;
return x;
}
Без удаления x перед использованием возвращаемого значения?(Извините, я думаю, это может быть не вопрос указателя, но это было в главе, посвященной указателям.)по крайней мере, GCC может определить, что строки не изменены, и сохранить их в сегменте данных .rodata
только для чтения.Однако это может быть оптимизация со строковыми литералами.Ваш пример с другим примитивным типом данных (int
) также работает нормально, потому что C передает все по значению как при вызовах функций, так и при возвратах функций.Однако, если вы возвращаете указатель на объект, размещенный в стеке, то static
абсолютно необходим, поскольку он определяет, где в памяти находится объект:
$ cat stackarray.c ; make stackarray
#include <stdio.h>
struct foo { int x; };
struct foo *bar() {
struct foo array[2];
array[0].x=1;
array[1].x=2;
return &array[1];
}
int main(int argc, char* argv[]) {
struct foo* fp;
fp = bar();
printf("foo.x: %d\n", fp->x);
return 0;
}
cc stackarray.c -o stackarray
stackarray.c: In function ‘bar’:
stackarray.c:9:2: warning: function returns address of local variable
Если вы измените продолжительность храненияот array
до static
, то возвращаемый адрес не автоматически назначается и будет продолжать работать даже после возврата функции:
$ cat staticstackarray.c ; make staticstackarray ; ./staticstackarray
#include <stdio.h>
struct foo { int x; };
struct foo *bar() {
static struct foo array[2];
array[0].x=1;
array[1].x=2;
return &array[1];
}
int main(int argc, char* argv[]) {
struct foo* fp;
fp = bar();
printf("foo.x: %d\n", fp->x);
return 0;
}
cc staticstackarray.c -o staticstackarray
foo.x: 2
Вы можетеПосмотрите, где изменяется распределение памяти между stackarray
и staticstackarray
:
$ readelf -S stackarray | grep -A 3 '\.data'
[24] .data PROGBITS 0000000000601010 00001010
0000000000000010 0000000000000000 WA 0 0 8
[25] .bss NOBITS 0000000000601020 00001020
0000000000000010 0000000000000000 WA 0 0 8
$ readelf -S staticstackarray | grep -A 3 '\.data'
[24] .data PROGBITS 0000000000601010 00001010
0000000000000010 0000000000000000 WA 0 0 8
[25] .bss NOBITS 0000000000601020 00001020
0000000000000018 0000000000000000 WA 0 0 8
Раздел .bss
в версии без static
на 8 байт меньше, чем раздел .bss
в версии сstatic
.Эти 8 байтов в разделе .bss
обеспечивают возвращаемый постоянный адрес.
Таким образом, вы можете видеть, что случай со строками на самом деле не имеет значения - по крайней мере, GCC это не волнует -но указатели на другие типы объектов, static
имеют все значение в мире.
Однако большинство функций, которые возвращают данные в хранилище function-local- static
, вышли из строя.strtok(3)
, например, извлекает токены из строки, и если последующие вызовы strtok(3)
включают NULL
в качестве первого аргумента, чтобы указать, что функция должна повторно использовать строку, переданную в first вызов.Это удобно, но означает, что программа никогда не может токенизировать две отдельные строки одновременно, а многопоточные программы не могут надежно использовать эту процедуру.Таким образом, доступна версия reentrant , strtok_r(3)
, которая принимает дополнительный аргумент для хранения информации между вызовами.man -k _r
покажет удивительное количество функций, в которых доступны реентерабельные версии, и основным изменением является сокращение использования static
в функциях.
4) Есть несколько сложных объявлений, таких как
char (*(*x())[])()
Я действительно смущен тем, что происходит.То есть часть x()
означает функцию x
, которая возвращает указатель?Но какого рода указатель возвращает просто «» без int
, void
или w / e.Или это означает, что указатель на функцию (но я подумал, что это будет похоже на (*x)())
? А потом после добавления скобок (потому что я предполагаю, что скобки имеют следующий приоритет) ... что это? Массив функций?
Этот вид связан с моей путаницей с указателями на функции. Если у вас есть что-то вроде
int (*func)()
Это означает, что указатель на функцию, которая возвращает int, и имя этого указателя является func
, но что это значит, когда это похоже на int
(*x[3])()
. Я не понимаю, как вы можете заменить имя указателя на массив.
Во-первых, не паникуйте. Вы будетепочти никогда ничего не нужно , это сложно. Иногда очень удобно иметь таблицу указателей функций и вызывать следующий на основе диаграммы перехода состояний. Иногда вы устанавливаете обработчики сигналов с sigaction(2)
.Тогда вам понадобятся немного более сложные указатели функций, однако, если вы используете cdecl(1)
для расшифровки того, что вам нужно, это будет иметь смысл:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
cdecl(1)
понимает только подмножество of C нативных типов, поэтому замените siginfo_t
на void
, и вы можете примерно увидеть, что требуется:
$ cdecl
Type `help' or `?' for help
cdecl> explain void (*sa_sigaction)(int, void *, void *);
declare sa_sigaction as pointer to function
(int, pointer to void, pointer to void) returning void
Программирование на Expert C: Deep C Secrets имеет отличную главу, посвященнуюдля понимания более сложных объявлений и даже включает версию cdecl
, на случай, если вы захотите расширить ее, чтобы включить больше типов и обработку typedef
.Это стоит прочитать.