Указатель против значения
Часть раздела Object Oriented Programming путешествия по C на Coddy — урок 9 из 61.
Когда вы передаете структуру в функцию, C предоставляет вам два варианта: передать ее по значению (копирование всей структуры) или передать ее по указателю (копирование только адреса). Этот выбор существенно влияет на производительность.
Передача по значению создает полную копию структуры в стеке. Для небольшой структуры с несколькими целыми числами это нормально. Но представьте себе структуру размером в сотни байт — каждый вызов функции копирует все эти данные:
typedef struct {
char name[100];
int scores[50];
double average;
} Student;
// По значению: копирует ~608 байт при каждом вызове
void print_by_value(Student s) {
printf("%s: %.2f\n", s.name, s.average);
}
// По указателю: копирует только 8 байт (адрес)
void print_by_pointer(const Student *s) {
printf("%s: %.2f\n", s->name, s->average);
}
Синтаксис также различается. При использовании параметра-значения вы используете оператор «точка» (s.name). При использовании указателя вы используете оператор «стрелка» (s->name).
Версия с указателем более эффективна, так как она передает только адрес памяти — обычно 8 байт независимо от размера структуры.
Вот почему программисты на C почти всегда передают структуры по указателю. Используйте const, когда функция не должна изменять данные, и обычный указатель, когда должна. Вы получаете как эффективность, так и ясность намерений.
Задание
ЛегкоДавайте создадим модуль Product, который демонстрирует разницу между передачей структур по значению и по указателю. Вы создадите две функции отображения — одну, которая получает всю структуру как копию, и другую, которая получает только указатель на нее.
Вы создадите три файла:
product.h: Объявите структуруProductс тремя членами:char name[50],double priceиint quantity. Также объявите две функции:display_by_value— принимаетProductпо значению и выводит информацию о немdisplay_by_pointer— принимаетconst Product *и выводит ту же информацию
PRODUCT_H.product.c: Реализуйте обе функции. Обратите внимание на разницу в синтаксисе: версия по значению использует оператор «точка» (p.name), в то время как версия с указателем использует оператор «стрелка» (p->name). Обе функции должны выводить данные в одинаковом формате.main.c: Создайте Product, затем вызовите обе функции отображения, чтобы показать, что они выдают идентичный результат, несмотря на использование разных стилей параметров.
Вы получите три входных значения: название продукта (строка), цена (double) и количество (целое число).
В вашем основном файле инициализируйте Product этими значениями, затем вызовите display_by_value, а за ней — display_by_pointer.
Обе функции должны выводить данные в следующем формате:
Product: {name}, Price: {price}, Qty: {quantity}Используйте %.2f для цены, чтобы отобразить два знака после запятой.
Например, при входных данных Laptop, 999.99 и 5 результат будет следующим:
Product: Laptop, Price: 999.99, Qty: 5
Product: Laptop, Price: 999.99, Qty: 5Шпаргалка
При передаче структур в функции вы можете выбирать между передачей по значению (копирование всей структуры) или передачей по указателю (копирование только адреса).
Передача по значению создает полную копию структуры:
void print_by_value(Student s) {
printf("%s: %.2f\n", s.name, s.average); // Используйте оператор "точка"
}
Передача по указателю передает только адрес в памяти:
void print_by_pointer(const Student *s) {
printf("%s: %.2f\n", s->name, s->average); // Используйте оператор "стрелка"
}
Ключевые различия:
- При передаче по значению используется оператор "точка" (
s.member) - При передаче по указателю используется оператор "стрелка" (
s->member) - Указатели более эффективны для больших структур (всего 8 байт против размера всей структуры)
- Используйте
constс указателями, если функция не должна изменять данные
Программисты на C обычно предпочитают передавать структуры по указателю для лучшей производительности.
Попробуйте сами
#include <stdio.h>
#include <string.h>
#include "product.h"
int main() {
char name[50];
double price;
int quantity;
// Считать входные данные
scanf("%s", name);
scanf("%lf", &price);
scanf("%d", &quantity);
// TODO: Создать переменную Product и инициализировать её входными значениями
// Hint: Используйте strcpy, чтобы скопировать name в поле name структуры
// TODO: Вызвать display_by_value с Product
// TODO: Вызвать display_by_pointer с указателем на Product
return 0;
}
В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.
Все уроки раздела Object Oriented Programming
1Основы модульного программирования
Заголовочные файлыСтражи включенияИсходные файлыСтатические функцииПовторение: Модульный калькулятор4Инкапсуляция
Концепция непрозрачных указателейОпределение непрозрачных структурГеттеры и сеттерыВалидация в сеттерахИтоги: Секретный ящик2Объекты и методы
Структуры как объектыУказатель 'Self'Константная корректностьУказатель против значенияВспомогательные методыИтоги: Point Manager5Проект: Простой банковский счет
Настройка проектаРеализация счета3Жизненный цикл объекта
Паттерн «Конструктор»Паттерн «Деструктор»Инициализация в стекеГлубокое копированиеПовторение: String Wrapper6Наследование через композицию
Встраивание структурПравило первого элементаДоступ к элементам родителяUpcastingПовторение: Иерархия фигур9Проект: Рисование фигур
Обзор проектаРеализация круга