2017-02-05 5 views
1

В языке программирования C переменная может иметь адрес памяти и значение. И как я понял, каждая функция также имеет адрес, а также данные, которые выделяются по этому адресу. Мой вопрос в том, каков смысл данных, на которые указывают эти функции?данные зарезервированы в функции в C

+0

Этот вопрос может касаться внутреннего представления функций и указателей функций. Ну, все детали зависят от платформы (особенно учитывая общие библиотеки/библиотеки DLL). –

ответ

0

Вы можете найти свой ответ here. Язык C поддерживает два вида распределения памяти через переменные в программах C:

  • Статических распределения: что происходит, когда вы объявляете статический или глобальные переменные. Каждая статическая или глобальная переменная определяет один блок пространства фиксированного размера. Пространство выделяется один раз, когда ваша программа запускается (часть операции exec) и никогда не освобождается.
  • Автоматическое распределение: происходит, когда вы объявляете автоматическую переменную, например аргумента функции в или локальной переменной. Пространство для автоматической переменной выделяется при вводе составного оператора, содержащего объявление, и освобождается при выходе этого составного оператора.

В GNU C размер автоматического хранилища может быть выражением, которое варьируется. В других реализациях C она должна быть константой.

+2

Указатели функций концептуально не указатели данных. –

2

Язык программирования С (как и любой язык программирования) а спецификация (в некоторых докладе). Это не программное обеспечение. Вероятно, вы должны прочитать отчет n1570 (проект спецификации C11).

Концептуально, функция не имеет каких-либо данных в C (но его код может ссылаться на статические адреса, содержат буквенные константы - включая pointers- и т.д. ...). Он имеет некоторое поведение, которое практически реализуется некоторыми кодом. Что такое код не определяется стандартом C.

Практически говоря, и это зависит от конкретной реализации (смотреть на разницу между Harvard машины & computer architectures и Von Neumann из них), указатель на функцию некоторый адрес machine code (часто мишенью для CALLmachine instruction перевод вызовов C к нему).

На рабочих столах & ноутбуков & таблетки с некоторой обычной операционной системой (например, Linux, Windows, MacOSX, прошивка, Android ...) -всом являются фон Нейман архитектура: x86-64 или АРМЫ, ваш процесс имеет один virtual address space содержащие code segments и data segments и данные кучи. Тогда указатели функций и указатели данных имеют один и тот же вид, и это практически, что значимо для их броска. Канонический пример - использование POSIX dlsym: вы часто приводите его результат к некоторому указателю функции (например, внутри некоторого plugin, который является dynamically loaded с dlopen). Адрес функции - , фактически говорящий адрес его первой машинной инструкции (сидящий в некотором сегменте кода в общем адресном пространстве). Прочитано this & that для творческих примеров.Другим полезным примером являются JIT compilation библиотеки, такие как asmjit, GNU lightning, libgccjit, LLVM: они позволяют генерировать машинный код во время выполнения и получать от них (свежую) функцию. Ни dlsym, ни JIT библиотеки в строгом смысле соответствует стандарту C, так как в чисто стандартной программе, соответствующие C множество функций статический известно и любая функция указатель должен указывать на какую-существующого функции тех же сигнатуры (читайте около calling conventions & ABI s), в противном случае это undefined behavior.

На некоторых embedded computers с архитектурой Гарварда (например, Arduino) код и данные размещаются в разных пространствах, а адрес кода может не иметь того же количества бит, что и адрес данных. В таких системах литье между функциями и указателями данных бессмысленно (если вы не погрузитесь в детали глубокой реализации). Стандарт C был определен как достаточно общий, чтобы учитывать такие странные компьютеры.

Читайте также много о closures и continuations. Стандарт C их не имеет (следовательно, callbacksусловно взять некоторый аргумент данных клиента). Вероятно, вы узнаете много, прочитав SICP. Читайте также о homoiconicity.

Читайте также о Operating Systems: Если вы используете Linux (который я рекомендую, потому что это в основном из free software, исходный код вы можете изучить), прочитать Advanced Linux Programming. Читайте также Operating Systems: Three Easy Pieces.

Другими словами: ваш вопрос (по указателям функций и адресам) имеет разные подходы. Догматический подход к программированию на языке программирования (и проблема заключается в глубоком понимании указателей функций в стандартах C CompCert & Frama-C); прагматичная операционная система и конкретный подход к внедрению (а затем это зависит от вашего компьютера, его instruction set и его ОС и даже ваших флагов C compiler - и версии - и optimization, и вы можете даже иметь некоторые «магические механизмы» - например, dlsym & dlopen или библиотеки компиляции JIT - создают функции во время выполнения, что составляет magic, потому что стандарты C не думают об этом).

2

Вы уже получили (хорошие) ответы, но я думаю, что некоторые (неясно?) Факт о С следует отметить, что касается вашего вопроса:

В языке программирования C, переменная может иметь адрес памяти и значение.

