a = str1; b = str2;
А. Богатырев, 1992-95 - 99 - Си в UNIX
while( *b )
*++a = *b++;
printf( "str=%s a=%s\n", str1, a );
}
Ответ:
str=xyzdef a=def
str=xxyzef a=zef
2.38. Что печатает программа?
char *s;
for(s = "Ситроен"; *s; s+= 2){
putchar(s[0]); if(!s[1]) break;
}
putchar('\n');
2.39. Что напечатает программа? Рассмотрите продвижение указателя s, указателей -
элементов массива strs[]. Разберитесь с порядком выполнения операций. В каких случаях
++ изменяет указатель, а в каких - букву в строке? Нарисуйте себе картинку, изобража-
ющую состояние указателей - она поможет вам распутать эти спагетти. Уделите разбору
этого примера достаточное время!
#include /* определение NULL */
/* Латинский алфавит: abcdefghijklmnopqrstuvwxyz */
char *strs[] = {
"abcd","ABCD","0fpx","159",
"hello","-gop","A1479",NULL
};
main(){
char c, **s = strs, *p;
c = *++*s; printf("#1 %d %c %s\n", s-strs, c, *s);
c = **++s; printf("#2 %d %c %s\n", s-strs, c, *s);
c = **s++; printf("#3 %d %c %s\n", s-strs, c, *s);
c = ++**s; printf("#4 %d %c %s\n", s-strs, c, *s);
c = (**s)++; printf("#5 %d %c %s\n", s-strs, c, *s);
c = ++*++*s; printf("#6 %d %c %s\n", s-strs, c, *s);
c = *++*s++; printf("#7 %d %c %s %s\n",
s-strs, c, *s, strs[2]);
c = ++*++*s++; printf("#8 %d %c %s %s\n",
s-strs, c, *s, strs[3]);
c = ++*++*++s; printf("#9 %d %c %s\n", s-strs,c,*s);
c = ++**s++; printf("#10 %d %c %s\n",s-strs,c,*s);
p = *s; c = ++*(*s)++;
printf("#11 %d %c %s %s %s\n",s-strs,c,*s,strs[6],p);
c = ++*((*s)++); printf("#12 %d %c %s %s\n",
s-strs, c, *s, strs[6]);
c = (*++(*s))++; printf("#13 %d %c %s %s\n",
s-strs, c, *s, strs[6]);
for(s=strs; *s; s++)
printf("strs[%d]=\"%s\"\n", s-strs, *s);
putchar('\n');
}
Печатается:
А. Богатырев, 1992-95 - 100 - Си в UNIX
#1 0 b bcd strs[0]="bcd"
#2 1 A ABCD strs[1]="ABCD"
#3 2 A 0fpx strs[2]="px"
#4 2 1 1fpx strs[3]="69"
#5 2 1 2fpx strs[4]="hello"
#6 2 g gpx strs[5]="iop"
#7 3 p 159 px strs[6]="89"
#8 4 6 hello 69
#9 5 h hop
#10 6 i A1479
#11 6 B 1479 1479 B1479
#12 6 2 479 479
#13 6 7 89 89
Учтите, что конструкция
char *strs[1] = { "hello" };
означает, что в strs[0] содержится указатель на начальный байт безымянного массива,
содержащего строку "hello". Этот указатель можно изменять! Попробуйте составить еще
подобные примеры из *, ++, ().
2.40. Что печатает программа?
char str[25] = "Hi, ";
char *f(char **s){ int cnt;
for(cnt=0; **s != '\0'; (*s)++, ++cnt);
return("ny" + (cnt && (*s)[-1] == ' ') + (!cnt));
}
void main(void){ char *s = str;
if( *f(&s) == 'y') strcat(s, "dude");
else strcat(s, " dude");
printf("%s\n", str);
}
Что она напечатает, если задать
char str[25]="Hi,"; или char str[25]="";
2.41. В чем состоит ошибка? (Любимая ошибка начинающих)
main(){
char *buf; /* или char buf[]; */
gets( buf );
printf( "%s\n", buf );
}
Ответ: память под строку buf не выделена, указатель buf не проинициализирован и смот-
рит неизвестно куда. Надо было писать например так:
char buf[80];
или
char mem[80], *buf = mem;
Обратите на этот пример особое внимание, поскольку, описав указатель (но никуда его
не направив), новички успокаиваются, не заботясь о выделении памяти для хранения дан-
ных. Указатель должен указывать на ЧТО-ТО, в чем можно хранить данные, а не "висеть",
указывая "пальцем в небо"! Запись информации по "висячему" указателю разрушает память
программы и приводит к скорому (но часто не немедленному и потому таинственному)
краху.
А. Богатырев, 1992-95 - 101 - Си в UNIX
Вот программа, которая также использует неинициализированный указатель. На
машине SPARCstation 20 эта программа убивается операционной системой с диагностикой
"Segmentation fault" (SIGSEGV). Это как раз и значит обращение по указателю, указы-
вающему "пальцем в небо".
main(){
int *iptr;
int ival = *iptr;
printf("%d\n", ival);
}
2.42. Для получения строки "Life is life" написана программа:
main(){
char buf[ 60 ];
strcat( buf, "Life " );
strcat( buf, "is " );
strcat( buf, "life" );
printf( "%s\n", buf );
}
Что окажется в массиве buf?
Ответ: в начале массива окажется мусор, поскольку автоматический массив не инициали-
зируется байтами '\0', а функция strcat() приписывает строки к концу строки. Для исп-
равления можно написать
*buf = '\0';
перед первым strcat()-ом, либо вместо первого strcat()-а написать
strcpy( buf, "Life " );
2.43. Составьте макроопределение copystr(s1, s2) для копирования строки s2 в строку
s1.
2.44. Составьте макроопределение lenstr(s) для вычисления длины строки.
Многие современные компиляторы сами обращаются с подобными короткими (1-3 опера-
тора) стандартными функциями как с макросами, то есть при обращении к ним генерят не
вызов функции, а подставляют текст ее тела в место обращения. Это делает объектный
код несколько "толще", но зато быстрее. В расширенных диалектах Си и в Си++ компиля-
тору можно предложить обращаться так и с вашей функцией - для этого функцию следует
объявить как inline (такие функции называются еще "intrinsic").
2.45. Составьте рекурсивную и нерекурсивную версии программы инвертирования (зер-
кального отображения) строки:
abcdef --> fedcba.
2.46. Составьте функцию index(s, t), возвращающую номер первого вхождения символа t
в строку s; если символ t в строку не входит, функция возвращает -1.
Перепишите эту функцию с указателями, чтобы она возвращала указатель на первое
вхождение символа. Если символ в строке отсутствует - выдавать NULL. В UNIX System-V
такая функция называется strchr. Вот возможный ответ:
char *strchr(s, c) register char *s, c;
{ while(*s && *s != c) s++;
return *s == c ? s : NULL;
}
А. Богатырев, 1992-95 - 102 - Си в UNIX
Заметьте, что p=strchr(s,'\0'); выдает указатель на конец строки. Вот пример исполь-
зования:
extern char *strchr();
char *s = "abcd/efgh/ijklm";
char *p = strchr(s, '/');
printf("%s\n", p==NULL ? "буквы / нет" : p);
if(p) printf("Индекс вхождения = s[%d]\n", p - s );
2.47. Напишите функцию strrchr(), указывающую на последнее вхождение символа.
Ответ:
char *strrchr(s, c) register char *s, c;
{ char *last = NULL;
do if(*s == c) last = s; while(*s++);
return last;
}
Вот пример ее использования:
extern char *strrchr();
char p[] = "wsh"; /* эталон */
main(argc, argv) char *argv[];{
char *s = argv[1]; /* проверяемое имя */
/* попробуйте вызывать
* a.out csh
* a.out /bin/csh
* a.out wsh
* a.out /usr/local/bin/wsh
*/
char *base =
(base = strrchr(s, '/')) ? base+1 : s;
if( !strcmp(p, base))
printf("Да, это %s\n" , p);
else printf("Нет, это %s\n", base);
/* еще более изощренный вариант: */
if( !strcmp(p,(base=strrchr(s,'/')) ? ++base :
(base=s))
) printf("Yes %s\n", p);
else printf("No %s\n", base);
}
2.48. Напишите макрос substr(to,from,n,len) который записывает в to кусок строки
from начиная с n-ой позиции и длиной len. Используйте стандартную функцию strncpy.
Ответ:
#define substr(to, from, n, len) strncpy(to, from+n, len)
или более корректная функция:
А. Богатырев, 1992-95 - 103 - Си в UNIX
char *substr(to, from, n, len) char *to, *from;
{
int lfrom = strlen(from);
if(n < 0 ){ len += n; n = 0; }
if(n >= lfrom || len <= 0)
*to = '\0'; /* пустая строка */
else{
/* длина остатка строки: */
if(len > lfrom-n) len = lfrom - n;
strncpy(to, from+n, len);
to[len] = '\0';
}
return to;
}
2.49. Напишите функцию, проверяющую, оканчивается ли строка на ".abc", и если нет -
приписывающую ".abc" к концу. Если же строка уже имеет такое окончание - ничего не
делать. Эта функция полезна для генерации имен файлов с заданным расширением. Сде-
лайте расширение аргументом функции.
Для сравнения конца строки s со строкой p следует использовать:
int ls = strlen(s), lp = strlen(p);
if(ls >= lp && !strcmp(s+ls-lp, p)) ...совпали...;
2.50. Напишите функции вставки символа c в указанную позицию строки (с раздвижкой
строки) и удаления символа в заданной позиции (со сдвижкой строки). Строка должна
изменяться "на месте", т.е. никуда не копируясь. Ответ:
/* удаление */
char delete(s, at) register char *s;
{
char c;
s += at; if((c = *s) == '\0') return c;
while( s[0] = s[1] ) s++;
return c;
}
/* либо просто strcpy(s+at, s+at+1); */
/* вставка */
insert(s, at, c) char s[], c;
{
register char *p;
s += at; p = s;
while(*p) p++; /* на конец строки */
p[1] = '\0'; /* закрыть строку */
for( ; p != s; p-- )
p[0] = p[-1];
*s = c;
}
2.51. Составьте программу удаления символа c из строки s в каждом случае, когда он
встречается.
Ответ:
А. Богатырев, 1992-95 - 104 - Си в UNIX
delc(s, c) register char *s; char c;
{
register char *p = s;
while( *s )
if( *s != c ) *p++ = *s++;
else s++;
*p = '\0'; /* не забывайте закрывать строку ! */
}
2.52. Составьте программу удаления из строки S1 каждого символа, совпадающего с
каким-либо символом строки S2.
2.53. Составьте функцию scopy(s,t), которая копирует строку s в t, при этом символы
табуляции и перевода строки должны заменяться на специальные двухсимвольные последо-
вательности "\n" и "\t". Используйте switch.
2.54. Составьте функцию, которая "укорачивает" строку, заменяя изображения спецсим-
волов (вроде "\n") на сами эти символы ('\n'). Ответ:
extern char *strchr();
void unquote(s) char *s;
{ static char from[] = "nrtfbae",
to [] = "\n\r\t\f\b\7\33";
char c, *p, *d;
for(d=s; c = *s; s++)
if( c == '\\'){
if( !(c = *++s)) break;
p = strchr(from, c);
*d++ = p ? to[p - from] : c;
}else *d++ = c;
*d = '\0';
}
2.55. Напишите программу, заменяющую в строке S все вхождения подстроки P на строку
Q, например:
P = "ура"; Q = "ой";
S = "ура-ура-ура!";
Результат: "ой-ой-ой!"
2.56. Кроме функций работы со строками (где предполагается, что массив байт заверша-