#c #void-pointers
#c #void-указатели
Вопрос:
Я хочу передать массив указателей void в функцию, которая заполняет его результатом, с которым он работает нормально, integer
но у меня проблемы с возвратом char*
int function(void *arr[]);
int main(void) {
void *arr[3];
function(arr);
printf("%dn", *(int *)amp;arr[0]);
printf("%dn", *(int *)amp;arr[1]);
printf("%sn", (char *)amp;arr[2]);
}
int function(void *arr[]){
arr[0] = (void*)(int)4;
arr[1] = (void*)(int)6;
char* test = "abc";
arr[2] = (void*)test;
return 0;
}
для строки я не получаю правильное значение
Комментарии:
1. «Я хочу передать массив указателей void в функцию, которая заполняет его результатом» — почему ?!
2.
integer
не является типом иsizeof(int)
не обязательноsizeof(void*)
.3. @Dai это всего лишь пример, я хочу передать строку sql в функцию, и sql определяет, сколько значений результата я получу.
4. Какое отношение SQL имеет к злоупотреблению
void*
?5. Вы кодируете на языке с неопределенным поведением . «Работает» на самом деле имеет мало общего с корректностью кода. Вы можете просто
(int)arr[0]
, и это также будет «работать», а также будет правильным.
Ответ №1:
arr
имеет тип void(*)[3]
.
arr[0]
имеет тип void*
. Тот факт, что он хранит соответствующим образом преобразованное значение 4
, не имеет значения.
amp;arr[0]
имеет тип void**
.
(int *)amp;arr[0]
имеет тип int*
, но он указывает на объект типа void*
вместо того, чтобы указывать на объект типа int
. Это не то, что обычно делают указатели. У вас может быть такой указатель, но единственное, что вы можете сделать с ним по закону, это преобразовать его обратно в правильный тип, в данном случае void**
. Вы этого не делаете.
*(int *)amp;arr[0]
имеет тип int
.
Здесь вы получаете доступ к объекту типа void*
через значение lvalue типа int
. Это неопределенное поведение. Не делайте этого.
Если вы хотите преобразовать arr[0]
обратно int
, просто сделайте это:
printf("%dn", (int)arr[0]);
Аналогично, если вы хотите преобразовать arr[2]
обратно в char*
, сделайте именно это:
printf("%sn", (char*)arr[2]);
Ответ №2:
Вы можете передать массив void
s и назначить элементы этого массива динамически выделяемой области памяти, в которой хранится указанное значение.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int function(void *arr[]){
arr[0] = malloc(sizeof(int));
if (arr[0] == NULL) goto ERR_MALLOC_0;
*(int*)arr[0] = 4;
arr[1] = malloc(sizeof(int));
if (arr[1] == NULL) goto ERR_MALLOC_1;
*(int*)arr[1] = 6;
const char *const test = "abc";
arr[2] = malloc(strlen(test));
if (arr[2] == NULL) goto ERR_MALLOC_2;
strcpy(arr[2], test);
return 0;
// remember about error checking
free(arr[2]);
ERR_MALLOC_2:
free(arr[1]);
ERR_MALLOC_1:
free(arr[0]);
ERR_MALLOC_0:
return -1;
}
int main(void) {
void *arr[3];
int err = function(arr);
if (err == -1) abort();
// amp; is the address of element, not it's element
printf("%dn", *(int*)arr[0]);
printf("%dn", *(int*)arr[1]);
printf("%sn", (char*)arr[2]);
// remember to free memory
for (int i = 0; i < 3; i) {
// funny how we do not need to know the effective type
free(arr[i]);
}
}
Но такая функция просто сбивает с толку и приведет ко многим, многим ошибкам и проблемам. Вместо этого просто используйте переменные правильного типа:
#include <stdlib.h>
#include <string.h>
int function(int *val1, int *val2, char **string) {
*val1 = 3;
*val2 = 6;
const char *test = "abc";
*string = malloc(strlen(test));
if (*string == NULL) return -1;
strcpy(*string, test);
return 0;
}
int main(void) {
int val1;
int val2;
char *string;
int err = function(amp;val1, amp;val2, amp;string);
if (err == -1) abort();
printf("%dn", val1);
printf("%dn", val2);
printf("%sn", string);
free(string);
}
Если вы действительно стремитесь реализовать какое-то виртуальное представление и операции с разными типами данных, использование простого «массива void
указателей» ни к чему вас не приведет, потому что такой массив не знает, каков базовый тип значения, которое хранится — вы, как программист, должны знать, что внутриэтот массив (т.е. это arr[0]
int
и arr[2]
есть a char*
), поэтому, поскольку вы это знаете, вы могли бы с таким же успехом просто использовать переменные правильных типов с самого начала.
Комментарии:
1. «итак, поскольку вы это знаете, вы могли бы с таким же успехом просто использовать переменные правильных типов с самого начала». — и когда вы этого не сделаете, вам нужно использовать тип tagged-union .
2. Спасибо! может быть, я использую array of
int*
и array ofchar*
, потому что это единственные типы, которые мне нужны. Но количество результатов для каждого типа не определено.3.
and when you don't you need to use a tagged-union type
как это сделал бы программист среднего уровня, что приводит к тому, что я называю big fat switched , которые трудно поддерживать, рассуждать и очень сложно реорганизовать. Лучший дизайн , который будет работать годами, — это создание интерфейса с правильной таблицей отправки . В любом случае, дизайн всегда зависит.
Ответ №3:
arr[0] = (void*)(int)4;
Приведенная выше строка преобразует целое число 4 в указатель void (адрес) и сохраняет его в массиве arr
как адрес указателя. Я думаю, это не то, что вы хотели.
Чтобы получить доступ к данным с помощью указателя void так, как вы хотите, сначала сделайте так, чтобы он указывал на действительный адрес памяти, который будет содержать данные при выходе из функции. Вы должны быть осторожны при назначении адреса памяти указателю void внутри функции, все переменные в стеке этой функции (локальные переменные) будут удалены из стека при выходе из функции, что делает эти адреса памяти недействительными. Используйте только статическую (или глобальную) или динамическую память при назначении адреса памяти вашему указателю внутри функции, если вы хотите использовать его вне функции.