Страницы

Поиск по вопросам

пятница, 10 января 2020 г.

Передача массива из функции

#c #массивы #функции #указатели


Бинарный файл составляю из 2 source-файлов, в одном основной код, в другой функции
генерации ssl-сертификата.
Функция формирования сертификата (дополнительные функции не привожу) из второго файла.

int create_cert(unsigned char *buf, int *len) {
 BIO *bio_err;
 X509 *x509=NULL;
 EVP_PKEY *pkey=NULL;
 unsigned char *p;
 int i;

 CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON);
 bio_err=BIO_new_fp(stderr,BIO_NOCLOSE);
 mkcert(&x509,&pkey,2048,0,1490);

 *len=i2d_X509(x509,NULL);
 buf=malloc(*len);
 if (buf==NULL) return -1;
 p=buf;
 i2d_X509(x509,&p);

 X509_free(x509);
 EVP_PKEY_free(pkey);
 #ifndef OPENSSL_NO_ENGINE
  ENGINE_cleanup();
 #endif
 CRYPTO_cleanup_all_ex_data();
 CRYPTO_mem_leaks(bio_err);
 BIO_free(bio_err);

 return 1;
}


Тут я генерирую сертификат средствами openssl, с помощью i2d_X509 перевожу в DER-формат
и записываю в память под указателем buf (а так же размер массива сертификата), который
должен возвратиться туда где я вызываю эту функцию (т.е. в основной файл).

А вот главная функция первого файла.

int main(void) {
 char Cert;
 unsigned char *cbCertificate;
 int certLen;

 create_cert(&cbCertificate,&certLen);
}


Тут я принимаю указатель и размер массива сертификата с предыдущей функции. Все никак
не могу понять как можно взять массив из памяти и присвоить его в переменную Cert,
чтобы после передать другой функции.
Я попытался записать сертификат в файл с помощью

  FILE *fp=fopen("cert.cer","w");
  if (fp!=NULL) {
   fwrite(&cbCertificate,1,certLen,fp);
   fclose(fp);
  }


Записывается всякая хрень. Я понимаю что cbCertificate это указатель на указатель.
Я понимаю как можно взять адрес указателя, но вот к данным получить доступ не понимаю как.
    


Ответы

Ответ 1



Если я правильно Вас поняла, и Вы хотите вернуть из внутренней функции наружу выделенный внутри нее массив, используя в качестве параметра этой функции двойной указатель, то, для начала, нужно описать в параметрах этой функции двойной указатель: int create_cert(unsigned char **buf, int *len){...} Далее, выделять память под массив Вам нужно с учетом, что вы используете двойной указатель (Вы аналогично уже работаете с len): *buf = (unsigned char *)malloc(*len); Тогда, после вывова функции create_cert() в cbCertificate будет лежать указатель на новый, выделенный внутри create_cert() массив. Все никак не могу понять как можно взять массив из памяти и присвоить его в переменную Cert Так, как у Вас описано, это точно не получится, так как переменная Cert у вас имеет тип char, - т.е. 1 элемент. Нельзя одному элементу типа char присвоить весь массив. Если же Вы, после того, как получили с помощью функции create_cert() данные, хотите записать их в файл, то запись будет выглядеть вот так : fwrite(cbCertificate,1,certLen,fp); так как unsigned char *cbCertificate; это не двойной, а обычный указатель. Вот когда вы берете его адрес, как здесь : create_cert(&cbCertificate,&certLen); внутри функции create_cert() получается двойной указатель. UPD: Как выглядят двойные указатели: Объявление : int **cbCertificate; Тогда инициализация такого указателя может выглядеть вот так : int a = 0; int *ptr = &a; int **cbCertificate = &ptr; И передавать такой двойной указатель в функцию можно будет вот так : void foo(int ** pptr){...} ... int main(){ int a = 0; int *ptr = &a; int **cbCertificate = &ptr; foo(cbCertificate); } Если же двойной указатель нам нужен только для того, чтобы вернуть из функции адрес выделенного внутри этой функции буффера, то можно обойтись использованием двойного указателя только на стеке функции, тогда вышеприведенный пример можно записать так : void foo(int ** pptr){...} ... int main(){ int a = 0; int *ptr = &a; foo(&ptr); } Двойной указатель является не чем иным, как адресом некоторого указателя, следовательно, работать с самим указателем и данными внутри функции foo() можно следующим образом: void foo(int **pptr){ ... *pptr = NULL; // инициализация указателя, адрес котрого хранится в pptr //(pptr все еще хранит значение, указывающее на некоторую ячейку памяти, //а вот эта самая память уже никуда не указывает) *pptr = (int *)malloc(10*sizeof(int)); // ячейка, на которую указывает // pptr, указывает на массив из 10 значений типа int (*pptr)[0] = 5; // инициализируем первый элемент выделенного массива ... }

Ответ 2



если грубо, то вы передаёте в fwrite() адрес начала блока данных, и, начиная с этого адреса, вы вычитываете 1*certLen байт (точнее читаются, а потом пишутся в файл, но не суть). unsigned char *cbCertificate; вы уже объявли переменную как указатель. Т.е. после: create_cert(&cbCertificate,&certLen); значение cbCertificate - адрес начала блока памяти, где лежат какие-то данные (сертификат в вашем случае). В общем случае - это просто число. Когда вы делаете так: &cbCertificate вы получаете новый адрес - адрес переменной в которой хранится значение (число) адреса вашего буфера. Естественно, что когда вы передаёте его в таком виде в fwrite(), то вы передаёте не адрес начала буффера данных, а адрес переменной где хранится этот самый буффер. Размер адреса 4 или 8 байт или... зависит от архитектуры - 4 - 32бит, 8 - 64бит (не догма!). Т.е. тот мусор, который вы записали состоит или 4 или 8 первых байт - адреса буффера с данными, а потом просто что-то, что просто лежит в памяти за этой переменной. Т.е. в вашем случае должно быть просто: fwrite(cbCertificate, 1, certLen, fp); если, конечно, вы ничего не напутали в create_cert() :-) UPD: хотя ошибка в create_cert() есть тоже. Вы хотите из него получить адрес указателя и его размер. Т.е. вам нужно модифицировать переменную, которая хранит значение адреса. Таким образом, в create_cert() нужно передавать указатель на указатель: int create_cert(unsigned char **buf, int *len) Тогда аллоцировать память нужно так: *buf = malloc(*len); if (*buf == NULL) return -1; p = *buf; А вот вызов такой и останется: create_cert(&cbCertificate,&certLen); А вообще, рекомендую включить предупреждения компилятора, типа -Wall -Wextra для GCC. Они много подобных нюансов ловят и дают предупреждения (отсюда и правило: хороший код - без ворнингов).

