передайте строки ссылкой в C

Я предложил бы не, начиная со Строки. Формат не был разработан для конкатенации, это был дизайн для форматирования вывода различных исходных данных, таких как дата.

String s = String.Format("Today is {0:dd-MMM-yyyy}.", DateTime.Today);
8
задан 8 revs, 4 users 80% 8 December 2009 в 03:46
поделиться

8 ответов

First thing, those mallocs should be for strlen(whatever)+1 bytes. C strings have a 0 character to indicate the end, called the NUL terminator, and it isn't included in the length measured by strlen.

Next thing, strtok modifies the string you're searching. You are passing it a pointer to a string which you're not allowed to modify (you can't modify literal strings). That could be the cause of the segfault. So instead of using a pointer to the non-modifiable string literal, you could copy it to your own, modifiable buffer, like this:

char mybaz[] = "hello:world";

What this does is put a size 12 char array on the stack, and copy the bytes of the string literal into that array. It works because the compiler knows, at compile time, how long the string is, and can make space accordingly. This saves using malloc for that particular copy.

The problem you have with references is that you're currently passing the value of mybaz, myfoo, and mybar into your function. You can't modify the caller's variables unless you pass a pointer to myfoo and mybar. Since myfoo is a char*, a pointer to it is a char**:

void
fn(char *baz, char **foo, char **bar) // take pointers-to-pointers

*foo = malloc(...);  // set the value pointed to by foo

fn(mybaz, &myfoo, &mybar);  // pass pointers to myfoo and mybar

Modifying foo in the function in your code has absolutely no effect on myfoo. myfoo is uninitialised, so if neither of the first two things is causing it, the segfault is most likely occurring when you come to print using that uninitialised pointer.

Once you've got it basically working, you might want to add some error-handling. strtok can return NULL if it doesn't find the separator it's looking for, and you can't call strlen with NULL. malloc can return NULL if there isn't enough memory, and you can't call strcpy with NULL either.

10
ответ дан 5 December 2019 в 10:42
поделиться

Одна вещь, которую все упускают из виду, - это то, что вы вызываете strtok для массива, хранящегося в константной памяти. strtok записывает в массив, который вы передаете, поэтому убедитесь, что вы скопировали его во временный массив, прежде чем вызывать для него strtok, или просто выделите исходный, например:

char mybaz[] = "hello:world";
2
ответ дан 5 December 2019 в 10:42
поделиться

Ooh yes, little problem there.

As a rule, if you're going to be manipulating strings from inside a function, the storage for those strings had better be outside the function. The easy way to achieve this is to declare arrays outside the function (e.g. in main()) and to pass the arrays (which automatically become pointers to their beginnings) to the function. This works fine as long as your result strings don't overflow the space allocated in the arrays.

You've gone the more versatile but slightly more difficult route: You use malloc() to create space for your results (good so far!) and then try to assign the malloc'd space to the pointers you pass in. That, alas, will not work.

The pointer coming in is a value; you cannot change it. The solution is to pass a pointer to a pointer, and use it inside the function to change what the pointer is pointing to.

If you got that, great. If not, please ask for more clarification.

2
ответ дан 5 December 2019 в 10:42
поделиться

In C you typically pass by reference by passing 1) a pointer of the first element of the array, and 2) the length of the array.