На самом деле определяющее свойство переменного является то, что всегда имеет значения - если это неинициализированное, семантический она все еще имеет значение, только то, что это значение является «неопределенным значением» и чтение «неопределенного значения "вызывает неопределенное поведение.

Но, это важно, но не каждая переменная в C имеет адрес! Существует этот небольшой классификатор хранения register, который точно означает, что большинство людей не полностью понимают. Наиболее распространенная и неправильная интерпретация заключается в том, что register означает, что переменная должна помещаться только в регистры.Проблема заключается в том, что существуют архитектуры команд, в которых регистры не существуют, но C был разработан для того, чтобы быть по-прежнему жизнеспособным для них.

Истинный смысл register классификатора есть, что вы не можете взять адрес переменных, которая является регистром, что означает, что вы не можете создавать указатели к нему.

. Результатом этого является то, что переменная, которая равна register, имеет значение только для нее. И вполне логично, чтобы компилятор C генерировал код, который полностью отбрасывает «место» (будь то регистр, местоположение памяти или что-то совсем другое), где его значение пришло, если оно способно верно воссоздать значение в что семантически соответствует тексту программы. Это также означает, что вполне законно выполнять полное перерасчет того, что должно было быть выполнено для получения окончательного значения. Вот почему применение квалификатора хранилища register к переменной может привести к резкому увеличению размера кода и капля производительности.

В качестве такого классификатора register хранений не является механизм для оптимизации кода, но должен рассматриваться как инструмент специального назначения для написания кода, который не является ни время, ни размером критическим, но должен работать при очень специфических, жестких ограничениях. Одним из примеров может быть, например, загрузчик или код инициализации системы, задача которого состоит в том, чтобы инициализировать доступ к памяти в первую очередь и работать с несколькими байтами - или даже без - полезной памяти, но может повторно вычислять требуемые значения для каждого шага.

+0

Вопрос был больше о указателях функций, чем о переменных. –

+0

@BasileStarynkevitch: Да, но само название вопроса не совсем ясно. Люди, которые задаются вопросом о том, «где переменные в области функций» (ака автоматическое хранилище) проживают, могут преодолеть этот вопрос. И я чувствовал, что определенная подтема также требует рассмотрения. – datenwolf

0

Указатели функций указывают на блоки команд машины, которые выполняются при вызове функции.

Скажем у вас есть это:

#include <stdio.h> 
int plus_42(int x) 
{ 
    int res=x+42; 
    printf("%d + 42 = %d\n", x,res); 
    return res; 
} 
int main() 
{ 
    return plus_42(1); 
} 

Если вы скомпилировать его, связать его и запустить objdump -d на результат:

gcc plus_42.c && objdump -d a.out 

вы получите (в зависимости от вашей архитектуры, что-то вроде:

0000000000400536 <plus_42>: 
    400536:  55      push %rbp 
    400537:  48 89 e5    mov %rsp,%rbp 
    40053a:  48 83 ec 20    sub $0x20,%rsp 
    40053e:  89 7d ec    mov %edi,-0x14(%rbp) 
    400541:  8b 45 ec    mov -0x14(%rbp),%eax 
    400544:  83 c0 2a    add $0x2a,%eax 
    400547:  89 45 fc    mov %eax,-0x4(%rbp) 
    40054a:  8b 55 fc    mov -0x4(%rbp),%edx 
    40054d:  8b 45 ec    mov -0x14(%rbp),%eax 
    400550:  89 c6     mov %eax,%esi 
    400552:  bf 04 06 40 00   mov $0x400604,%edi 
    400557:  b8 00 00 00 00   mov $0x0,%eax 
    40055c:  e8 af fe ff ff   callq 400410 <[email protected]> 
    400561:  8b 45 fc    mov -0x4(%rbp),%eax 
    400564:  c9      leaveq 
    400565:  c3      retq 

0000000000400566 <main>: 
    400566:  55      push %rbp 
    400567:  48 89 e5    mov %rsp,%rbp 
    40056a:  bf 01 00 00 00   mov $0x1,%edi 
    40056f:  e8 c2 ff ff ff   callq 400536 <plus_42> 
    400574:  5d      pop %rbp 
    400575:  c3      retq 
    400576:  66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1) 
    40057d:  00 00 00 

plus некоторые шаблоны.

Здесь 0000000000400536 и 0000000000400566 являются адреса main и plus_42 (= указатели, которые main и plus_42 пункт), соответственно, и шестнадцатеричные числа, которые вы видите во 2-м столбце данные, которые декодируются в 3d колонке в человеческие читаемые имена машинных инструкций, которые представляют данные.

+1

Я бы предложил вместо этого скомпилировать с 'gcc -S -fverbose-asm -O plus_42.c' и изучить испущенный' plus_42.s' –

+0

@BasileStarynkevitch Спасибо за предложение. Я чувствую фактическую сборку с гексами, а фактические (несимвольные) адреса лучше иллюстрируют мою точку зрения. – PSkocik

Смежные вопросы