Ответ 3



Ваша программа некорректна. Функция create_cert объявлена, как имеющаяя первый параметр с типом unsigned char *: int create_cert(unsigned char *buf, int *len); Вы же вызываете ее, передавая аргумент, имеющий тип unsigned char **: unsigned char *cbCertificate; //... create_cert(&cbCertificate,&certLen); ^^^^^^^^^^^^^^ Поэтому ваша программа имеет неопределенное поведение Функция следует объявлена как int create_cert(unsigned char **buf, int *len); и функции выделять память для buf как *buf = malloc( *len ); Также вызывает большие сомнения предложения p=buf; i2d_X509(x509,&p); в этой функции. Что касается вашего вопроса Все никак не могу понять как можно взять массив из памяти и присвоить его в переменную Cert то переменная Cert у вас объявлена, как имеющая тип char int main(void) { char Cert; //... вы не можете присвоить ей массив. И более того массивы не имеют оператора присваивания. Либо вы объявляете эту переменную как указатель char * и присваиваете ей адрес первого элемента массива, с которым собираетесь иметь дело. Либо выделяете память, равную памяти, занимаемой массивом, с помощью функции malloc И копируете туда массив, например, с помощью функции memcpy

Ответ 4



int create_cert(unsigned char **buf, int *len) { BIO *bio_err; X509 *x509=NULL; EVP_PKEY *pkey=NULL; unsigned char *p; int i; CRYPTO_mem_ctrl(CRYPTO_MEM_CHECK_ON); bio_err=BIO_new_fp(stderr,BIO_NOCLOSE); mkcert(&x509,&pkey,2048,0,1490); *len=i2d_X509(x509,NULL); *buf=malloc(*len); if (*buf==NULL) return -1; p=*buf; i2d_X509(x509,&p); X509_free(x509); EVP_PKEY_free(pkey); #ifndef OPENSSL_NO_ENGINE ENGINE_cleanup(); #endif CRYPTO_cleanup_all_ex_data(); CRYPTO_mem_leaks(bio_err); BIO_free(bio_err); return 1; } int main(void) { char Cert; unsigned char *cbCertificate; int certLen; create_cert(&cbCertificate,&certLen); FILE *fp=fopen("cert.cer","w"); if (fp!=NULL) { fwrite(cbCertificate,1,certLen,fp); fclose(fp); } }

Комментариев нет:

Отправить комментарий