The length of the array can be ommitted sometimes if you are sure about your buffer size, and one would know the length of the string by looking for a null terminated character (A character with the value of 0 or '\0'.

It seems from your code example though that you are trying to set the value of what a pointer points to. So you probably want a char** pointer. And you would pass in the address of your char* variable(s) that you want to set.

1
ответ дан 5 December 2019 в 10:42
поделиться

You're wanting to pass back 2 pointers. So you need to call it with a pair of pointers to pointers. Something like this:

void
fn(char *baz, char **foo, char **bar) {
   ...
   *foo = malloc( ... );
   ...
   *bar = malloc( ... );
   ...
}
1
ответ дан 5 December 2019 в 10:42
поделиться

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

Также вы передаете только указатель in. Поскольку указатель представляет собой 32-битное значение (на 32-битной машине), вы просто передаете значение унифицированного указателя в «fn». Точно так же вы не могли бы выразить целое число, переданное в функцию, которая должна быть возвращена вызывающей функции (без явного его возврата), вы не можете ожидать, что указатель сделает то же самое. Таким образом, новые значения указателя никогда не возвращаются обратно в основную функцию. Обычно вы делаете это, передавая указатель на указатель в C.

Также не забывайте освобождать динамически выделяемую память !!

void
fn(char *baz, char **foo, char **bar)
{
     char *pch;

     /* this is the part I'm having trouble with */

     pch = strtok (baz, ":");
     *foo = malloc(strlen(pch) + 1);
     strcpy(*foo, pch);

     pch = strtok (NULL, ":");
     *bar = malloc(strlen(pch) + 1);
     strcpy(*bar, pch);

     return;
}

int
main(void)
{
     char *mybaz, *myfoo, *mybar;

     mybaz = "hello:world";

     fn(mybaz, &myfoo, &mybar);

     fprintf(stderr, "%s %s", myfoo, mybar);

     free( myFoo );
     free( myBar );
}
0
ответ дан 5 December 2019 в 10:42
поделиться

В других ответах описывается, как исправить свой ответ работать, но простой способ выполнить то, что вы имели в виду , - это strdup (), которая выделяет новую память соответствующего размера и копирует в нее правильные символы.

Все еще нужно исправить дело с помощью char * vs char **, хотя. Это просто невозможно.

0
ответ дан 5 December 2019 в 10:42
поделиться

Существенная проблема заключается в том, что хотя хранилище когда-либо выделяется (с помощью malloc () ) для результатов, которые вы пытаетесь вернуть как myfoo и mybar , указатели на эти выделения фактически не возвращаются в main () . В результате более поздний вызов printf () , скорее всего, приведет к сбросу ядра.

Решение состоит в том, чтобы объявить аргументы как ponter на указатель на char и передать адреса myfoo и mybar по fn . Что-то вроде этого (непроверенного) должно помочь:

void
fn(char *baz, char **foo, char **bar)
{
     char *pch;

     /* this is the part I'm having trouble with */

     pch = strtok (baz, ":");
     *foo = malloc(strlen(pch)+1);  /* include space for NUL termination */
     strcpy(*foo, pch);

     pch = strtok (NULL, ":");
     *bar = malloc(strlen(pch)+1);  /* include space for NUL termination */
     strcpy(*bar, pch);

     return;
}

int
main(void)
{
     char mybaz[] = "hello:world";
     char *myfoo, *mybar;

     fn(mybaz, &myfoo, &mybar);
     fprintf(stderr, "%s %s", myfoo, mybar);
     free(myfoo);
     free(mybar);
}

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

Чтобы выполнить и malloc (), и strcpy () за один вызов, было бы лучше использовать strdup () , так как он также запоминает, что нужно выделить место для завершающего NUL, которое вы оставили в написанном коде. * foo = strdup (pch) намного понятнее и проще в обслуживании, чем альтернатива. Поскольку strdup () - это POSIX, а не ANSI C, вам, возможно, придется реализовать его самостоятельно, но эти усилия окупаются за счет ясности этого вида использования.

Другой традиционный способ возврата строка из функции C предназначена для вызывающего абонента, чтобы выделить память и предоставить свой адрес функции. Это метод, используемый, например, sprintf () . Он страдает от проблемы, заключающейся в том, что нет способа сделать такой сайт вызова полностью безопасным от ошибок переполнения буфера, вызванных вызываемой функцией, предполагая, что было выделено больше места, чем фактически доступно. Традиционное решение этой проблемы состоит в том, чтобы потребовать, чтобы также был передан аргумент длины буфера, и тщательно проверить как фактическое выделение, так и длину, заявленную на сайте вызова при проверке кода.

Изменить:

Фактический segfault, который вы , скорее всего, находятся внутри strtok () , а не printf () , потому что ваш образец, как написано, пытается передать строковую константу в strtok () , который должна иметь возможность изменять строку. Официально это Undefined Behavior.

Чтобы устранить эту проблему, убедитесь, что bybaz объявлен как инициализированный массив, а не как указатель на char . Инициализированный массив будет расположен в доступной для записи памяти, а строковая константа, скорее всего, будет находиться в доступной только для чтения памяти. Во многих случаях строковые константы хранятся в той же части памяти, которая используется для хранения самого исполняемого кода, и все современные системы пытаются усложнить программе изменение собственного выполняемого кода.

Во встроенных системах я работаю если заработать себе на жизнь, код, скорее всего, будет храниться в каком-либо ПЗУ и не может быть физически изменен.

0
ответ дан 5 December 2019 в 10:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: