2016-04-11 2 views
3

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

В других терминах я хотел бы передать printf содержание foo по значению и, таким образом, передать эти аргументы в стек.

#include <stdarg.h> 
#include <stdio.h> 

void main() 
{ 
    int foo[] = {1,2,3,4}; 

    printf("%d, %d, %d, %d\n", foo); 
} 

Я знаю, что этот пример выглядит глупо, потому что я могу использовать printf("%d, %d, %d, %d\n", 1,2,3,4);. Только представьте, я звоню void bar(char** a, ...) вместо этого и массив что-то я получаю от RS232 ...

EDIT

Другими словами, я хотел бы избежать этого:

#include <stdarg.h> 
#include <stdio.h> 

void main() 
{ 
    int foo[] = {1,2,3,4}; 

    switch(sizeof(foo)) 
    { 
     case 1: printf("%d, %d, %d, %d\n", foo[0]); break; 
     case 2: printf("%d, %d, %d, %d\n", foo[0], foo[1]); break; 
     case 3: printf("%d, %d, %d, %d\n", foo[0], foo[1], foo[2]); break; 
     case 4: printf("%d, %d, %d, %d\n", foo[0], foo[1], foo[2], foo[3]); break; 
     ... 
    } 
} 
+3

Нет, это невозможно. Если у вас есть массив, вам нужно вызвать функцию, которая может обрабатывать переданный массив.В случае вашего простого примера вам нужно создать функцию-оболочку, которая принимает массив как аргумент и петли над массивом и в свою очередь вызывает 'printf'. –

+0

Размер массива известен только в том случае, если он был создан статически! Если вы хотите передать массив функции, вы должны определить элемент хвоста или передать количество элементов в функцию. – jboockmann

+1

Создайте [свой собственный printf] (http://stackoverflow.com/questions/16647278/minimal-fast-implementation-of-sprintf-for-embedded). Во время обработки вы поймете, где проблема с этим вопросом! – imbearr

ответ

2

Я хотел бы передать printf содержание foo по значению и, таким образом, передать эти аргументы в стек.

Вы не можете передать массив по значению. Не по «нормальному» вызову функции, а не по varargs (который, в основном, просто отличается от , читает стек).

Всякий раз, когда вы используете массив как аргумент функции, то вызываемая функция принимает является указателем.

Самый простой пример для этого - массив символов, a.k.a. "string".

int main() 
{ 
    char buffer1[100]; 
    char buffer2[] = "Hello"; 
    strcpy(buffer2, buffer1); 
} 

Что strcpy() «видит» не два массива, а два указателя:

char * strcpy(char * restrict s1, const char * restrict s2) 
{ 
    // Yes I know this is a naive implementation in more than one way. 
    char * rc = s1; 
    while ((*s1++ = *s2++)); 
    return rc; 
} 

(Вот почему размер массива известно лишь в рамках массив был объявлен in. После того, как вы передадите его, это просто указатель, где нет места для размещения информации о размере.)

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

Вы может передать массив по ссылке и делать полезные вещи с ним в вызываемой функции, если:

  1. вы передаете (указатель на) массив и количество элементов (думаю argc/argv), или
  2. вызывающий и вызываемый абонент согласен на фиксированный размер, или
  3. вызывающий и вызываемый абонент согласен на массив «каким-то образом» заканчивается.

Стандартный printf() делает последний для "%s" и строк (которые оканчивающихся '\0'), но не оборудован, чтобы сделать это с, как в вашем примере, в int[] массив. Таким образом, вам придется написать свой собственный printme().

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

0

Хорошо, посмотрите на этот пример, из моего кода. Это простой способ.

void my_printf(char const * frmt, ...) 
{ 
    va_list argl; 
    unsigned char const * tmp; 
    unsigned char chr; 
    va_start(argl,frmt); 
    while ((chr = (unsigned char)*frmt) != (char)0x0) { 
     frmt += 1; 
     if (chr != '%') { 
      dbg_chr(chr); 
      continue; 
     } 
     chr = (unsigned char)*frmt; 
     frmt += 1; 
     switch (chr) { 
     ... 
     case 'S': 
      tmp = va_arg(argl,unsigned char const *); 
      dbg_buf_str(tmp,(uint16_t)va_arg(argl,int)); 
      break; 
     case 'H': 
      tmp = va_arg(argl,unsigned char const *); 
      dbg_buf_hex(tmp,(uint16_t)va_arg(argl,int)); 
      break; 
     case '%': dbg_chr('%'); break; 
     } 
    } 
    va_end(argl); 
} 

Там dbg_chr (uint8_t байт) падение байт USART и включить передатчик.

Использование Пример:

#define TEST_LEN 0x4 
uint8_t test_buf[TEST_LEN] = {'A','B','C','D'}; 
my_printf("This is hex buf: \"%H\"",test_buf,TEST_LEN); 
1

Короткий ответ: Нет, вы не можете это сделать, это невозможно.

Немного более длинный ответ: Ну, может быть, вы можете это сделать, но это супер сложно. Вы в основном пытаетесь вызвать функцию с списком аргументов, который неизвестен до времени выполнения. Существуют библиотеки, которые могут помочь вам динамически создавать списки аргументов и вызывать функции с ними; одна библиотека - libffi: https://sourceware.org/libffi/.

Смотрите также вопрос 15.13 в C FAQ списке: How can I call a function with an argument list built up at run time?

Смотрите также эти предыдущие StackOverflow вопросы:
C late binding with unknown arguments
How to call functions by their pointers passing multiple arguments in C?
Calling a variadic function with an unknown number of parameters

+0

[** Предоставить контекст для ссылок **] (http://stackoverflow.com/help/how-to-answer). Рекомендуются ссылки на внешние ресурсы, но, пожалуйста, добавьте контекст вокруг ссылки, чтобы ваши соплеменники какая-то идея, что это такое и почему она есть. Всегда указывайте наиболее значимую часть важной ссылки, если целевой сайт недоступен или постоянно находится в автономном режиме. – DevSolar

1

Как уже было сказано, вы не можете передать массив по значению в a va_arg напрямую. Это возможно, хотя он упакован внутри struct. Он не переносимый, но можно сделать некоторые вещи, когда реализация известна.

Вот пример, который может помочь.

void call(size_t siz, ...); 

struct xx1 { int arr[1]; }; 
struct xx10 { int arr[10]; }; 
struct xx20 { int arr[20]; }; 

void call(size_t siz, ...) 
{ 
    va_list va; 
    va_start(va, siz); 

    struct xx20 x = va_arg(va, struct xx20); 
    printf("HEXDUMP:%s\n", HEXDUMP(&x, siz)); 
    va_end(va); 
} 

int main(void) 
{ 
    struct xx10 aa = { {1,2,3,4,5,[9]=-1}}; 
    struct xx20 bb = { {[10]=1,2,3,4,5,[19]=-1}}; 
    struct xx1 cc = { {-1}}; 

    call(sizeof aa, aa); 
    call(sizeof bb, bb); 
    call(sizeof cc, cc); 
} 

Напечатает следующее (HEXDUMP() это одна из моих функций отладки, то очевидно, что он делает).

HEXDUMP: 
    0x7fff1f154160:01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ................ 
    0x7fff1f154170:05 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
    0x7fff1f154180:00 00 00 00 ff ff ff ff       ........ 

HEXDUMP: 
    0x7fff1f154160:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
    0x7fff1f154170:00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 
    0x7fff1f154180:00 00 00 00 00 00 00 00 01 00 00 00 02 00 00 00 ................ 
    0x7fff1f154190:03 00 00 00 04 00 00 00 05 00 00 00 00 00 00 00 ................ 
    0x7fff1f1541a0:00 00 00 00 00 00 00 00 00 00 00 00 ff ff ff ff ................ 

Испытано на Linux x86_64 скомпилированные с GCC 5.1 и Solaris SPARC9 скомпилирован с GCC 3.4

Я не знаю, если это полезно, но это может быть начало. Как видно, использование большого массива struct в функциях va_arg позволяет обрабатывать меньшие массивы, если размер известен. Но будьте осторожны, вероятно, он заполнен неопределенными поведением (например, если вы вызываете функцию с размером массива структуры меньше 4 int, она не работает в Linux x86_64, потому что структура передается по регистрам, а не как массив на стеке, но на встроенном процессоре он может работать).

+0

Это очень хороший пример, и да, это полезно – nowox

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