Шаг 56.
Microsoft Visual C++ 2010. Язык С/С++. Указатели и структуры данных. Указатели на символы и возврат указателя из функции

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

    Символьная константа или массив символов в языке С - это строка символов с признаком конца (символом '\0').

    Если, например, имеем char а [10];, то а - это указатель на первый элемент массива а[0].

    Если, с другой стороны, имеем char *p=&a[0], то наряду с инициализацией а[] =" abc"; можем записать *р="abc". Компилятор в обоих случаях, начиная с адреса, помещенного в указатель р, разместит символы а, b, с.

    Следовательно, оперирование именем массива и указателем на этот массив равносильно. Но за исключением некоторого небольшого обстоятельства: если мы хотим записать строку символов в некоторое место памяти, то при объявлении

  char a[100];
компилятор выделит 100 байтов для помещения строки символов, и мы сможем записать в массив а[] свои символы. Если же объявить указатель char *p, то чтобы записать символы, начиная с адреса, указанного в р, указатель должен быть предварительно инициализирован, т. е. ему должен быть присвоен некий адрес, указывающий на участок, где будут располагаться объекты типа char. Этот участок должен быть получен либо с помощью функции new(), которая возвратит указатель на выделенный участок, после чего значение этого указателя надо будет присвоить указателю р, либо вы должны объявить массив символов размерности, соответствующей вводимой строке, и настроить указатель на этот массив. После этого можно работать с указателем.

    Кстати, функции тоже могут быть "указателями", т. е. возвращать указатели на объекты заданного типа. В этом случае функция при ее объявлении имеет вид:

<тип объекта, на который указывает указатель>* <имя функции>(аргументы функции).

    Например, рассмотрим функцию char* strsave(s). Приведем в качестве примера программу, работающую с указателями символьного типа. Программа содержит функции работы с символьными строками, в которых отражена работа с указателями.

// 56_1.cpp: главный файл проекта.

#include "stdafx.h"
#include <clocale>     //обязательно для функции setlocale()

#include <stdio.h>     //for getchar(),putchar()
#include <conio.h> 
#include <string.h> 

using namespace System;

#define MAXLINE 1000 
#define eof -1        //Ctrl+z
 
// --- Ввод строки с клавиатуры ---
int getline(char s[], int lim)
{
	int c,i;
	for(i=0; i<lim-1 && (c=getchar()) != eof && c != '\n'; i++)
		s[i]=c;
	s[i]='\0';
	i++;    //для подсчета количества 
	return i;
}

// --- Копирует t[] в s[] ---
void strcpy1 (char s[], char t[]) 
{
	int i=0; 
	while((s[i]=t[i])!='\0')
		i++;
}

// --- Копирует строку, на которую указывает указатель t, 
// в строку, на которую указывает указатель s ---
void strcpy2 (char *s, char *t)
{
	while((*s=*t) != '\0')
	{
		s++;
		t++;
	}
}

// --- Выделяет с помощью new() память по длине строки, на которую указывает s, 
// и в эту память помещает саму строку, а затем выдает указатель на начало 
// помещенной в буфер строки. ---
char *strsave(char *s)
{
	char *p;
	int i = strlen(s) + 1; 
	p =  new char [i]; //выделение памяти в куче
	if((p != NULL))
		strcpy2(p,s);  //копирует строку в кучу
	return p;  //возвращается указатель на строку, помещенную в кучу
}

void main()
{
	setlocale(LC_ALL,"Russian"); //функция setlocale() с аргументами
	                             //для корректного вывода кириллицы

	//Проверка strcpy1()
	printf("Задайте строку для strcpy1 >");
	char s[MAXLINE], t[MAXLINE];
	getline(t,MAXLINE); //ввод строки с клавиатуры в t
	strcpy1(s,t);
	printf("Входная строка = %s\nВыходная строка = %s\n",t,s);
	//Проверка strcpy2()
	printf("Задайте строку для strcpy2 >");
	getline(t,MAXLINE);   //ввод строки с клавиатуры в  t
	strcpy2(&s[0],&t[0]);
	printf("Входная строка = %s\nВыходная строка = %s\n",t,s);
	//Проверка strsave()
	printf("Задайте строку для strsave >");
	getline(s,MAXLINE);   //ввод строки в s
	char *p = strsave(&s[0]); //в р указатель на память, куда записана строка из s
	printf("Сохраненная строка = %s\n", p);
	delete [] p; //возврат памяти в кучу
	_getch();
}
Архив проекта можно взять здесь.

    Результат работы приложения изображен на рисунке 1.


Рис.1. Результат работы приложения

    Функция void strcpy1(char s[],char t[]) копирует массив в массив. Это происходит поэлементно с помощью цикла while, в заголовочно части его находится выражение, которое надо вычислить, чтобы принять решение о продолжении/завершении цикла.

    При вычислении выражения происходит поэлементная пересылка: пересылается один элемент, затем начинает работать тело while, в котором всего один оператор наращивания индекса элемента массива. Когда тело завершается, управление передается в заголовочную часть while, где снова вычисляется выражение, обеспечивающее пересылку следующего элемента массива в массив s и т. д., пока не будет переслан последний символ: '\0'. Когда это произойдет, результат выражения станет равным нулю, и функция завершится. Функция не возвращает никакого значения (ее тип void), но через параметр, адрес которого передается в функцию (массив s[]), возвращает копия массива t[].

    Параметрами функции void strcpy2(char *s, char *t) являются указател типа char. Эта функция копирует символы, начиная с адреса, на которы указывает указатель t, в область, на которую указывает указатель s. Перед применением этой функции (как отмечалось ранее) надо определить область памяти, на которую указывает указатель s: либо через массив, либо через функцию new(). В теле strcpy2() организован цикл посимвольного копирования символов из одной области в другую с помощью указателей этих областей: указатель *t передает элемент входной области, а указатель *s - выходной.

    После того как первый элемент перешлется, в теле while произойдет приращение значений указателей на единицу, которое позволит обратиться к следующему элементу объекта. Программа снова возвратится в заголовочную часть while, обеспечивающую пересылку второго элемента в область, на которую указывает s (и т. д., пока не будет переслан последний элемент - признак конца массива). При этом значение выражения в заголовочной части while станет равным нулю и циклическая конструкция while завершит работу.

    Функция char *strsave(char *s) копирует строку символов, на которую указывает указатель s, в буфер памяти, выделяемый стандартной функцией new(), и возвращает указатель на начало этого буфера, чтобы в дальнейшем можно было извлекать сохраненную в нем строку.

    Копирование производится с помощью ранее рассмотренной функции strcpy2(). Работа происходит следующим образом: длина исходной строки с учетом признака конца строки определяется с помощью стандартной функции strlen(). Затем с помощью функции new() выделяется участок памяти такого же размера, как длина входной строки. Перед тем как начать копирование с использованием указателя, возвращенного функцией new(), проверяется, успешно ли сработала эта функция (т. е. действительно ли выделено место в памяти). Это существенный вопрос, т. к. свободной памяти в данный момент могло не оказаться или мог произойти какой-либо сбой. Если память выделена, то new() возвращает ненулевой указатель. После выделения памяти происходит собственно копирование в буфер и возврат указателя, содержащего адрес начала области копирования.

    При пояснении основной программы стоит обратить внимание только на проверку strcpy2(). Так как эта функция работает с указателями, то ей мы и передаем указатели: адреса первых элементов массивов.


   Важное замечание. Обратите внимание на строку
  delete [] p; //возврат памяти в кучу
Считается "хорошим тоном" возвращать выделенную память в кучу, после того, когда переменная больше не нужна!

    На следующем шаге мы рассмотрим особенности использования массива массивов в качестве аргумента функции.




Предыдущий шаг Содержание Следующий шаг